chore(react/type_widgets): save if needed

This commit is contained in:
Elian Doran
2025-10-05 17:53:19 +03:00
parent 3f7b8447d0
commit 608605af12
9 changed files with 30 additions and 20 deletions

View File

@@ -1,4 +1,4 @@
import { useNoteContext, useTriliumEvent } from "./react/hooks"
import { useNoteContext, useTriliumEvent, useTriliumEvents } from "./react/hooks"
import FNote from "../entities/fnote";
import protected_session_holder from "../services/protected_session_holder";
import { useEffect, useRef, useState } from "preact/hooks";
@@ -9,6 +9,7 @@ import "./NoteDetail.css";
import attributes from "../services/attributes";
import { ExtendedNoteType, TYPE_MAPPINGS } from "./note_types";
import { dynamicRequire, isMobile } from "../services/utils";
import { ReactWrappedWidget } from "./basic_widget";
/**
* The note detail is in charge of rendering the content of a note, by determining its type (e.g. text, code) and using the appropriate view widget.

View File

@@ -13,18 +13,6 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
appContext.addBeforeUnloadListener(this);
}
async beforeNoteSwitchEvent({ noteContext }: EventData<"beforeNoteSwitch">) {
if (this.isNoteContext(noteContext.ntxId)) {
await this.spacedUpdate.updateNowIfNecessary();
}
}
async beforeNoteContextRemoveEvent({ ntxIds }: EventData<"beforeNoteContextRemove">) {
if (this.isNoteContext(ntxIds)) {
await this.spacedUpdate.updateNowIfNecessary();
}
}
async runActiveNoteCommand(params: CommandListenerData<"runActiveNote">) {
if (this.isNoteContext(params.ntxId)) {
// make sure that script is saved before running it #4028

View File

@@ -23,6 +23,7 @@ import protected_session_holder from "../../services/protected_session_holder";
import server from "../../services/server";
import { removeIndividualBinding } from "../../services/shortcuts";
import { ViewScope } from "../../services/link";
import { VirtualConsolePrinter } from "happy-dom";
export function useTriliumEvent<T extends EventNames>(eventName: T, handler: (data: EventData<T>) => void) {
const parentComponent = useContext(ParentComponent);
@@ -77,8 +78,9 @@ export function useSpacedUpdate(callback: () => void | Promise<void>, interval =
return spacedUpdateRef.current;
}
export function useEditorSpacedUpdate({ note, getData, onContentChange, dataSaved, updateInterval }: {
export function useEditorSpacedUpdate({ note, noteContext, getData, onContentChange, dataSaved, updateInterval }: {
note: FNote,
noteContext: NoteContext | null | undefined,
getData: () => Promise<object | undefined> | object | undefined,
onContentChange: (newContent: string) => void,
dataSaved?: () => void,
@@ -114,6 +116,18 @@ export function useEditorSpacedUpdate({ note, getData, onContentChange, dataSave
spacedUpdate.setUpdateInterval(updateInterval);
}, [ updateInterval ]);
// Save if needed upon switching tabs.
useTriliumEvent("beforeNoteSwitch", async ({ noteContext: eventNoteContext }) => {
if (eventNoteContext.ntxId !== noteContext?.ntxId) return;
await spacedUpdate.updateNowIfNecessary();
});
// Save if needed upon tab closing.
useTriliumEvent("beforeNoteContextRemove", async ({ ntxIds }) => {
if (!noteContext?.ntxId || !ntxIds.includes(noteContext.ntxId)) return;
await spacedUpdate.updateNowIfNecessary();
})
return spacedUpdate;
}

View File

@@ -7,6 +7,7 @@ export default function AiChat({ note, noteContext }: TypeWidgetProps) {
const dataRef = useRef<object>();
const spacedUpdate = useEditorSpacedUpdate({
note,
noteContext,
getData: async () => ({
content: JSON.stringify(dataRef.current)
}),

View File

@@ -11,6 +11,7 @@ import { RefObject } from "preact";
import server from "../../services/server";
import { ExcalidrawElement, NonDeletedExcalidrawElement } from "@excalidraw/excalidraw/element/types";
import { goToLinkExt } from "../../services/link";
import NoteContext from "../../components/note_context";
// currently required by excalidraw, in order to allows self-hosting fonts locally.
// this avoids making excalidraw load the fonts from an external CDN.
@@ -27,14 +28,14 @@ interface CanvasContent {
appState: Partial<AppState>;
}
export default function Canvas({ note }: TypeWidgetProps) {
export default function Canvas({ note, noteContext }: TypeWidgetProps) {
const apiRef = useRef<ExcalidrawImperativeAPI>(null);
const [ isReadOnly ] = useNoteLabelBoolean(note, "readOnly");
const themeStyle = useMemo(() => {
const documentStyle = window.getComputedStyle(document.documentElement);
return documentStyle.getPropertyValue("--theme-style")?.trim() as AppState["theme"];
}, []);
const persistence = usePersistence(note, apiRef, themeStyle, isReadOnly);
const persistence = usePersistence(note, noteContext, apiRef, themeStyle, isReadOnly);
/** Use excalidraw's native zoom instead of the global zoom. */
const onWheel = useCallback((e: MouseEvent) => {
@@ -85,7 +86,7 @@ export default function Canvas({ note }: TypeWidgetProps) {
)
}
function usePersistence(note: FNote, apiRef: RefObject<ExcalidrawImperativeAPI>, theme: AppState["theme"], isReadOnly: boolean): Partial<ExcalidrawProps> {
function usePersistence(note: FNote, noteContext: NoteContext, apiRef: RefObject<ExcalidrawImperativeAPI>, theme: AppState["theme"], isReadOnly: boolean): Partial<ExcalidrawProps> {
const libraryChanged = useRef(false);
/**
@@ -104,6 +105,7 @@ function usePersistence(note: FNote, apiRef: RefObject<ExcalidrawImperativeAPI>,
const spacedUpdate = useEditorSpacedUpdate({
note,
noteContext,
onContentChange(newContent) {
const api = apiRef.current;
if (!api) return;

View File

@@ -22,13 +22,14 @@ interface MindElixirProps {
onChange?: () => void;
}
export default function MindMap({ note, ntxId }: TypeWidgetProps) {
export default function MindMap({ note, ntxId, noteContext }: TypeWidgetProps) {
const content = VanillaMindElixir.new(NEW_TOPIC_NAME);
const apiRef = useRef<MindElixirInstance>(null);
const containerRef = useRef<HTMLDivElement>(null);
const [ isReadOnly ] = useNoteLabelBoolean(note, "readOnly");
const spacedUpdate = useEditorSpacedUpdate({
note,
noteContext,
getData: async () => {
if (!apiRef.current) return;
return {

View File

@@ -70,12 +70,13 @@ function formatViewSource(note: FNote, content: string) {
return content;
}
export function EditableCode({ note, ntxId, debounceUpdate, parentComponent, updateInterval, onContentChanged, dataSaved, ...editorProps }: EditableCodeProps) {
export function EditableCode({ note, ntxId, noteContext, debounceUpdate, parentComponent, updateInterval, onContentChanged, dataSaved, ...editorProps }: EditableCodeProps) {
const editorRef = useRef<VanillaCodeMirror>(null);
const containerRef = useRef<HTMLPreElement>(null);
const [ vimKeymapEnabled ] = useTriliumOptionBool("vimKeymapEnabled");
const spacedUpdate = useEditorSpacedUpdate({
note,
noteContext,
getData: () => ({ content: editorRef.current?.getText() }),
onContentChange: (content) => {
const codeEditor = editorRef.current;

View File

@@ -43,7 +43,7 @@ declare module "jsplumb" {
}
}
export default function RelationMap({ note, ntxId }: TypeWidgetProps) {
export default function RelationMap({ note, noteContext, ntxId }: TypeWidgetProps) {
const [ data, setData ] = useState<MapData>();
const containerRef = useRef<HTMLDivElement>(null);
const mapApiRef = useRef<RelationMapApi>(null);
@@ -51,6 +51,7 @@ export default function RelationMap({ note, ntxId }: TypeWidgetProps) {
const spacedUpdate = useEditorSpacedUpdate({
note,
noteContext,
getData() {
return {
content: JSON.stringify(data),

View File

@@ -37,6 +37,7 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
const initialized = useRef(deferred<void>());
const spacedUpdate = useEditorSpacedUpdate({
note,
noteContext,
getData() {
const editor = watchdogRef.current?.editor;
if (!editor) {