From 7cfa8fd877612e1675fe551dd9f3a796866367fb Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Wed, 11 Feb 2026 12:58:32 -0800 Subject: [PATCH] ui permissions --- .../hooks/use-page-permission.ts | 26 +++++++++++++ apps/client/src/ee/page-permission/index.ts | 1 + .../components/comment-list-with-tabs.tsx | 39 +++++++++---------- .../src/features/editor/title-editor.tsx | 13 ++++--- apps/client/src/pages/page/page.tsx | 21 ++-------- 5 files changed, 58 insertions(+), 42 deletions(-) create mode 100644 apps/client/src/ee/page-permission/hooks/use-page-permission.ts diff --git a/apps/client/src/ee/page-permission/hooks/use-page-permission.ts b/apps/client/src/ee/page-permission/hooks/use-page-permission.ts new file mode 100644 index 00000000..deaa8aea --- /dev/null +++ b/apps/client/src/ee/page-permission/hooks/use-page-permission.ts @@ -0,0 +1,26 @@ +import { useSpaceAbility } from "@/features/space/permissions/use-space-ability"; +import { + SpaceCaslAction, + SpaceCaslSubject, +} from "@/features/space/permissions/permissions.type"; +import { usePageRestrictionInfoQuery } from "@/ee/page-permission/queries/page-permission-query"; + +export function usePagePermission(pageId: string, spaceRules: any) { + const spaceAbility = useSpaceAbility(spaceRules); + const { data: restrictionInfo, isLoading } = + usePageRestrictionInfoQuery(pageId); + + if (isLoading || !restrictionInfo) { + return { canEdit: false, restrictionInfo: undefined }; + } + + const hasRestriction = + restrictionInfo.hasDirectRestriction || + restrictionInfo.hasInheritedRestriction; + + const canEdit = hasRestriction + ? (restrictionInfo.userAccess?.canEdit ?? false) + : spaceAbility.can(SpaceCaslAction.Manage, SpaceCaslSubject.Page); + + return { canEdit, restrictionInfo }; +} diff --git a/apps/client/src/ee/page-permission/index.ts b/apps/client/src/ee/page-permission/index.ts index 32597373..4555ce54 100644 --- a/apps/client/src/ee/page-permission/index.ts +++ b/apps/client/src/ee/page-permission/index.ts @@ -4,6 +4,7 @@ export * from "./components/publish-tab"; export * from "./components/page-permission-list"; export * from "./components/page-permission-item"; export * from "./components/general-access-select"; +export * from "./hooks/use-page-permission"; export * from "./queries/page-permission-query"; export * from "./services/page-permission-service"; export * from "./types/page-permission.types"; diff --git a/apps/client/src/features/comment/components/comment-list-with-tabs.tsx b/apps/client/src/features/comment/components/comment-list-with-tabs.tsx index 35c7d472..c493513d 100644 --- a/apps/client/src/features/comment/components/comment-list-with-tabs.tsx +++ b/apps/client/src/features/comment/components/comment-list-with-tabs.tsx @@ -17,11 +17,7 @@ import { useTranslation } from "react-i18next"; import { useQueryEmit } from "@/features/websocket/use-query-emit"; import { useIsCloudEE } from "@/hooks/use-is-cloud-ee"; import { useGetSpaceBySlugQuery } from "@/features/space/queries/space-query.ts"; -import { useSpaceAbility } from "@/features/space/permissions/use-space-ability.ts"; -import { - SpaceCaslAction, - SpaceCaslSubject, -} from "@/features/space/permissions/permissions.type.ts"; +import { usePagePermission } from "@/ee/page-permission"; function CommentListWithTabs() { const { t } = useTranslation(); @@ -38,13 +34,9 @@ function CommentListWithTabs() { const isCloudEE = useIsCloudEE(); const { data: space } = useGetSpaceBySlugQuery(page?.space?.slug); - const spaceRules = space?.membership?.permissions; - const spaceAbility = useSpaceAbility(spaceRules); - - - const canComment: boolean = spaceAbility.can( - SpaceCaslAction.Manage, - SpaceCaslSubject.Page + const { canEdit: canComment } = usePagePermission( + page?.id, + space?.membership?.permissions, ); // Separate active and resolved comments @@ -54,14 +46,14 @@ function CommentListWithTabs() { } const parentComments = comments.items.filter( - (comment: IComment) => comment.parentCommentId === null + (comment: IComment) => comment.parentCommentId === null, ); const active = parentComments.filter( - (comment: IComment) => !comment.resolvedAt + (comment: IComment) => !comment.resolvedAt, ); const resolved = parentComments.filter( - (comment: IComment) => comment.resolvedAt + (comment: IComment) => comment.resolvedAt, ); return { activeComments: active, resolvedComments: resolved }; @@ -89,7 +81,7 @@ function CommentListWithTabs() { setIsLoading(false); } }, - [createCommentMutation, page?.id] + [createCommentMutation, page?.id], ); const renderComments = useCallback( @@ -131,7 +123,7 @@ function CommentListWithTabs() { )} ), - [comments, handleAddReply, isLoading, space?.membership?.role] + [comments, handleAddReply, isLoading, space?.membership?.role], ); if (isCommentsLoading) { @@ -199,7 +191,14 @@ function CommentListWithTabs() { } return ( -
+
comments.items.filter( - (comment: IComment) => comment.parentCommentId === parentId + (comment: IComment) => comment.parentCommentId === parentId, ), - [comments.items] + [comments.items], ); return ( diff --git a/apps/client/src/features/editor/title-editor.tsx b/apps/client/src/features/editor/title-editor.tsx index 33d1984e..5f8d9271 100644 --- a/apps/client/src/features/editor/title-editor.tsx +++ b/apps/client/src/features/editor/title-editor.tsx @@ -171,11 +171,14 @@ export function TitleEditor({ }, [pageId]); useEffect(() => { - // honor user default page edit mode preference - if (userPageEditMode && titleEditor && editable) { - if (userPageEditMode === PageEditMode.Edit) { - titleEditor.setEditable(true); - } else if (userPageEditMode === PageEditMode.Read) { + if (titleEditor) { + if (userPageEditMode && editable) { + if (userPageEditMode === PageEditMode.Edit) { + titleEditor.setEditable(true); + } else if (userPageEditMode === PageEditMode.Read) { + titleEditor.setEditable(false); + } + } else { titleEditor.setEditable(false); } } diff --git a/apps/client/src/pages/page/page.tsx b/apps/client/src/pages/page/page.tsx index a1dfee6f..dc12f815 100644 --- a/apps/client/src/pages/page/page.tsx +++ b/apps/client/src/pages/page/page.tsx @@ -6,13 +6,9 @@ import { Helmet } from "react-helmet-async"; import PageHeader from "@/features/page/components/header/page-header.tsx"; import { extractPageSlugId } from "@/lib"; import { useGetSpaceBySlugQuery } from "@/features/space/queries/space-query.ts"; -import { useSpaceAbility } from "@/features/space/permissions/use-space-ability.ts"; -import { - SpaceCaslAction, - SpaceCaslSubject, -} from "@/features/space/permissions/permissions.type.ts"; import { useTranslation } from "react-i18next"; import React from "react"; +import { usePagePermission } from "@/ee/page-permission"; const MemoizedFullEditor = React.memo(FullEditor); const MemoizedPageHeader = React.memo(PageHeader); @@ -30,8 +26,7 @@ export default function Page() { } = usePageQuery({ pageId: extractPageSlugId(pageSlug) }); const { data: space } = useGetSpaceBySlugQuery(page?.space?.slug); - const spaceRules = space?.membership?.permissions; - const spaceAbility = useSpaceAbility(spaceRules); + const { canEdit } = usePagePermission(page?.id, space?.membership?.permissions); if (isLoading) { return <>; @@ -55,12 +50,7 @@ export default function Page() { {`${page?.icon || ""} ${page?.title || t("untitled")}`} - +