import { useEffect, useState } from 'react'; import { apiFetch } from '../lib/api'; export type Plan = 'free' | 'pro' | 'legend'; /** * Single source of truth für den eingeloggten User. /api/auth/me joint * `auth.users` mit `rebreak.profiles` server-side — wir bekommen alles in * einem Request: plan, avatar, nickname, streak. * * Live-Update-Pattern (siehe RECOVERY_LOG): nach Profile-Edit (PATCH /api/auth/me) * MUSS `invalidateMe()` aufgerufen werden — alle useMe-Konsumenten (AppHeader, * PostCard, ComposeCard, etc.) re-fetchen automatisch via Listener-Subscribe. * * WICHTIG: nicht aus `supabase.auth.getUser().user_metadata` lesen — das * sind nur die JWT-Claims vom Signup-Zeitpunkt, NICHT der aktuelle Profile- * Stand (Avatar/Nickname/Plan werden via Profile-Edit-API geupdated, landen * in der DB, NICHT zurück ins JWT-Claim). */ export type OnboardingStep = | 'welcome' | 'account' | 'plan' | 'pre_protection' | 'done' // legacy (alte Builds könnten das im Profile haben — wird im neuen Flow nicht gesetzt) | 'nickname' | 'block'; export type Me = { id: string; email: string; username: string; nickname: string | null; avatar: string | null; plan: Plan; streak: number; lyraVoiceId: string | null; onboardingStep: OnboardingStep; created_at?: string; presenceVisible?: boolean; callsEnabled?: boolean; }; let cachedMe: Me | null = null; const listeners = new Set<() => void>(); /** * Lädt /api/auth/me neu und benachrichtigt ALLE useMe-Konsumenten in der App. * Nach jedem PATCH /api/auth/me aufrufen — sonst sehen Konsumenten alten Cache. */ export function invalidateMe(): void { cachedMe = null; for (const cb of listeners) cb(); } export function useMe(): { me: Me | null; loading: boolean; reload: () => void } { const [me, setMe] = useState(cachedMe); const [loading, setLoading] = useState(cachedMe === null); const [version, setVersion] = useState(0); // Auf globale Invalidierung lauschen (Avatar-/Nickname-Update aus Profile-Edit) useEffect(() => { const cb = () => setVersion((v) => v + 1); listeners.add(cb); return () => { listeners.delete(cb); }; }, []); useEffect(() => { let cancelled = false; (async () => { // Falls cache schon frisch ist (von anderem Konsumenten gerade geladen): nutzen if (cachedMe !== null) { setMe(cachedMe); setLoading(false); return; } try { const res = await apiFetch('/api/auth/me'); if (cancelled) return; cachedMe = res; setMe(res); } catch (e) { console.warn('[useMe] fetch failed:', e); } finally { if (!cancelled) setLoading(false); } })(); return () => { cancelled = true; }; }, [version]); return { me, loading, reload: () => { invalidateMe(); }, }; }