diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx index 9796a6d4f..24a7948dc 100644 --- a/apps/client/src/widgets/react/hooks.tsx +++ b/apps/client/src/widgets/react/hooks.tsx @@ -78,12 +78,23 @@ export function useSpacedUpdate(callback: () => void | Promise, interval = return spacedUpdateRef.current; } +export interface SavedData { + content: string; + attachments?: { + role: string; + title: string; + mime: string; + content: string; + position: number; + }[]; +} + export function useEditorSpacedUpdate({ note, noteContext, getData, onContentChange, dataSaved, updateInterval }: { note: FNote, noteContext: NoteContext | null | undefined, - getData: () => Promise | object | undefined, + getData: () => Promise | SavedData | undefined, onContentChange: (newContent: string) => void, - dataSaved?: () => void, + dataSaved?: (savedData: SavedData) => void, updateInterval?: number; }) { const parentComponent = useContext(ParentComponent); @@ -99,7 +110,7 @@ export function useEditorSpacedUpdate({ note, noteContext, getData, onContentCha protected_session_holder.touchProtectedSessionIfNecessary(note); await server.put(`notes/${note.noteId}/data`, data, parentComponent?.componentId); - dataSaved?.(); + dataSaved?.(data); } }, [ note, getData, dataSaved ]) const spacedUpdate = useSpacedUpdate(callback); diff --git a/apps/client/src/widgets/type_widgets/canvas/persistence.ts b/apps/client/src/widgets/type_widgets/canvas/persistence.ts index 17dad2cfb..9a4172ecb 100644 --- a/apps/client/src/widgets/type_widgets/canvas/persistence.ts +++ b/apps/client/src/widgets/type_widgets/canvas/persistence.ts @@ -3,7 +3,7 @@ import NoteContext from "../../../components/note_context"; import FNote from "../../../entities/fnote"; import { AppState, BinaryFileData, ExcalidrawImperativeAPI, ExcalidrawProps, LibraryItem } from "@excalidraw/excalidraw/types"; import { useRef } from "preact/hooks"; -import { useEditorSpacedUpdate } from "../../react/hooks"; +import { SavedData, useEditorSpacedUpdate } from "../../react/hooks"; import { ExcalidrawElement, NonDeletedExcalidrawElement } from "@excalidraw/excalidraw/element/types"; import { exportToSvg, getSceneVersion } from "@excalidraw/excalidraw"; import server from "../../../services/server"; @@ -77,7 +77,7 @@ export default function useCanvasPersistence(note: FNote, noteContext: NoteConte const api = apiRef.current; if (!api) return; const { content, svg } = await getData(api); - const attachments = [{ role: "image", title: "canvas-export.svg", mime: "image/svg+xml", content: svg, position: 0 }]; + const attachments: SavedData["attachments"] = [{ role: "image", title: "canvas-export.svg", mime: "image/svg+xml", content: svg, position: 0 }]; // libraryChanged is unset in dataSaved() if (libraryChanged.current) { @@ -124,7 +124,7 @@ export default function useCanvasPersistence(note: FNote, noteContext: NoteConte title: libraryItem.id + libraryItem.name, mime: "application/json", content: JSON.stringify(libraryItem), - position: position + position }); position += 10; diff --git a/apps/client/src/widgets/type_widgets/text/EditableText.tsx b/apps/client/src/widgets/type_widgets/text/EditableText.tsx index 0106ae0c0..e3ba69c04 100644 --- a/apps/client/src/widgets/type_widgets/text/EditableText.tsx +++ b/apps/client/src/widgets/type_widgets/text/EditableText.tsx @@ -57,6 +57,10 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext onContentChange(newContent) { contentRef.current = newContent; watchdogRef.current?.editor?.setData(newContent); + }, + dataSaved(savedData) { + // Store back the saved data in order to retrieve it in case the CKEditor crashes. + contentRef.current = savedData.content; } }); const templates = useTemplates(); @@ -245,7 +249,9 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext } initialized.current.resolve(); - editor.setData(contentRef.current ?? ""); + // Restore the data, either on the first render or if the editor crashes. + // We are not using CKEditor's built-in watch dog content, instead we are using the data we store regularly in the spaced update (see `dataSaved`). + editor.setData(contentRef.current); parentComponent?.triggerEvent("textEditorRefreshed", { ntxId, editor }); }} />}