diff --git a/apps/client/src/widgets/basic_widget.ts b/apps/client/src/widgets/basic_widget.ts index 3eb2e86f5..ff3ea276c 100644 --- a/apps/client/src/widgets/basic_widget.ts +++ b/apps/client/src/widgets/basic_widget.ts @@ -5,6 +5,7 @@ import { t } from "../services/i18n.js"; import toastService from "../services/toast.js"; import { renderReactWidget } from "./react/react_utils.jsx"; import { EventNames, EventData } from "../components/app_context.js"; +import { Handler } from "leaflet"; export class TypedBasicWidget> extends TypedComponent { protected attrs: Record; @@ -277,10 +278,12 @@ export function wrapReactWidgets>(components: (T | return wrappedResult; } +type EventHandler = ((data: any) => void); + export class ReactWrappedWidget extends BasicWidget { private el: VNode; - listeners: Record void> = {}; + private listeners: Record = {}; constructor(el: VNode) { super(); @@ -292,8 +295,38 @@ export class ReactWrappedWidget extends BasicWidget { } handleEvent(name: T, data: EventData): Promise | null | undefined { - const listener = this.listeners[name]; - listener?.(data); + if (!this.listeners[name]) { + return; + } + + for (const listener of this.listeners[name]) { + listener(data); + } + } + + registerHandler(name: T, handler: EventHandler) { + if (!this.listeners[name]) { + this.listeners[name] = []; + } + + if (this.listeners[name].includes(handler)) { + return; + } + + this.listeners[name].push(handler); + } + + removeHandler(name: T, handler: EventHandler) { + if (!this.listeners[name]?.includes(handler)) { + return; + } + + this.listeners[name] = this.listeners[name] + .filter(listener => listener !== handler); + + if (!this.listeners[name].length) { + delete this.listeners[name]; + } } } diff --git a/apps/client/src/widgets/note_title.bak b/apps/client/src/widgets/note_title.bak index ebe7ad809..e07b8380e 100644 --- a/apps/client/src/widgets/note_title.bak +++ b/apps/client/src/widgets/note_title.bak @@ -37,8 +37,6 @@ const TPL = /*html*/` text-shadow: 4px 4px 4px var(--muted-text-color); } - - `; export default class NoteTitleWidget extends NoteContextAwareWidget { @@ -51,13 +49,7 @@ export default class NoteTitleWidget extends NoteContextAwareWidget { super(); this.spacedUpdate = new SpacedUpdate(async () => { - const title = this.$noteTitle.val(); - - if (this.note) { - protectedSessionHolder.touchProtectedSessionIfNecessary(this.note); - } - - await server.put(`notes/${this.noteId}/title`, { title }, this.componentId); + }); this.deleteNoteOnEscape = false; diff --git a/apps/client/src/widgets/note_title.tsx b/apps/client/src/widgets/note_title.tsx index 7fe057f28..d74779dfa 100644 --- a/apps/client/src/widgets/note_title.tsx +++ b/apps/client/src/widgets/note_title.tsx @@ -1,11 +1,36 @@ -import { useNoteContext } from "./react/hooks"; +import { useEffect, useRef, useState } from "preact/hooks"; +import { t } from "../services/i18n"; +import FormTextBox from "./react/FormTextBox"; +import { useNoteContext, useSpacedUpdate } from "./react/hooks"; +import protected_session_holder from "../services/protected_session_holder"; +import server from "../services/server"; export default function NoteTitleWidget() { - const { ntxId, noteId, note } = useNoteContext(); - + const { note, noteId, componentId } = useNoteContext(); + const [ title, setTitle ] = useState(note?.title); + useEffect(() => setTitle(note?.title), [ note?.title ]); + + const spacedUpdate = useSpacedUpdate(async () => { + if (!note) { + return; + } + protected_session_holder.touchProtectedSessionIfNecessary(note); + await server.put(`notes/${noteId}/title`, { title: title }, componentId); + }); + return ( <> -

{ ntxId }{ noteId }

+ { + setTitle(newValue); + spacedUpdate.scheduleUpdate(); + }} + /> ); } diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx index d43d25f61..d592a48a1 100644 --- a/apps/client/src/widgets/react/hooks.tsx +++ b/apps/client/src/widgets/react/hooks.tsx @@ -86,7 +86,9 @@ export default function useTriliumEvent(eventName: T, hand export function useTriliumEventBeta(eventName: T, handler: TriliumEventHandler) { const parentComponent = useContext(ParentComponent) as ReactWrappedWidget; - parentComponent.listeners[eventName] = handler; + parentComponent.registerHandler(eventName, handler); + + return (() => parentComponent.removeHandler(eventName, handler)); } export function useSpacedUpdate(callback: () => Promise, interval = 1000) { @@ -247,13 +249,16 @@ export function useNoteContext() { console.warn("Note switched", notePath); setNotePath(notePath); }); + + const parentComponent = useContext(ParentComponent) as ReactWrappedWidget; return { note: noteContext?.note, noteId: noteContext?.note?.noteId, notePath: noteContext?.notePath, hoistedNoteId: noteContext?.hoistedNoteId, - ntxId: noteContext?.ntxId + ntxId: noteContext?.ntxId, + componentId: parentComponent.componentId }; } \ No newline at end of file