mirror of
https://github.com/docmost/docmost.git
synced 2026-05-17 14:54:05 +08:00
fix: page mode toggle no longer overwrites default preference (#1996)
The header edit/read toggle now controls only the current session's mode without saving it as the user's preference. The saved preference (set in profile settings) is applied once on initial load and sticks across page navigations within the session, so navigating to a new page no longer resets the mode mid-session. Fixes #1693
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { atom } from "jotai";
|
||||
import { Editor } from "@tiptap/core";
|
||||
import { PageEditMode } from "@/features/user/types/user.types.ts";
|
||||
|
||||
export const pageEditorAtom = atom<Editor | null>(null);
|
||||
|
||||
@@ -12,3 +13,7 @@ export const yjsConnectionStatusAtom = atom<string>("");
|
||||
export const showAiMenuAtom = atom(false);
|
||||
|
||||
export const showLinkMenuAtom = atom(false);
|
||||
|
||||
// Current page's edit mode — initialized from the user's saved preference on
|
||||
// first load, can be toggled locally without persisting to the server.
|
||||
export const currentPageEditModeAtom = atom<PageEditMode>(PageEditMode.Edit);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import classes from "@/features/editor/styles/editor.module.css";
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import { TitleEditor } from "@/features/editor/title-editor";
|
||||
import PageEditor from "@/features/editor/page-editor";
|
||||
import {
|
||||
@@ -24,6 +24,7 @@ import { FixedToolbar } from "@/features/editor/components/fixed-toolbar/fixed-t
|
||||
import { PageEditMode } from "@/features/user/types/user.types.ts";
|
||||
import useToggleAside from "@/hooks/use-toggle-aside.tsx";
|
||||
import clsx from "clsx";
|
||||
import { currentPageEditModeAtom } from "@/features/editor/atoms/editor-atoms.ts";
|
||||
|
||||
const MemoizedTitleEditor = React.memo(TitleEditor);
|
||||
const MemoizedPageEditor = React.memo(PageEditor);
|
||||
@@ -34,6 +35,10 @@ type PageCreator = {
|
||||
avatarUrl: string;
|
||||
};
|
||||
|
||||
// Module-level flag: survives component unmount/remount on page navigation,
|
||||
// reset only on full page reload (i.e. a new app session).
|
||||
let defaultEditModeApplied = false;
|
||||
|
||||
export interface FullEditorProps {
|
||||
pageId: string;
|
||||
slugId: string;
|
||||
@@ -61,9 +66,19 @@ export function FullEditor({
|
||||
const fullPageWidth = user.settings?.preferences?.fullPageWidth;
|
||||
const editorToolbarEnabled =
|
||||
user.settings?.preferences?.editorToolbar ?? false;
|
||||
const [currentPageEditMode, setCurrentPageEditMode] = useAtom(currentPageEditModeAtom);
|
||||
const userPageEditMode =
|
||||
user.settings?.preferences?.pageEditMode ?? PageEditMode.Edit;
|
||||
const isEditMode = userPageEditMode === PageEditMode.Edit;
|
||||
const isEditMode = currentPageEditMode === PageEditMode.Edit;
|
||||
|
||||
// Apply the user's saved preference only once on initial load, not on every
|
||||
// page navigation — so the mode sticks across navigations within a session.
|
||||
useEffect(() => {
|
||||
if (!defaultEditModeApplied) {
|
||||
setCurrentPageEditMode(userPageEditMode);
|
||||
defaultEditModeApplied = true;
|
||||
}
|
||||
}, [userPageEditMode, setCurrentPageEditMode]);
|
||||
|
||||
return (
|
||||
<Container
|
||||
|
||||
@@ -26,10 +26,11 @@ import {
|
||||
collabExtensions,
|
||||
mainExtensions,
|
||||
} from "@/features/editor/extensions/extensions";
|
||||
import { useAtom } from "jotai";
|
||||
import { useAtom, useAtomValue } from "jotai";
|
||||
import useCollaborationUrl from "@/features/editor/hooks/use-collaboration-url";
|
||||
import { currentUserAtom } from "@/features/user/atoms/current-user-atom";
|
||||
import {
|
||||
currentPageEditModeAtom,
|
||||
pageEditorAtom,
|
||||
yjsConnectionStatusAtom,
|
||||
} from "@/features/editor/atoms/editor-atoms";
|
||||
@@ -112,8 +113,7 @@ export default function PageEditor({
|
||||
const documentState = useDocumentVisibility();
|
||||
const { pageSlug } = useParams();
|
||||
const slugId = extractPageSlugId(pageSlug);
|
||||
const userPageEditMode =
|
||||
currentUser?.user?.settings?.preferences?.pageEditMode ?? PageEditMode.Edit;
|
||||
const currentPageEditMode = useAtomValue(currentPageEditModeAtom);
|
||||
const canScroll = useCallback(
|
||||
() => Boolean(isComponentMounted.current && editorRef.current),
|
||||
[isComponentMounted],
|
||||
@@ -373,19 +373,9 @@ export default function PageEditor({
|
||||
return () => clearTimeout(timeout);
|
||||
}, [yjsConnectionStatus, isSynced]);
|
||||
useEffect(() => {
|
||||
// Only honor user default page edit mode preference and permissions
|
||||
if (editor) {
|
||||
if (userPageEditMode && editable) {
|
||||
if (userPageEditMode === PageEditMode.Edit) {
|
||||
editor.setEditable(true);
|
||||
} else if (userPageEditMode === PageEditMode.Read) {
|
||||
editor.setEditable(false);
|
||||
}
|
||||
} else {
|
||||
editor.setEditable(false);
|
||||
}
|
||||
}
|
||||
}, [userPageEditMode, editor, editable]);
|
||||
if (!editor) return;
|
||||
editor.setEditable(editable && currentPageEditMode === PageEditMode.Edit);
|
||||
}, [currentPageEditMode, editor, editable]);
|
||||
|
||||
const hasConnectedOnceRef = useRef(false);
|
||||
const [showStatic, setShowStatic] = useState(true);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Text } from "@tiptap/extension-text";
|
||||
import { Placeholder } from "@tiptap/extension-placeholder";
|
||||
import { useAtomValue } from "jotai";
|
||||
import {
|
||||
currentPageEditModeAtom,
|
||||
pageEditorAtom,
|
||||
titleEditorAtom,
|
||||
} from "@/features/editor/atoms/editor-atoms";
|
||||
@@ -24,7 +25,6 @@ import { useTranslation } from "react-i18next";
|
||||
import EmojiCommand from "@/features/editor/extensions/emoji-command.ts";
|
||||
import { UpdateEvent } from "@/features/websocket/types";
|
||||
import localEmitter from "@/lib/local-emitter.ts";
|
||||
import { currentUserAtom } from "@/features/user/atoms/current-user-atom.ts";
|
||||
import { PageEditMode } from "@/features/user/types/user.types.ts";
|
||||
import { searchSpotlight } from "@/features/search/constants.ts";
|
||||
import { platformModifierKey } from "@/lib";
|
||||
@@ -52,9 +52,7 @@ export function TitleEditor({
|
||||
const emit = useQueryEmit();
|
||||
const navigate = useNavigate();
|
||||
const [activePageId, setActivePageId] = useState(pageId);
|
||||
const [currentUser] = useAtom(currentUserAtom);
|
||||
const userPageEditMode =
|
||||
currentUser?.user?.settings?.preferences?.pageEditMode ?? PageEditMode.Edit;
|
||||
const currentPageEditMode = useAtomValue(currentPageEditModeAtom);
|
||||
|
||||
const titleEditor = useEditor({
|
||||
extensions: [
|
||||
@@ -172,18 +170,9 @@ export function TitleEditor({
|
||||
}, [pageId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (titleEditor) {
|
||||
if (userPageEditMode && editable) {
|
||||
if (userPageEditMode === PageEditMode.Edit) {
|
||||
titleEditor.setEditable(true);
|
||||
} else if (userPageEditMode === PageEditMode.Read) {
|
||||
titleEditor.setEditable(false);
|
||||
}
|
||||
} else {
|
||||
titleEditor.setEditable(false);
|
||||
}
|
||||
}
|
||||
}, [userPageEditMode, titleEditor, editable]);
|
||||
if (!titleEditor) return;
|
||||
titleEditor.setEditable(editable && currentPageEditMode === PageEditMode.Edit);
|
||||
}, [currentPageEditMode, titleEditor, editable]);
|
||||
|
||||
const openSearchDialog = () => {
|
||||
const event = new CustomEvent("openFindDialogFromEditor", {});
|
||||
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
yjsConnectionStatusAtom,
|
||||
} from "@/features/editor/atoms/editor-atoms.ts";
|
||||
import { formattedDate } from "@/lib/time.ts";
|
||||
import { PageStateSegmentedControl } from "@/features/user/components/page-state-pref.tsx";
|
||||
import { PageEditModeToggle } from "@/features/user/components/page-state-pref.tsx";
|
||||
import MovePageModal from "@/features/page/components/move-page-modal.tsx";
|
||||
import { useTimeAgo } from "@/hooks/use-time-ago.tsx";
|
||||
import { PageShareModal } from "@/ee/page-permission";
|
||||
@@ -91,7 +91,7 @@ export default function PageHeaderMenu({ readOnly }: PageHeaderMenuProps) {
|
||||
<>
|
||||
<ConnectionWarning />
|
||||
|
||||
{!readOnly && <PageStateSegmentedControl size="xs" />}
|
||||
{!readOnly && <PageEditModeToggle size="xs" />}
|
||||
|
||||
<PageShareModal readOnly={readOnly} />
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import React, { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { PageEditMode } from "@/features/user/types/user.types.ts";
|
||||
import { ResponsiveSettingsRow, ResponsiveSettingsContent, ResponsiveSettingsControl } from "@/components/ui/responsive-settings-row";
|
||||
import { currentPageEditModeAtom } from "@/features/editor/atoms/editor-atoms.ts";
|
||||
|
||||
export default function PageStatePref() {
|
||||
const { t } = useTranslation();
|
||||
@@ -71,3 +72,24 @@ export function PageStateSegmentedControl({
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Header variant: updates the current page's mode locally without persisting
|
||||
// the preference to the server.
|
||||
export function PageEditModeToggle({ size }: { size?: MantineSize }) {
|
||||
const { t } = useTranslation();
|
||||
const [currentPageEditMode, setCurrentPageEditMode] = useAtom(
|
||||
currentPageEditModeAtom,
|
||||
);
|
||||
|
||||
return (
|
||||
<SegmentedControl
|
||||
size={size}
|
||||
value={currentPageEditMode}
|
||||
onChange={(v) => setCurrentPageEditMode(v as PageEditMode)}
|
||||
data={[
|
||||
{ label: t("Edit"), value: PageEditMode.Edit },
|
||||
{ label: t("Read"), value: PageEditMode.Read },
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user