This commit is contained in:
Philipinho
2025-04-22 15:10:05 +01:00
parent a66fb875ac
commit f74688663d
9 changed files with 116 additions and 18 deletions

View File

@ -380,5 +380,8 @@
"Delete public share link": "Delete public share link",
"Delete share": "Delete share",
"Are you sure you want to delete this shared link?": "Are you sure you want to delete this shared link?",
"Publicly shared pages from spaces you are a member of will appear here": "Publicly shared pages from spaces you are a member of will appear here"
"Publicly shared pages from spaces you are a member of will appear here": "Publicly shared pages from spaces you are a member of will appear here",
"Share deleted successfully": "Share deleted successfully",
"Share not found": "Share not found",
"Failed to share page": "Failed to share page"
}

View File

@ -29,6 +29,7 @@ import { useRedirectToCloudSelect } from "@/ee/hooks/use-redirect-to-cloud-selec
import SharedPage from "@/pages/share/shared-page.tsx";
import Shares from "@/pages/settings/shares/shares.tsx";
import ShareLayout from "@/features/share/components/share-layout.tsx";
import ShareRedirect from '@/pages/share/share-redirect.tsx';
export default function App() {
const { t } = useTranslation();
@ -59,6 +60,7 @@ export default function App() {
<Route path={"/share/p/:pageSlug"} element={<SharedPage />} />
</Route>
<Route path={"/share/:shareId"} element={<ShareRedirect />} />
<Route path={"/p/:pageSlug"} element={<PageRedirect />} />
<Route element={<Layout />}>

View File

@ -9,6 +9,7 @@ import { notifications } from "@mantine/notifications";
import { useTranslation } from "react-i18next";
import {
ICreateShare,
IShare,
ISharedItem,
ISharedPage,
ISharedPageTree,
@ -22,6 +23,7 @@ import {
getSharedPageTree,
getShareForPage,
getShareInfo,
getSharePageInfo,
getShares,
updateShare,
} from "@/features/share/services/share-service.ts";
@ -39,12 +41,24 @@ export function useGetSharesQuery(
});
}
export function useShareQuery(
export function useGetShareByIdQuery(
shareId: string,
): UseQueryResult<IShare, Error> {
const query = useQuery({
queryKey: ["share-by-id", shareId],
queryFn: () => getShareInfo(shareId),
enabled: !!shareId,
});
return query;
}
export function useSharePageQuery(
shareInput: Partial<IShareInfoInput>,
): UseQueryResult<ISharedPage, Error> {
const query = useQuery({
queryKey: ["shares", shareInput],
queryFn: () => getShareInfo(shareInput),
queryFn: () => getSharePageInfo(shareInput),
enabled: !!shareInput.pageId,
});
@ -84,7 +98,9 @@ export function useCreateShareMutation() {
}
export function useUpdateShareMutation() {
const { t } = useTranslation();
const queryClient = useQueryClient();
return useMutation<any, Error, IUpdateShare>({
mutationFn: (data) => updateShare(data),
onSuccess: (data) => {
@ -99,10 +115,16 @@ export function useUpdateShareMutation() {
predicate: (item) =>
["share-for-page"].includes(item.queryKey[0] as string),
});
notifications.show({
message: t("Share not found"),
color: "red",
});
return;
}
notifications.show({
message: error?.["response"]?.data?.message || "Share share not found",
message: error?.["response"]?.data?.message || "Share not found",
color: "red",
});
},

View File

@ -3,12 +3,14 @@ import { IPage } from "@/features/page/types/page.types";
import {
ICreateShare,
ISharedItem, ISharedPage,
IShare,
ISharedItem,
ISharedPage,
ISharedPageTree,
IShareForPage,
IShareInfoInput,
IUpdateShare,
} from '@/features/share/types/share.types.ts';
} from "@/features/share/types/share.types.ts";
import { IPagination, QueryParams } from "@/lib/types.ts";
export async function getShares(
@ -23,6 +25,11 @@ export async function createShare(data: ICreateShare): Promise<any> {
return req.data;
}
export async function getShareInfo(shareId: string): Promise<IShare> {
const req = await api.post<IShare>("/shares/info", { shareId });
return req.data;
}
export async function updateShare(data: IUpdateShare): Promise<any> {
const req = await api.post<any>("/shares/update", data);
return req.data;
@ -33,7 +40,7 @@ export async function getShareForPage(pageId: string): Promise<IShareForPage> {
return req.data;
}
export async function getShareInfo(
export async function getSharePageInfo(
shareInput: Partial<IShareInfoInput>,
): Promise<ISharedPage> {
const req = await api.post<ISharedPage>("/shares/page-info", shareInput);

View File

@ -12,6 +12,7 @@ export interface IShare {
createdAt: string;
updatedAt: string;
deletedAt: string | null;
sharedPage?: ISharePage;
}
export interface ISharedItem extends IShare {
@ -44,12 +45,14 @@ export interface ISharedPage extends IShare {
export interface IShareForPage extends IShare {
level: number;
sharedPage: {
sharedPage: ISharePage;
}
interface ISharePage {
id: string;
slugId: string;
title: string;
icon: string;
};
}
export interface ICreateShare {

View File

@ -0,0 +1,35 @@
import { useNavigate, useParams } from "react-router-dom";
import { useEffect } from "react";
import { buildSharedPageUrl } from "@/features/page/page.utils.ts";
import { Error404 } from "@/components/ui/error-404.tsx";
import { useGetShareByIdQuery } from "@/features/share/queries/share-query.ts";
export default function ShareRedirect() {
const { shareId } = useParams();
const navigate = useNavigate();
const { data: share, isLoading, isError } = useGetShareByIdQuery(shareId);
useEffect(() => {
if (share) {
navigate(
buildSharedPageUrl({
shareId: share.key,
pageSlugId: share?.sharedPage.slugId,
pageTitle: share?.sharedPage.title,
}),
{ replace: true },
);
}
}, [isLoading, share]);
if (isError) {
return <Error404 />;
}
if (isLoading) {
return <></>;
}
return null;
}

View File

@ -1,7 +1,7 @@
import { useNavigate, useParams } from "react-router-dom";
import { Helmet } from "react-helmet-async";
import { useTranslation } from "react-i18next";
import { useShareQuery } from "@/features/share/queries/share-query.ts";
import { useSharePageQuery } from "@/features/share/queries/share-query.ts";
import { Container } from "@mantine/core";
import React, { useEffect } from "react";
import ReadonlyPageEditor from "@/features/editor/readonly-page-editor.tsx";
@ -14,7 +14,7 @@ export default function SingleSharedPage() {
const { shareId } = useParams();
const navigate = useNavigate();
const { data, isLoading, isError, error } = useShareQuery({
const { data, isLoading, isError, error } = useSharePageQuery({
pageId: extractPageSlugId(pageSlug),
});

View File

@ -67,7 +67,9 @@ export class ShareController {
@HttpCode(HttpStatus.OK)
@Post('/info')
async getShare(@Body() dto: ShareIdDto, @AuthUser() user: User) {
const share = await this.shareRepo.findById(dto.shareId);
const share = await this.shareRepo.findById(dto.shareId, {
includeSharedPage: true,
});
if (!share) {
throw new NotFoundException('Share not found');

View File

@ -39,6 +39,7 @@ export class ShareRepo {
async findById(
shareId: string,
opts?: {
includeSharedPage?: boolean;
includeCreator?: boolean;
withLock?: boolean;
trx?: KyselyTransaction;
@ -48,6 +49,10 @@ export class ShareRepo {
let query = db.selectFrom('shares').select(this.baseFields);
if (opts?.includeSharedPage) {
query = query.select((eb) => this.withSharedPage(eb));
}
if (opts?.includeCreator) {
query = query.select((eb) => this.withCreator(eb));
}
@ -98,7 +103,11 @@ export class ShareRepo {
return dbOrTx(this.db, trx)
.updateTable('shares')
.set({ ...updatableShare, updatedAt: new Date() })
.where(isValidUUID(shareId) ? 'id' : sql`LOWER(key)`, '=', shareId.toLowerCase())
.where(
isValidUUID(shareId) ? 'id' : sql`LOWER(key)`,
'=',
shareId.toLowerCase(),
)
.returning(this.baseFields)
.executeTakeFirst();
}
@ -215,4 +224,19 @@ export class ShareRepo {
.whereRef('users.id', '=', 'shares.creatorId'),
).as('creator');
}
withSharedPage(eb: ExpressionBuilder<DB, 'shares'>) {
return jsonObjectFrom(
eb
.selectFrom('pages')
.select([
'pages.id',
'pages.slugId',
'pages.title',
'pages.icon',
'pages.parentPageId',
])
.whereRef('pages.id', '=', 'shares.pageId'),
).as('sharedPage');
}
}