feat(core): add use guard hook (#11180)

Previously, we used `useLiveData(guardService.can$())` to get the guard result, but `guardService.can$()` will request the server to revalidate the permission when calling it, will cause additional network requests when re-render.

This pr make a new hook `useGuard` to fix this problem.

And the side effect in `can$` is moved to `revalidateCan()` to make that the subscribe method is pure
This commit is contained in:
EYHN
2025-03-25 14:15:29 +00:00
parent a10acf304b
commit 3df51a217d
28 changed files with 154 additions and 163 deletions
@@ -11,13 +11,13 @@ import {
DocPropertyRow,
} from '@affine/core/components/doc-properties';
import { CreatePropertyMenuItems } from '@affine/core/components/doc-properties/menu/create-doc-property';
import { useGuard } from '@affine/core/components/guard';
import { LinksRow } from '@affine/core/desktop/dialogs/doc-info/links-row';
import { TimeRow } from '@affine/core/desktop/dialogs/doc-info/time-row';
import type { DocCustomPropertyInfo } from '@affine/core/modules/db';
import { DocsService } from '@affine/core/modules/doc';
import { DocDatabaseBacklinkInfo } from '@affine/core/modules/doc-info';
import { DocsSearchService } from '@affine/core/modules/docs-search';
import { GuardService } from '@affine/core/modules/permissions';
import { useI18n } from '@affine/i18n';
import { PlusIcon } from '@blocksuite/icons/rc';
import { LiveData, useLiveData, useServices } from '@toeverything/infra';
@@ -31,17 +31,14 @@ export const DocInfoSheet = ({
docId: string;
defaultOpenProperty?: DefaultOpenProperty;
}) => {
const { docsSearchService, docsService, guardService } = useServices({
const { docsSearchService, docsService } = useServices({
DocsSearchService,
DocsService,
GuardService,
});
const t = useI18n();
const canEditPropertyInfo = useLiveData(
guardService.can$('Workspace_Properties_Update')
);
const canEditProperty = useLiveData(guardService.can$('Doc_Update', docId));
const canEditPropertyInfo = useGuard('Workspace_Properties_Update');
const canEditProperty = useGuard('Doc_Update', docId);
const links = useLiveData(
useMemo(
() => LiveData.from(docsSearchService.watchRefsFrom(docId), null),
@@ -1,5 +1,5 @@
import { Loading } from '@affine/component';
import { DocPermissionGuard } from '@affine/core/components/guard/doc-guard';
import { Guard } from '@affine/core/components/guard';
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
import { DocsService } from '@affine/core/modules/doc';
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
@@ -134,7 +134,7 @@ export const ExplorerDocNode = ({
operations={finalOperations}
data-testid={`explorer-doc-${docId}`}
>
<DocPermissionGuard docId={docId} permission="Doc_Read">
<Guard docId={docId} permission="Doc_Read">
{canRead =>
canRead
? children?.map((child, index) => (
@@ -146,8 +146,8 @@ export const ExplorerDocNode = ({
))
: null
}
</DocPermissionGuard>
<DocPermissionGuard docId={docId} permission="Doc_Update">
</Guard>
<Guard docId={docId} permission="Doc_Update">
{canEdit =>
canEdit ? (
<AddItemPlaceholder
@@ -156,7 +156,7 @@ export const ExplorerDocNode = ({
/>
) : null
}
</DocPermissionGuard>
</Guard>
</ExplorerTreeNode>
);
};
@@ -6,7 +6,7 @@ import {
useConfirmModal,
} from '@affine/component';
import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils';
import { DocPermissionGuard } from '@affine/core/components/guard/doc-guard';
import { Guard } from '@affine/core/components/guard';
import { useBlockSuiteMetaHelper } from '@affine/core/components/hooks/affine/use-block-suite-meta-helper';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
@@ -187,7 +187,7 @@ export const useExplorerDocNodeOperationsMenu = (
{
index: 10,
view: (
<DocPermissionGuard docId={docId} permission="Doc_Update">
<Guard docId={docId} permission="Doc_Update">
{canEdit => (
<DocRenameSubMenu
onConfirm={handleRename}
@@ -195,7 +195,7 @@ export const useExplorerDocNodeOperationsMenu = (
disabled={!canEdit}
/>
)}
</DocPermissionGuard>
</Guard>
),
},
{
@@ -224,7 +224,7 @@ export const useExplorerDocNodeOperationsMenu = (
{
index: 97,
view: (
<DocPermissionGuard docId={docId} permission="Doc_Update">
<Guard docId={docId} permission="Doc_Update">
{canEdit => (
<MenuItem
prefixIcon={<LinkedPageIcon />}
@@ -234,7 +234,7 @@ export const useExplorerDocNodeOperationsMenu = (
{t['com.affine.page-operation.add-linked-page']()}
</MenuItem>
)}
</DocPermissionGuard>
</Guard>
),
},
{
@@ -273,7 +273,7 @@ export const useExplorerDocNodeOperationsMenu = (
{
index: 10000,
view: (
<DocPermissionGuard docId={docId} permission="Doc_Trash">
<Guard docId={docId} permission="Doc_Trash">
{canMoveToTrash => (
<MenuItem
type={'danger'}
@@ -284,7 +284,7 @@ export const useExplorerDocNodeOperationsMenu = (
{t['com.affine.moveToTrash.title']()}
</MenuItem>
)}
</DocPermissionGuard>
</Guard>
),
},
],
@@ -5,7 +5,7 @@ import {
MobileMenuSub,
useConfirmModal,
} from '@affine/component';
import { DocPermissionGuard } from '@affine/core/components/guard/doc-guard';
import { Guard } from '@affine/core/components/guard';
import { MoveToTrash } from '@affine/core/components/page-list';
import {
type DocRecord,
@@ -59,7 +59,7 @@ export const ResolveConflictOperations = ({
return (
<>
<DocPermissionGuard docId={docRecord.id} permission="Doc_Update">
<Guard docId={docRecord.id} permission="Doc_Update">
{canEdit => (
<MobileMenuItem
prefixIcon={<CalendarXmarkIcon />}
@@ -72,15 +72,15 @@ export const ResolveConflictOperations = ({
{t['com.affine.page-properties.property.journal-remove']()}
</MobileMenuItem>
)}
</DocPermissionGuard>
<DocPermissionGuard docId={docRecord.id} permission="Doc_Trash">
</Guard>
<Guard docId={docRecord.id} permission="Doc_Trash">
{canTrash => (
<MoveToTrash
onSelect={() => handleOpenTrashModal(docRecord)}
disabled={!canTrash}
/>
)}
</DocPermissionGuard>
</Guard>
</>
);
};
@@ -2,6 +2,7 @@ import { useThemeColorV2 } from '@affine/component';
import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-editor';
import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary';
import { useGuard } from '@affine/core/components/guard';
import { useActiveBlocksuiteEditor } from '@affine/core/components/hooks/use-block-suite-editor';
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
import { PageDetailEditor } from '@affine/core/components/page-detail-editor';
@@ -16,7 +17,6 @@ import { EditorService } from '@affine/core/modules/editor';
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
import { GlobalContextService } from '@affine/core/modules/global-context';
import { JournalService } from '@affine/core/modules/journal';
import { GuardService } from '@affine/core/modules/permissions';
import { WorkbenchService } from '@affine/core/modules/workbench';
import { ViewService } from '@affine/core/modules/workbench/services/view';
import { WorkspaceService } from '@affine/core/modules/workspace';
@@ -55,7 +55,6 @@ const DetailPageImpl = () => {
globalContextService,
featureFlagService,
aIButtonService,
guardService,
} = useServices({
WorkbenchService,
ViewService,
@@ -65,7 +64,6 @@ const DetailPageImpl = () => {
GlobalContextService,
FeatureFlagService,
AIButtonService,
GuardService,
});
const editor = editorService.editor;
const workspace = workspaceService.workspace;
@@ -192,7 +190,7 @@ const DetailPageImpl = () => {
[docCollection.id, editor, jumpToPageBlock, openPage, server]
);
const canEdit = useLiveData(guardService.can$('Doc_Update', doc.id));
const canEdit = useGuard('Doc_Update', doc.id);
const readonly =
!canEdit ||
@@ -254,8 +252,7 @@ const MobileDetailPage = ({
const [showTitle, setShowTitle] = useState(checkShowTitle);
const title = useLiveData(docDisplayMetaService.title$(pageId));
const guardService = useService(GuardService);
const canAccess = useLiveData(guardService.can$('Doc_Read', pageId));
const canAccess = useGuard('Doc_Read', pageId);
const allJournalDates = useLiveData(journalService.allJournalDates$);
@@ -6,12 +6,12 @@ import {
MobileMenuItem,
} from '@affine/component/ui/menu';
import { useFavorite } from '@affine/core/blocksuite/block-suite-header/favorite';
import { useGuard } from '@affine/core/components/guard';
import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
import { DocInfoSheet } from '@affine/core/mobile/components';
import { MobileTocMenu } from '@affine/core/mobile/components/toc-menu';
import { DocService } from '@affine/core/modules/doc';
import { EditorService } from '@affine/core/modules/editor';
import { GuardService } from '@affine/core/modules/permissions';
import { ViewService } from '@affine/core/modules/workbench/services/view';
import { preventDefault } from '@affine/core/utils';
import { useI18n } from '@affine/i18n';
@@ -35,8 +35,7 @@ export const PageHeaderMenuButton = () => {
const t = useI18n();
const docId = useService(DocService).doc.id;
const guardService = useService(GuardService);
const canEdit = useLiveData(guardService.can$('Doc_Update', docId));
const canEdit = useGuard('Doc_Update', docId);
const editorService = useService(EditorService);
const editorContainer = useLiveData(editorService.editor.editorContainer$);