diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx index 0a9177209..b3bd6cf29 100644 --- a/apps/client/src/widgets/react/hooks.tsx +++ b/apps/client/src/widgets/react/hooks.tsx @@ -1,3 +1,4 @@ +import { CKTextEditor } from "@triliumnext/ckeditor5"; import { FilterLabelsByType, KeyboardActionNames, OptionNames, RelationNames } from "@triliumnext/commons"; import { Tooltip } from "bootstrap"; import Mark from "mark.js"; @@ -25,6 +26,7 @@ import utils, { escapeRegExp, randomString, reloadFrontendApp } from "../../serv import BasicWidget, { ReactWrappedWidget } from "../basic_widget"; import NoteContextAwareWidget from "../note_context_aware_widget"; import { DragData } from "../note_tree"; +import CKEditor from "./CKEditor"; import { noteSavedDataStore } from "./NoteStore"; import { NoteContextContext, ParentComponent, refToJQuerySelector } from "./react_utils"; @@ -1085,3 +1087,31 @@ export function useNoteColorClass(note: FNote | null | undefined) { }, [ color, note ]); return colorClass; } + +export function useTextEditor(noteContext: NoteContext | null | undefined) { + const [ textEditor, setTextEditor ] = useState(null); + const requestIdRef = useRef(0); + + // React to note context change and initial state. + useEffect(() => { + if (!noteContext) { + setTextEditor(null); + return; + } + + const requestId = ++requestIdRef.current; + noteContext.getTextEditor((textEditor) => { + // Prevent stale async. + if (requestId !== requestIdRef.current) return; + setTextEditor(textEditor); + }); + }, [ noteContext ]); + + // React to editor initializing. + useTriliumEvent("textEditorRefreshed", ({ ntxId: eventNtxId, editor }) => { + if (eventNtxId !== noteContext?.ntxId) return; + setTextEditor(editor); + }); + + return textEditor; +} diff --git a/apps/client/src/widgets/sidebar/TableOfContents.tsx b/apps/client/src/widgets/sidebar/TableOfContents.tsx index 1bbdb225e..14378248b 100644 --- a/apps/client/src/widgets/sidebar/TableOfContents.tsx +++ b/apps/client/src/widgets/sidebar/TableOfContents.tsx @@ -2,7 +2,7 @@ import { CKTextEditor, ModelElement } from "@triliumnext/ckeditor5"; import { useEffect, useState } from "preact/hooks"; import { t } from "../../services/i18n"; -import { useActiveNoteContext, useIsNoteReadOnly, useNoteProperty, useTriliumEvent } from "../react/hooks"; +import { useActiveNoteContext, useIsNoteReadOnly, useNoteProperty, useTextEditor, useTriliumEvent } from "../react/hooks"; import RightPanelWidget from "./RightPanelWidget"; interface CKHeading { @@ -24,18 +24,13 @@ export default function TableOfContents() { } function EditableTextTableOfContents() { - const { ntxId } = useActiveNoteContext(); - const [ textEditor, setTextEditor ] = useState(null); + const { note, noteContext } = useActiveNoteContext(); + const textEditor = useTextEditor(noteContext); const [ headings, setHeadings ] = useState([]); - useTriliumEvent("textEditorRefreshed", ({ ntxId: eventNtxId, editor }) => { - if (eventNtxId !== ntxId) return; - setTextEditor(editor); - }); - useEffect(() => { if (!textEditor) return; - const headings = extractTocFromTextEditor(textEditor); + const headings = extractTocFromTextEditor(textEditor); // React to changes. const changeCallback = () => { @@ -55,7 +50,7 @@ function EditableTextTableOfContents() { setHeadings(headings); return () => textEditor.model.document.off("change:data", changeCallback); - }, [ textEditor ]); + }, [ textEditor, note ]); return ; }