mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	fix(react/settings): useTriliumEvent not cleaning up properly
This commit is contained in:
		| @@ -5,7 +5,10 @@ import SpacedUpdate from "../../services/spaced_update"; | |||||||
| import { OptionNames } from "@triliumnext/commons"; | import { OptionNames } from "@triliumnext/commons"; | ||||||
| import options from "../../services/options"; | import options from "../../services/options"; | ||||||
| import utils, { reloadFrontendApp } from "../../services/utils"; | import utils, { reloadFrontendApp } from "../../services/utils"; | ||||||
| import { __values } from "tslib"; | import Component from "../../components/component"; | ||||||
|  |  | ||||||
|  | type TriliumEventHandler<T extends EventNames> = (data: EventData<T>) => void; | ||||||
|  | const registeredHandlers: Map<Component, Map<EventNames, TriliumEventHandler<any>[]>> = new Map(); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Allows a React component to react to Trilium events (e.g. `entitiesReloaded`). When the desired event is triggered, the handler is invoked with the event parameters. |  * Allows a React component to react to Trilium events (e.g. `entitiesReloaded`). When the desired event is triggered, the handler is invoked with the event parameters. | ||||||
| @@ -16,32 +19,59 @@ import { __values } from "tslib"; | |||||||
|  * @param handler the handler to be invoked when the event is triggered. |  * @param handler the handler to be invoked when the event is triggered. | ||||||
|  * @param enabled determines whether the event should be listened to or not. Useful to conditionally limit the listener based on a state (e.g. a modal being displayed). |  * @param enabled determines whether the event should be listened to or not. Useful to conditionally limit the listener based on a state (e.g. a modal being displayed). | ||||||
|  */ |  */ | ||||||
| export default function useTriliumEvent<T extends EventNames>(eventName: T, handler: (data: EventData<T>) => void, enabled = true) { | export default function useTriliumEvent<T extends EventNames>(eventName: T, handler: TriliumEventHandler<T>, enabled = true) { | ||||||
|     const parentWidget = useContext(ParentComponent); |     const parentWidget = useContext(ParentComponent); | ||||||
|     useEffect(() => { |     if (!parentWidget) { | ||||||
|         if (!parentWidget || !enabled) {             |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|         // Create a unique handler name for this specific event listener |  | ||||||
|     const handlerName = `${eventName}Event`; |     const handlerName = `${eventName}Event`; | ||||||
|         const originalHandler = parentWidget[handlerName]; |     const customHandler  = useMemo(() => { | ||||||
|  |         return async (data: EventData<T>) => { | ||||||
|         // Override the event handler to call our handler |             // Inform the attached event listeners. | ||||||
|         parentWidget[handlerName] = async function(data: EventData<T>) { |             const eventHandlers = registeredHandlers.get(parentWidget)?.get(eventName) ?? []; | ||||||
|             // Call original handler if it exists |             for (const eventHandler of eventHandlers) { | ||||||
|             if (originalHandler) { |                 eventHandler(data); | ||||||
|                 await originalHandler.call(parentWidget, data); |  | ||||||
|             } |             } | ||||||
|             // Call our React component's handler |         } | ||||||
|             handler(data); |     }, [ eventName, parentWidget ]);     | ||||||
|         }; |  | ||||||
|  |     useEffect(() => { | ||||||
|  |         // Attach to the list of handlers. | ||||||
|  |         let handlersByWidget = registeredHandlers.get(parentWidget); | ||||||
|  |         if (!handlersByWidget) { | ||||||
|  |             handlersByWidget = new Map(); | ||||||
|  |             registeredHandlers.set(parentWidget, handlersByWidget); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let handlersByWidgetAndEventName = handlersByWidget.get(eventName); | ||||||
|  |         if (!handlersByWidgetAndEventName) { | ||||||
|  |             handlersByWidgetAndEventName = []; | ||||||
|  |             handlersByWidget.set(eventName, handlersByWidgetAndEventName); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!handlersByWidgetAndEventName.includes(handler)) { | ||||||
|  |             handlersByWidgetAndEventName.push(handler); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Apply the custom event handler. | ||||||
|  |         if (parentWidget[handlerName] && parentWidget[handlerName] !== customHandler) { | ||||||
|  |             console.warn(`Widget ${parentWidget.componentId} already had an event listener and it was replaced by the React one.`); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         parentWidget[handlerName] = customHandler; | ||||||
|      |      | ||||||
|         // Cleanup: restore original handler on unmount or when disabled |  | ||||||
|         return () => { |         return () => { | ||||||
|             parentWidget[handlerName] = originalHandler; |             const eventHandlers = registeredHandlers.get(parentWidget)?.get(eventName); | ||||||
|  |             if (!eventHandlers || !eventHandlers.includes(handler)) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |      | ||||||
|  |             // Remove the event handler from the array. | ||||||
|  |             const newEventHandlers = eventHandlers.filter(e => e !== handler); | ||||||
|  |             registeredHandlers.get(parentWidget)?.set(eventName, newEventHandlers);         | ||||||
|         }; |         }; | ||||||
|     }, [parentWidget, enabled, eventName, handler]); |     }, [ eventName, parentWidget, handler ]); | ||||||
| } | } | ||||||
|  |  | ||||||
| export function useSpacedUpdate(callback: () => Promise<void>, interval = 1000) { | export function useSpacedUpdate(callback: () => Promise<void>, interval = 1000) { | ||||||
|   | |||||||
| @@ -74,12 +74,14 @@ const FONT_FAMILIES: FontGroup[] = [ | |||||||
| ]; | ]; | ||||||
|  |  | ||||||
| export default function AppearanceSettings() {     | export default function AppearanceSettings() {     | ||||||
|  |     const [ overrideThemeFonts ] = useTriliumOption("overrideThemeFonts"); | ||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|         <> |         <div> | ||||||
|             <LayoutOrientation /> |             <LayoutOrientation /> | ||||||
|             <ApplicationTheme /> |             <ApplicationTheme /> | ||||||
|             <Fonts /> |             {overrideThemeFonts === "true" && <Fonts />} | ||||||
|         </> |         </div> | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user