diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-11-02 00:00:35 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-11-02 00:00:35 +0900 |
| commit | 104341ddc4add57f83c58cb3fabb23b6fbfdd3e4 (patch) | |
| tree | 862b109fe257e6170a88929729dae3bddfb6eb49 /frontend/src/contexts/AuthContext.tsx | |
| parent | ba1e0c904f810193f25d4f88cc2bb168f1d625fe (diff) | |
| download | feedaka-104341ddc4add57f83c58cb3fabb23b6fbfdd3e4.tar.gz feedaka-104341ddc4add57f83c58cb3fabb23b6fbfdd3e4.tar.zst feedaka-104341ddc4add57f83c58cb3fabb23b6fbfdd3e4.zip | |
Diffstat (limited to 'frontend/src/contexts/AuthContext.tsx')
| -rw-r--r-- | frontend/src/contexts/AuthContext.tsx | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/frontend/src/contexts/AuthContext.tsx b/frontend/src/contexts/AuthContext.tsx new file mode 100644 index 0000000..4f97355 --- /dev/null +++ b/frontend/src/contexts/AuthContext.tsx @@ -0,0 +1,95 @@ +import { + createContext, + type ReactNode, + useContext, + useEffect, + useState, +} from "react"; +import { useMutation, useQuery } from "urql"; +import { + GetMeDocument, + LoginDocument, + LogoutDocument, +} from "../graphql/generated/graphql"; + +interface User { + id: string; + username: string; +} + +interface AuthContextType { + user: User | null; + isLoading: boolean; + login: (username: string, password: string) => Promise<boolean>; + logout: () => Promise<void>; +} + +const AuthContext = createContext<AuthContextType | undefined>(undefined); + +export function AuthProvider({ children }: { children: ReactNode }) { + const [user, setUser] = useState<User | null>(null); + const [isLoading, setIsLoading] = useState(true); + + const [, executeLogin] = useMutation(LoginDocument); + const [, executeLogout] = useMutation(LogoutDocument); + const [meResult, reexecuteMe] = useQuery({ query: GetMeDocument }); + + // Update user from Me query + useEffect(() => { + if (meResult.data?.me) { + setUser(meResult.data.me); + } else { + setUser(null); + } + if (!meResult.fetching) { + setIsLoading(false); + } + }, [meResult.data, meResult.fetching]); + + const login = async ( + username: string, + password: string, + ): Promise<boolean> => { + try { + const result = await executeLogin({ username, password }); + + if (result.data?.login?.user) { + setUser(result.data.login.user); + // Refetch Me query to ensure session is established + reexecuteMe({ requestPolicy: "network-only" }); + return true; + } + + return false; + } catch (error) { + console.error("Login failed:", error); + return false; + } + }; + + const logout = async () => { + try { + await executeLogout({}); + } catch (error) { + console.error("Logout failed:", error); + } finally { + setUser(null); + // Refetch Me query to ensure session is cleared + reexecuteMe({ requestPolicy: "network-only" }); + } + }; + + return ( + <AuthContext.Provider value={{ user, isLoading, login, logout }}> + {children} + </AuthContext.Provider> + ); +} + +export function useAuth() { + const context = useContext(AuthContext); + if (context === undefined) { + throw new Error("useAuth must be used within an AuthProvider"); + } + return context; +} |
