refactor(session): one session store for login, logout and 401

This commit is contained in:
lobo committed 2026-09-30 07:36:08 +02:00
1 parent 2a22789435
commit cbafa3d540
23 files changed
+246 -280

No files matched your search

+3 -1
View File
@@ -4,6 +4,7 @@ import {act, cleanup, fireEvent, render, screen, waitFor} from '@testing-library
import {createMemoryRouter, RouterProvider, useLocation, useNavigate} from 'react-router-dom';
import GameLibrary from './GameLibrary.jsx';
import {PreferencesProvider} from '../context/PreferencesContext.jsx';
import {CurrentUserProvider} from '../context/CurrentUserContext.jsx';
import {fetchRoutes, json} from '../test/fetchRoutes.js';
vi.mock('react-i18next', () => import('../test/i18n-mock.js'));
@@ -14,6 +15,7 @@ const DEBOUNCE_WAIT_MS = 700;
const page = {content: [{appid: 220, name: 'Half-Life 2', playtimeMinutes: 0, status: 'BACKLOG'}], page: 0, size: 20, totalElements: 1, totalPages: 1};
const answer = (games = page.content, hasSteamId = true) => fetchRoutes({
'GET /api/users/me': json(200, {settings: {}}),
'GET /api/library/games': json(200, {...page, content: games, totalElements: games.length, totalPages: games.length ? 1 : 0}),
'GET /api/library/games-count': json(200, {count: games.length, hasSteamId}),
});
@@ -34,7 +36,7 @@ function renderLibrary(initialEntry = '/library') {
}
const router = createMemoryRouter([{
path: '/library',
element: <PreferencesProvider><GameLibrary/><Spy/></PreferencesProvider>,
element: <PreferencesProvider><CurrentUserProvider><GameLibrary/><Spy/></CurrentUserProvider></PreferencesProvider>,
}], {initialEntries: [initialEntry]});
render(<RouterProvider router={router}/>);
return {
+3 -2
View File
@@ -11,7 +11,7 @@ import {Skeleton} from '@/components/ui/skeleton';
import GameCover from '../components/GameCover.jsx';
import Logo from '../components/Logo.jsx';
import StatCard from '../components/StatCard.jsx';
import {hasSession} from '../utils/auth.js';
import useSession from '../hooks/useSession.js';
import usePublicStats from '../hooks/usePublicStats.js';
@@ -39,6 +39,7 @@ function RankedGames({title, games, formatValue}) {
export default function LandingPage() {
const {t, i18n} = useTranslation();
const {data, isLoading} = usePublicStats();
const {active} = useSession();
const auth = data.auth;
const core = data.core;
@@ -65,7 +66,7 @@ export default function LandingPage() {
<h1 className="text-4xl font-bold tracking-tight md:text-6xl">{t('MAIN.WELCOME')}</h1>
<p className="mx-auto mt-6 max-w-2xl text-lg text-muted-foreground">{t('MAIN.DESCRIPTION')}</p>
<Button asChild size="lg" className="mt-10">
{hasSession() ? (
{active ? (
<Link to="/dashboard">{t('NAV.TO_DASHBOARD')}<ArrowRight/></Link>
) : (
<Link to="/login">{t('MAIN.GET_STARTED')}<ArrowRight/></Link>
-3
View File
@@ -10,7 +10,6 @@ import AuthLayout from '../components/AuthLayout.jsx';
import FeedbackAlert from '../components/FeedbackAlert.jsx';
import {hasSession, markSession, safeReturnPath} from '../utils/auth.js';
import {clientFetch} from '../utils/api.js';
import {useCurrentUserActions} from '../context/CurrentUserContext.jsx';
const LOGIN_ERROR_KEY = {
INVALID_CREDENTIALS: 'LOGIN.ERROR_INVALID_CREDENTIALS',
@@ -21,7 +20,6 @@ const LOGIN_ERROR_KEY = {
export default function LoginPage() {
const {t} = useTranslation();
const navigate = useNavigate();
const {reload: reloadUser} = useCurrentUserActions();
const {state} = useLocation();
const target = safeReturnPath(state?.from) ?? '/dashboard';
@@ -56,7 +54,6 @@ export default function LoginPage() {
if (response.ok) {
markSession();
reloadUser();
navigate(target, {replace: true});
} else {
const minutes = Math.ceil(Number(response.headers.get('Retry-After') ?? 900) / 60);
+3 -3
View File
@@ -11,7 +11,7 @@ import GameCover from '../components/GameCover.jsx';
import GameStatusBadge, {STATUS_META} from '../components/GameStatusBadge.jsx';
import {GAME_STATUSES} from '../utils/limits.js';
import {formatPlaytime} from '../utils/games.js';
import {hasSession} from '../utils/auth.js';
import useSession from '../hooks/useSession.js';
import {normalizeProfileStyle} from '../utils/profileStyle.js';
import {DEFAULT_PROFILE, PROFILE_TILES, PROFILE_TITLES, normalizeLayout, saveTiles} from '../utils/tiles.js';
import useCurrentUser from '../hooks/useCurrentUser.js';
@@ -37,7 +37,7 @@ export default function PublicProfilePage() {
const [error, setError] = useState(null);
const [editing, setEditing] = useState(false);
const [reloads, setReloads] = useState(0);
const viewer = useCurrentUser(hasSession());
const viewer = useCurrentUser(useSession().active);
const runSave = useSaveResponse();
useEffect(() => {
@@ -154,7 +154,7 @@ export default function PublicProfilePage() {
</Button>
)}
</Card>
<TileGrid tiles={PROFILE_TILES} titles={PROFILE_TITLES} layout={layout} renderTile={renderTile} editing={editing}
<TileGrid tiles={PROFILE_TILES} titles={PROFILE_TITLES} layout={layout} renderTile={renderTile} editing={owner && editing}
defaultLayout={DEFAULT_PROFILE} onSave={saveLayout} onCancel={() => setEditing(false)}/>
</PageContainer>
</div>