mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-10-09 15:58:34 +00:00
f9eded887f
Keep stored boolean updates pure and propagate global broadcast state through terminal panes so orphan sessions can toggle and receive broadcasts correctly.
63 lines
2.6 KiB
TypeScript
63 lines
2.6 KiB
TypeScript
import { useCallback, useEffect, useRef, useState } from "react";
|
|
import { localStorageAdapter } from "../../infrastructure/persistence/localStorageAdapter";
|
|
|
|
/**
|
|
* Hook for persisting a boolean value to localStorage.
|
|
* Syncs across components in the same window via a custom event,
|
|
* and across windows via the native storage event.
|
|
* @param storageKey - The key to use for localStorage
|
|
* @param fallback - The default value if no stored value exists (defaults to false)
|
|
* @returns A tuple of [value, setValue] similar to useState
|
|
*/
|
|
export const useStoredBoolean = (
|
|
storageKey: string,
|
|
fallback: boolean = false,
|
|
) => {
|
|
const [value, setValue] = useState<boolean>(() => {
|
|
const stored = localStorageAdapter.readBoolean(storageKey);
|
|
return stored ?? fallback;
|
|
});
|
|
const valueRef = useRef(value);
|
|
|
|
const setAndPersist = useCallback((next: boolean | ((prev: boolean) => boolean)) => {
|
|
// Resolve functional updates before scheduling React state. The updater
|
|
// must stay pure because React may evaluate it while rendering.
|
|
const resolved = typeof next === "function" ? next(valueRef.current) : next;
|
|
valueRef.current = resolved;
|
|
setValue(resolved);
|
|
localStorageAdapter.writeBoolean(storageKey, resolved);
|
|
// Notify other same-window consumers outside the state updater.
|
|
window.dispatchEvent(
|
|
new CustomEvent("stored-boolean-change", { detail: { key: storageKey, value: resolved } }),
|
|
);
|
|
}, [storageKey]);
|
|
|
|
useEffect(() => {
|
|
// Sync from other components in the same window
|
|
const handleCustom = (e: Event) => {
|
|
const { key, value: newValue } = (e as CustomEvent).detail;
|
|
if (key === storageKey) {
|
|
valueRef.current = newValue;
|
|
setValue(newValue);
|
|
}
|
|
};
|
|
// Sync from other windows
|
|
const handleStorage = (e: StorageEvent) => {
|
|
if (e.key === storageKey) {
|
|
const stored = localStorageAdapter.readBoolean(storageKey);
|
|
const nextValue = stored ?? fallback;
|
|
valueRef.current = nextValue;
|
|
setValue(nextValue);
|
|
}
|
|
};
|
|
window.addEventListener("stored-boolean-change", handleCustom);
|
|
window.addEventListener("storage", handleStorage);
|
|
return () => {
|
|
window.removeEventListener("stored-boolean-change", handleCustom);
|
|
window.removeEventListener("storage", handleStorage);
|
|
};
|
|
}, [storageKey, fallback]);
|
|
|
|
return [value, setAndPersist] as const;
|
|
};
|