File
Blob: src/client/lib/config.ts
| 1 | import { createContext, use } from "react"; |
| 2 | |
| 3 | export type SocialProvider = "github" | "google"; |
| 4 | |
| 5 | // Public client config served by /api/config. The worker fails closed |
| 6 | // with 503 when Turnstile is not configured; consumers see an explicit |
| 7 | // `unavailable` state so they can render an actionable error instead of |
| 8 | // an indefinite loading spinner. |
| 9 | export type ClientConfig = |
| 10 | | { |
| 11 | status: "ok"; |
| 12 | turnstileSiteKey: string; |
| 13 | issuer: string; |
| 14 | socialProviders: SocialProvider[]; |
| 15 | operatorName: string; |
| 16 | operatorContactEmail: string; |
| 17 | } |
| 18 | | { status: "unavailable" }; |
| 19 | |
| 20 | // `null` while the initial fetch is in flight; resolves to the |
| 21 | // ClientConfig union once /api/config has answered. Consumers that need |
| 22 | // to render before resolution (sign-in, invite-accept) handle the |
| 23 | // `null` case by falling back to a loading-state placeholder. |
| 24 | export type ClientConfigState = ClientConfig | null; |
| 25 | |
| 26 | // Created in this stable .ts module so Vite Fast Refresh of the Provider |
| 27 | // component does not recreate the context instance and orphan consumers. |
| 28 | export const ClientConfigContext = createContext<ClientConfigState>(null); |
| 29 | |
| 30 | // Returns: |
| 31 | // - `null` while the initial /api/config fetch is in flight |
| 32 | // - `{ status: "ok", ... }` when the worker answered with a usable payload |
| 33 | // - `{ status: "unavailable" }` when /api/config returned 503 or the |
| 34 | // fetch threw |
| 35 | export const useClientConfig = (): ClientConfigState => use(ClientConfigContext); |