Files
MineGeo/components/SiteSettingsProvider.tsx

85 lines
2.2 KiB
TypeScript

"use client";
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { usePathname } from "next/navigation";
import { api } from "@/lib/client-api";
import {
defaultSiteSettings,
siteSettingsSchema,
type SiteSettings,
} from "@/shared/site-settings";
const SettingsContext = createContext({
settings: defaultSiteSettings,
updateSettings: (_settings: SiteSettings) => {},
});
export function SiteSettingsProvider({
initialSettings,
children,
}: {
initialSettings: SiteSettings;
children: React.ReactNode;
}) {
const [settings, setSettings] = useState(initialSettings);
const version = useRef(0);
const pending = useRef(false);
const pathname = usePathname();
const updateSettings = useCallback((next: SiteSettings) => {
version.current++;
setSettings(next);
}, []);
const refresh = useCallback(async () => {
if (pending.current || document.visibilityState === "hidden") return;
pending.current = true;
const requestVersion = version.current;
try {
const next = siteSettingsSchema.parse(await api("site-settings"));
// An older poll must not overwrite a moderator's just-saved setting.
if (requestVersion === version.current) setSettings(next);
} catch {
// Keep the last known setting while disconnected.
} finally {
pending.current = false;
}
}, []);
useEffect(() => {
void refresh();
}, [pathname, refresh]);
useEffect(() => {
const timer = window.setInterval(() => {
void refresh();
}, 30000);
const resume = () => {
void refresh();
};
document.addEventListener("visibilitychange", resume);
window.addEventListener("focus", resume);
return () => {
window.clearInterval(timer);
document.removeEventListener("visibilitychange", resume);
window.removeEventListener("focus", resume);
};
}, [refresh]);
const value = useMemo(
() => ({ settings, updateSettings }),
[settings, updateSettings],
);
return (
<SettingsContext.Provider value={value}>
{children}
</SettingsContext.Provider>
);
}
export function useSiteSettings() {
return useContext(SettingsContext);
}