From 2c81ffffaaee816f271b1bfe488e64d0288050fc Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 8 Nov 2025 16:09:01 +0900 Subject: fix(frontend): Don't discard GraphQL error --- frontend/src/contexts/AuthContext.tsx | 55 +++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 15 deletions(-) (limited to 'frontend/src/contexts/AuthContext.tsx') diff --git a/frontend/src/contexts/AuthContext.tsx b/frontend/src/contexts/AuthContext.tsx index fc6b237..e43a1ec 100644 --- a/frontend/src/contexts/AuthContext.tsx +++ b/frontend/src/contexts/AuthContext.tsx @@ -8,19 +8,20 @@ import { import { useMutation, useQuery } from "urql"; import { GetCurrentUserDocument, + type GetCurrentUserQuery, LoginDocument, LogoutDocument, } from "../graphql/generated/graphql"; -interface User { - id: string; - username: string; -} +type User = NonNullable; + +type LoginResult = { success: true } | { success: false; error: string }; interface AuthContextType { user: User | null; isLoading: boolean; - login: (username: string, password: string) => Promise; + error: string | null; + login: (username: string, password: string) => Promise; logout: () => Promise; } @@ -29,6 +30,7 @@ const AuthContext = createContext(undefined); export function AuthProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(null); const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); const [, executeLogin] = useMutation(LoginDocument); const [, executeLogout] = useMutation(LogoutDocument); @@ -40,49 +42,72 @@ export function AuthProvider({ children }: { children: ReactNode }) { useEffect(() => { if (currentUserResult.data?.currentUser) { setUser(currentUserResult.data.currentUser); + setError(null); } else { setUser(null); } + + if (currentUserResult.error) { + setError(currentUserResult.error.message); + } + if (!currentUserResult.fetching) { setIsLoading(false); } - }, [currentUserResult.data, currentUserResult.fetching]); + }, [ + currentUserResult.data, + currentUserResult.fetching, + currentUserResult.error, + ]); const login = async ( username: string, password: string, - ): Promise => { + ): Promise => { + setError(null); + try { const result = await executeLogin({ username, password }); + if (result.error) { + const errorMessage = + result.error.graphQLErrors[0]?.message || result.error.message; + setError(errorMessage); + return { success: false, error: errorMessage }; + } + if (result.data?.login?.user) { - setUser(result.data.login.user); // Refetch CurrentUser query to ensure session is established reexecuteCurrentUser({ requestPolicy: "network-only" }); - return true; + return { success: true }; } - return false; + const errorMessage = "Invalid username or password"; + setError(errorMessage); + return { success: false, error: errorMessage }; } catch (error) { + const errorMessage = + error instanceof Error ? error.message : "An unknown error occurred"; console.error("Login failed:", error); - return false; + setError(errorMessage); + return { success: false, error: errorMessage }; } }; const logout = async () => { try { await executeLogout({}); + // Refetch CurrentUser query to ensure session is cleared + reexecuteCurrentUser({ requestPolicy: "network-only" }); } catch (error) { console.error("Logout failed:", error); - } finally { - setUser(null); - // Refetch CurrentUser query to ensure session is cleared + // Even on error, refetch to get the latest state reexecuteCurrentUser({ requestPolicy: "network-only" }); } }; return ( - + {children} ); -- cgit v1.2.3-70-g09d2