web: logging out ends the studio session, not just the cookie
A session followed the browser, not the account: log in, open a frame, log out, come back as a guest — the same photo stood on the stage, because the studio's own store (localStorage knobs + the photo in IndexedDB) outlived the cookie with nothing to clear it. clearSession() now drops both, and the three log-out buttons call it. The studio's own button reloads after the delete has committed — a reload mid- delete aborts the transaction, so the promise resolves on tx.oncomplete, not on the request. The account's frames are untouched: they reopen from MY PHOTOS.
This commit is contained in:
@@ -12,7 +12,7 @@ import { useI18n } from './i18n/I18nProvider';
|
||||
import { loadEngine } from './engine/assets';
|
||||
import { renderPhoto, type RenderAssets } from './engine/exportEngine';
|
||||
import { download, fileToBytes, readGps, readIso, resizedJpeg } from './engine/imageOps';
|
||||
import { loadPhoto, loadState, savePhoto, saveState } from './engine/session';
|
||||
import { clearSession, loadPhoto, loadState, savePhoto, saveState } from './engine/session';
|
||||
import { api, type Photo, type SavedRecipe, type User } from './api';
|
||||
import { TIERS } from './config/tiers';
|
||||
import { DEFAULT_ADJUSTMENTS, DEFAULT_RECIPES, FILM_SIMS, filmSimToRecipe } from '../shared/utils/defaultRecipes';
|
||||
@@ -1740,7 +1740,10 @@ export function Workspace() {
|
||||
onSignup={() => setAuthMode('signup')}
|
||||
onLogout={async () => {
|
||||
await api.logout().catch(() => undefined);
|
||||
setUser(null);
|
||||
await clearSession();
|
||||
// the stage still holds the old photo in memory; a reload is the one
|
||||
// line that empties it and every knob with it.
|
||||
window.location.reload();
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { api, type Photo, type PhotoSlot, type Rating, type User } from './api';
|
||||
import { clearSession } from './engine/session';
|
||||
import { useI18n } from './i18n/I18nProvider';
|
||||
import { ACCENTS, FONT_SETS, useTheme, type FontSetId } from './theme/ThemeProvider';
|
||||
import { AuthModal } from './ui/AuthModal';
|
||||
@@ -308,6 +309,7 @@ export function Landing() {
|
||||
|
||||
const signOut = useCallback(async () => {
|
||||
await api.logout().catch(() => undefined);
|
||||
void clearSession();
|
||||
setUser(null);
|
||||
setAccountMenu(false);
|
||||
}, []);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { api, type User } from './api';
|
||||
import { clearSession } from './engine/session';
|
||||
import { useI18n } from './i18n/I18nProvider';
|
||||
import { Profile } from './ui/Profile';
|
||||
import { Avatar } from './ui/Avatar';
|
||||
@@ -36,6 +37,7 @@ export function ProfilePage() {
|
||||
data-key="profile-logout"
|
||||
onClick={async () => {
|
||||
await api.logout().catch(() => undefined);
|
||||
await clearSession();
|
||||
window.location.assign('/');
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -88,3 +88,31 @@ export async function loadPhoto(): Promise<{ name: string; bytes: Uint8Array } |
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Signing out ends the session: drop the knobs and the working photo, so the
|
||||
// next visit — guest or another account — opens the studio empty. The account's
|
||||
// own photos stay in MY PHOTOS, ready to reopen.
|
||||
//
|
||||
// Async on purpose: the caller reloads the moment this returns, and a reload
|
||||
// aborts a delete still in flight. Resolve on the transaction's completion, not
|
||||
// on the request's success, or the bytes outlive the logout.
|
||||
export async function clearSession(): Promise<void> {
|
||||
try {
|
||||
localStorage.removeItem(STATE_KEY);
|
||||
} catch {
|
||||
// Nothing to clear.
|
||||
}
|
||||
try {
|
||||
const db = await openDb();
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const tx = db.transaction(STORE, 'readwrite');
|
||||
tx.objectStore(STORE).delete(PHOTO_KEY);
|
||||
tx.oncomplete = () => resolve();
|
||||
tx.onerror = () => reject(tx.error);
|
||||
tx.onabort = () => reject(tx.error);
|
||||
});
|
||||
db.close();
|
||||
} catch {
|
||||
// Private mode / no IndexedDB: there is nothing stored to forget.
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user