diff --git a/packages/frontend/core/src/blocksuite/block-suite-editor/bi-directional-link-panel.tsx b/packages/frontend/core/src/blocksuite/block-suite-editor/bi-directional-link-panel.tsx index 177c8911c9..eabe7a2bea 100644 --- a/packages/frontend/core/src/blocksuite/block-suite-editor/bi-directional-link-panel.tsx +++ b/packages/frontend/core/src/blocksuite/block-suite-editor/bi-directional-link-panel.tsx @@ -1,4 +1,5 @@ import { Button, Divider, useLitPortalFactory } from '@affine/component'; +import { useGuard } from '@affine/core/components/guard'; import { useEnableAI } from '@affine/core/components/hooks/affine/use-enable-ai'; import { DocService } from '@affine/core/modules/doc'; import { @@ -7,7 +8,6 @@ import { type Link, } from '@affine/core/modules/doc-link'; import { toURLSearchParams } from '@affine/core/modules/navigation'; -import { GuardService } from '@affine/core/modules/permissions'; import { GlobalSessionStateService } from '@affine/core/modules/storage'; import { WorkbenchLink } from '@affine/core/modules/workbench'; import { @@ -26,7 +26,6 @@ import { LiveData, useFramework, useLiveData, - useService, useServices, } from '@toeverything/infra'; import { @@ -265,8 +264,7 @@ export const LinkPreview = ({ linkGroup: BacklinkGroups; textRendererOptions: TextRendererOptions; }) => { - const guardService = useService(GuardService); - const canAccess = useLiveData(guardService.can$('Doc_Read', linkGroup.docId)); + const canAccess = useGuard('Doc_Read', linkGroup.docId); const t = useI18n(); if (!canAccess) { diff --git a/packages/frontend/core/src/blocksuite/block-suite-header/menu/index.tsx b/packages/frontend/core/src/blocksuite/block-suite-header/menu/index.tsx index 0929147480..f1fab858c0 100644 --- a/packages/frontend/core/src/blocksuite/block-suite-header/menu/index.tsx +++ b/packages/frontend/core/src/blocksuite/block-suite-header/menu/index.tsx @@ -6,6 +6,7 @@ import { MenuSub, } from '@affine/component/ui/menu'; import { PageHistoryModal } from '@affine/core/components/affine/page-history-modal'; +import { useGuard } from '@affine/core/components/guard'; import { useBlockSuiteMetaHelper } from '@affine/core/components/hooks/affine/use-block-suite-meta-helper'; import { useEnableCloud } from '@affine/core/components/hooks/affine/use-enable-cloud'; import { useExportPage } from '@affine/core/components/hooks/affine/use-export-page'; @@ -333,8 +334,8 @@ const PageHeaderMenuItem = ({ openInAppService?.showOpenInAppPage(); }, [openInAppService]); - const canEdit = useLiveData(guardService.can$('Doc_Update', pageId)); - const canMoveToTrash = useLiveData(guardService.can$('Doc_Trash', pageId)); + const canEdit = useGuard('Doc_Update', pageId); + const canMoveToTrash = useGuard('Doc_Trash', pageId); return ( <> diff --git a/packages/frontend/core/src/blocksuite/block-suite-header/title/index.tsx b/packages/frontend/core/src/blocksuite/block-suite-header/title/index.tsx index 3bd5fdca4b..68146ccbf1 100644 --- a/packages/frontend/core/src/blocksuite/block-suite-header/title/index.tsx +++ b/packages/frontend/core/src/blocksuite/block-suite-header/title/index.tsx @@ -1,8 +1,8 @@ import type { InlineEditProps } from '@affine/component'; import { InlineEdit } from '@affine/component'; +import { useGuard } from '@affine/core/components/guard'; import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; import { DocService, DocsService } from '@affine/core/modules/doc'; -import { GuardService } from '@affine/core/modules/permissions'; import { WorkspaceService } from '@affine/core/modules/workspace'; import { track } from '@affine/track'; import { useLiveData, useService } from '@toeverything/infra'; @@ -25,7 +25,6 @@ export const BlocksuiteHeaderTitle = (props: BlockSuiteHeaderTitleProps) => { const workspaceService = useService(WorkspaceService); const isSharedMode = workspaceService.workspace.openOptions.isSharedMode; const docsService = useService(DocsService); - const guardService = useService(GuardService); const docService = useService(DocService); const docTitle = useLiveData(docService.doc.record.title$); @@ -37,9 +36,7 @@ export const BlocksuiteHeaderTitle = (props: BlockSuiteHeaderTitleProps) => { [docService.doc.id, docsService] ); - const canEdit = useLiveData( - guardService.can$('Doc_Update', docService.doc.id) - ); + const canEdit = useGuard('Doc_Update', docService.doc.id); return ( (); - const guardService = useService(GuardService); - const pageDocId = useMemo(() => { return docCollection.getDoc(pageId)?.spaceDoc.guid ?? pageId; }, [pageId, docCollection]); @@ -441,7 +437,7 @@ const PageHistoryManager = ({ const i18n = useI18n(); const title = useLiveData(docDisplayMetaService.title$(pageId)); - const canEdit = useLiveData(guardService.can$('Doc_Update', pageDocId)); + const canEdit = useGuard('Doc_Update', pageDocId); const onConfirmRestore = useCallback(() => { openConfirmModal({ diff --git a/packages/frontend/core/src/components/doc-properties/manager/index.tsx b/packages/frontend/core/src/components/doc-properties/manager/index.tsx index 9a164d6ce6..f5e1bd9f86 100644 --- a/packages/frontend/core/src/components/doc-properties/manager/index.tsx +++ b/packages/frontend/core/src/components/doc-properties/manager/index.tsx @@ -8,7 +8,6 @@ import { } from '@affine/component'; import type { DocCustomPropertyInfo } from '@affine/core/modules/db'; import { DocsService } from '@affine/core/modules/doc'; -import { GuardService } from '@affine/core/modules/permissions'; import { WorkspaceService } from '@affine/core/modules/workspace'; import type { AffineDNDData } from '@affine/core/types/dnd'; import { useI18n } from '@affine/i18n'; @@ -17,6 +16,7 @@ import { useLiveData, useService } from '@toeverything/infra'; import clsx from 'clsx'; import { type HTMLProps, useCallback, useState } from 'react'; +import { useGuard } from '../../guard'; import { DocPropertyIcon } from '../icons/doc-property-icon'; import { EditDocPropertyMenuItems } from '../menu/edit-doc-property'; import { @@ -38,13 +38,10 @@ const PropertyItem = ({ ) => void; }) => { const t = useI18n(); - const guardService = useService(GuardService); const workspaceService = useService(WorkspaceService); const docsService = useService(DocsService); const [moreMenuOpen, setMoreMenuOpen] = useState(defaultOpenEditMenu); - const canEditPropertyInfo = useLiveData( - guardService.can$('Workspace_Properties_Update') - ); + const canEditPropertyInfo = useGuard('Workspace_Properties_Update'); const typeInfo = isSupportedDocPropertyType(propertyInfo.type) ? DocPropertyTypes[propertyInfo.type] diff --git a/packages/frontend/core/src/components/doc-properties/sidebar/index.tsx b/packages/frontend/core/src/components/doc-properties/sidebar/index.tsx index c527cea0c6..ec5e87fa34 100644 --- a/packages/frontend/core/src/components/doc-properties/sidebar/index.tsx +++ b/packages/frontend/core/src/components/doc-properties/sidebar/index.tsx @@ -1,7 +1,6 @@ import { Divider, IconButton, Tooltip } from '@affine/component'; import type { DocCustomPropertyInfo } from '@affine/core/modules/db'; import { DocsService } from '@affine/core/modules/doc'; -import { GuardService } from '@affine/core/modules/permissions'; import { generateUniqueNameInSequence } from '@affine/core/utils/unique-name'; import { useI18n } from '@affine/i18n'; import track from '@affine/track'; @@ -13,6 +12,7 @@ import { import { useLiveData, useService } from '@toeverything/infra'; import { useCallback, useState } from 'react'; +import { useGuard } from '../../guard'; import { DocPropertyManager } from '../manager'; import { DocPropertyTypes, @@ -29,12 +29,9 @@ export const DocPropertySidebar = () => { const [newPropertyId, setNewPropertyId] = useState(); const docsService = useService(DocsService); - const guardService = useService(GuardService); const propertyList = docsService.propertyList; const properties = useLiveData(propertyList.properties$); - const canEditPropertyInfo = useLiveData( - guardService.can$('Workspace_Properties_Update') - ); + const canEditPropertyInfo = useGuard('Workspace_Properties_Update'); const onAddProperty = useCallback( (option: { type: string; name: string }) => { if (!isSupportedDocPropertyType(option.type)) { diff --git a/packages/frontend/core/src/components/doc-properties/table.tsx b/packages/frontend/core/src/components/doc-properties/table.tsx index a510d82b0e..d739a5a33d 100644 --- a/packages/frontend/core/src/components/doc-properties/table.tsx +++ b/packages/frontend/core/src/components/doc-properties/table.tsx @@ -16,7 +16,6 @@ import type { DatabaseValueCell, } from '@affine/core/modules/doc-info/types'; import { DocIntegrationPropertiesTable } from '@affine/core/modules/integration'; -import { GuardService } from '@affine/core/modules/permissions'; import { ViewService, WorkbenchService } from '@affine/core/modules/workbench'; import type { AffineDNDData } from '@affine/core/types/dnd'; import { useI18n } from '@affine/i18n'; @@ -32,6 +31,7 @@ import clsx from 'clsx'; import type React from 'react'; import { forwardRef, useCallback, useMemo, useState } from 'react'; +import { useGuard } from '../guard'; import { DocPropertyIcon } from './icons/doc-property-icon'; import { CreatePropertyMenuItems } from './menu/create-doc-property'; import { EditDocPropertyMenuItems } from './menu/edit-doc-property'; @@ -290,18 +290,13 @@ const DocWorkspacePropertiesTableBody = forwardRef< const workbenchService = useService(WorkbenchService); const viewService = useServiceOptional(ViewService); const docService = useService(DocService); - const guardService = useService(GuardService); const properties = useLiveData(docsService.propertyList.sortedProperties$); const [addMoreCollapsed, setAddMoreCollapsed] = useState(true); const [newPropertyId, setNewPropertyId] = useState(null); - const canEditProperty = useLiveData( - guardService.can$('Doc_Update', docService.doc.id) - ); - const canEditPropertyInfo = useLiveData( - guardService.can$('Workspace_Properties_Update') - ); + const canEditProperty = useGuard('Doc_Update', docService.doc.id); + const canEditPropertyInfo = useGuard('Workspace_Properties_Update'); const handlePropertyAdded = useCallback( (property: DocCustomPropertyInfo) => { diff --git a/packages/frontend/core/src/components/guard/doc-guard.tsx b/packages/frontend/core/src/components/guard/doc-guard.tsx deleted file mode 100644 index 2cb3345148..0000000000 --- a/packages/frontend/core/src/components/guard/doc-guard.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { - type DocPermissionActions, - GuardService, -} from '@affine/core/modules/permissions'; -import { useLiveData, useService } from '@toeverything/infra'; -import type React from 'react'; - -export const DocPermissionGuard = ({ - docId, - children, - permission, -}: { - docId: string; - permission: DocPermissionActions; - children: (can: boolean | undefined) => React.ReactNode; -}) => { - const guardService = useService(GuardService); - const can = useLiveData(guardService.can$(permission, docId)); - - if (typeof children === 'function') { - return children(can); - } - throw new Error('children must be a function'); -}; diff --git a/packages/frontend/core/src/components/guard/guard.tsx b/packages/frontend/core/src/components/guard/guard.tsx new file mode 100644 index 0000000000..d2bb381415 --- /dev/null +++ b/packages/frontend/core/src/components/guard/guard.tsx @@ -0,0 +1,25 @@ +import { + type DocPermissionActions, + type WorkspacePermissionActions, +} from '@affine/core/modules/permissions'; +import type React from 'react'; + +import { useGuard } from './use-guard'; + +export const Guard = < + T extends WorkspacePermissionActions | DocPermissionActions, +>( + props: { + permission: T; + children: (can: boolean | undefined) => React.ReactNode; + } & (T extends DocPermissionActions ? { docId: string } : {}) +) => { + const { permission, children, ...rest } = props; + const docId = 'docId' in rest ? [rest.docId] : []; + const can = useGuard(permission, ...(docId as any)); + + if (typeof children === 'function') { + return children(can); + } + throw new Error('children must be a function'); +}; diff --git a/packages/frontend/core/src/components/guard/index.ts b/packages/frontend/core/src/components/guard/index.ts new file mode 100644 index 0000000000..865576fc24 --- /dev/null +++ b/packages/frontend/core/src/components/guard/index.ts @@ -0,0 +1,2 @@ +export * from './guard'; +export * from './use-guard'; diff --git a/packages/frontend/core/src/components/guard/use-guard.tsx b/packages/frontend/core/src/components/guard/use-guard.tsx new file mode 100644 index 0000000000..9f0e779b0f --- /dev/null +++ b/packages/frontend/core/src/components/guard/use-guard.tsx @@ -0,0 +1,29 @@ +import { + type DocPermissionActions, + GuardService, + type WorkspacePermissionActions, +} from '@affine/core/modules/permissions'; +import { useLiveData, useService } from '@toeverything/infra'; +import { useEffect, useMemo } from 'react'; + +export const useGuard = < + T extends WorkspacePermissionActions | DocPermissionActions, +>( + action: T, + ...args: T extends DocPermissionActions ? [string] : [] +) => { + const guardService = useService(GuardService); + useEffect(() => { + guardService.revalidateCan(action, ...args); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [action, guardService, ...args]); + + const livedata$ = useMemo( + () => guardService.can$(action, ...args), + // eslint-disable-next-line react-hooks/exhaustive-deps + [action, guardService, ...args] + ); + + const can = useLiveData(livedata$); + return can; +}; diff --git a/packages/frontend/core/src/components/page-list/operation-cell.tsx b/packages/frontend/core/src/components/page-list/operation-cell.tsx index 083ce5e283..323e1840e1 100644 --- a/packages/frontend/core/src/components/page-list/operation-cell.tsx +++ b/packages/frontend/core/src/components/page-list/operation-cell.tsx @@ -14,7 +14,6 @@ import { CompatibleFavoriteItemsAdapter, FavoriteService, } from '@affine/core/modules/favorite'; -import { GuardService } from '@affine/core/modules/permissions'; import { WorkbenchService } from '@affine/core/modules/workbench'; import { WorkspaceService } from '@affine/core/modules/workspace'; import type { Collection, DeleteCollectionInfo } from '@affine/env/filter'; @@ -41,6 +40,7 @@ import { useCallback, useState } from 'react'; import { usePageHelper } from '../../blocksuite/block-suite-page-list/utils'; import type { CollectionService } from '../../modules/collection'; +import { useGuard } from '../guard'; import { IsFavoriteIcon } from '../pure/icons'; import { FavoriteTag } from './components/favorite-tag'; import * as styles from './list.css'; @@ -68,15 +68,13 @@ const PageOperationCellMenuItem = ({ workspaceService, compatibleFavoriteItemsAdapter: favAdapter, workbenchService, - guardService, } = useServices({ WorkspaceService, CompatibleFavoriteItemsAdapter, WorkbenchService, - GuardService, }); - const canMoveToTrash = useLiveData(guardService.can$('Doc_Trash', page.id)); + const canMoveToTrash = useGuard('Doc_Trash', page.id); const currentWorkspace = workspaceService.workspace; const favourite = useLiveData(favAdapter.isFavorite$(page.id, 'doc')); const workbench = workbenchService.workbench; diff --git a/packages/frontend/core/src/desktop/pages/workspace/detail-page/detail-page.tsx b/packages/frontend/core/src/desktop/pages/workspace/detail-page/detail-page.tsx index 9daabd2e51..399b614aea 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/detail-page/detail-page.tsx +++ b/packages/frontend/core/src/desktop/pages/workspace/detail-page/detail-page.tsx @@ -6,13 +6,13 @@ import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite- import { EditorOutlineViewer } from '@affine/core/blocksuite/outline-viewer'; import { PageAIOnboarding } from '@affine/core/components/affine/ai-onboarding'; import { DocPropertySidebar } from '@affine/core/components/doc-properties/sidebar'; +import { useGuard } from '@affine/core/components/guard'; import { useAppSettingHelper } from '@affine/core/components/hooks/affine/use-app-setting-helper'; import { useEnableAI } from '@affine/core/components/hooks/affine/use-enable-ai'; import { DocService } from '@affine/core/modules/doc'; import { EditorService } from '@affine/core/modules/editor'; import { GlobalContextService } from '@affine/core/modules/global-context'; import { PeekViewService } from '@affine/core/modules/peek-view'; -import { GuardService } from '@affine/core/modules/permissions'; import { RecentDocsService } from '@affine/core/modules/quicksearch'; import { ViewService } from '@affine/core/modules/workbench'; import { WorkspaceService } from '@affine/core/modules/workspace'; @@ -69,7 +69,6 @@ const DetailPageImpl = memo(function DetailPageImpl() { docService, workspaceService, globalContextService, - guardService, } = useServices({ WorkbenchService, ViewService, @@ -77,7 +76,6 @@ const DetailPageImpl = memo(function DetailPageImpl() { DocService, WorkspaceService, GlobalContextService, - GuardService, }); const workbench = workbenchService.workbench; const editor = editorService.editor; @@ -262,7 +260,7 @@ const DetailPageImpl = memo(function DetailPageImpl() { const [dragging, setDragging] = useState(false); - const canEdit = useLiveData(guardService.can$('Doc_Update', doc.id)); + const canEdit = useGuard('Doc_Update', doc.id); const readonly = !canEdit || isInTrash; @@ -368,7 +366,6 @@ const DetailPageImpl = memo(function DetailPageImpl() { export const Component = () => { const params = useParams(); const recentPages = useService(RecentDocsService); - const guardService = useService(GuardService); useEffect(() => { if (params.pageId) { @@ -380,9 +377,7 @@ export const Component = () => { }, [params, recentPages]); const pageId = params.pageId; - const canAccess = useLiveData( - pageId ? guardService.can$('Doc_Read', pageId) : undefined - ); + const canAccess = useGuard('Doc_Read', pageId ?? ''); return pageId ? ( - + {canEdit => ( } @@ -386,19 +383,16 @@ const ConflictList = ({ ]()} )} - + - + {canTrash => ( handleOpenTrashModal(docRecord)} disabled={!canTrash} /> )} - + } > diff --git a/packages/frontend/core/src/mobile/components/doc-info/doc-info.tsx b/packages/frontend/core/src/mobile/components/doc-info/doc-info.tsx index 3738b31637..89b468d093 100644 --- a/packages/frontend/core/src/mobile/components/doc-info/doc-info.tsx +++ b/packages/frontend/core/src/mobile/components/doc-info/doc-info.tsx @@ -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), diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/doc/index.tsx b/packages/frontend/core/src/mobile/components/explorer/nodes/doc/index.tsx index 1140962fe4..5fa4e35d94 100644 --- a/packages/frontend/core/src/mobile/components/explorer/nodes/doc/index.tsx +++ b/packages/frontend/core/src/mobile/components/explorer/nodes/doc/index.tsx @@ -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}`} > - + {canRead => canRead ? children?.map((child, index) => ( @@ -146,8 +146,8 @@ export const ExplorerDocNode = ({ )) : null } - - + + {canEdit => canEdit ? ( ) : null } - + ); }; diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/doc/operations.tsx b/packages/frontend/core/src/mobile/components/explorer/nodes/doc/operations.tsx index 688b3812ba..38e31bf5e6 100644 --- a/packages/frontend/core/src/mobile/components/explorer/nodes/doc/operations.tsx +++ b/packages/frontend/core/src/mobile/components/explorer/nodes/doc/operations.tsx @@ -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: ( - + {canEdit => ( )} - + ), }, { @@ -224,7 +224,7 @@ export const useExplorerDocNodeOperationsMenu = ( { index: 97, view: ( - + {canEdit => ( } @@ -234,7 +234,7 @@ export const useExplorerDocNodeOperationsMenu = ( {t['com.affine.page-operation.add-linked-page']()} )} - + ), }, { @@ -273,7 +273,7 @@ export const useExplorerDocNodeOperationsMenu = ( { index: 10000, view: ( - + {canMoveToTrash => ( )} - + ), }, ], diff --git a/packages/frontend/core/src/mobile/pages/workspace/detail/menu/journal-conflicts.tsx b/packages/frontend/core/src/mobile/pages/workspace/detail/menu/journal-conflicts.tsx index f20d7e1708..15b587bbab 100644 --- a/packages/frontend/core/src/mobile/pages/workspace/detail/menu/journal-conflicts.tsx +++ b/packages/frontend/core/src/mobile/pages/workspace/detail/menu/journal-conflicts.tsx @@ -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 ( <> - + {canEdit => ( } @@ -72,15 +72,15 @@ export const ResolveConflictOperations = ({ {t['com.affine.page-properties.property.journal-remove']()} )} - - + + {canTrash => ( handleOpenTrashModal(docRecord)} disabled={!canTrash} /> )} - + ); }; diff --git a/packages/frontend/core/src/mobile/pages/workspace/detail/mobile-detail-page.tsx b/packages/frontend/core/src/mobile/pages/workspace/detail/mobile-detail-page.tsx index 736183fe48..560b49c04b 100644 --- a/packages/frontend/core/src/mobile/pages/workspace/detail/mobile-detail-page.tsx +++ b/packages/frontend/core/src/mobile/pages/workspace/detail/mobile-detail-page.tsx @@ -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$); diff --git a/packages/frontend/core/src/mobile/pages/workspace/detail/page-header-more-button.tsx b/packages/frontend/core/src/mobile/pages/workspace/detail/page-header-more-button.tsx index 075c07b6aa..9d5439e4f0 100644 --- a/packages/frontend/core/src/mobile/pages/workspace/detail/page-header-more-button.tsx +++ b/packages/frontend/core/src/mobile/pages/workspace/detail/page-header-more-button.tsx @@ -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$); diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/doc/index.tsx b/packages/frontend/core/src/modules/explorer/views/nodes/doc/index.tsx index 1493403a79..3dad699c5b 100644 --- a/packages/frontend/core/src/modules/explorer/views/nodes/doc/index.tsx +++ b/packages/frontend/core/src/modules/explorer/views/nodes/doc/index.tsx @@ -5,7 +5,7 @@ import { toast, Tooltip, } from '@affine/component'; -import { DocPermissionGuard } from '@affine/core/components/guard/doc-guard'; +import { Guard } from '@affine/core/components/guard'; import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; import { WorkspaceDialogService } from '@affine/core/modules/dialogs'; import { DocsService } from '@affine/core/modules/doc'; @@ -274,7 +274,7 @@ export const ExplorerDocNode = ({ dropEffect={handleDropEffectOnDoc} data-testid={`explorer-doc-${docId}`} > - + {canRead => canRead ? children?.map((child, index) => ( @@ -291,7 +291,7 @@ export const ExplorerDocNode = ({ )) : null } - + ); }; diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/doc/operations.tsx b/packages/frontend/core/src/modules/explorer/views/nodes/doc/operations.tsx index 527c0f83cc..e6163cc686 100644 --- a/packages/frontend/core/src/modules/explorer/views/nodes/doc/operations.tsx +++ b/packages/frontend/core/src/modules/explorer/views/nodes/doc/operations.tsx @@ -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'; @@ -175,7 +175,7 @@ export const useExplorerDocNodeOperations = ( { index: 99, view: ( - + {canEdit => ( } @@ -185,7 +185,7 @@ export const useExplorerDocNodeOperations = ( {t['com.affine.page-operation.add-linked-page']()} )} - + ), }, { @@ -239,7 +239,7 @@ export const useExplorerDocNodeOperations = ( { index: 10000, view: ( - + {canMoveToTrash => ( )} - + ), }, ], diff --git a/packages/frontend/core/src/modules/explorer/views/tree/node.tsx b/packages/frontend/core/src/modules/explorer/views/tree/node.tsx index e3ce2870b8..edb1f33dcf 100644 --- a/packages/frontend/core/src/modules/explorer/views/tree/node.tsx +++ b/packages/frontend/core/src/modules/explorer/views/tree/node.tsx @@ -10,7 +10,7 @@ import { useDropTarget, } from '@affine/component'; import { RenameModal } from '@affine/component/rename-modal'; -import { DocPermissionGuard } from '@affine/core/components/guard/doc-guard'; +import { Guard } from '@affine/core/components/guard'; import { AppSidebarService } from '@affine/core/modules/app-sidebar'; import type { DocPermissionActions } from '@affine/core/modules/permissions'; import { WorkbenchLink } from '@affine/core/modules/workbench'; @@ -285,7 +285,7 @@ export const ExplorerTreeNode = ({ ? { index: 0, view: renameableGuard ? ( - @@ -300,7 +300,7 @@ export const ExplorerTreeNode = ({ {t['com.affine.menu.rename']()} )} - + ) : ( { - // revalidate permission - if (docId) { - this.revalidateDocPermission(docId); - } else { - this.revalidateWorkspacePermission(); - } - // revalidate workspace permission if it's not initialized - if (this.isAdmin$.value === null) { - this.workspacePermissionService.permission.revalidate(); - } - let prev: boolean | undefined = undefined; const subscription = combineLatest([ @@ -128,6 +117,29 @@ export class GuardService extends Service { return permissions[action as keyof typeof permissions] ?? false; } + revalidateCan( + _action: T, + ...args: T extends DocPermissionActions ? [string] : [] + ) { + // revalidate workspace permission if it's not initialized + if (this.isAdmin$.value === null) { + this.workspacePermissionService.permission.revalidate(); + } + + if (this.isAdmin$.value === true) { + // if the user is admin, the permission is always true + return; + } + + const docId = args[0]; + // revalidate permission + if (docId) { + this.revalidateDocPermission(docId); + } else { + this.revalidateWorkspacePermission(); + } + } + private readonly revalidateWorkspacePermission = effect( exhaustMapWithTrailing(() => fromPromise(() => this.guardStore.getWorkspacePermissions()).pipe( diff --git a/packages/frontend/core/src/modules/share-menu/view/share-menu/member-management/member-item.tsx b/packages/frontend/core/src/modules/share-menu/view/share-menu/member-management/member-item.tsx index 1ce900be1f..e975a8aefb 100644 --- a/packages/frontend/core/src/modules/share-menu/view/share-menu/member-management/member-item.tsx +++ b/packages/frontend/core/src/modules/share-menu/view/share-menu/member-management/member-item.tsx @@ -8,12 +8,12 @@ import { Tooltip, useConfirmModal, } from '@affine/component'; +import { useGuard } from '@affine/core/components/guard'; import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; import { DocService } from '@affine/core/modules/doc'; import { DocGrantedUsersService, type GrantedUser, - GuardService, WorkspacePermissionService, } from '@affine/core/modules/permissions'; import { UserFriendlyError } from '@affine/error'; @@ -131,7 +131,6 @@ const Options = ({ const t = useI18n(); const docGrantedUsersService = useService(DocGrantedUsersService); const docService = useService(DocService); - const guardService = useService(GuardService); const workspacePermissionService = useService(WorkspacePermissionService); const isWorkspaceOwner = useLiveData( workspacePermissionService.permission.isOwner$ @@ -140,11 +139,8 @@ const Options = ({ const { openConfirmModal } = useConfirmModal(); const canTransferOwner = - useLiveData(guardService.can$('Doc_TransferOwner', docService.doc.id)) && - !!isWorkspaceOwner; - const canManageUsers = useLiveData( - guardService.can$('Doc_Users_Manage', docService.doc.id) - ); + useGuard('Doc_TransferOwner', docService.doc.id) && !!isWorkspaceOwner; + const canManageUsers = useGuard('Doc_Users_Manage', docService.doc.id); const updateUserRole = useCallback( async (userId: string, role: DocRole) => { diff --git a/packages/frontend/core/src/modules/share-menu/view/share-menu/member-management/member-management.tsx b/packages/frontend/core/src/modules/share-menu/view/share-menu/member-management/member-management.tsx index 232dc69435..2023fa9071 100644 --- a/packages/frontend/core/src/modules/share-menu/view/share-menu/member-management/member-management.tsx +++ b/packages/frontend/core/src/modules/share-menu/view/share-menu/member-management/member-management.tsx @@ -1,9 +1,9 @@ import { Skeleton } from '@affine/component'; +import { useGuard } from '@affine/core/components/guard'; import { DocService } from '@affine/core/modules/doc'; import { DocGrantedUsersService, type GrantedUser, - GuardService, } from '@affine/core/modules/permissions'; import { useI18n } from '@affine/i18n'; import { ArrowLeftBigIcon } from '@blocksuite/icons/rc'; @@ -33,11 +33,8 @@ export const MemberManagement = ({ docGrantedUsersService.grantedUserCount$ ); const docService = useService(DocService); - const guardService = useService(GuardService); - const canManageUsers = useLiveData( - guardService.can$('Doc_Users_Manage', docService.doc.id) - ); + const canManageUsers = useGuard('Doc_Users_Manage', docService.doc.id); const t = useI18n(); diff --git a/packages/frontend/core/src/modules/share-menu/view/share-menu/share-page.tsx b/packages/frontend/core/src/modules/share-menu/view/share-menu/share-page.tsx index 4d20ff1bec..349fd5556c 100644 --- a/packages/frontend/core/src/modules/share-menu/view/share-menu/share-page.tsx +++ b/packages/frontend/core/src/modules/share-menu/view/share-menu/share-page.tsx @@ -1,8 +1,8 @@ import { Divider, Skeleton } from '@affine/component'; import { Button } from '@affine/component/ui/button'; +import { useGuard } from '@affine/core/components/guard'; import { ServerService } from '@affine/core/modules/cloud'; import { DocService } from '@affine/core/modules/doc'; -import { GuardService } from '@affine/core/modules/permissions'; import { ShareInfoService } from '@affine/core/modules/share-doc'; import { useI18n } from '@affine/i18n'; import { useLiveData, useService } from '@toeverything/infra'; @@ -64,15 +64,10 @@ export const AFFiNESharePage = ( const shareInfoService = useService(ShareInfoService); const serverService = useService(ServerService); const docService = useService(DocService); - const guardService = useService(GuardService); - const canManageUsers = useLiveData( - guardService.can$('Doc_Users_Manage', docService.doc.id) - ); + const canManageUsers = useGuard('Doc_Users_Manage', docService.doc.id); - const canPublish = useLiveData( - guardService.can$('Doc_Publish', docService.doc.id) - ); + const canPublish = useGuard('Doc_Publish', docService.doc.id); useEffect(() => { shareInfoService.shareInfo.revalidate();