Improve MineGeo interface and functionality
This commit is contained in:
84
components/SiteSettingsProvider.tsx
Normal file
84
components/SiteSettingsProvider.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
"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);
|
||||
}
|
||||
Reference in New Issue
Block a user