import Turnstile, { BoundTurnstileObject } from "react-turnstile"; import { create } from "zustand"; import { immer } from "zustand/middleware/immer"; import { conf } from "@/setup/config"; export interface TurnstileStore { turnstile: BoundTurnstileObject | null; cbs: ((token: string | null) => void)[]; setTurnstile(v: BoundTurnstileObject | null): void; getToken(): Promise; processToken(token: string | null): void; } export const useTurnstileStore = create( immer((set, get) => ({ turnstile: null, cbs: [], processToken(token) { const cbs = get().cbs; cbs.forEach((fn) => fn(token)); set((s) => { s.cbs = []; }); }, getToken() { return new Promise((resolve, reject) => { set((s) => { s.cbs = [ ...s.cbs, (token) => { if (!token) reject(new Error("Failed to get token")); else resolve(token); }, ]; }); }); }, setTurnstile(v) { set((s) => { s.turnstile = v; }); }, })) ); export function getTurnstile() { return useTurnstileStore.getState().turnstile; } export function isTurnstileInitialized() { return !!getTurnstile(); } export function getTurnstileToken() { const turnstile = getTurnstile(); turnstile?.reset(); turnstile?.execute(); return useTurnstileStore.getState().getToken(); } export function TurnstileProvider() { const siteKey = conf().TURNSTILE_KEY; const setTurnstile = useTurnstileStore((s) => s.setTurnstile); const processToken = useTurnstileStore((s) => s.processToken); if (!siteKey) return null; return ( { setTurnstile(bound); }} onError={() => { processToken(null); }} onVerify={(token) => { processToken(token); }} /> ); }