| 
									
										
										
										
											2025-08-14 17:53:24 +03:00
										 |  |  | import { type Dispatch, type StateUpdater, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks"; | 
					
						
							| 
									
										
										
										
											2025-08-08 20:08:06 +03:00
										 |  |  | import { EventData, EventNames } from "../../components/app_context"; | 
					
						
							|  |  |  | import { ParentComponent } from "./ReactBasicWidget"; | 
					
						
							| 
									
										
										
										
											2025-08-08 23:23:07 +03:00
										 |  |  | import SpacedUpdate from "../../services/spaced_update"; | 
					
						
							| 
									
										
										
										
											2025-08-14 17:36:11 +03:00
										 |  |  | import { OptionNames } from "@triliumnext/commons"; | 
					
						
							|  |  |  | import options from "../../services/options"; | 
					
						
							| 
									
										
										
										
											2025-08-14 18:18:45 +03:00
										 |  |  | import utils, { reloadFrontendApp } from "../../services/utils"; | 
					
						
							| 
									
										
										
										
											2025-08-08 20:08:06 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-10 15:11:43 +03:00
										 |  |  | /** | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  *  | 
					
						
							|  |  |  |  * Under the hood, it works by altering the parent (Trilium) component of the React element to introduce the corresponding event. | 
					
						
							|  |  |  |  *  | 
					
						
							|  |  |  |  * @param eventName the name of the Trilium event to listen for. | 
					
						
							|  |  |  |  * @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). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export default function useTriliumEvent<T extends EventNames>(eventName: T, handler: (data: EventData<T>) => void, enabled = true) { | 
					
						
							| 
									
										
										
										
											2025-08-08 20:08:06 +03:00
										 |  |  |     const parentWidget = useContext(ParentComponent); | 
					
						
							|  |  |  |     useEffect(() => { | 
					
						
							| 
									
										
										
										
											2025-08-10 15:11:43 +03:00
										 |  |  |         if (!parentWidget || !enabled) {             | 
					
						
							| 
									
										
										
										
											2025-08-08 20:08:06 +03:00
										 |  |  |             return; | 
					
						
							| 
									
										
										
										
											2025-08-10 15:11:43 +03:00
										 |  |  |         }         | 
					
						
							| 
									
										
										
										
											2025-08-08 20:08:06 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Create a unique handler name for this specific event listener
 | 
					
						
							|  |  |  |         const handlerName = `${eventName}Event`; | 
					
						
							|  |  |  |         const originalHandler = parentWidget[handlerName]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Override the event handler to call our handler
 | 
					
						
							|  |  |  |         parentWidget[handlerName] = async function(data: EventData<T>) { | 
					
						
							|  |  |  |             // Call original handler if it exists
 | 
					
						
							|  |  |  |             if (originalHandler) { | 
					
						
							|  |  |  |                 await originalHandler.call(parentWidget, data); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             // Call our React component's handler
 | 
					
						
							|  |  |  |             handler(data); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-10 15:11:43 +03:00
										 |  |  |         // Cleanup: restore original handler on unmount or when disabled
 | 
					
						
							| 
									
										
										
										
											2025-08-08 20:08:06 +03:00
										 |  |  |         return () => { | 
					
						
							|  |  |  |             parentWidget[handlerName] = originalHandler; | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2025-08-10 15:11:43 +03:00
										 |  |  |     }, [parentWidget, enabled, eventName, handler]); | 
					
						
							| 
									
										
										
										
											2025-08-08 23:23:07 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function useSpacedUpdate(callback: () => Promise<void>, interval = 1000) { | 
					
						
							| 
									
										
										
										
											2025-08-09 09:15:54 +03:00
										 |  |  |     const callbackRef = useRef(callback); | 
					
						
							|  |  |  |     const spacedUpdateRef = useRef<SpacedUpdate>(); | 
					
						
							| 
									
										
										
										
											2025-08-08 23:23:07 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-09 09:15:54 +03:00
										 |  |  |     // Update callback ref when it changes
 | 
					
						
							|  |  |  |     useEffect(() => { | 
					
						
							|  |  |  |         callbackRef.current = callback; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Create SpacedUpdate instance only once
 | 
					
						
							|  |  |  |     if (!spacedUpdateRef.current) { | 
					
						
							|  |  |  |         spacedUpdateRef.current = new SpacedUpdate( | 
					
						
							|  |  |  |             () => callbackRef.current(), | 
					
						
							|  |  |  |             interval | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Update interval if it changes
 | 
					
						
							|  |  |  |     useEffect(() => { | 
					
						
							|  |  |  |         spacedUpdateRef.current?.setUpdateInterval(interval); | 
					
						
							|  |  |  |     }, [interval]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return spacedUpdateRef.current; | 
					
						
							| 
									
										
										
										
											2025-08-14 17:36:11 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-14 18:18:45 +03:00
										 |  |  | export function useTriliumOption(name: OptionNames, needsRefresh?: boolean): [string, (newValue: string) => Promise<void>] { | 
					
						
							| 
									
										
										
										
											2025-08-14 17:36:11 +03:00
										 |  |  |     const initialValue = options.get(name); | 
					
						
							|  |  |  |     const [ value, setValue ] = useState(initialValue); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-14 17:57:08 +03:00
										 |  |  |     async function wrappedSetValue(newValue: string) { | 
					
						
							|  |  |  |         await options.save(name, newValue); | 
					
						
							| 
									
										
										
										
											2025-08-14 18:18:45 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (needsRefresh) { | 
					
						
							|  |  |  |             reloadFrontendApp(`option change: ${name}`); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-08-14 17:47:45 +03:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     useTriliumEvent("entitiesReloaded", ({ loadResults }) => { | 
					
						
							|  |  |  |         if (loadResults.getOptionNames().includes(name)) { | 
					
						
							|  |  |  |             const newValue = options.get(name); | 
					
						
							|  |  |  |             setValue(newValue); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-14 17:36:11 +03:00
										 |  |  |     return [ | 
					
						
							|  |  |  |         value, | 
					
						
							| 
									
										
										
										
											2025-08-14 17:47:45 +03:00
										 |  |  |         wrappedSetValue | 
					
						
							| 
									
										
										
										
											2025-08-14 17:36:11 +03:00
										 |  |  |     ] | 
					
						
							| 
									
										
										
										
											2025-08-14 17:53:24 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Generates a unique name via a random alphanumeric string of a fixed length. | 
					
						
							|  |  |  |  *  | 
					
						
							|  |  |  |  * <p> | 
					
						
							|  |  |  |  * Generally used to assign names to inputs that are unique, especially useful for widgets inside tabs. | 
					
						
							|  |  |  |  *  | 
					
						
							|  |  |  |  * @param prefix a prefix to add to the unique name. | 
					
						
							|  |  |  |  * @returns a name with the given prefix and a random alpanumeric string appended to it. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function useUniqueName(prefix: string) { | 
					
						
							|  |  |  |     return useMemo(() => prefix + utils.randomString(10), [ prefix]); | 
					
						
							| 
									
										
										
										
											2025-08-08 20:08:06 +03:00
										 |  |  | } |