mirror of
https://github.com/docmost/docmost.git
synced 2026-05-19 07:54:05 +08:00
keep page title editor and page tree in sync
This commit is contained in:
@@ -85,12 +85,12 @@ const ChildComments = ({ comments, parentId }) => {
|
|||||||
const CommentEditorWithActions = ({ commentId, onSave, isLoading }) => {
|
const CommentEditorWithActions = ({ commentId, onSave, isLoading }) => {
|
||||||
const [content, setContent] = useState('');
|
const [content, setContent] = useState('');
|
||||||
const { ref, focused } = useFocusWithin();
|
const { ref, focused } = useFocusWithin();
|
||||||
const commentEditorRef = useRef();
|
const commentEditorRef = useRef(null);
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
onSave(commentId, content);
|
onSave(commentId, content);
|
||||||
setContent('');
|
setContent('');
|
||||||
commentEditorRef?.current.clearContent();
|
commentEditorRef.current?.clearContent();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { editorAtom, titleEditorAtom } from '@/features/editor/atoms/editorAtom'
|
|||||||
import { useUpdatePageMutation } from '@/features/page/queries/page-query';
|
import { useUpdatePageMutation } from '@/features/page/queries/page-query';
|
||||||
import { useDebouncedValue } from '@mantine/hooks';
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
|
import { treeDataAtom } from '@/features/page/tree/atoms/tree-data-atom';
|
||||||
|
import { updateTreeNodeName } from '@/features/page/tree/utils';
|
||||||
|
|
||||||
export interface TitleEditorProps {
|
export interface TitleEditorProps {
|
||||||
pageId: string;
|
pageId: string;
|
||||||
@@ -22,6 +24,7 @@ export function TitleEditor({ pageId, title }: TitleEditorProps) {
|
|||||||
const updatePageMutation = useUpdatePageMutation();
|
const updatePageMutation = useUpdatePageMutation();
|
||||||
const contentEditor = useAtomValue(editorAtom);
|
const contentEditor = useAtomValue(editorAtom);
|
||||||
const [, setTitleEditor] = useAtom(titleEditorAtom);
|
const [, setTitleEditor] = useAtom(titleEditorAtom);
|
||||||
|
const [treeData, setTreeData] = useAtom(treeDataAtom);
|
||||||
|
|
||||||
const titleEditor = useEditor({
|
const titleEditor = useEditor({
|
||||||
extensions: [
|
extensions: [
|
||||||
@@ -52,9 +55,19 @@ export function TitleEditor({ pageId, title }: TitleEditorProps) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (debouncedTitle !== '') {
|
if (debouncedTitle !== '') {
|
||||||
updatePageMutation.mutate({ id: pageId, title: debouncedTitle });
|
updatePageMutation.mutate({ id: pageId, title: debouncedTitle });
|
||||||
|
|
||||||
|
const newTreeData = updateTreeNodeName(treeData, pageId, debouncedTitle);
|
||||||
|
setTreeData(newTreeData);
|
||||||
|
|
||||||
}
|
}
|
||||||
}, [debouncedTitle]);
|
}, [debouncedTitle]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (titleEditor && title !== titleEditor.getText()) {
|
||||||
|
titleEditor.commands.setContent(title);
|
||||||
|
}
|
||||||
|
}, [title, titleEditor]);
|
||||||
|
|
||||||
function handleTitleKeyDown(event) {
|
function handleTitleKeyDown(event) {
|
||||||
if (!titleEditor || !contentEditor || event.shiftKey) return;
|
if (!titleEditor || !contentEditor || event.shiftKey) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { atom } from "jotai";
|
import { atom } from "jotai";
|
||||||
|
|
||||||
export const historyAtoms = atom<boolean>(false);
|
export const historyAtoms = atom<boolean>(false);
|
||||||
export const activeHistoryIdAtom = atom(null);
|
export const activeHistoryIdAtom = atom<string>('');
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function HistoryItem({ historyItem, onSelect, isActive }: HistoryItemProps) {
|
|||||||
<Group gap={4} wrap="nowrap">
|
<Group gap={4} wrap="nowrap">
|
||||||
<UserAvatar color="blue" size="sm" avatarUrl={historyItem.lastUpdatedBy.avatarUrl}
|
<UserAvatar color="blue" size="sm" avatarUrl={historyItem.lastUpdatedBy.avatarUrl}
|
||||||
name={historyItem.lastUpdatedBy.name} />
|
name={historyItem.lastUpdatedBy.name} />
|
||||||
<Text size="sm" c="dimmed" lineClamp={1} fontWeight={400}>
|
<Text size="sm" c="dimmed" lineClamp={1}>
|
||||||
{historyItem.lastUpdatedBy.name}
|
{historyItem.lastUpdatedBy.name}
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
import { atomFamily } from 'jotai/utils';
|
|
||||||
import { atom } from 'jotai';
|
|
||||||
import { IPage } from '@/features/page/types/page.types';
|
|
||||||
|
|
||||||
export const pageAtom = atomFamily((pageId) => atom<IPage>(null));
|
|
||||||
@@ -35,8 +35,13 @@ export function useCreatePageMutation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useUpdatePageMutation() {
|
export function useUpdatePageMutation() {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<IPage, Error, Partial<IPage>>({
|
return useMutation<IPage, Error, Partial<IPage>>({
|
||||||
mutationFn: (data) => updatePage(data),
|
mutationFn: (data) => updatePage(data),
|
||||||
|
onSuccess: (data) => {
|
||||||
|
queryClient.setQueryData(['pages', data.id], data);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,3 +48,15 @@ export function findBreadcrumbPath(tree: TreeNode[], pageId: string, path: TreeN
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const updateTreeNodeName = (nodes: TreeNode[], nodeId: string, newName: string): TreeNode[] => {
|
||||||
|
return nodes.map(node => {
|
||||||
|
if (node.id === nodeId) {
|
||||||
|
return { ...node, name: newName };
|
||||||
|
}
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
return { ...node, children: updateTreeNodeName(node.children, nodeId, newName) };
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,23 +1,12 @@
|
|||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import React, { useEffect } from 'react';
|
|
||||||
import { useAtom } from 'jotai';
|
|
||||||
import { pageAtom } from '@/features/page/atoms/page-atom';
|
|
||||||
import { usePageQuery } from '@/features/page/queries/page-query';
|
import { usePageQuery } from '@/features/page/queries/page-query';
|
||||||
import { FullEditor } from '@/features/editor/full-editor';
|
import { FullEditor } from '@/features/editor/full-editor';
|
||||||
import HistoryModal from '@/features/page-history/components/history-modal';
|
import HistoryModal from '@/features/page-history/components/history-modal';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const { pageId } = useParams();
|
const { pageId } = useParams();
|
||||||
const [, setPage] = useAtom(pageAtom(pageId));
|
|
||||||
const { data, isLoading, isError } = usePageQuery(pageId);
|
const { data, isLoading, isError } = usePageQuery(pageId);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (data) {
|
|
||||||
// @ts-ignore
|
|
||||||
setPage(data);
|
|
||||||
}
|
|
||||||
}, [data, isLoading, setPage, pageId]);
|
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
@@ -30,7 +19,7 @@ export default function Page() {
|
|||||||
data && (
|
data && (
|
||||||
<div>
|
<div>
|
||||||
<FullEditor key={pageId} pageId={pageId} title={data.title} />
|
<FullEditor key={pageId} pageId={pageId} title={data.title} />
|
||||||
<HistoryModal/>
|
<HistoryModal />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user