mirror of
https://github.com/docmost/docmost.git
synced 2026-05-07 06:23:06 +08:00
fix: public sharing performance improvements (#1841)
This commit is contained in:
@@ -269,12 +269,15 @@ function Node({ node, style, dragHandle, tree }: NodeRendererProps<any>) {
|
|||||||
const toggleMobileSidebar = useToggleSidebar(mobileSidebarAtom);
|
const toggleMobileSidebar = useToggleSidebar(mobileSidebarAtom);
|
||||||
|
|
||||||
const prefetchPage = () => {
|
const prefetchPage = () => {
|
||||||
timerRef.current = setTimeout(() => {
|
timerRef.current = setTimeout(async () => {
|
||||||
queryClient.prefetchQuery({
|
const page = await queryClient.fetchQuery({
|
||||||
queryKey: ["pages", node.data.slugId],
|
queryKey: ["pages", node.data.id],
|
||||||
queryFn: () => getPageById({ pageId: node.data.slugId }),
|
queryFn: () => getPageById({ pageId: node.data.id }),
|
||||||
staleTime: 5 * 60 * 1000,
|
staleTime: 5 * 60 * 1000,
|
||||||
});
|
});
|
||||||
|
if (page?.slugId) {
|
||||||
|
queryClient.setQueryData(["pages", page.slugId], page);
|
||||||
|
}
|
||||||
}, 150);
|
}, 150);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
Tooltip,
|
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { IconExternalLink, IconWorld, IconLock } from "@tabler/icons-react";
|
import { IconExternalLink, IconWorld, IconLock } from "@tabler/icons-react";
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
@@ -21,12 +20,12 @@ import {
|
|||||||
import { Link, useNavigate, useParams } from "react-router-dom";
|
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||||
import { extractPageSlugId, getPageIcon } from "@/lib";
|
import { extractPageSlugId, getPageIcon } from "@/lib";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { usePageQuery } from "@/features/page/queries/page-query.ts";
|
||||||
import CopyTextButton from "@/components/common/copy.tsx";
|
import CopyTextButton from "@/components/common/copy.tsx";
|
||||||
import { getAppUrl, isCloud } from "@/lib/config.ts";
|
import { getAppUrl, isCloud } from "@/lib/config.ts";
|
||||||
import { buildPageUrl } from "@/features/page/page.utils.ts";
|
import { buildPageUrl } from "@/features/page/page.utils.ts";
|
||||||
import classes from "@/features/share/components/share.module.css";
|
import classes from "@/features/share/components/share.module.css";
|
||||||
import useTrial from "@/ee/hooks/use-trial.tsx";
|
import useTrial from "@/ee/hooks/use-trial.tsx";
|
||||||
import { getCheckoutLink } from "@/ee/billing/services/billing-service.ts";
|
|
||||||
|
|
||||||
interface ShareModalProps {
|
interface ShareModalProps {
|
||||||
readOnly: boolean;
|
readOnly: boolean;
|
||||||
@@ -35,7 +34,9 @@ export default function ShareModal({ readOnly }: ShareModalProps) {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { pageSlug } = useParams();
|
const { pageSlug } = useParams();
|
||||||
const pageId = extractPageSlugId(pageSlug);
|
const pageSlugId = extractPageSlugId(pageSlug);
|
||||||
|
const { data: page } = usePageQuery({ pageId: pageSlugId });
|
||||||
|
const pageId = page?.id;
|
||||||
const { data: share } = useShareForPageQuery(pageId);
|
const { data: share } = useShareForPageQuery(pageId);
|
||||||
const { spaceSlug } = useParams();
|
const { spaceSlug } = useParams();
|
||||||
const { isTrial } = useTrial();
|
const { isTrial } = useTrial();
|
||||||
|
|||||||
@@ -27,9 +27,7 @@ import {
|
|||||||
getShares,
|
getShares,
|
||||||
updateShare,
|
updateShare,
|
||||||
} from "@/features/share/services/share-service.ts";
|
} from "@/features/share/services/share-service.ts";
|
||||||
import { IPage } from "@/features/page/types/page.types.ts";
|
|
||||||
import { IPagination, QueryParams } from "@/lib/types.ts";
|
import { IPagination, QueryParams } from "@/lib/types.ts";
|
||||||
import { useEffect } from "react";
|
|
||||||
|
|
||||||
export function useGetSharesQuery(
|
export function useGetSharesQuery(
|
||||||
params?: QueryParams,
|
params?: QueryParams,
|
||||||
@@ -72,7 +70,7 @@ export function useShareForPageQuery(
|
|||||||
queryKey: ["share-for-page", pageId],
|
queryKey: ["share-for-page", pageId],
|
||||||
queryFn: () => getShareForPage(pageId),
|
queryFn: () => getShareForPage(pageId),
|
||||||
enabled: !!pageId,
|
enabled: !!pageId,
|
||||||
staleTime: 0,
|
staleTime: 60 * 1000,
|
||||||
retry: false,
|
retry: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -123,80 +123,82 @@ export class ShareService {
|
|||||||
.withRecursive('page_hierarchy', (cte) =>
|
.withRecursive('page_hierarchy', (cte) =>
|
||||||
cte
|
cte
|
||||||
.selectFrom('pages')
|
.selectFrom('pages')
|
||||||
|
.leftJoin('shares', 'shares.pageId', 'pages.id')
|
||||||
.select([
|
.select([
|
||||||
'id',
|
'pages.id',
|
||||||
'slugId',
|
'pages.slugId',
|
||||||
'pages.title',
|
'pages.title',
|
||||||
'pages.icon',
|
'pages.icon',
|
||||||
'parentPageId',
|
'pages.parentPageId',
|
||||||
sql`0`.as('level'),
|
sql`0`.as('level'),
|
||||||
|
'shares.id as shareId',
|
||||||
|
'shares.key as shareKey',
|
||||||
|
'shares.includeSubPages',
|
||||||
|
'shares.searchIndexing',
|
||||||
|
'shares.creatorId',
|
||||||
|
'shares.spaceId',
|
||||||
|
'shares.workspaceId',
|
||||||
|
'shares.createdAt',
|
||||||
])
|
])
|
||||||
.where(isValidUUID(pageId) ? 'id' : 'slugId', '=', pageId)
|
.where(isValidUUID(pageId) ? 'pages.id' : 'pages.slugId', '=', pageId)
|
||||||
.where('deletedAt', 'is', null)
|
.where('pages.deletedAt', 'is', null)
|
||||||
.unionAll((union) =>
|
.unionAll(
|
||||||
union
|
(union) =>
|
||||||
.selectFrom('pages as p')
|
union
|
||||||
.select([
|
.selectFrom('pages as p')
|
||||||
'p.id',
|
.innerJoin('page_hierarchy as ph', 'ph.parentPageId', 'p.id')
|
||||||
'p.slugId',
|
.leftJoin('shares as s', 's.pageId', 'p.id')
|
||||||
'p.title',
|
.select([
|
||||||
'p.icon',
|
'p.id',
|
||||||
'p.parentPageId',
|
'p.slugId',
|
||||||
// Increase the level by 1 for each ancestor.
|
'p.title',
|
||||||
sql`ph.level + 1`.as('level'),
|
'p.icon',
|
||||||
])
|
'p.parentPageId',
|
||||||
.innerJoin('page_hierarchy as ph', 'ph.parentPageId', 'p.id')
|
sql`ph.level + 1`.as('level'),
|
||||||
.where('p.deletedAt', 'is', null),
|
's.id as shareId',
|
||||||
|
's.key as shareKey',
|
||||||
|
's.includeSubPages',
|
||||||
|
's.searchIndexing',
|
||||||
|
's.creatorId',
|
||||||
|
's.spaceId',
|
||||||
|
's.workspaceId',
|
||||||
|
's.createdAt',
|
||||||
|
])
|
||||||
|
.where('p.deletedAt', 'is', null)
|
||||||
|
.where(sql`ph.share_id`, 'is', null) // stop if share found
|
||||||
|
.where(sql`ph.level`, '<', sql`25`), // prevent loop
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.selectFrom('page_hierarchy')
|
.selectFrom('page_hierarchy')
|
||||||
.leftJoin('shares', 'shares.pageId', 'page_hierarchy.id')
|
.selectAll()
|
||||||
.select([
|
.where('shareId', 'is not', null)
|
||||||
'page_hierarchy.id as sharedPageId',
|
.limit(1)
|
||||||
'page_hierarchy.slugId as sharedPageSlugId',
|
|
||||||
'page_hierarchy.title as sharedPageTitle',
|
|
||||||
'page_hierarchy.icon as sharedPageIcon',
|
|
||||||
'page_hierarchy.level as level',
|
|
||||||
'shares.id',
|
|
||||||
'shares.key',
|
|
||||||
'shares.pageId',
|
|
||||||
'shares.includeSubPages',
|
|
||||||
'shares.searchIndexing',
|
|
||||||
'shares.creatorId',
|
|
||||||
'shares.spaceId',
|
|
||||||
'shares.workspaceId',
|
|
||||||
'shares.createdAt',
|
|
||||||
'shares.updatedAt',
|
|
||||||
])
|
|
||||||
.where('shares.id', 'is not', null)
|
|
||||||
.orderBy('page_hierarchy.level', 'asc')
|
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
|
|
||||||
if (!share || share.workspaceId != workspaceId) {
|
if (!share || share.workspaceId !== workspaceId) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (share.level === 1 && !share.includeSubPages) {
|
if ((share.level as number) > 0 && !share.includeSubPages) {
|
||||||
// we can only show a page if its shared ancestor permits it
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: share.id,
|
id: share.shareId,
|
||||||
key: share.key,
|
key: share.shareKey,
|
||||||
includeSubPages: share.includeSubPages,
|
includeSubPages: share.includeSubPages,
|
||||||
searchIndexing: share.searchIndexing,
|
searchIndexing: share.searchIndexing,
|
||||||
pageId: share.pageId,
|
pageId: share.id,
|
||||||
creatorId: share.creatorId,
|
creatorId: share.creatorId,
|
||||||
spaceId: share.spaceId,
|
spaceId: share.spaceId,
|
||||||
workspaceId: share.workspaceId,
|
workspaceId: share.workspaceId,
|
||||||
createdAt: share.createdAt,
|
createdAt: share.createdAt,
|
||||||
level: share.level,
|
level: share.level,
|
||||||
sharedPage: {
|
sharedPage: {
|
||||||
id: share.sharedPageId,
|
id: share.id,
|
||||||
slugId: share.sharedPageSlugId,
|
slugId: share.slugId,
|
||||||
title: share.sharedPageTitle,
|
title: share.title,
|
||||||
icon: share.sharedPageIcon,
|
icon: share.icon,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user