import { type ReactNode, useEffect, useState } from "react"; import { ClientConfigContext, type ClientConfig, type ClientConfigState, type SocialProvider, } from "@/client/lib/config"; import { apiRequest } from "@/client/lib/http"; const isSocialProvider = (value: unknown): value is SocialProvider => value === "github" || value === "google"; const fetchClientConfig = async (): Promise => { try { const res = await apiRequest("/api/config"); if (!res.ok) return { status: "unavailable" }; const body = (await res.json()) as { turnstileSiteKey?: unknown; issuer?: unknown; socialProviders?: unknown; operatorName?: unknown; operatorContactEmail?: unknown; }; if (typeof body.turnstileSiteKey !== "string" || body.turnstileSiteKey.length === 0) { return { status: "unavailable" }; } if (typeof body.issuer !== "string" || body.issuer.length === 0) { return { status: "unavailable" }; } if (typeof body.operatorName !== "string" || body.operatorName.length === 0) { return { status: "unavailable" }; } if (typeof body.operatorContactEmail !== "string" || body.operatorContactEmail.length === 0) { return { status: "unavailable" }; } const socialProviders = Array.isArray(body.socialProviders) ? body.socialProviders.filter(isSocialProvider) : []; return { status: "ok", turnstileSiteKey: body.turnstileSiteKey, issuer: body.issuer, socialProviders, operatorName: body.operatorName, operatorContactEmail: body.operatorContactEmail, }; } catch { return { status: "unavailable" }; } }; interface ClientConfigProviderProps { children: ReactNode; } // Single source for /api/config across the app. One round-trip per // page load is shared across every consumer that calls useClientConfig, // so each component reads the same resolved value without duplicating // the fetch effect. export const ClientConfigProvider = ({ children }: ClientConfigProviderProps) => { const [config, setConfig] = useState(null); useEffect(() => { let cancelled = false; fetchClientConfig().then((value) => { if (!cancelled) setConfig(value); }); return () => { cancelled = true; }; }, []); return {children}; };