feat: add auth
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import { auth, type User } from "@/lib/auth";
|
||||
|
||||
/**
|
||||
* AuthProvider holds the current-user state and exposes the auth actions
|
||||
* to the rest of the app.
|
||||
*
|
||||
* On mount it calls /auth/me to hydrate. Until that completes, `loading`
|
||||
* is true and consumers should render a placeholder rather than assuming
|
||||
* the user is logged out.
|
||||
*
|
||||
* After a successful login/signup/logout the provider re-fetches /auth/me
|
||||
* to keep state in sync with the cookie.
|
||||
*/
|
||||
type AuthContextValue = {
|
||||
user: User | null;
|
||||
loading: boolean;
|
||||
signUp: (email: string, password: string, name: string) => Promise<User>;
|
||||
logIn: (email: string, password: string) => Promise<User>;
|
||||
logOut: () => Promise<void>;
|
||||
refresh: () => Promise<void>;
|
||||
/** Replace the user state without a network round-trip (after profile edit). */
|
||||
setUser: (u: User | null) => void;
|
||||
};
|
||||
|
||||
const AuthContext = createContext<AuthContextValue | null>(null);
|
||||
|
||||
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
try {
|
||||
const me = await auth.me();
|
||||
setUser(me);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
void refresh();
|
||||
}, [refresh]);
|
||||
|
||||
const signUp = useCallback(async (email: string, password: string, name: string) => {
|
||||
const u = await auth.signUp(email, password, name);
|
||||
setUser(u);
|
||||
return u;
|
||||
}, []);
|
||||
|
||||
const logIn = useCallback(async (email: string, password: string) => {
|
||||
const u = await auth.logIn(email, password);
|
||||
setUser(u);
|
||||
return u;
|
||||
}, []);
|
||||
|
||||
const logOut = useCallback(async () => {
|
||||
await auth.logOut();
|
||||
setUser(null);
|
||||
}, []);
|
||||
|
||||
const value = useMemo<AuthContextValue>(
|
||||
() => ({ user, loading, signUp, logIn, logOut, refresh, setUser }),
|
||||
[user, loading, signUp, logIn, logOut, refresh],
|
||||
);
|
||||
|
||||
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 inside <AuthProvider>");
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
Reference in New Issue
Block a user