94 lines
2.8 KiB
TypeScript
94 lines
2.8 KiB
TypeScript
|
|
import { createContext, useContext, useEffect, useState, useCallback, useMemo } from 'react'
|
||
|
|
import type { User, Session, AuthError } from '@supabase/supabase-js'
|
||
|
|
import { supabase } from '../lib/supabase'
|
||
|
|
|
||
|
|
interface AuthState {
|
||
|
|
user: User | null
|
||
|
|
session: Session | null
|
||
|
|
loading: boolean
|
||
|
|
}
|
||
|
|
|
||
|
|
interface AuthContextValue extends AuthState {
|
||
|
|
signInWithEmail: (email: string, password: string) => Promise<{ error: AuthError | null }>
|
||
|
|
signUpWithEmail: (email: string, password: string) => Promise<{ error: AuthError | null }>
|
||
|
|
signInWithGoogle: () => Promise<{ error: AuthError | null }>
|
||
|
|
signInWithDiscord: () => Promise<{ error: AuthError | null }>
|
||
|
|
signOut: () => Promise<void>
|
||
|
|
}
|
||
|
|
|
||
|
|
const AuthContext = createContext<AuthContextValue | null>(null)
|
||
|
|
|
||
|
|
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||
|
|
const [state, setState] = useState<AuthState>({
|
||
|
|
user: null,
|
||
|
|
session: null,
|
||
|
|
loading: true,
|
||
|
|
})
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
supabase.auth.getSession().then(({ data: { session } }) => {
|
||
|
|
setState({ user: session?.user ?? null, session, loading: false })
|
||
|
|
})
|
||
|
|
|
||
|
|
const {
|
||
|
|
data: { subscription },
|
||
|
|
} = supabase.auth.onAuthStateChange((_event, session) => {
|
||
|
|
setState({ user: session?.user ?? null, session, loading: false })
|
||
|
|
})
|
||
|
|
|
||
|
|
return () => subscription.unsubscribe()
|
||
|
|
}, [])
|
||
|
|
|
||
|
|
const signInWithEmail = useCallback(async (email: string, password: string) => {
|
||
|
|
const { error } = await supabase.auth.signInWithPassword({ email, password })
|
||
|
|
return { error }
|
||
|
|
}, [])
|
||
|
|
|
||
|
|
const signUpWithEmail = useCallback(async (email: string, password: string) => {
|
||
|
|
const { error } = await supabase.auth.signUp({ email, password })
|
||
|
|
return { error }
|
||
|
|
}, [])
|
||
|
|
|
||
|
|
const signInWithGoogle = useCallback(async () => {
|
||
|
|
const { error } = await supabase.auth.signInWithOAuth({
|
||
|
|
provider: 'google',
|
||
|
|
options: { redirectTo: `${window.location.origin}/auth/callback` },
|
||
|
|
})
|
||
|
|
return { error }
|
||
|
|
}, [])
|
||
|
|
|
||
|
|
const signInWithDiscord = useCallback(async () => {
|
||
|
|
const { error } = await supabase.auth.signInWithOAuth({
|
||
|
|
provider: 'discord',
|
||
|
|
options: { redirectTo: `${window.location.origin}/auth/callback` },
|
||
|
|
})
|
||
|
|
return { error }
|
||
|
|
}, [])
|
||
|
|
|
||
|
|
const signOut = useCallback(async () => {
|
||
|
|
await supabase.auth.signOut()
|
||
|
|
}, [])
|
||
|
|
|
||
|
|
const value = useMemo(
|
||
|
|
() => ({
|
||
|
|
...state,
|
||
|
|
signInWithEmail,
|
||
|
|
signUpWithEmail,
|
||
|
|
signInWithGoogle,
|
||
|
|
signInWithDiscord,
|
||
|
|
signOut,
|
||
|
|
}),
|
||
|
|
[state, signInWithEmail, signUpWithEmail, signInWithGoogle, signInWithDiscord, signOut]
|
||
|
|
)
|
||
|
|
|
||
|
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useAuth() {
|
||
|
|
const context = useContext(AuthContext)
|
||
|
|
if (!context) {
|
||
|
|
throw new Error('useAuth must be used within an AuthProvider')
|
||
|
|
}
|
||
|
|
return context
|
||
|
|
}
|