"use client"; import { useCallback, useEffect, useState } from "react"; import { AUTH_ENABLED } from "@/lib/supabase/config"; import { createClient } from "@/lib/supabase/client"; import { syncOnLogin } from "@/lib/persistence/cloudSync"; import type { AuthChangeEvent, Session, User } from "@supabase/supabase-js"; export function UserChip() { const [user, setUser] = useState(null); const [menuOpen, setMenuOpen] = useState(false); useEffect(() => { if (!AUTH_ENABLED) return; const supabase = createClient(); supabase.auth.getUser().then(({ data }: { data: { user: User | null } }) => setUser(data.user)); const { data: { subscription }, } = supabase.auth.onAuthStateChange((event: AuthChangeEvent, session: Session | null) => { setUser(session?.user ?? null); // A signed-in user — a fresh login (SIGNED_IN) OR an already-authed mount // (INITIAL_SESSION fires on subscribe with the current session) — triggers // a full reconcile. syncOnLogin serializes via its in-flight guard, so // overlapping events never run concurrent syncs (Req 4.1, 4.2, 4.3). This // is the single global trigger point; AuthModal instances don't duplicate it. if (session?.user && (event === "SIGNED_IN" || event === "INITIAL_SESSION")) { void syncOnLogin(); } }); return () => subscription.unsubscribe(); }, []); const handleLogout = useCallback(async () => { const supabase = createClient(); await supabase.auth.signOut(); setUser(null); setMenuOpen(false); }, []); if (!AUTH_ENABLED || !user) return null; const label = user.user_metadata?.full_name ?? user.email?.split("@")[0] ?? "User"; const avatarUrl = user.user_metadata?.avatar_url as string | undefined; const initial = label.charAt(0).toUpperCase(); return (
{menuOpen && ( <>
setMenuOpen(false)} />
)}
); }