thread-master/apps/web/src/auth/AuthContext.tsx

80 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from "react";
import type { Member } from "../domain/types";
import type { MemberProfilePatch } from "../data/repos";
import { useRepos } from "../data/DataContext";
type AuthContextValue = {
member: Member | null;
loading: boolean;
login: (email: string, password: string) => Promise<void>;
logout: () => Promise<void>;
reload: () => Promise<void>;
updateProfile: (patch: MemberProfilePatch) => Promise<Member>;
};
const AuthContext = createContext<AuthContextValue | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const repos = useRepos();
const [member, setMember] = useState<Member | null>(null);
const [loading, setLoading] = useState(true);
const reload = useCallback(async () => {
// 已進過 app 後不要再把 loading=true否則 RequireAuth 會卸載整棵 /app
// 各頁本地 state如用量「全體」分頁會被重掛成預設。
try {
const me = await repos.auth.me();
setMember(me);
} finally {
setLoading(false);
}
}, [repos.auth]);
useEffect(() => {
void reload();
}, [reload]);
const login = useCallback(
async (email: string, password: string) => {
const { member: m } = await repos.auth.login(email, password);
setMember(m);
},
[repos.auth],
);
const logout = useCallback(async () => {
await repos.auth.logout();
setMember(null);
}, [repos.auth]);
const updateProfile = useCallback(
async (patch: MemberProfilePatch) => {
const next = await repos.auth.updateProfile(patch);
setMember(next);
return next;
},
[repos.auth],
);
const value = useMemo(
() => ({ member, loading, login, logout, reload, updateProfile }),
[member, loading, login, logout, reload, updateProfile],
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
export function useAuth(): AuthContextValue {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error("useAuth must be used within AuthProvider");
return ctx;
}