/* global React, supabase */ // ============================================================ // Supabase client + auth + Pro entitlement sync // Degrades gracefully: if not configured (window.RB_LIVE false), // sb is null and the app runs in local/demo mode. // ============================================================ const RB = window.RB_CONFIG || {}; let sb = null; if (window.RB_LIVE && window.supabase && window.supabase.createClient) { try { sb = window.supabase.createClient(RB.SUPABASE_URL, RB.SUPABASE_ANON_KEY); } catch (e) { console.warn("Supabase init failed:", e); } } window.sb = sb; // ---- entitlement cache (read by pro.jsx) ------------------- window.__rbProActive = null; // null = unknown/loading, true/false once known window.__rbUser = null; function emitPro() { window.dispatchEvent(new Event("rb-pro-change")); } function emitAuth() { window.dispatchEvent(new Event("rb-auth-change")); } // Fetch the signed-in user's active subscription → set the Pro flag. async function refreshEntitlement() { if (!sb || !window.__rbUser) { window.__rbProActive = false; emitPro(); return false; } try { // newest subscription row for this user (any state) const { data, error } = await sb .from("subscriptions") .select("status, raw_status, current_period_end, charge_at") .eq("user_id", window.__rbUser.id) .order("created_at", { ascending: false }) .limit(1); if (error) throw error; const row = data && data[0]; const withinPeriod = row && row.current_period_end && new Date(row.current_period_end) > new Date(); const live = !!row && ( ["active", "authenticated"].includes(row.status) || (withinPeriod && !["expired", "halted"].includes(row.status)) ); window.__rbProActive = live; window.__rbProStatus = row ? row.status : null; // active|pending|halted|cancelled|... window.__rbProUntil = row ? row.current_period_end : null; window.__rbProNextCharge = row ? row.charge_at : null; } catch (e) { console.warn("entitlement check failed:", e); window.__rbProActive = false; window.__rbProStatus = null; } emitPro(); return window.__rbProActive; } window.refreshEntitlement = refreshEntitlement; // ---- auth API ---------------------------------------------- const RBAuth = { enabled() { return !!sb; }, user() { return window.__rbUser; }, async init() { if (!sb) return; const { data } = await sb.auth.getUser(); window.__rbUser = (data && data.user) || null; emitAuth(); await refreshEntitlement(); sb.auth.onAuthStateChange((_evt, session) => { window.__rbUser = (session && session.user) || null; emitAuth(); refreshEntitlement(); }); }, async signUp(email, password, name) { if (!sb) throw new Error("Auth not configured"); const { data, error } = await sb.auth.signUp({ email, password, options: { data: { full_name: name || "" } }, }); if (error) throw error; return data; }, async signIn(email, password) { if (!sb) throw new Error("Auth not configured"); const { data, error } = await sb.auth.signInWithPassword({ email, password }); if (error) throw error; return data; }, async magicLink(email) { if (!sb) throw new Error("Auth not configured"); const { error } = await sb.auth.signInWithOtp({ email, options: { emailRedirectTo: window.location.href } }); if (error) throw error; return true; }, async signOut() { if (!sb) return; await sb.auth.signOut(); window.__rbUser = null; window.__rbProActive = false; emitAuth(); emitPro(); }, }; window.RBAuth = RBAuth; // React hook: current auth user (re-renders on change) function useAuth() { const [, force] = React.useState(0); React.useEffect(() => { const h = () => force((x) => x + 1); window.addEventListener("rb-auth-change", h); return () => window.removeEventListener("rb-auth-change", h); }, []); return { user: window.__rbUser, enabled: RBAuth.enabled() }; } window.useAuth = useAuth; // kick off session restore if (sb) { RBAuth.init(); }