109 lines
2.5 KiB
TypeScript
109 lines
2.5 KiB
TypeScript
import {
|
|
createContext,
|
|
useContext,
|
|
useEffect,
|
|
useMemo,
|
|
useState,
|
|
type PropsWithChildren,
|
|
} from "react";
|
|
import { apiGet } from "./api";
|
|
import { AUTH_TOKEN_STORAGE_KEY, USER_STORAGE_KEY } from "./storage";
|
|
import type { SessionResponse, UserOption } from "./types";
|
|
|
|
interface SessionContextValue {
|
|
user: UserOption | null;
|
|
ready: boolean;
|
|
setSession: (session: SessionResponse | null) => void;
|
|
}
|
|
|
|
const SessionContext = createContext<SessionContextValue>({
|
|
user: null,
|
|
ready: false,
|
|
setSession: () => undefined,
|
|
});
|
|
|
|
function loadStoredUser(): UserOption | null {
|
|
const raw = window.localStorage.getItem(USER_STORAGE_KEY);
|
|
if (!raw) {
|
|
return null;
|
|
}
|
|
try {
|
|
return JSON.parse(raw) as UserOption;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function SessionProvider({ children }: PropsWithChildren) {
|
|
const [user, setUserState] = useState<UserOption | null>(() => loadStoredUser());
|
|
const [ready, setReady] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const token = window.localStorage.getItem(AUTH_TOKEN_STORAGE_KEY);
|
|
if (!token) {
|
|
setReady(true);
|
|
return;
|
|
}
|
|
|
|
let cancelled = false;
|
|
void apiGet<UserOption>("/session/me")
|
|
.then((currentUser) => {
|
|
if (!cancelled) {
|
|
setUserState(currentUser);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
if (!cancelled) {
|
|
window.localStorage.removeItem(AUTH_TOKEN_STORAGE_KEY);
|
|
window.localStorage.removeItem(USER_STORAGE_KEY);
|
|
setUserState(null);
|
|
}
|
|
})
|
|
.finally(() => {
|
|
if (!cancelled) {
|
|
setReady(true);
|
|
}
|
|
});
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (user) {
|
|
window.localStorage.setItem(USER_STORAGE_KEY, JSON.stringify(user));
|
|
return;
|
|
}
|
|
window.localStorage.removeItem(USER_STORAGE_KEY);
|
|
}, [user]);
|
|
|
|
function setSession(session: SessionResponse | null) {
|
|
if (session) {
|
|
window.localStorage.setItem(AUTH_TOKEN_STORAGE_KEY, session.token);
|
|
setUserState(session.user);
|
|
setReady(true);
|
|
return;
|
|
}
|
|
window.localStorage.removeItem(AUTH_TOKEN_STORAGE_KEY);
|
|
window.localStorage.removeItem(USER_STORAGE_KEY);
|
|
setUserState(null);
|
|
setReady(true);
|
|
}
|
|
|
|
const value = useMemo(
|
|
() => ({
|
|
user,
|
|
ready,
|
|
setSession,
|
|
}),
|
|
[ready, user],
|
|
);
|
|
|
|
return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>;
|
|
}
|
|
|
|
export function useSession() {
|
|
return useContext(SessionContext);
|
|
}
|