From f74688663d410bab311190ab0326ea5a084c0062 Mon Sep 17 00:00:00 2001
From: Philipinho <16838612+Philipinho@users.noreply.github.com>
Date: Tue, 22 Apr 2025 15:10:05 +0100
Subject: [PATCH] WIP
---
.../public/locales/en-US/translation.json | 5 ++-
apps/client/src/App.tsx | 4 ++-
.../src/features/share/queries/share-query.ts | 28 +++++++++++++--
.../features/share/services/share-service.ts | 13 +++++--
.../src/features/share/types/share.types.ts | 15 ++++----
.../client/src/pages/share/share-redirect.tsx | 35 +++++++++++++++++++
apps/client/src/pages/share/shared-page.tsx | 4 +--
.../server/src/core/share/share.controller.ts | 4 ++-
.../src/database/repos/share/share.repo.ts | 26 +++++++++++++-
9 files changed, 116 insertions(+), 18 deletions(-)
create mode 100644 apps/client/src/pages/share/share-redirect.tsx
diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json
index fde67ecf..0746ed15 100644
--- a/apps/client/public/locales/en-US/translation.json
+++ b/apps/client/public/locales/en-US/translation.json
@@ -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"
}
diff --git a/apps/client/src/App.tsx b/apps/client/src/App.tsx
index 6bc5a778..cff59009 100644
--- a/apps/client/src/App.tsx
+++ b/apps/client/src/App.tsx
@@ -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();
@@ -58,7 +59,8 @@ export default function App() {
} />
} />
-
+
+ } />
} />
}>
diff --git a/apps/client/src/features/share/queries/share-query.ts b/apps/client/src/features/share/queries/share-query.ts
index 194d50ea..dea047bf 100644
--- a/apps/client/src/features/share/queries/share-query.ts
+++ b/apps/client/src/features/share/queries/share-query.ts
@@ -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 {
+ const query = useQuery({
+ queryKey: ["share-by-id", shareId],
+ queryFn: () => getShareInfo(shareId),
+ enabled: !!shareId,
+ });
+
+ return query;
+}
+
+export function useSharePageQuery(
shareInput: Partial,
): UseQueryResult {
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({
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",
});
},
diff --git a/apps/client/src/features/share/services/share-service.ts b/apps/client/src/features/share/services/share-service.ts
index e810f198..2f43ba20 100644
--- a/apps/client/src/features/share/services/share-service.ts
+++ b/apps/client/src/features/share/services/share-service.ts
@@ -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 {
return req.data;
}
+export async function getShareInfo(shareId: string): Promise {
+ const req = await api.post("/shares/info", { shareId });
+ return req.data;
+}
+
export async function updateShare(data: IUpdateShare): Promise {
const req = await api.post("/shares/update", data);
return req.data;
@@ -33,7 +40,7 @@ export async function getShareForPage(pageId: string): Promise {
return req.data;
}
-export async function getShareInfo(
+export async function getSharePageInfo(
shareInput: Partial,
): Promise {
const req = await api.post("/shares/page-info", shareInput);
diff --git a/apps/client/src/features/share/types/share.types.ts b/apps/client/src/features/share/types/share.types.ts
index 8163fa06..c40801e8 100644
--- a/apps/client/src/features/share/types/share.types.ts
+++ b/apps/client/src/features/share/types/share.types.ts
@@ -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: {
- id: string;
- slugId: string;
- title: string;
- icon: string;
- };
+ sharedPage: ISharePage;
+}
+
+interface ISharePage {
+ id: string;
+ slugId: string;
+ title: string;
+ icon: string;
}
export interface ICreateShare {
diff --git a/apps/client/src/pages/share/share-redirect.tsx b/apps/client/src/pages/share/share-redirect.tsx
new file mode 100644
index 00000000..5653e83f
--- /dev/null
+++ b/apps/client/src/pages/share/share-redirect.tsx
@@ -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 ;
+ }
+
+ if (isLoading) {
+ return <>>;
+ }
+
+ return null;
+}
diff --git a/apps/client/src/pages/share/shared-page.tsx b/apps/client/src/pages/share/shared-page.tsx
index 93493475..8cd69836 100644
--- a/apps/client/src/pages/share/shared-page.tsx
+++ b/apps/client/src/pages/share/shared-page.tsx
@@ -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),
});
diff --git a/apps/server/src/core/share/share.controller.ts b/apps/server/src/core/share/share.controller.ts
index 11fef67e..6ea13799 100644
--- a/apps/server/src/core/share/share.controller.ts
+++ b/apps/server/src/core/share/share.controller.ts
@@ -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');
diff --git a/apps/server/src/database/repos/share/share.repo.ts b/apps/server/src/database/repos/share/share.repo.ts
index 44e8cd33..bcdd7474 100644
--- a/apps/server/src/database/repos/share/share.repo.ts
+++ b/apps/server/src/database/repos/share/share.repo.ts
@@ -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) {
+ return jsonObjectFrom(
+ eb
+ .selectFrom('pages')
+ .select([
+ 'pages.id',
+ 'pages.slugId',
+ 'pages.title',
+ 'pages.icon',
+ 'pages.parentPageId',
+ ])
+ .whereRef('pages.id', '=', 'shares.pageId'),
+ ).as('sharedPage');
+ }
}