useAuth.tsx (2343B)
1 "use client"; 2 3 import { useState, useEffect, useContext, createContext, ReactNode } from 'react'; 4 import { createClient, User } from '@supabase/supabase-js'; 5 import OneSignal from 'react-onesignal'; 6 7 export const supabase = createClient( 8 process.env.NEXT_PUBLIC_SUPABASE_URL!, 9 process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! 10 ); 11 12 type AuthContextType = { 13 user: User | null; 14 isLoggedIn: boolean; 15 isLoading: boolean; 16 loginWithDiscord: () => Promise<void>; 17 logout: () => Promise<void>; 18 avatarUrl: string | null; 19 displayName: string | null; 20 }; 21 22 const AuthContext = createContext<AuthContextType | null>(null); 23 24 export const AuthProvider = ({ children }: { children: ReactNode }) => { 25 const [user, setUser] = useState<User | null>(null); 26 const [isLoading, setIsLoading] = useState(true); 27 28 useEffect(() => { 29 const checkSession = async () => { 30 const { data: { session } } = await supabase.auth.getSession(); 31 setUser(session?.user ?? null); 32 setIsLoading(false); 33 }; 34 checkSession(); 35 36 const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { 37 setUser(session?.user ?? null); 38 setIsLoading(false); 39 }); 40 41 return () => subscription.unsubscribe(); 42 }, []); 43 44 const loginWithDiscord = async () => { 45 const currentOrigin = typeof window !== 'undefined' ? window.location.origin : undefined; 46 47 await supabase.auth.signInWithOAuth({ 48 provider: 'discord', 49 options: { 50 redirectTo: currentOrigin, 51 }, 52 }); 53 }; 54 55 const logout = async () => { 56 await supabase.auth.signOut(); 57 try { 58 await OneSignal.logout(); 59 } catch (e) { 60 console.error("OneSignal logout error", e); 61 } 62 setUser(null); 63 }; 64 65 const avatarUrl = user?.user_metadata?.avatar_url || null; 66 const displayName = user?.user_metadata?.full_name || user?.user_metadata?.custom_claims?.global_name || user?.email || "先生"; 67 68 return ( 69 <AuthContext.Provider value={{ 70 user, 71 isLoggedIn: !!user, 72 isLoading, 73 loginWithDiscord, 74 logout, 75 avatarUrl, 76 displayName 77 }}> 78 {children} 79 </AuthContext.Provider> 80 ); 81 }; 82 83 export const useAuth = () => { 84 const context = useContext(AuthContext); 85 if (!context) throw new Error("useAuth must be used within AuthProvider"); 86 return context; 87 };