- Stop overwriting AI session titles with the "Planning session" fallback during cross-tab broadcasts before initialPlan has hydrated on resume. - Persist SessionNotificationBanner dismissals to localStorage keyed by updatedAt so they survive refresh and auto-re-show on the next event. - Add Settings → Appearance toggle to hide the banner entirely. - Drop the inner scrollbar on the planning question list; let the outer pane handle all scrolling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
46 lines
1.3 KiB
TypeScript
46 lines
1.3 KiB
TypeScript
import { useSyncExternalStore } from "react";
|
|
|
|
const STORAGE_KEY = "fusion:hide-session-banners";
|
|
const EVENT_NAME = "fusion:session-banner-pref-changed";
|
|
|
|
function read(): boolean {
|
|
if (typeof window === "undefined") return false;
|
|
try {
|
|
return window.localStorage.getItem(STORAGE_KEY) === "1";
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function subscribe(onChange: () => void): () => void {
|
|
if (typeof window === "undefined") return () => {};
|
|
const handleStorage = (e: StorageEvent) => {
|
|
if (e.key === STORAGE_KEY) onChange();
|
|
};
|
|
const handleCustom = () => onChange();
|
|
window.addEventListener("storage", handleStorage);
|
|
window.addEventListener(EVENT_NAME, handleCustom);
|
|
return () => {
|
|
window.removeEventListener("storage", handleStorage);
|
|
window.removeEventListener(EVENT_NAME, handleCustom);
|
|
};
|
|
}
|
|
|
|
export function setSessionBannersHidden(hidden: boolean): void {
|
|
if (typeof window === "undefined") return;
|
|
try {
|
|
if (hidden) {
|
|
window.localStorage.setItem(STORAGE_KEY, "1");
|
|
} else {
|
|
window.localStorage.removeItem(STORAGE_KEY);
|
|
}
|
|
window.dispatchEvent(new Event(EVENT_NAME));
|
|
} catch {
|
|
// ignore
|
|
}
|
|
}
|
|
|
|
export function useSessionBannersHidden(): boolean {
|
|
return useSyncExternalStore(subscribe, read, () => false);
|
|
}
|