Files
dm-companion/src/context/auth/AuthContext.tsx
2025-07-15 10:53:28 -07:00

88 lines
2.4 KiB
TypeScript

import { createContext, useContext, useCallback, useState } from "react";
import type { ReactNode } from "react";
import { pb } from "@/lib/pocketbase";
import type { AuthRecord } from "pocketbase";
import { useNavigate } from "@tanstack/react-router";
/**
* Represents the shape of the authenticated user object from PocketBase.
*/
/**
* Context value for authentication state and actions.
*/
export interface AuthContextValue {
user: AuthRecord | null;
isLoading: boolean;
login: (email: string, password: string) => Promise<void>;
signup: (
email: string,
password: string,
passwordConfirm: string,
) => Promise<void>;
logout: () => Promise<void>;
}
const AuthContext = createContext<AuthContextValue | undefined>(undefined);
/**
* Provider for authentication context.
*/
export function AuthProvider({ children }: { children: ReactNode }) {
const [isLoading, setIsLoading] = useState(false);
const [user, setUser] = useState<AuthRecord | null>(pb.authStore.record);
const navigate = useNavigate();
function updateUser() {
if (pb.authStore.isValid) {
setUser(pb.authStore.record);
}
setIsLoading(false);
}
const login = useCallback(async (email: string, password: string) => {
console.log("login");
setIsLoading(true);
await pb.collection("users").authWithPassword(email, password);
updateUser();
navigate({ to: "/campaigns" });
}, []);
const signup = useCallback(
async (email: string, password: string, passwordConfirm: string) => {
console.log("signup");
setIsLoading(true);
await pb.collection("users").create({ email, password, passwordConfirm });
await pb.collection("users").authWithPassword(email, password);
updateUser();
navigate({ to: "/campaigns" });
},
[],
);
const logout = useCallback(async () => {
console.log("logout");
pb.authStore.clear();
setUser(null);
navigate({ to: "/" });
}, []);
return (
<AuthContext.Provider
value={{ user: user ?? null, isLoading, login, signup, logout }}
>
{children}
</AuthContext.Provider>
);
}
/**
* Hook to access authentication context.
* Throws if used outside of AuthProvider.
*/
export function useAuth(): AuthContextValue {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error("useAuth must be used within an AuthProvider");
return ctx;
}