import { useCallback, useEffect, useState } from "react"; import { Outlet } from "react-router-dom"; import { useAuth } from "../hooks/useAuth"; import { useDensity } from "../hooks/useDensity"; import { useModules } from "../hooks/useModules"; import { useSSE } from "../hooks/useSSE"; import { Sidebar } from "./Sidebar"; export function AppShell() { const { user, loading, logout } = useAuth(); const [density, toggleDensity] = useDensity(); const { modules, refresh: refreshModules } = useModules(); const { on } = useSSE(); const [toast, setToast] = useState(null); // Listen for settings.changed SSE events useEffect(() => { return on("settings.changed", (raw) => { try { const data = JSON.parse(raw) as { module: string; changed_keys: string[]; updated_by: string; }; refreshModules(); if (data.updated_by !== user?.username) { setToast(`Settings updated by ${data.updated_by}`); } } catch { // ignore malformed events } }); }, [on, refreshModules, user?.username]); // Auto-dismiss toast useEffect(() => { if (!toast) return; const timer = setTimeout(() => setToast(null), 5000); return () => clearTimeout(timer); }, [toast]); const [sidebarOpen, setSidebarOpen] = useState(() => { return localStorage.getItem("silo-sidebar") !== "closed"; }); const toggleSidebar = useCallback(() => { setSidebarOpen((prev) => { const next = !prev; localStorage.setItem("silo-sidebar", next ? "open" : "closed"); return next; }); }, []); // Ctrl+J to toggle sidebar useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.ctrlKey && e.key === "j") { e.preventDefault(); toggleSidebar(); } }; window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, [toggleSidebar]); if (loading) { return (
); } return (
{toast && (
setToast(null)} > {toast}
)}
); }