feat(core): add no access to share menu (#10927)

This commit is contained in:
JimmFly
2025-03-20 07:31:11 +00:00
parent c16ae2d5b4
commit d47bb64597
13 changed files with 209 additions and 91 deletions

View File

@@ -6,8 +6,10 @@ import { EmptyNodeChildren } from '../../layouts/empty-node-children';
export const Empty = ({
onDrop,
noAccessible = false,
}: {
onDrop: (data: DropTargetDropEvent<AffineDNDData>) => void;
noAccessible?: boolean;
}) => {
const { dropTargetRef } = useDropTarget<AffineDNDData>(
() => ({
@@ -19,7 +21,9 @@ export const Empty = ({
return (
<EmptyNodeChildren ref={dropTargetRef}>
{t['com.affine.rootAppSidebar.docs.no-subdoc']()}
{noAccessible
? t['com.affine.share-menu.option.permission.no-access']()
: t['com.affine.rootAppSidebar.docs.no-subdoc']()}
</EmptyNodeChildren>
);
};

View File

@@ -5,6 +5,7 @@ import {
toast,
Tooltip,
} from '@affine/component';
import { DocPermissionGuard } from '@affine/core/components/guard/doc-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';
@@ -43,6 +44,7 @@ export const ExplorerDocNode = ({
}: {
docId: string;
isLinked?: boolean;
forwardKey?: string;
} & GenericExplorerNode) => {
const t = useI18n();
const {
@@ -261,24 +263,35 @@ export const ExplorerDocNode = ({
}}
onRename={handleRename}
childrenPlaceholder={
searching ? null : <Empty onDrop={handleDropOnPlaceholder} />
searching ? null : (
<Empty
onDrop={handleDropOnPlaceholder}
noAccessible={!!children && children.length > 0}
/>
)
}
operations={finalOperations}
dropEffect={handleDropEffectOnDoc}
data-testid={`explorer-doc-${docId}`}
>
{children?.map(child => (
<ExplorerDocNode
key={child.docId}
docId={child.docId}
reorderable={false}
location={{
at: 'explorer:doc:linked-docs',
docId,
}}
isLinked
/>
))}
<DocPermissionGuard docId={docId} permission="Doc_Read">
{canRead =>
canRead
? children?.map((child, index) => (
<ExplorerDocNode
key={`${child.docId}-${index}`}
docId={child.docId}
reorderable={false}
location={{
at: 'explorer:doc:linked-docs',
docId,
}}
isLinked
/>
))
: null
}
</DocPermissionGuard>
</ExplorerTreeNode>
);
};

View File

@@ -73,6 +73,7 @@ export interface BaseExplorerTreeNodeProps {
operations?: NodeOperation[];
childrenOperations?: NodeOperation[];
childrenPlaceholder?: React.ReactNode;
linkComponent?: React.ComponentType<
React.PropsWithChildren<{ to: To; className?: string }> &
RefAttributes<any> & { draggable?: boolean }
@@ -492,7 +493,7 @@ export const ExplorerTreeNode = ({
<Collapsible.Content style={{ display: dragging ? 'none' : undefined }}>
{/* For lastInGroup check, the placeholder must be placed above all children in the dom */}
<div className={styles.collapseContentPlaceholder}>
{childCount === 0 && !collapsed && childrenPlaceholder}
{childCount === 0 && !collapsed ? childrenPlaceholder : null}
</div>
<ExplorerTreeContext.Provider value={contextValue}>
{collapsed ? null : children}

View File

@@ -220,9 +220,12 @@ export function DocPeekPreview({
!animating
);
const guardService = useService(GuardService);
const canAccess = useLiveData(guardService.can$('Doc_Read', docId));
// if sync engine has been synced and the page is null, show 404 page.
if (!doc || !editor) {
return loading ? (
if (!doc || !editor || !canAccess) {
return loading || canAccess === undefined ? (
<PageDetailSkeleton key="current-page-is-null" />
) : (
<PageNotFound noPermission />

View File

@@ -28,6 +28,8 @@ const getRoleName = (t: ReturnType<typeof useI18n>, role?: DocRole) => {
return t['com.affine.share-menu.option.permission.can-edit']();
case DocRole.Reader:
return t['com.affine.share-menu.option.permission.can-read']();
case DocRole.None:
return t['com.affine.share-menu.option.permission.no-access']();
default:
return '';
}
@@ -53,7 +55,9 @@ export const MembersPermission = ({
[docDefaultRole, t]
);
const showTips =
docDefaultRole === DocRole.Reader || docDefaultRole === DocRole.Editor;
docDefaultRole === DocRole.Reader ||
docDefaultRole === DocRole.Editor ||
docDefaultRole === DocRole.None;
const changePermission = useAsyncCallback(
async (docRole: DocRole) => {
try {
@@ -93,6 +97,14 @@ export const MembersPermission = ({
changePermission(DocRole.Reader);
}, [changePermission, hittingPaywall, openPaywallModal]);
const selectNone = useCallback(() => {
if (hittingPaywall) {
openPaywallModal?.();
return;
}
changePermission(DocRole.None);
}, [changePermission, hittingPaywall, openPaywallModal]);
return (
<div className={styles.rowContainerStyle}>
<div className={styles.labelStyle}>
@@ -141,6 +153,17 @@ export const MembersPermission = ({
</div>
</div>
</MenuItem>
<MenuItem
onSelect={selectNone}
selected={docDefaultRole === DocRole.None}
>
<div className={styles.publicItemRowStyle}>
<div className={styles.tagContainerStyle}>
{t['com.affine.share-menu.option.permission.no-access']()}
{hittingPaywall ? <PlanTag /> : null}
</div>
</div>
</MenuItem>
</>
}
>