mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 23:16:46 +00:00
fix(auth): send the queued changes before a logout deletes the offline database
Logging out deleted this user's offline database straight away, so a change made offline and not yet sent was lost without a word. With a connection, the logout now flushes the queue first, for at most five seconds.
This commit is contained in:
@@ -7,7 +7,8 @@ import { getApiErrorMessage } from '../types'
|
||||
import { tripSyncManager } from '../sync/tripSyncManager'
|
||||
import { reopenForUser, deleteCurrentUserDb } from '../db/offlineDb'
|
||||
import { setAuthed } from '../sync/authGate'
|
||||
import { setForcedOffline } from '../sync/networkMode'
|
||||
import { isEffectivelyOffline, setForcedOffline } from '../sync/networkMode'
|
||||
import { mutationQueue } from '../sync/mutationQueue'
|
||||
import { registerSyncTriggers, unregisterSyncTriggers } from '../sync/syncTriggers'
|
||||
import { useSystemNoticeStore } from './systemNoticeStore.js'
|
||||
import { clearAppearanceSnapshot } from '../theme/applyAppearance'
|
||||
@@ -18,6 +19,9 @@ import { forgetResumeRoute } from '../utils/resumeRoute'
|
||||
import { markSignedOut, clearSignedOut } from '../utils/signedOut'
|
||||
import { forgetPushDeviceOnLogout, resyncPushSubscription } from '../push/webPush'
|
||||
|
||||
/** How long a logout waits for the queued changes to go out. */
|
||||
const LOGOUT_FLUSH_MS = 5000
|
||||
|
||||
interface AuthResponse {
|
||||
user: User
|
||||
token: string
|
||||
@@ -238,6 +242,15 @@ export const useAuthStore = create<AuthState>()(
|
||||
},
|
||||
|
||||
logout: async () => {
|
||||
// 0. Send what is still queued while the session can. Step 6 deletes this
|
||||
// user's offline database, and an edit made offline and not yet sent would
|
||||
// go with it. Bounded, so a slow server never holds the logout.
|
||||
if (!isEffectivelyOffline()) {
|
||||
await Promise.race([
|
||||
mutationQueue.flush().catch(() => {}),
|
||||
new Promise(resolve => setTimeout(resolve, LOGOUT_FLUSH_MS)),
|
||||
])
|
||||
}
|
||||
// 1. Gate first so any in-flight flush/syncAll bails before we wipe the DB.
|
||||
setAuthed(false)
|
||||
// Flagged in the same update that drops the session: clearing isAuthenticated
|
||||
|
||||
@@ -147,6 +147,36 @@ describe('authStore', () => {
|
||||
expect(state.user).toBeNull();
|
||||
expect(state.isAuthenticated).toBe(false);
|
||||
});
|
||||
|
||||
it('sends the queued changes before the offline database is deleted', async () => {
|
||||
const { mutationQueue } = await import('../../../src/sync/mutationQueue');
|
||||
useAuthStore.setState({ user: buildUser(), isAuthenticated: true });
|
||||
setAuthed(true);
|
||||
let authedDuringFlush: boolean | null = null;
|
||||
const flush = vi.spyOn(mutationQueue, 'flush').mockImplementation(async () => { authedDuringFlush = isAuthed(); });
|
||||
|
||||
await useAuthStore.getState().logout();
|
||||
|
||||
expect(flush).toHaveBeenCalledOnce();
|
||||
expect(authedDuringFlush).toBe(true);
|
||||
expect(isAuthed()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not wait on a flush that hangs', async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const { mutationQueue } = await import('../../../src/sync/mutationQueue');
|
||||
vi.spyOn(mutationQueue, 'flush').mockImplementation(() => new Promise(() => {}));
|
||||
useAuthStore.setState({ user: buildUser(), isAuthenticated: true });
|
||||
const done = useAuthStore.getState().logout();
|
||||
await vi.advanceTimersByTimeAsync(5001);
|
||||
await vi.runAllTimersAsync();
|
||||
await done;
|
||||
expect(useAuthStore.getState().user).toBeNull();
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('FE-AUTH-007: Register success', () => {
|
||||
|
||||
Reference in New Issue
Block a user