mirror of
https://github.com/docmost/docmost.git
synced 2026-08-23 06:02:11 +10:00
feat(ee): space control to hide comments from viewers role
This commit is contained in:
@@ -508,6 +508,11 @@
|
||||
"Allow viewers to comment": "Allow viewers to comment",
|
||||
"Allow viewers to add comments on pages in this space.": "Allow viewers to add comments on pages in this space.",
|
||||
"Toggle viewer comments": "Toggle viewer comments",
|
||||
"Hide comments from viewers": "Hide comments from viewers",
|
||||
"Viewers cannot see or add comments on pages in this space.": "Viewers cannot see or add comments on pages in this space.",
|
||||
"Toggle hide comments from viewers": "Toggle hide comments from viewers",
|
||||
"Turn off 'Allow viewers to comment' first": "Turn off 'Allow viewers to comment' first",
|
||||
"Turn off 'Hide comments from viewers' first": "Turn off 'Hide comments from viewers' first",
|
||||
"Public sharing is disabled at the workspace level": "Public sharing is disabled at the workspace level",
|
||||
"Prevent pages in this space from being shared publicly.": "Prevent pages in this space from being shared publicly.",
|
||||
"Page permissions": "Page permissions",
|
||||
|
||||
@@ -11,11 +11,13 @@ import { pageEditorAtom } from "@/features/editor/atoms/editor-atoms.ts";
|
||||
import AsideChatPanel from "@/ee/ai-chat/components/aside-chat-panel";
|
||||
import { PageDetailsAside } from "@/features/page-details/components/page-details-aside.tsx";
|
||||
import { ASIDE_PANEL_ID } from "@/hooks/use-toggle-aside.tsx";
|
||||
import { useCanViewComments } from "@/features/comment/hooks/use-can-view-comments.ts";
|
||||
|
||||
export default function Aside() {
|
||||
const [{ tab, isAsideOpen }, setAsideState] = useAtom(asideStateAtom);
|
||||
const { t } = useTranslation();
|
||||
const pageEditor = useAtomValue(pageEditorAtom);
|
||||
const canViewComments = useCanViewComments();
|
||||
const closeAside = () => setAsideState((s) => ({ ...s, isAsideOpen: false }));
|
||||
|
||||
useEffect(() => {
|
||||
@@ -23,12 +25,18 @@ export default function Aside() {
|
||||
document.getElementById(ASIDE_PANEL_ID)?.focus();
|
||||
}, [isAsideOpen, tab]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isAsideOpen && tab === "comments" && !canViewComments) {
|
||||
setAsideState({ tab: "", isAsideOpen: false });
|
||||
}
|
||||
}, [isAsideOpen, tab, canViewComments, setAsideState]);
|
||||
|
||||
let title: string;
|
||||
let component: ReactNode;
|
||||
|
||||
switch (tab) {
|
||||
case "comments":
|
||||
component = <CommentListWithTabs />;
|
||||
component = canViewComments ? <CommentListWithTabs /> : null;
|
||||
title = "Comments";
|
||||
break;
|
||||
case "toc":
|
||||
|
||||
@@ -19,6 +19,7 @@ export const Feature = {
|
||||
SHARING_CONTROLS: 'sharing:controls',
|
||||
TEMPLATES: 'templates',
|
||||
VIEWER_COMMENTS: 'comment:viewer',
|
||||
HIDE_COMMENTS: 'comment:hide',
|
||||
PERSONAL_SPACES: 'spaces:personal',
|
||||
DOCX_EXPORT: 'export:docx',
|
||||
BASES: 'bases',
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Group, Text, Switch, Tooltip } from "@mantine/core";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ISpace } from "@/features/space/types/space.types.ts";
|
||||
import { useUpdateSpaceMutation } from "@/features/space/queries/space-query.ts";
|
||||
import { useHasFeature } from "@/ee/hooks/use-feature.ts";
|
||||
import { Feature } from "@/ee/features.ts";
|
||||
import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label.ts";
|
||||
|
||||
type SpaceHideCommentsToggleProps = {
|
||||
space: ISpace;
|
||||
};
|
||||
|
||||
export default function SpaceHideCommentsToggle({
|
||||
space,
|
||||
}: SpaceHideCommentsToggleProps) {
|
||||
const { t } = useTranslation();
|
||||
const hasHideComments = useHasFeature(Feature.HIDE_COMMENTS);
|
||||
const upgradeLabel = useUpgradeLabel();
|
||||
const allowViewerCommentsEnabled =
|
||||
space.settings?.comments?.allowViewerComments === true;
|
||||
const isDisabled = !hasHideComments || allowViewerCommentsEnabled;
|
||||
const tooltipLabel = !hasHideComments
|
||||
? upgradeLabel
|
||||
: t("Turn off 'Allow viewers to comment' first");
|
||||
const [checked, setChecked] = useState(
|
||||
space.settings?.comments?.hideCommentsFromViewers === true,
|
||||
);
|
||||
const updateSpaceMutation = useUpdateSpaceMutation();
|
||||
|
||||
const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.currentTarget.checked;
|
||||
try {
|
||||
await updateSpaceMutation.mutateAsync({
|
||||
spaceId: space.id,
|
||||
hideCommentsFromViewers: value,
|
||||
});
|
||||
setChecked(value);
|
||||
} catch {
|
||||
// error handled by mutation
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Group justify="space-between" wrap="nowrap" gap="xl">
|
||||
<div>
|
||||
<Text size="md">{t("Hide comments from viewers")}</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t("Viewers cannot see or add comments on pages in this space.")}
|
||||
</Text>
|
||||
</div>
|
||||
<Tooltip label={tooltipLabel} disabled={!isDisabled} refProp="rootRef">
|
||||
<Switch
|
||||
checked={checked}
|
||||
onChange={handleChange}
|
||||
disabled={isDisabled}
|
||||
aria-label={t("Toggle hide comments from viewers")}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
@@ -17,7 +17,12 @@ export default function SpaceViewerCommentsToggle({
|
||||
const { t } = useTranslation();
|
||||
const hasViewerComments = useHasFeature(Feature.VIEWER_COMMENTS);
|
||||
const upgradeLabel = useUpgradeLabel();
|
||||
const isDisabled = !hasViewerComments;
|
||||
const hideCommentsEnabled =
|
||||
space.settings?.comments?.hideCommentsFromViewers === true;
|
||||
const isDisabled = !hasViewerComments || hideCommentsEnabled;
|
||||
const tooltipLabel = !hasViewerComments
|
||||
? upgradeLabel
|
||||
: t("Turn off 'Hide comments from viewers' first");
|
||||
const [checked, setChecked] = useState(
|
||||
space.settings?.comments?.allowViewerComments === true,
|
||||
);
|
||||
@@ -45,7 +50,7 @@ export default function SpaceViewerCommentsToggle({
|
||||
</Text>
|
||||
</div>
|
||||
<Tooltip
|
||||
label={upgradeLabel}
|
||||
label={tooltipLabel}
|
||||
disabled={!isDisabled}
|
||||
refProp="rootRef"
|
||||
>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import { usePageQuery } from "@/features/page/queries/page-query.ts";
|
||||
import { useGetSpaceBySlugQuery } from "@/features/space/queries/space-query.ts";
|
||||
import { extractPageSlugId } from "@/lib";
|
||||
|
||||
export function useCanViewComments(): boolean {
|
||||
const { pageSlug } = useParams();
|
||||
const { data: page } = usePageQuery({ pageId: extractPageSlugId(pageSlug) });
|
||||
const { data: space } = useGetSpaceBySlugQuery(page?.space?.slug);
|
||||
|
||||
const canEdit = !page?.deletedAt && (page?.permissions?.canEdit ?? false);
|
||||
return (
|
||||
canEdit || space?.settings?.comments?.hideCommentsFromViewers !== true
|
||||
);
|
||||
}
|
||||
@@ -82,6 +82,8 @@ import {
|
||||
getCollabSocket,
|
||||
releaseCollabSocket,
|
||||
} from "@/features/editor/collab-socket";
|
||||
import clsx from "clsx";
|
||||
import { useCanViewComments } from "@/features/comment/hooks/use-can-view-comments.ts";
|
||||
|
||||
interface PageEditorProps {
|
||||
pageId: string;
|
||||
@@ -196,6 +198,7 @@ function CollabPageEditor({
|
||||
const { pageSlug } = useParams();
|
||||
const slugId = extractPageSlugId(pageSlug);
|
||||
const currentPageEditMode = useAtomValue(currentPageEditModeAtom);
|
||||
const canViewComments = useCanViewComments();
|
||||
const canScroll = useCallback(
|
||||
() => Boolean(isComponentMounted.current && editorRef.current),
|
||||
[isComponentMounted],
|
||||
@@ -372,6 +375,7 @@ function CollabPageEditor({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!canViewComments) return;
|
||||
document.addEventListener("ACTIVE_COMMENT_EVENT", handleActiveCommentEvent);
|
||||
return () => {
|
||||
document.removeEventListener(
|
||||
@@ -379,7 +383,7 @@ function CollabPageEditor({
|
||||
handleActiveCommentEvent,
|
||||
);
|
||||
};
|
||||
}, []);
|
||||
}, [canViewComments]);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveCommentId(null);
|
||||
@@ -430,7 +434,13 @@ function CollabPageEditor({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="editor-container" style={{ position: "relative" }}>
|
||||
<div
|
||||
className={clsx(
|
||||
"editor-container",
|
||||
!canViewComments && "comments-hidden",
|
||||
)}
|
||||
style={{ position: "relative" }}
|
||||
>
|
||||
<div ref={menuContainerRef}>
|
||||
<EditorContent editor={editor} />
|
||||
|
||||
@@ -480,17 +490,21 @@ function StaticPageEditor({
|
||||
content: any;
|
||||
ariaLabel: string;
|
||||
}) {
|
||||
const canViewComments = useCanViewComments();
|
||||
|
||||
return (
|
||||
<EditorProvider
|
||||
editable={false}
|
||||
immediatelyRender={true}
|
||||
extensions={mainExtensions}
|
||||
content={content}
|
||||
editorProps={{
|
||||
attributes: {
|
||||
"aria-label": ariaLabel,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<div className={clsx(!canViewComments && "comments-hidden")}>
|
||||
<EditorProvider
|
||||
editable={false}
|
||||
immediatelyRender={true}
|
||||
extensions={mainExtensions}
|
||||
content={content}
|
||||
editorProps={{
|
||||
attributes: {
|
||||
"aria-label": ariaLabel,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -315,3 +315,8 @@
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.comments-hidden .ProseMirror .comment-mark {
|
||||
background: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ import {
|
||||
useWatchPageMutation,
|
||||
useUnwatchPageMutation,
|
||||
} from "@/features/page/queries/watcher-query";
|
||||
import { useCanViewComments } from "@/features/comment/hooks/use-can-view-comments.ts";
|
||||
|
||||
interface PageHeaderMenuProps {
|
||||
readOnly?: boolean;
|
||||
@@ -65,6 +66,7 @@ interface PageHeaderMenuProps {
|
||||
export default function PageHeaderMenu({ readOnly }: PageHeaderMenuProps) {
|
||||
const { t } = useTranslation();
|
||||
const commentsTriggerProps = useAsideTriggerProps("comments");
|
||||
const canViewComments = useCanViewComments();
|
||||
const tocTriggerProps = useAsideTriggerProps("toc");
|
||||
const { pageSlug } = useParams();
|
||||
const { data: page } = usePageQuery({
|
||||
@@ -105,16 +107,18 @@ export default function PageHeaderMenu({ readOnly }: PageHeaderMenuProps) {
|
||||
|
||||
<PageShareModal readOnly={readOnly} />
|
||||
|
||||
<Tooltip label={t("Comments")} openDelay={250} withArrow>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
color="dark"
|
||||
aria-label={t("Comments")}
|
||||
{...commentsTriggerProps}
|
||||
>
|
||||
<IconMessage size={20} stroke={2} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
{canViewComments && (
|
||||
<Tooltip label={t("Comments")} openDelay={250} withArrow>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
color="dark"
|
||||
aria-label={t("Comments")}
|
||||
{...commentsTriggerProps}
|
||||
>
|
||||
<IconMessage size={20} stroke={2} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{!page?.isBase && (
|
||||
<Tooltip label={t("Table of contents")} openDelay={250} withArrow>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { ISpace } from "@/features/space/types/space.types.ts";
|
||||
import SpacePublicSharingToggle from "@/ee/security/components/space-public-sharing-toggle.tsx";
|
||||
import SpaceViewerCommentsToggle from "@/ee/security/components/space-viewer-comments-toggle.tsx";
|
||||
import SpaceHideCommentsToggle from "@/ee/security/components/space-hide-comments-toggle.tsx";
|
||||
|
||||
type SpaceSecuritySettingsProps = {
|
||||
space: ISpace;
|
||||
@@ -29,6 +30,10 @@ export default function SpaceSecuritySettings({
|
||||
<Divider my="lg" />
|
||||
|
||||
<SpaceViewerCommentsToggle space={space} />
|
||||
|
||||
<Divider my="lg" />
|
||||
|
||||
<SpaceHideCommentsToggle space={space} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface ISpaceSharingSettings {
|
||||
|
||||
export interface ISpaceCommentsSettings {
|
||||
allowViewerComments?: boolean;
|
||||
hideCommentsFromViewers?: boolean;
|
||||
}
|
||||
|
||||
export interface ISpaceSettings {
|
||||
@@ -36,6 +37,7 @@ export interface ISpace {
|
||||
// for updates
|
||||
disablePublicSharing?: boolean;
|
||||
allowViewerComments?: boolean;
|
||||
hideCommentsFromViewers?: boolean;
|
||||
}
|
||||
|
||||
interface IMembership {
|
||||
|
||||
Reference in New Issue
Block a user