diff --git a/frontend/src/features/auth/session.tsx b/frontend/src/features/auth/session.tsx index b8fc813..2d15ccf 100644 --- a/frontend/src/features/auth/session.tsx +++ b/frontend/src/features/auth/session.tsx @@ -3,6 +3,7 @@ import { createContext, useCallback, useContext, useMemo, type ReactNode } from import { api } from '@shared/api/client' import type { Me } from '@shared/types' import { fetchMe, login, logout, register } from './api' +import { hasSessionHint, setSessionHint } from './sessionHint' type Session = { status: 'loading' | 'anonymous' | 'authenticated' @@ -17,13 +18,23 @@ const SessionContext = createContext(null) export function SessionProvider({ children }: { children: ReactNode }) { const client = useQueryClient() // Restores the session after a reload: the refresh cookie survives, the - // in-memory access token does not. - const restored = useQuery({ queryKey: ['session-restore'], queryFn: () => api.refresh(), staleTime: Infinity }) + // in-memory access token does not. Guests skip the call (see sessionHint). + const restored = useQuery({ + queryKey: ['session-restore'], + queryFn: async () => { + if (!hasSessionHint()) return false + const ok = await api.refresh() + setSessionHint(ok) + return ok + }, + staleTime: Infinity, + }) const meQuery = useQuery({ queryKey: ['me'], queryFn: fetchMe, enabled: restored.data === true }) const signIn = useCallback( async (email: string, password: string) => { await login(email, password) + setSessionHint(true) client.setQueryData(['session-restore'], true) await client.fetchQuery({ queryKey: ['me'], queryFn: fetchMe }) }, @@ -40,6 +51,7 @@ export function SessionProvider({ children }: { children: ReactNode }) { const signOut = useCallback(async () => { await logout() + setSessionHint(false) client.clear() client.setQueryData(['session-restore'], false) }, [client]) diff --git a/frontend/src/features/auth/sessionHint.ts b/frontend/src/features/auth/sessionHint.ts new file mode 100644 index 0000000..936672b --- /dev/null +++ b/frontend/src/features/auth/sessionHint.ts @@ -0,0 +1,23 @@ +const KEY = 'lv_session' + +/** + * A guest has no refresh cookie, so asking /auth/refresh only produces a 401 (a console error + * and a wasted round trip on every first visit). This flag records "this browser signed in"; + * without it the restore step is skipped. Storage may be unavailable, so it never throws. + */ +export function hasSessionHint(): boolean { + try { + return localStorage.getItem(KEY) === '1' + } catch { + return false + } +} + +export function setSessionHint(on: boolean): void { + try { + if (on) localStorage.setItem(KEY, '1') + else localStorage.removeItem(KEY) + } catch { + // Private mode or blocked storage: the hint is only an optimisation. + } +} diff --git a/frontend/src/features/clickgui/screens/ModulesScreen.tsx b/frontend/src/features/clickgui/screens/ModulesScreen.tsx index ca18947..b46d493 100644 --- a/frontend/src/features/clickgui/screens/ModulesScreen.tsx +++ b/frontend/src/features/clickgui/screens/ModulesScreen.tsx @@ -24,9 +24,9 @@ function Panel({ category, state, dispatch }: { category: Category } & Props) { return (
-

+

{category.name} -

+