ui permissions

This commit is contained in:
Philipinho
2026-02-11 12:58:32 -08:00
parent 046d9a31f1
commit 7cfa8fd877
5 changed files with 58 additions and 42 deletions
@@ -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 };
}
@@ -4,6 +4,7 @@ export * from "./components/publish-tab";
export * from "./components/page-permission-list"; export * from "./components/page-permission-list";
export * from "./components/page-permission-item"; export * from "./components/page-permission-item";
export * from "./components/general-access-select"; export * from "./components/general-access-select";
export * from "./hooks/use-page-permission";
export * from "./queries/page-permission-query"; export * from "./queries/page-permission-query";
export * from "./services/page-permission-service"; export * from "./services/page-permission-service";
export * from "./types/page-permission.types"; export * from "./types/page-permission.types";
@@ -17,11 +17,7 @@ import { useTranslation } from "react-i18next";
import { useQueryEmit } from "@/features/websocket/use-query-emit"; import { useQueryEmit } from "@/features/websocket/use-query-emit";
import { useIsCloudEE } from "@/hooks/use-is-cloud-ee"; import { useIsCloudEE } from "@/hooks/use-is-cloud-ee";
import { useGetSpaceBySlugQuery } from "@/features/space/queries/space-query.ts"; import { useGetSpaceBySlugQuery } from "@/features/space/queries/space-query.ts";
import { useSpaceAbility } from "@/features/space/permissions/use-space-ability.ts"; import { usePagePermission } from "@/ee/page-permission";
import {
SpaceCaslAction,
SpaceCaslSubject,
} from "@/features/space/permissions/permissions.type.ts";
function CommentListWithTabs() { function CommentListWithTabs() {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -38,13 +34,9 @@ function CommentListWithTabs() {
const isCloudEE = useIsCloudEE(); const isCloudEE = useIsCloudEE();
const { data: space } = useGetSpaceBySlugQuery(page?.space?.slug); const { data: space } = useGetSpaceBySlugQuery(page?.space?.slug);
const spaceRules = space?.membership?.permissions; const { canEdit: canComment } = usePagePermission(
const spaceAbility = useSpaceAbility(spaceRules); page?.id,
space?.membership?.permissions,
const canComment: boolean = spaceAbility.can(
SpaceCaslAction.Manage,
SpaceCaslSubject.Page
); );
// Separate active and resolved comments // Separate active and resolved comments
@@ -54,14 +46,14 @@ function CommentListWithTabs() {
} }
const parentComments = comments.items.filter( const parentComments = comments.items.filter(
(comment: IComment) => comment.parentCommentId === null (comment: IComment) => comment.parentCommentId === null,
); );
const active = parentComments.filter( const active = parentComments.filter(
(comment: IComment) => !comment.resolvedAt (comment: IComment) => !comment.resolvedAt,
); );
const resolved = parentComments.filter( const resolved = parentComments.filter(
(comment: IComment) => comment.resolvedAt (comment: IComment) => comment.resolvedAt,
); );
return { activeComments: active, resolvedComments: resolved }; return { activeComments: active, resolvedComments: resolved };
@@ -89,7 +81,7 @@ function CommentListWithTabs() {
setIsLoading(false); setIsLoading(false);
} }
}, },
[createCommentMutation, page?.id] [createCommentMutation, page?.id],
); );
const renderComments = useCallback( const renderComments = useCallback(
@@ -131,7 +123,7 @@ function CommentListWithTabs() {
)} )}
</Paper> </Paper>
), ),
[comments, handleAddReply, isLoading, space?.membership?.role] [comments, handleAddReply, isLoading, space?.membership?.role],
); );
if (isCommentsLoading) { if (isCommentsLoading) {
@@ -199,7 +191,14 @@ function CommentListWithTabs() {
} }
return ( return (
<div style={{ height: "85vh", display: "flex", flexDirection: "column", marginTop: '-15px' }}> <div
style={{
height: "85vh",
display: "flex",
flexDirection: "column",
marginTop: "-15px",
}}
>
<Tabs defaultValue="open" variant="default" style={{ flex: "0 0 auto" }}> <Tabs defaultValue="open" variant="default" style={{ flex: "0 0 auto" }}>
<Tabs.List justify="center"> <Tabs.List justify="center">
<Tabs.Tab <Tabs.Tab
@@ -273,9 +272,9 @@ const ChildComments = ({
const getChildComments = useCallback( const getChildComments = useCallback(
(parentId: string) => (parentId: string) =>
comments.items.filter( comments.items.filter(
(comment: IComment) => comment.parentCommentId === parentId (comment: IComment) => comment.parentCommentId === parentId,
), ),
[comments.items] [comments.items],
); );
return ( return (
@@ -171,11 +171,14 @@ export function TitleEditor({
}, [pageId]); }, [pageId]);
useEffect(() => { useEffect(() => {
// honor user default page edit mode preference if (titleEditor) {
if (userPageEditMode && titleEditor && editable) { if (userPageEditMode && editable) {
if (userPageEditMode === PageEditMode.Edit) { if (userPageEditMode === PageEditMode.Edit) {
titleEditor.setEditable(true); titleEditor.setEditable(true);
} else if (userPageEditMode === PageEditMode.Read) { } else if (userPageEditMode === PageEditMode.Read) {
titleEditor.setEditable(false);
}
} else {
titleEditor.setEditable(false); titleEditor.setEditable(false);
} }
} }
+4 -17
View File
@@ -6,13 +6,9 @@ import { Helmet } from "react-helmet-async";
import PageHeader from "@/features/page/components/header/page-header.tsx"; import PageHeader from "@/features/page/components/header/page-header.tsx";
import { extractPageSlugId } from "@/lib"; import { extractPageSlugId } from "@/lib";
import { useGetSpaceBySlugQuery } from "@/features/space/queries/space-query.ts"; 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 { useTranslation } from "react-i18next";
import React from "react"; import React from "react";
import { usePagePermission } from "@/ee/page-permission";
const MemoizedFullEditor = React.memo(FullEditor); const MemoizedFullEditor = React.memo(FullEditor);
const MemoizedPageHeader = React.memo(PageHeader); const MemoizedPageHeader = React.memo(PageHeader);
@@ -30,8 +26,7 @@ export default function Page() {
} = usePageQuery({ pageId: extractPageSlugId(pageSlug) }); } = usePageQuery({ pageId: extractPageSlugId(pageSlug) });
const { data: space } = useGetSpaceBySlugQuery(page?.space?.slug); const { data: space } = useGetSpaceBySlugQuery(page?.space?.slug);
const spaceRules = space?.membership?.permissions; const { canEdit } = usePagePermission(page?.id, space?.membership?.permissions);
const spaceAbility = useSpaceAbility(spaceRules);
if (isLoading) { if (isLoading) {
return <></>; return <></>;
@@ -55,12 +50,7 @@ export default function Page() {
<title>{`${page?.icon || ""} ${page?.title || t("untitled")}`}</title> <title>{`${page?.icon || ""} ${page?.title || t("untitled")}`}</title>
</Helmet> </Helmet>
<MemoizedPageHeader <MemoizedPageHeader readOnly={!canEdit} />
readOnly={spaceAbility.cannot(
SpaceCaslAction.Manage,
SpaceCaslSubject.Page,
)}
/>
<MemoizedFullEditor <MemoizedFullEditor
key={page.id} key={page.id}
@@ -69,10 +59,7 @@ export default function Page() {
content={page.content} content={page.content}
slugId={page.slugId} slugId={page.slugId}
spaceSlug={page?.space?.slug} spaceSlug={page?.space?.slug}
editable={spaceAbility.can( editable={canEdit}
SpaceCaslAction.Manage,
SpaceCaslSubject.Page,
)}
/> />
<MemoizedHistoryModal pageId={page.id} /> <MemoizedHistoryModal pageId={page.id} />
</div> </div>