import {createContext, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
import {request} from '../utils/api.js';
import {hasSession} from '../utils/auth.js';
import useSession from '../hooks/useSession.js';
import {ME_URL, REQUEST_TIMEOUT_MS} from '../utils/settings.js';
const CurrentUserContext = createContext(null);
/**
* GET /api/users/me for the whole app. `user` is undefined until the first answer and null without a session.
* `reload()` loads it again and resolves to the `request()` result (not ok for a 200 without a body).
* `updateUser(fn)` changes it locally after a save, and skips it when the user is gone by then. It follows the
* session. A save that replaces settings still loads /me itself (see saveSettings), as it must be fresh.
*/
export function CurrentUserProvider({children}) {
const [user, setUser] = useState(() => (hasSession() ? undefined : null));
const latest = useRef(0);
const reload = useCallback(async () => {
const call = ++latest.current;
// After a logout, "no user" becomes "loading" (e.g. the import waits for the favorite platform).
setUser((current) => current ?? undefined);
const response = await request(ME_URL(), {signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)});
const result = {...response, ok: response.ok && response.data != null};
if (call === latest.current) {
// Only a 401 means "no user"; another failure keeps the user shown so far, or ends "loading".
if (result.ok) setUser(result.data);
else if (result.status === 401) setUser(null);
else setUser((current) => current ?? null);
}
return result;
}, []);
// A new stamp is a login, a "view as" or a settings save, also in another tab.
const {stamp} = useSession();
useEffect(() => {
if (stamp) {
reload();
} else {
// A /me answer still on the way must not bring the user back.
latest.current += 1;
setUser(null);
}
}, [stamp, reload]);
const updateUser = useCallback((change) => setUser((current) => current && change(current)), []);
const value = useMemo(() => ({user, reload, updateUser}), [user, reload, updateUser]);
return {children};
}
/**
* `{user, reload, updateUser}`.
*/
export function useCurrentUserContext() {
const value = useContext(CurrentUserContext);
if (!value) throw new Error('useCurrentUserContext must be used inside ');
return value;
}