Write on the margins of the internet. Powered by the AT Protocol. margin.at
extension web atproto comments
at main 1.9 kB view raw
1import { useState, createContext, useContext, useEffect } from "react"; 2import { getSession, logout } from "../api/client"; 3 4const AuthContext = createContext(null); 5 6export function AuthProvider({ children }) { 7 const [user, setUser] = useState(null); 8 const [loading, setLoading] = useState(true); 9 10 useEffect(() => { 11 checkSession(); 12 }, []); 13 14 const checkSession = async () => { 15 try { 16 const data = await getSession(); 17 if (data.authenticated) { 18 let avatar = null; 19 let displayName = null; 20 try { 21 const profileRes = await fetch( 22 `https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(data.did)}`, 23 ); 24 if (profileRes.ok) { 25 const profile = await profileRes.json(); 26 avatar = profile.avatar; 27 displayName = profile.displayName; 28 } 29 } catch (e) { 30 console.error("Failed to fetch profile:", e); 31 } 32 setUser({ 33 did: data.did, 34 handle: data.handle, 35 avatar, 36 displayName: displayName || data.handle, 37 }); 38 } else { 39 setUser(null); 40 } 41 } catch { 42 setUser(null); 43 } finally { 44 setLoading(false); 45 } 46 }; 47 48 const handleLogout = async () => { 49 try { 50 await logout(); 51 } catch (e) { 52 console.warn("Logout failed", e); 53 } 54 setUser(null); 55 }; 56 57 const value = { 58 user, 59 loading, 60 isAuthenticated: !!user, 61 login: () => (window.location.href = "/login"), 62 logout: handleLogout, 63 refresh: checkSession, 64 }; 65 66 return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; 67} 68 69// eslint-disable-next-line react-refresh/only-export-components 70export function useAuth() { 71 const context = useContext(AuthContext); 72 if (!context) { 73 throw new Error("useAuth must be used within AuthProvider"); 74 } 75 return context; 76}