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:
@@ -1,4 +1,5 @@
|
|||||||
import { Button, Divider, useLitPortalFactory } from '@affine/component';
|
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 { useEnableAI } from '@affine/core/components/hooks/affine/use-enable-ai';
|
||||||
import { DocService } from '@affine/core/modules/doc';
|
import { DocService } from '@affine/core/modules/doc';
|
||||||
import {
|
import {
|
||||||
@@ -7,7 +8,6 @@ import {
|
|||||||
type Link,
|
type Link,
|
||||||
} from '@affine/core/modules/doc-link';
|
} from '@affine/core/modules/doc-link';
|
||||||
import { toURLSearchParams } from '@affine/core/modules/navigation';
|
import { toURLSearchParams } from '@affine/core/modules/navigation';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
|
||||||
import { GlobalSessionStateService } from '@affine/core/modules/storage';
|
import { GlobalSessionStateService } from '@affine/core/modules/storage';
|
||||||
import { WorkbenchLink } from '@affine/core/modules/workbench';
|
import { WorkbenchLink } from '@affine/core/modules/workbench';
|
||||||
import {
|
import {
|
||||||
@@ -26,7 +26,6 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
useFramework,
|
useFramework,
|
||||||
useLiveData,
|
useLiveData,
|
||||||
useService,
|
|
||||||
useServices,
|
useServices,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import {
|
import {
|
||||||
@@ -265,8 +264,7 @@ export const LinkPreview = ({
|
|||||||
linkGroup: BacklinkGroups;
|
linkGroup: BacklinkGroups;
|
||||||
textRendererOptions: TextRendererOptions;
|
textRendererOptions: TextRendererOptions;
|
||||||
}) => {
|
}) => {
|
||||||
const guardService = useService(GuardService);
|
const canAccess = useGuard('Doc_Read', linkGroup.docId);
|
||||||
const canAccess = useLiveData(guardService.can$('Doc_Read', linkGroup.docId));
|
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
if (!canAccess) {
|
if (!canAccess) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
MenuSub,
|
MenuSub,
|
||||||
} from '@affine/component/ui/menu';
|
} from '@affine/component/ui/menu';
|
||||||
import { PageHistoryModal } from '@affine/core/components/affine/page-history-modal';
|
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 { useBlockSuiteMetaHelper } from '@affine/core/components/hooks/affine/use-block-suite-meta-helper';
|
||||||
import { useEnableCloud } from '@affine/core/components/hooks/affine/use-enable-cloud';
|
import { useEnableCloud } from '@affine/core/components/hooks/affine/use-enable-cloud';
|
||||||
import { useExportPage } from '@affine/core/components/hooks/affine/use-export-page';
|
import { useExportPage } from '@affine/core/components/hooks/affine/use-export-page';
|
||||||
@@ -333,8 +334,8 @@ const PageHeaderMenuItem = ({
|
|||||||
openInAppService?.showOpenInAppPage();
|
openInAppService?.showOpenInAppPage();
|
||||||
}, [openInAppService]);
|
}, [openInAppService]);
|
||||||
|
|
||||||
const canEdit = useLiveData(guardService.can$('Doc_Update', pageId));
|
const canEdit = useGuard('Doc_Update', pageId);
|
||||||
const canMoveToTrash = useLiveData(guardService.can$('Doc_Trash', pageId));
|
const canMoveToTrash = useGuard('Doc_Trash', pageId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { InlineEditProps } from '@affine/component';
|
import type { InlineEditProps } from '@affine/component';
|
||||||
import { InlineEdit } 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 { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
import { DocService, DocsService } from '@affine/core/modules/doc';
|
import { DocService, DocsService } from '@affine/core/modules/doc';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
|
||||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
@@ -25,7 +25,6 @@ export const BlocksuiteHeaderTitle = (props: BlockSuiteHeaderTitleProps) => {
|
|||||||
const workspaceService = useService(WorkspaceService);
|
const workspaceService = useService(WorkspaceService);
|
||||||
const isSharedMode = workspaceService.workspace.openOptions.isSharedMode;
|
const isSharedMode = workspaceService.workspace.openOptions.isSharedMode;
|
||||||
const docsService = useService(DocsService);
|
const docsService = useService(DocsService);
|
||||||
const guardService = useService(GuardService);
|
|
||||||
const docService = useService(DocService);
|
const docService = useService(DocService);
|
||||||
const docTitle = useLiveData(docService.doc.record.title$);
|
const docTitle = useLiveData(docService.doc.record.title$);
|
||||||
|
|
||||||
@@ -37,9 +36,7 @@ export const BlocksuiteHeaderTitle = (props: BlockSuiteHeaderTitleProps) => {
|
|||||||
[docService.doc.id, docsService]
|
[docService.doc.id, docsService]
|
||||||
);
|
);
|
||||||
|
|
||||||
const canEdit = useLiveData(
|
const canEdit = useGuard('Doc_Update', docService.doc.id);
|
||||||
guardService.can$('Doc_Update', docService.doc.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InlineEdit
|
<InlineEdit
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ import { Modal, useConfirmModal } from '@affine/component/ui/modal';
|
|||||||
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
||||||
import { EditorService } from '@affine/core/modules/editor';
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import {
|
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
||||||
GuardService,
|
|
||||||
WorkspacePermissionService,
|
|
||||||
} from '@affine/core/modules/permissions';
|
|
||||||
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
||||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
import { i18nTime, Trans, useI18n } from '@affine/i18n';
|
import { i18nTime, Trans, useI18n } from '@affine/i18n';
|
||||||
@@ -35,6 +32,7 @@ import { encodeStateAsUpdate } from 'yjs';
|
|||||||
import { BlockSuiteEditor } from '../../../blocksuite/block-suite-editor';
|
import { BlockSuiteEditor } from '../../../blocksuite/block-suite-editor';
|
||||||
import { PureEditorModeSwitch } from '../../../blocksuite/block-suite-mode-switch';
|
import { PureEditorModeSwitch } from '../../../blocksuite/block-suite-mode-switch';
|
||||||
import { pageHistoryModalAtom } from '../../atoms/page-history';
|
import { pageHistoryModalAtom } from '../../atoms/page-history';
|
||||||
|
import { useGuard } from '../../guard';
|
||||||
import { AffineErrorBoundary } from '../affine-error-boundary';
|
import { AffineErrorBoundary } from '../affine-error-boundary';
|
||||||
import {
|
import {
|
||||||
historyListGroupByDay,
|
historyListGroupByDay,
|
||||||
@@ -408,8 +406,6 @@ const PageHistoryManager = ({
|
|||||||
const workspaceId = docCollection.id;
|
const workspaceId = docCollection.id;
|
||||||
const [activeVersion, setActiveVersion] = useState<string>();
|
const [activeVersion, setActiveVersion] = useState<string>();
|
||||||
|
|
||||||
const guardService = useService(GuardService);
|
|
||||||
|
|
||||||
const pageDocId = useMemo(() => {
|
const pageDocId = useMemo(() => {
|
||||||
return docCollection.getDoc(pageId)?.spaceDoc.guid ?? pageId;
|
return docCollection.getDoc(pageId)?.spaceDoc.guid ?? pageId;
|
||||||
}, [pageId, docCollection]);
|
}, [pageId, docCollection]);
|
||||||
@@ -441,7 +437,7 @@ const PageHistoryManager = ({
|
|||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
|
|
||||||
const title = useLiveData(docDisplayMetaService.title$(pageId));
|
const title = useLiveData(docDisplayMetaService.title$(pageId));
|
||||||
const canEdit = useLiveData(guardService.can$('Doc_Update', pageDocId));
|
const canEdit = useGuard('Doc_Update', pageDocId);
|
||||||
|
|
||||||
const onConfirmRestore = useCallback(() => {
|
const onConfirmRestore = useCallback(() => {
|
||||||
openConfirmModal({
|
openConfirmModal({
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import type { DocCustomPropertyInfo } from '@affine/core/modules/db';
|
import type { DocCustomPropertyInfo } from '@affine/core/modules/db';
|
||||||
import { DocsService } from '@affine/core/modules/doc';
|
import { DocsService } from '@affine/core/modules/doc';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
|
||||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -17,6 +16,7 @@ import { useLiveData, useService } from '@toeverything/infra';
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { type HTMLProps, useCallback, useState } from 'react';
|
import { type HTMLProps, useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
import { useGuard } from '../../guard';
|
||||||
import { DocPropertyIcon } from '../icons/doc-property-icon';
|
import { DocPropertyIcon } from '../icons/doc-property-icon';
|
||||||
import { EditDocPropertyMenuItems } from '../menu/edit-doc-property';
|
import { EditDocPropertyMenuItems } from '../menu/edit-doc-property';
|
||||||
import {
|
import {
|
||||||
@@ -38,13 +38,10 @@ const PropertyItem = ({
|
|||||||
) => void;
|
) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const guardService = useService(GuardService);
|
|
||||||
const workspaceService = useService(WorkspaceService);
|
const workspaceService = useService(WorkspaceService);
|
||||||
const docsService = useService(DocsService);
|
const docsService = useService(DocsService);
|
||||||
const [moreMenuOpen, setMoreMenuOpen] = useState(defaultOpenEditMenu);
|
const [moreMenuOpen, setMoreMenuOpen] = useState(defaultOpenEditMenu);
|
||||||
const canEditPropertyInfo = useLiveData(
|
const canEditPropertyInfo = useGuard('Workspace_Properties_Update');
|
||||||
guardService.can$('Workspace_Properties_Update')
|
|
||||||
);
|
|
||||||
|
|
||||||
const typeInfo = isSupportedDocPropertyType(propertyInfo.type)
|
const typeInfo = isSupportedDocPropertyType(propertyInfo.type)
|
||||||
? DocPropertyTypes[propertyInfo.type]
|
? DocPropertyTypes[propertyInfo.type]
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Divider, IconButton, Tooltip } from '@affine/component';
|
import { Divider, IconButton, Tooltip } from '@affine/component';
|
||||||
import type { DocCustomPropertyInfo } from '@affine/core/modules/db';
|
import type { DocCustomPropertyInfo } from '@affine/core/modules/db';
|
||||||
import { DocsService } from '@affine/core/modules/doc';
|
import { DocsService } from '@affine/core/modules/doc';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
|
||||||
import { generateUniqueNameInSequence } from '@affine/core/utils/unique-name';
|
import { generateUniqueNameInSequence } from '@affine/core/utils/unique-name';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import track from '@affine/track';
|
import track from '@affine/track';
|
||||||
@@ -13,6 +12,7 @@ import {
|
|||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
import { useGuard } from '../../guard';
|
||||||
import { DocPropertyManager } from '../manager';
|
import { DocPropertyManager } from '../manager';
|
||||||
import {
|
import {
|
||||||
DocPropertyTypes,
|
DocPropertyTypes,
|
||||||
@@ -29,12 +29,9 @@ export const DocPropertySidebar = () => {
|
|||||||
const [newPropertyId, setNewPropertyId] = useState<string>();
|
const [newPropertyId, setNewPropertyId] = useState<string>();
|
||||||
|
|
||||||
const docsService = useService(DocsService);
|
const docsService = useService(DocsService);
|
||||||
const guardService = useService(GuardService);
|
|
||||||
const propertyList = docsService.propertyList;
|
const propertyList = docsService.propertyList;
|
||||||
const properties = useLiveData(propertyList.properties$);
|
const properties = useLiveData(propertyList.properties$);
|
||||||
const canEditPropertyInfo = useLiveData(
|
const canEditPropertyInfo = useGuard('Workspace_Properties_Update');
|
||||||
guardService.can$('Workspace_Properties_Update')
|
|
||||||
);
|
|
||||||
const onAddProperty = useCallback(
|
const onAddProperty = useCallback(
|
||||||
(option: { type: string; name: string }) => {
|
(option: { type: string; name: string }) => {
|
||||||
if (!isSupportedDocPropertyType(option.type)) {
|
if (!isSupportedDocPropertyType(option.type)) {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import type {
|
|||||||
DatabaseValueCell,
|
DatabaseValueCell,
|
||||||
} from '@affine/core/modules/doc-info/types';
|
} from '@affine/core/modules/doc-info/types';
|
||||||
import { DocIntegrationPropertiesTable } from '@affine/core/modules/integration';
|
import { DocIntegrationPropertiesTable } from '@affine/core/modules/integration';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
|
||||||
import { ViewService, WorkbenchService } from '@affine/core/modules/workbench';
|
import { ViewService, WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -32,6 +31,7 @@ import clsx from 'clsx';
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { forwardRef, useCallback, useMemo, useState } from 'react';
|
import { forwardRef, useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { useGuard } from '../guard';
|
||||||
import { DocPropertyIcon } from './icons/doc-property-icon';
|
import { DocPropertyIcon } from './icons/doc-property-icon';
|
||||||
import { CreatePropertyMenuItems } from './menu/create-doc-property';
|
import { CreatePropertyMenuItems } from './menu/create-doc-property';
|
||||||
import { EditDocPropertyMenuItems } from './menu/edit-doc-property';
|
import { EditDocPropertyMenuItems } from './menu/edit-doc-property';
|
||||||
@@ -290,18 +290,13 @@ const DocWorkspacePropertiesTableBody = forwardRef<
|
|||||||
const workbenchService = useService(WorkbenchService);
|
const workbenchService = useService(WorkbenchService);
|
||||||
const viewService = useServiceOptional(ViewService);
|
const viewService = useServiceOptional(ViewService);
|
||||||
const docService = useService(DocService);
|
const docService = useService(DocService);
|
||||||
const guardService = useService(GuardService);
|
|
||||||
const properties = useLiveData(docsService.propertyList.sortedProperties$);
|
const properties = useLiveData(docsService.propertyList.sortedProperties$);
|
||||||
const [addMoreCollapsed, setAddMoreCollapsed] = useState(true);
|
const [addMoreCollapsed, setAddMoreCollapsed] = useState(true);
|
||||||
|
|
||||||
const [newPropertyId, setNewPropertyId] = useState<string | null>(null);
|
const [newPropertyId, setNewPropertyId] = useState<string | null>(null);
|
||||||
|
|
||||||
const canEditProperty = useLiveData(
|
const canEditProperty = useGuard('Doc_Update', docService.doc.id);
|
||||||
guardService.can$('Doc_Update', docService.doc.id)
|
const canEditPropertyInfo = useGuard('Workspace_Properties_Update');
|
||||||
);
|
|
||||||
const canEditPropertyInfo = useLiveData(
|
|
||||||
guardService.can$('Workspace_Properties_Update')
|
|
||||||
);
|
|
||||||
|
|
||||||
const handlePropertyAdded = useCallback(
|
const handlePropertyAdded = useCallback(
|
||||||
(property: DocCustomPropertyInfo) => {
|
(property: DocCustomPropertyInfo) => {
|
||||||
|
|||||||
@@ -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');
|
|
||||||
};
|
|
||||||
25
packages/frontend/core/src/components/guard/guard.tsx
Normal file
25
packages/frontend/core/src/components/guard/guard.tsx
Normal file
@@ -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');
|
||||||
|
};
|
||||||
2
packages/frontend/core/src/components/guard/index.ts
Normal file
2
packages/frontend/core/src/components/guard/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './guard';
|
||||||
|
export * from './use-guard';
|
||||||
29
packages/frontend/core/src/components/guard/use-guard.tsx
Normal file
29
packages/frontend/core/src/components/guard/use-guard.tsx
Normal file
@@ -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;
|
||||||
|
};
|
||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
CompatibleFavoriteItemsAdapter,
|
CompatibleFavoriteItemsAdapter,
|
||||||
FavoriteService,
|
FavoriteService,
|
||||||
} from '@affine/core/modules/favorite';
|
} from '@affine/core/modules/favorite';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
import type { Collection, DeleteCollectionInfo } from '@affine/env/filter';
|
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 { usePageHelper } from '../../blocksuite/block-suite-page-list/utils';
|
||||||
import type { CollectionService } from '../../modules/collection';
|
import type { CollectionService } from '../../modules/collection';
|
||||||
|
import { useGuard } from '../guard';
|
||||||
import { IsFavoriteIcon } from '../pure/icons';
|
import { IsFavoriteIcon } from '../pure/icons';
|
||||||
import { FavoriteTag } from './components/favorite-tag';
|
import { FavoriteTag } from './components/favorite-tag';
|
||||||
import * as styles from './list.css';
|
import * as styles from './list.css';
|
||||||
@@ -68,15 +68,13 @@ const PageOperationCellMenuItem = ({
|
|||||||
workspaceService,
|
workspaceService,
|
||||||
compatibleFavoriteItemsAdapter: favAdapter,
|
compatibleFavoriteItemsAdapter: favAdapter,
|
||||||
workbenchService,
|
workbenchService,
|
||||||
guardService,
|
|
||||||
} = useServices({
|
} = useServices({
|
||||||
WorkspaceService,
|
WorkspaceService,
|
||||||
CompatibleFavoriteItemsAdapter,
|
CompatibleFavoriteItemsAdapter,
|
||||||
WorkbenchService,
|
WorkbenchService,
|
||||||
GuardService,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const canMoveToTrash = useLiveData(guardService.can$('Doc_Trash', page.id));
|
const canMoveToTrash = useGuard('Doc_Trash', page.id);
|
||||||
const currentWorkspace = workspaceService.workspace;
|
const currentWorkspace = workspaceService.workspace;
|
||||||
const favourite = useLiveData(favAdapter.isFavorite$(page.id, 'doc'));
|
const favourite = useLiveData(favAdapter.isFavorite$(page.id, 'doc'));
|
||||||
const workbench = workbenchService.workbench;
|
const workbench = workbenchService.workbench;
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-
|
|||||||
import { EditorOutlineViewer } from '@affine/core/blocksuite/outline-viewer';
|
import { EditorOutlineViewer } from '@affine/core/blocksuite/outline-viewer';
|
||||||
import { PageAIOnboarding } from '@affine/core/components/affine/ai-onboarding';
|
import { PageAIOnboarding } from '@affine/core/components/affine/ai-onboarding';
|
||||||
import { DocPropertySidebar } from '@affine/core/components/doc-properties/sidebar';
|
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 { useAppSettingHelper } from '@affine/core/components/hooks/affine/use-app-setting-helper';
|
||||||
import { useEnableAI } from '@affine/core/components/hooks/affine/use-enable-ai';
|
import { useEnableAI } from '@affine/core/components/hooks/affine/use-enable-ai';
|
||||||
import { DocService } from '@affine/core/modules/doc';
|
import { DocService } from '@affine/core/modules/doc';
|
||||||
import { EditorService } from '@affine/core/modules/editor';
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { GlobalContextService } from '@affine/core/modules/global-context';
|
import { GlobalContextService } from '@affine/core/modules/global-context';
|
||||||
import { PeekViewService } from '@affine/core/modules/peek-view';
|
import { PeekViewService } from '@affine/core/modules/peek-view';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
|
||||||
import { RecentDocsService } from '@affine/core/modules/quicksearch';
|
import { RecentDocsService } from '@affine/core/modules/quicksearch';
|
||||||
import { ViewService } from '@affine/core/modules/workbench';
|
import { ViewService } from '@affine/core/modules/workbench';
|
||||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
@@ -69,7 +69,6 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
docService,
|
docService,
|
||||||
workspaceService,
|
workspaceService,
|
||||||
globalContextService,
|
globalContextService,
|
||||||
guardService,
|
|
||||||
} = useServices({
|
} = useServices({
|
||||||
WorkbenchService,
|
WorkbenchService,
|
||||||
ViewService,
|
ViewService,
|
||||||
@@ -77,7 +76,6 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
DocService,
|
DocService,
|
||||||
WorkspaceService,
|
WorkspaceService,
|
||||||
GlobalContextService,
|
GlobalContextService,
|
||||||
GuardService,
|
|
||||||
});
|
});
|
||||||
const workbench = workbenchService.workbench;
|
const workbench = workbenchService.workbench;
|
||||||
const editor = editorService.editor;
|
const editor = editorService.editor;
|
||||||
@@ -262,7 +260,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
|
|
||||||
const [dragging, setDragging] = useState(false);
|
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;
|
const readonly = !canEdit || isInTrash;
|
||||||
|
|
||||||
@@ -368,7 +366,6 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
export const Component = () => {
|
export const Component = () => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const recentPages = useService(RecentDocsService);
|
const recentPages = useService(RecentDocsService);
|
||||||
const guardService = useService(GuardService);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (params.pageId) {
|
if (params.pageId) {
|
||||||
@@ -380,9 +377,7 @@ export const Component = () => {
|
|||||||
}, [params, recentPages]);
|
}, [params, recentPages]);
|
||||||
|
|
||||||
const pageId = params.pageId;
|
const pageId = params.pageId;
|
||||||
const canAccess = useLiveData(
|
const canAccess = useGuard('Doc_Read', pageId ?? '');
|
||||||
pageId ? guardService.can$('Doc_Read', pageId) : undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
return pageId ? (
|
return pageId ? (
|
||||||
<DetailPageWrapper
|
<DetailPageWrapper
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
Scrollable,
|
Scrollable,
|
||||||
useConfirmModal,
|
useConfirmModal,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { DocPermissionGuard } from '@affine/core/components/guard/doc-guard';
|
import { Guard } from '@affine/core/components/guard';
|
||||||
import { useJournalRouteHelper } from '@affine/core/components/hooks/use-journal';
|
import { useJournalRouteHelper } from '@affine/core/components/hooks/use-journal';
|
||||||
import { MoveToTrash } from '@affine/core/components/page-list';
|
import { MoveToTrash } from '@affine/core/components/page-list';
|
||||||
import {
|
import {
|
||||||
@@ -367,10 +367,7 @@ const ConflictList = ({
|
|||||||
}}
|
}}
|
||||||
items={
|
items={
|
||||||
<>
|
<>
|
||||||
<DocPermissionGuard
|
<Guard docId={docRecord.id} permission="Doc_Update">
|
||||||
docId={docRecord.id}
|
|
||||||
permission="Doc_Update"
|
|
||||||
>
|
|
||||||
{canEdit => (
|
{canEdit => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
prefixIcon={<CalendarXmarkIcon />}
|
prefixIcon={<CalendarXmarkIcon />}
|
||||||
@@ -386,19 +383,16 @@ const ConflictList = ({
|
|||||||
]()}
|
]()}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</DocPermissionGuard>
|
</Guard>
|
||||||
<MenuSeparator />
|
<MenuSeparator />
|
||||||
<DocPermissionGuard
|
<Guard docId={docRecord.id} permission="Doc_Trash">
|
||||||
docId={docRecord.id}
|
|
||||||
permission="Doc_Trash"
|
|
||||||
>
|
|
||||||
{canTrash => (
|
{canTrash => (
|
||||||
<MoveToTrash
|
<MoveToTrash
|
||||||
onSelect={() => handleOpenTrashModal(docRecord)}
|
onSelect={() => handleOpenTrashModal(docRecord)}
|
||||||
disabled={!canTrash}
|
disabled={!canTrash}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</DocPermissionGuard>
|
</Guard>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ import {
|
|||||||
DocPropertyRow,
|
DocPropertyRow,
|
||||||
} from '@affine/core/components/doc-properties';
|
} from '@affine/core/components/doc-properties';
|
||||||
import { CreatePropertyMenuItems } from '@affine/core/components/doc-properties/menu/create-doc-property';
|
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 { LinksRow } from '@affine/core/desktop/dialogs/doc-info/links-row';
|
||||||
import { TimeRow } from '@affine/core/desktop/dialogs/doc-info/time-row';
|
import { TimeRow } from '@affine/core/desktop/dialogs/doc-info/time-row';
|
||||||
import type { DocCustomPropertyInfo } from '@affine/core/modules/db';
|
import type { DocCustomPropertyInfo } from '@affine/core/modules/db';
|
||||||
import { DocsService } from '@affine/core/modules/doc';
|
import { DocsService } from '@affine/core/modules/doc';
|
||||||
import { DocDatabaseBacklinkInfo } from '@affine/core/modules/doc-info';
|
import { DocDatabaseBacklinkInfo } from '@affine/core/modules/doc-info';
|
||||||
import { DocsSearchService } from '@affine/core/modules/docs-search';
|
import { DocsSearchService } from '@affine/core/modules/docs-search';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { PlusIcon } from '@blocksuite/icons/rc';
|
import { PlusIcon } from '@blocksuite/icons/rc';
|
||||||
import { LiveData, useLiveData, useServices } from '@toeverything/infra';
|
import { LiveData, useLiveData, useServices } from '@toeverything/infra';
|
||||||
@@ -31,17 +31,14 @@ export const DocInfoSheet = ({
|
|||||||
docId: string;
|
docId: string;
|
||||||
defaultOpenProperty?: DefaultOpenProperty;
|
defaultOpenProperty?: DefaultOpenProperty;
|
||||||
}) => {
|
}) => {
|
||||||
const { docsSearchService, docsService, guardService } = useServices({
|
const { docsSearchService, docsService } = useServices({
|
||||||
DocsSearchService,
|
DocsSearchService,
|
||||||
DocsService,
|
DocsService,
|
||||||
GuardService,
|
|
||||||
});
|
});
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
const canEditPropertyInfo = useLiveData(
|
const canEditPropertyInfo = useGuard('Workspace_Properties_Update');
|
||||||
guardService.can$('Workspace_Properties_Update')
|
const canEditProperty = useGuard('Doc_Update', docId);
|
||||||
);
|
|
||||||
const canEditProperty = useLiveData(guardService.can$('Doc_Update', docId));
|
|
||||||
const links = useLiveData(
|
const links = useLiveData(
|
||||||
useMemo(
|
useMemo(
|
||||||
() => LiveData.from(docsSearchService.watchRefsFrom(docId), null),
|
() => LiveData.from(docsSearchService.watchRefsFrom(docId), null),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Loading } from '@affine/component';
|
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 { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import { DocsService } from '@affine/core/modules/doc';
|
import { DocsService } from '@affine/core/modules/doc';
|
||||||
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
||||||
@@ -134,7 +134,7 @@ export const ExplorerDocNode = ({
|
|||||||
operations={finalOperations}
|
operations={finalOperations}
|
||||||
data-testid={`explorer-doc-${docId}`}
|
data-testid={`explorer-doc-${docId}`}
|
||||||
>
|
>
|
||||||
<DocPermissionGuard docId={docId} permission="Doc_Read">
|
<Guard docId={docId} permission="Doc_Read">
|
||||||
{canRead =>
|
{canRead =>
|
||||||
canRead
|
canRead
|
||||||
? children?.map((child, index) => (
|
? children?.map((child, index) => (
|
||||||
@@ -146,8 +146,8 @@ export const ExplorerDocNode = ({
|
|||||||
))
|
))
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
</DocPermissionGuard>
|
</Guard>
|
||||||
<DocPermissionGuard docId={docId} permission="Doc_Update">
|
<Guard docId={docId} permission="Doc_Update">
|
||||||
{canEdit =>
|
{canEdit =>
|
||||||
canEdit ? (
|
canEdit ? (
|
||||||
<AddItemPlaceholder
|
<AddItemPlaceholder
|
||||||
@@ -156,7 +156,7 @@ export const ExplorerDocNode = ({
|
|||||||
/>
|
/>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
</DocPermissionGuard>
|
</Guard>
|
||||||
</ExplorerTreeNode>
|
</ExplorerTreeNode>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
useConfirmModal,
|
useConfirmModal,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils';
|
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 { useBlockSuiteMetaHelper } from '@affine/core/components/hooks/affine/use-block-suite-meta-helper';
|
||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
|
import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
|
||||||
@@ -187,7 +187,7 @@ export const useExplorerDocNodeOperationsMenu = (
|
|||||||
{
|
{
|
||||||
index: 10,
|
index: 10,
|
||||||
view: (
|
view: (
|
||||||
<DocPermissionGuard docId={docId} permission="Doc_Update">
|
<Guard docId={docId} permission="Doc_Update">
|
||||||
{canEdit => (
|
{canEdit => (
|
||||||
<DocRenameSubMenu
|
<DocRenameSubMenu
|
||||||
onConfirm={handleRename}
|
onConfirm={handleRename}
|
||||||
@@ -195,7 +195,7 @@ export const useExplorerDocNodeOperationsMenu = (
|
|||||||
disabled={!canEdit}
|
disabled={!canEdit}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</DocPermissionGuard>
|
</Guard>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -224,7 +224,7 @@ export const useExplorerDocNodeOperationsMenu = (
|
|||||||
{
|
{
|
||||||
index: 97,
|
index: 97,
|
||||||
view: (
|
view: (
|
||||||
<DocPermissionGuard docId={docId} permission="Doc_Update">
|
<Guard docId={docId} permission="Doc_Update">
|
||||||
{canEdit => (
|
{canEdit => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
prefixIcon={<LinkedPageIcon />}
|
prefixIcon={<LinkedPageIcon />}
|
||||||
@@ -234,7 +234,7 @@ export const useExplorerDocNodeOperationsMenu = (
|
|||||||
{t['com.affine.page-operation.add-linked-page']()}
|
{t['com.affine.page-operation.add-linked-page']()}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</DocPermissionGuard>
|
</Guard>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -273,7 +273,7 @@ export const useExplorerDocNodeOperationsMenu = (
|
|||||||
{
|
{
|
||||||
index: 10000,
|
index: 10000,
|
||||||
view: (
|
view: (
|
||||||
<DocPermissionGuard docId={docId} permission="Doc_Trash">
|
<Guard docId={docId} permission="Doc_Trash">
|
||||||
{canMoveToTrash => (
|
{canMoveToTrash => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
type={'danger'}
|
type={'danger'}
|
||||||
@@ -284,7 +284,7 @@ export const useExplorerDocNodeOperationsMenu = (
|
|||||||
{t['com.affine.moveToTrash.title']()}
|
{t['com.affine.moveToTrash.title']()}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</DocPermissionGuard>
|
</Guard>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
MobileMenuSub,
|
MobileMenuSub,
|
||||||
useConfirmModal,
|
useConfirmModal,
|
||||||
} from '@affine/component';
|
} 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 { MoveToTrash } from '@affine/core/components/page-list';
|
||||||
import {
|
import {
|
||||||
type DocRecord,
|
type DocRecord,
|
||||||
@@ -59,7 +59,7 @@ export const ResolveConflictOperations = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DocPermissionGuard docId={docRecord.id} permission="Doc_Update">
|
<Guard docId={docRecord.id} permission="Doc_Update">
|
||||||
{canEdit => (
|
{canEdit => (
|
||||||
<MobileMenuItem
|
<MobileMenuItem
|
||||||
prefixIcon={<CalendarXmarkIcon />}
|
prefixIcon={<CalendarXmarkIcon />}
|
||||||
@@ -72,15 +72,15 @@ export const ResolveConflictOperations = ({
|
|||||||
{t['com.affine.page-properties.property.journal-remove']()}
|
{t['com.affine.page-properties.property.journal-remove']()}
|
||||||
</MobileMenuItem>
|
</MobileMenuItem>
|
||||||
)}
|
)}
|
||||||
</DocPermissionGuard>
|
</Guard>
|
||||||
<DocPermissionGuard docId={docRecord.id} permission="Doc_Trash">
|
<Guard docId={docRecord.id} permission="Doc_Trash">
|
||||||
{canTrash => (
|
{canTrash => (
|
||||||
<MoveToTrash
|
<MoveToTrash
|
||||||
onSelect={() => handleOpenTrashModal(docRecord)}
|
onSelect={() => handleOpenTrashModal(docRecord)}
|
||||||
disabled={!canTrash}
|
disabled={!canTrash}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</DocPermissionGuard>
|
</Guard>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useThemeColorV2 } from '@affine/component';
|
|||||||
import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
|
import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
|
||||||
import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-editor';
|
import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-editor';
|
||||||
import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary';
|
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 { useActiveBlocksuiteEditor } from '@affine/core/components/hooks/use-block-suite-editor';
|
||||||
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
||||||
import { PageDetailEditor } from '@affine/core/components/page-detail-editor';
|
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 { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
import { GlobalContextService } from '@affine/core/modules/global-context';
|
import { GlobalContextService } from '@affine/core/modules/global-context';
|
||||||
import { JournalService } from '@affine/core/modules/journal';
|
import { JournalService } from '@affine/core/modules/journal';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
||||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
@@ -55,7 +55,6 @@ const DetailPageImpl = () => {
|
|||||||
globalContextService,
|
globalContextService,
|
||||||
featureFlagService,
|
featureFlagService,
|
||||||
aIButtonService,
|
aIButtonService,
|
||||||
guardService,
|
|
||||||
} = useServices({
|
} = useServices({
|
||||||
WorkbenchService,
|
WorkbenchService,
|
||||||
ViewService,
|
ViewService,
|
||||||
@@ -65,7 +64,6 @@ const DetailPageImpl = () => {
|
|||||||
GlobalContextService,
|
GlobalContextService,
|
||||||
FeatureFlagService,
|
FeatureFlagService,
|
||||||
AIButtonService,
|
AIButtonService,
|
||||||
GuardService,
|
|
||||||
});
|
});
|
||||||
const editor = editorService.editor;
|
const editor = editorService.editor;
|
||||||
const workspace = workspaceService.workspace;
|
const workspace = workspaceService.workspace;
|
||||||
@@ -192,7 +190,7 @@ const DetailPageImpl = () => {
|
|||||||
[docCollection.id, editor, jumpToPageBlock, openPage, server]
|
[docCollection.id, editor, jumpToPageBlock, openPage, server]
|
||||||
);
|
);
|
||||||
|
|
||||||
const canEdit = useLiveData(guardService.can$('Doc_Update', doc.id));
|
const canEdit = useGuard('Doc_Update', doc.id);
|
||||||
|
|
||||||
const readonly =
|
const readonly =
|
||||||
!canEdit ||
|
!canEdit ||
|
||||||
@@ -254,8 +252,7 @@ const MobileDetailPage = ({
|
|||||||
const [showTitle, setShowTitle] = useState(checkShowTitle);
|
const [showTitle, setShowTitle] = useState(checkShowTitle);
|
||||||
const title = useLiveData(docDisplayMetaService.title$(pageId));
|
const title = useLiveData(docDisplayMetaService.title$(pageId));
|
||||||
|
|
||||||
const guardService = useService(GuardService);
|
const canAccess = useGuard('Doc_Read', pageId);
|
||||||
const canAccess = useLiveData(guardService.can$('Doc_Read', pageId));
|
|
||||||
|
|
||||||
const allJournalDates = useLiveData(journalService.allJournalDates$);
|
const allJournalDates = useLiveData(journalService.allJournalDates$);
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import {
|
|||||||
MobileMenuItem,
|
MobileMenuItem,
|
||||||
} from '@affine/component/ui/menu';
|
} from '@affine/component/ui/menu';
|
||||||
import { useFavorite } from '@affine/core/blocksuite/block-suite-header/favorite';
|
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 { IsFavoriteIcon } from '@affine/core/components/pure/icons';
|
||||||
import { DocInfoSheet } from '@affine/core/mobile/components';
|
import { DocInfoSheet } from '@affine/core/mobile/components';
|
||||||
import { MobileTocMenu } from '@affine/core/mobile/components/toc-menu';
|
import { MobileTocMenu } from '@affine/core/mobile/components/toc-menu';
|
||||||
import { DocService } from '@affine/core/modules/doc';
|
import { DocService } from '@affine/core/modules/doc';
|
||||||
import { EditorService } from '@affine/core/modules/editor';
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
|
||||||
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
||||||
import { preventDefault } from '@affine/core/utils';
|
import { preventDefault } from '@affine/core/utils';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -35,8 +35,7 @@ export const PageHeaderMenuButton = () => {
|
|||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
const docId = useService(DocService).doc.id;
|
const docId = useService(DocService).doc.id;
|
||||||
const guardService = useService(GuardService);
|
const canEdit = useGuard('Doc_Update', docId);
|
||||||
const canEdit = useLiveData(guardService.can$('Doc_Update', docId));
|
|
||||||
|
|
||||||
const editorService = useService(EditorService);
|
const editorService = useService(EditorService);
|
||||||
const editorContainer = useLiveData(editorService.editor.editorContainer$);
|
const editorContainer = useLiveData(editorService.editor.editorContainer$);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
toast,
|
toast,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@affine/component';
|
} 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 { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import { DocsService } from '@affine/core/modules/doc';
|
import { DocsService } from '@affine/core/modules/doc';
|
||||||
@@ -274,7 +274,7 @@ export const ExplorerDocNode = ({
|
|||||||
dropEffect={handleDropEffectOnDoc}
|
dropEffect={handleDropEffectOnDoc}
|
||||||
data-testid={`explorer-doc-${docId}`}
|
data-testid={`explorer-doc-${docId}`}
|
||||||
>
|
>
|
||||||
<DocPermissionGuard docId={docId} permission="Doc_Read">
|
<Guard docId={docId} permission="Doc_Read">
|
||||||
{canRead =>
|
{canRead =>
|
||||||
canRead
|
canRead
|
||||||
? children?.map((child, index) => (
|
? children?.map((child, index) => (
|
||||||
@@ -291,7 +291,7 @@ export const ExplorerDocNode = ({
|
|||||||
))
|
))
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
</DocPermissionGuard>
|
</Guard>
|
||||||
</ExplorerTreeNode>
|
</ExplorerTreeNode>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
useConfirmModal,
|
useConfirmModal,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils';
|
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 { useBlockSuiteMetaHelper } from '@affine/core/components/hooks/affine/use-block-suite-meta-helper';
|
||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
|
import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
|
||||||
@@ -175,7 +175,7 @@ export const useExplorerDocNodeOperations = (
|
|||||||
{
|
{
|
||||||
index: 99,
|
index: 99,
|
||||||
view: (
|
view: (
|
||||||
<DocPermissionGuard docId={docId} permission="Doc_Update">
|
<Guard docId={docId} permission="Doc_Update">
|
||||||
{canEdit => (
|
{canEdit => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
prefixIcon={<LinkedPageIcon />}
|
prefixIcon={<LinkedPageIcon />}
|
||||||
@@ -185,7 +185,7 @@ export const useExplorerDocNodeOperations = (
|
|||||||
{t['com.affine.page-operation.add-linked-page']()}
|
{t['com.affine.page-operation.add-linked-page']()}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</DocPermissionGuard>
|
</Guard>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -239,7 +239,7 @@ export const useExplorerDocNodeOperations = (
|
|||||||
{
|
{
|
||||||
index: 10000,
|
index: 10000,
|
||||||
view: (
|
view: (
|
||||||
<DocPermissionGuard docId={docId} permission="Doc_Trash">
|
<Guard docId={docId} permission="Doc_Trash">
|
||||||
{canMoveToTrash => (
|
{canMoveToTrash => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
type={'danger'}
|
type={'danger'}
|
||||||
@@ -250,7 +250,7 @@ export const useExplorerDocNodeOperations = (
|
|||||||
{t['com.affine.moveToTrash.title']()}
|
{t['com.affine.moveToTrash.title']()}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</DocPermissionGuard>
|
</Guard>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
useDropTarget,
|
useDropTarget,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { RenameModal } from '@affine/component/rename-modal';
|
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 { AppSidebarService } from '@affine/core/modules/app-sidebar';
|
||||||
import type { DocPermissionActions } from '@affine/core/modules/permissions';
|
import type { DocPermissionActions } from '@affine/core/modules/permissions';
|
||||||
import { WorkbenchLink } from '@affine/core/modules/workbench';
|
import { WorkbenchLink } from '@affine/core/modules/workbench';
|
||||||
@@ -285,7 +285,7 @@ export const ExplorerTreeNode = ({
|
|||||||
? {
|
? {
|
||||||
index: 0,
|
index: 0,
|
||||||
view: renameableGuard ? (
|
view: renameableGuard ? (
|
||||||
<DocPermissionGuard
|
<Guard
|
||||||
permission={renameableGuard.action}
|
permission={renameableGuard.action}
|
||||||
docId={renameableGuard.docId}
|
docId={renameableGuard.docId}
|
||||||
>
|
>
|
||||||
@@ -300,7 +300,7 @@ export const ExplorerTreeNode = ({
|
|||||||
{t['com.affine.menu.rename']()}
|
{t['com.affine.menu.rename']()}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</DocPermissionGuard>
|
</Guard>
|
||||||
) : (
|
) : (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={'explorer-tree-rename'}
|
key={'explorer-tree-rename'}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { AIProvider } from '@affine/core/blocksuite/ai';
|
|||||||
import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-editor';
|
import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-editor';
|
||||||
import { EditorOutlineViewer } from '@affine/core/blocksuite/outline-viewer';
|
import { EditorOutlineViewer } from '@affine/core/blocksuite/outline-viewer';
|
||||||
import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary';
|
import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary';
|
||||||
|
import { useGuard } from '@affine/core/components/guard';
|
||||||
import { PageNotFound } from '@affine/core/desktop/pages/404';
|
import { PageNotFound } from '@affine/core/desktop/pages/404';
|
||||||
import { EditorService } from '@affine/core/modules/editor';
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
|
||||||
import { DebugLogger } from '@affine/debug';
|
import { DebugLogger } from '@affine/debug';
|
||||||
import { GfxControllerIdentifier } from '@blocksuite/affine/block-std/gfx';
|
import { GfxControllerIdentifier } from '@blocksuite/affine/block-std/gfx';
|
||||||
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
|
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
|
||||||
@@ -85,7 +85,6 @@ function DocPeekPreviewEditor({
|
|||||||
const defaultOpenProperty = useLiveData(editor.defaultOpenProperty$);
|
const defaultOpenProperty = useLiveData(editor.defaultOpenProperty$);
|
||||||
const workbench = useService(WorkbenchService).workbench;
|
const workbench = useService(WorkbenchService).workbench;
|
||||||
const peekView = useService(PeekViewService).peekView;
|
const peekView = useService(PeekViewService).peekView;
|
||||||
const guardService = useService(GuardService);
|
|
||||||
const editorElement = useLiveData(editor.editorContainer$);
|
const editorElement = useLiveData(editor.editorContainer$);
|
||||||
|
|
||||||
const isInTrash = useLiveData(doc.record.trash$);
|
const isInTrash = useLiveData(doc.record.trash$);
|
||||||
@@ -151,7 +150,7 @@ function DocPeekPreviewEditor({
|
|||||||
peekView.close();
|
peekView.close();
|
||||||
}, [doc, peekView, workbench]);
|
}, [doc, peekView, workbench]);
|
||||||
|
|
||||||
const canEdit = useLiveData(guardService.can$('Doc_Update', doc.id));
|
const canEdit = useGuard('Doc_Update', doc.id);
|
||||||
|
|
||||||
const readonly = !canEdit || isInTrash;
|
const readonly = !canEdit || isInTrash;
|
||||||
|
|
||||||
@@ -220,8 +219,7 @@ export function DocPeekPreview({
|
|||||||
!animating
|
!animating
|
||||||
);
|
);
|
||||||
|
|
||||||
const guardService = useService(GuardService);
|
const canAccess = useGuard('Doc_Read', docId);
|
||||||
const canAccess = useLiveData(guardService.can$('Doc_Read', docId));
|
|
||||||
|
|
||||||
// if sync engine has been synced and the page is null, show 404 page.
|
// if sync engine has been synced and the page is null, show 404 page.
|
||||||
if (!doc || !editor || !canAccess) {
|
if (!doc || !editor || !canAccess) {
|
||||||
|
|||||||
@@ -66,17 +66,6 @@ export class GuardService extends Service {
|
|||||||
const docId = args[0];
|
const docId = args[0];
|
||||||
return LiveData.from(
|
return LiveData.from(
|
||||||
new Observable(subscriber => {
|
new Observable(subscriber => {
|
||||||
// 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;
|
let prev: boolean | undefined = undefined;
|
||||||
|
|
||||||
const subscription = combineLatest([
|
const subscription = combineLatest([
|
||||||
@@ -128,6 +117,29 @@ export class GuardService extends Service {
|
|||||||
return permissions[action as keyof typeof permissions] ?? false;
|
return permissions[action as keyof typeof permissions] ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
revalidateCan<T extends WorkspacePermissionActions | DocPermissionActions>(
|
||||||
|
_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(
|
private readonly revalidateWorkspacePermission = effect(
|
||||||
exhaustMapWithTrailing(() =>
|
exhaustMapWithTrailing(() =>
|
||||||
fromPromise(() => this.guardStore.getWorkspacePermissions()).pipe(
|
fromPromise(() => this.guardStore.getWorkspacePermissions()).pipe(
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
useConfirmModal,
|
useConfirmModal,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
|
import { useGuard } from '@affine/core/components/guard';
|
||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
import { DocService } from '@affine/core/modules/doc';
|
import { DocService } from '@affine/core/modules/doc';
|
||||||
import {
|
import {
|
||||||
DocGrantedUsersService,
|
DocGrantedUsersService,
|
||||||
type GrantedUser,
|
type GrantedUser,
|
||||||
GuardService,
|
|
||||||
WorkspacePermissionService,
|
WorkspacePermissionService,
|
||||||
} from '@affine/core/modules/permissions';
|
} from '@affine/core/modules/permissions';
|
||||||
import { UserFriendlyError } from '@affine/error';
|
import { UserFriendlyError } from '@affine/error';
|
||||||
@@ -131,7 +131,6 @@ const Options = ({
|
|||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const docGrantedUsersService = useService(DocGrantedUsersService);
|
const docGrantedUsersService = useService(DocGrantedUsersService);
|
||||||
const docService = useService(DocService);
|
const docService = useService(DocService);
|
||||||
const guardService = useService(GuardService);
|
|
||||||
const workspacePermissionService = useService(WorkspacePermissionService);
|
const workspacePermissionService = useService(WorkspacePermissionService);
|
||||||
const isWorkspaceOwner = useLiveData(
|
const isWorkspaceOwner = useLiveData(
|
||||||
workspacePermissionService.permission.isOwner$
|
workspacePermissionService.permission.isOwner$
|
||||||
@@ -140,11 +139,8 @@ const Options = ({
|
|||||||
const { openConfirmModal } = useConfirmModal();
|
const { openConfirmModal } = useConfirmModal();
|
||||||
|
|
||||||
const canTransferOwner =
|
const canTransferOwner =
|
||||||
useLiveData(guardService.can$('Doc_TransferOwner', docService.doc.id)) &&
|
useGuard('Doc_TransferOwner', docService.doc.id) && !!isWorkspaceOwner;
|
||||||
!!isWorkspaceOwner;
|
const canManageUsers = useGuard('Doc_Users_Manage', docService.doc.id);
|
||||||
const canManageUsers = useLiveData(
|
|
||||||
guardService.can$('Doc_Users_Manage', docService.doc.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateUserRole = useCallback(
|
const updateUserRole = useCallback(
|
||||||
async (userId: string, role: DocRole) => {
|
async (userId: string, role: DocRole) => {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Skeleton } from '@affine/component';
|
import { Skeleton } from '@affine/component';
|
||||||
|
import { useGuard } from '@affine/core/components/guard';
|
||||||
import { DocService } from '@affine/core/modules/doc';
|
import { DocService } from '@affine/core/modules/doc';
|
||||||
import {
|
import {
|
||||||
DocGrantedUsersService,
|
DocGrantedUsersService,
|
||||||
type GrantedUser,
|
type GrantedUser,
|
||||||
GuardService,
|
|
||||||
} from '@affine/core/modules/permissions';
|
} from '@affine/core/modules/permissions';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { ArrowLeftBigIcon } from '@blocksuite/icons/rc';
|
import { ArrowLeftBigIcon } from '@blocksuite/icons/rc';
|
||||||
@@ -33,11 +33,8 @@ export const MemberManagement = ({
|
|||||||
docGrantedUsersService.grantedUserCount$
|
docGrantedUsersService.grantedUserCount$
|
||||||
);
|
);
|
||||||
const docService = useService(DocService);
|
const docService = useService(DocService);
|
||||||
const guardService = useService(GuardService);
|
|
||||||
|
|
||||||
const canManageUsers = useLiveData(
|
const canManageUsers = useGuard('Doc_Users_Manage', docService.doc.id);
|
||||||
guardService.can$('Doc_Users_Manage', docService.doc.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Divider, Skeleton } from '@affine/component';
|
import { Divider, Skeleton } from '@affine/component';
|
||||||
import { Button } from '@affine/component/ui/button';
|
import { Button } from '@affine/component/ui/button';
|
||||||
|
import { useGuard } from '@affine/core/components/guard';
|
||||||
import { ServerService } from '@affine/core/modules/cloud';
|
import { ServerService } from '@affine/core/modules/cloud';
|
||||||
import { DocService } from '@affine/core/modules/doc';
|
import { DocService } from '@affine/core/modules/doc';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
|
||||||
import { ShareInfoService } from '@affine/core/modules/share-doc';
|
import { ShareInfoService } from '@affine/core/modules/share-doc';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
@@ -64,15 +64,10 @@ export const AFFiNESharePage = (
|
|||||||
const shareInfoService = useService(ShareInfoService);
|
const shareInfoService = useService(ShareInfoService);
|
||||||
const serverService = useService(ServerService);
|
const serverService = useService(ServerService);
|
||||||
const docService = useService(DocService);
|
const docService = useService(DocService);
|
||||||
const guardService = useService(GuardService);
|
|
||||||
|
|
||||||
const canManageUsers = useLiveData(
|
const canManageUsers = useGuard('Doc_Users_Manage', docService.doc.id);
|
||||||
guardService.can$('Doc_Users_Manage', docService.doc.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
const canPublish = useLiveData(
|
const canPublish = useGuard('Doc_Publish', docService.doc.id);
|
||||||
guardService.can$('Doc_Publish', docService.doc.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
shareInfoService.shareInfo.revalidate();
|
shareInfoService.shareInfo.revalidate();
|
||||||
|
|||||||
Reference in New Issue
Block a user