mirror of
https://github.com/bulwarkmail/native.git
synced 2026-10-31 00:06:17 +00:00
fix: clear recent searches and calendar feeds when the last account signs out
This commit is contained in:
@@ -125,6 +125,8 @@ import { useAccountStore } from '../account-store';
|
||||
import { useAuthStore } from '../auth-store';
|
||||
import { useEmailStore } from '../email-store';
|
||||
import { useOutboxStore } from '../outbox-store';
|
||||
import { useSearchHistoryStore } from '../search-history-store';
|
||||
import { useCalendarSubscriptionsStore, type CalendarSubscription } from '../calendar-subscriptions-store';
|
||||
|
||||
const { A, B, C } = client;
|
||||
const settle = async () => {
|
||||
@@ -244,4 +246,49 @@ describe('what a sign-out leaves behind', () => {
|
||||
await useAuthStore.getState().logoutAll();
|
||||
expect(vi.mocked(forgetRefreshRotations).mock.calls.at(-1)).toEqual([`rt-${B}`]);
|
||||
});
|
||||
|
||||
const feed = (localAccountId: string): CalendarSubscription => ({
|
||||
id: `sub-${localAccountId}`,
|
||||
name: 'Team',
|
||||
url: 'https://cal.example.org/feed?token=secret',
|
||||
calendarId: 'c1',
|
||||
localAccountId,
|
||||
lastSyncAt: null,
|
||||
lastError: null,
|
||||
});
|
||||
const leftovers = () => ({
|
||||
searches: useSearchHistoryStore.getState().recentSearches,
|
||||
feeds: useCalendarSubscriptionsStore.getState().subscriptions.map((s) => s.id),
|
||||
});
|
||||
|
||||
it('keeps the recent searches and calendar feeds while an account is still signed in', async () => {
|
||||
await signedIn(A, B);
|
||||
useSearchHistoryStore.setState({ recentSearches: ['invoice'] });
|
||||
useCalendarSubscriptionsStore.setState({ subscriptions: [feed(A), feed(B)] });
|
||||
|
||||
await useAuthStore.getState().logout();
|
||||
await settle();
|
||||
|
||||
expect(leftovers()).toEqual({ searches: ['invoice'], feeds: [`sub-${A}`, `sub-${B}`] });
|
||||
});
|
||||
|
||||
it('clears them when the last account signs out', async () => {
|
||||
await signedIn(A);
|
||||
useSearchHistoryStore.setState({ recentSearches: ['invoice'] });
|
||||
useCalendarSubscriptionsStore.setState({ subscriptions: [feed(A)] });
|
||||
|
||||
await useAuthStore.getState().logout();
|
||||
|
||||
expect(leftovers()).toEqual({ searches: [], feeds: [] });
|
||||
});
|
||||
|
||||
it('clears them when everyone signs out at once', async () => {
|
||||
await signedIn(A, B);
|
||||
useSearchHistoryStore.setState({ recentSearches: ['invoice'] });
|
||||
useCalendarSubscriptionsStore.setState({ subscriptions: [feed(A), feed(B)] });
|
||||
|
||||
await useAuthStore.getState().logoutAll();
|
||||
|
||||
expect(leftovers()).toEqual({ searches: [], feeds: [] });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,8 @@ import { useCalendarStore } from './calendar-store';
|
||||
import { useSettingsStore } from './settings-store';
|
||||
import { useFilterStore } from './filter-store';
|
||||
import { useOutboxStore } from './outbox-store';
|
||||
import { useSearchHistoryStore } from './search-history-store';
|
||||
import { useCalendarSubscriptionsStore } from './calendar-subscriptions-store';
|
||||
import { flushPersistedWrites } from './persist-storage';
|
||||
import { clearEmailDetailCache } from '../lib/email-detail-cache';
|
||||
import { clearBodyDocuments } from '../lib/email-body-document';
|
||||
@@ -130,10 +132,21 @@ function clearAllFeatureStores(): void {
|
||||
useContactsStore.getState().reset();
|
||||
useCalendarStore.getState().reset();
|
||||
useFilterStore.getState().clearState();
|
||||
clearDeviceWideAccountData();
|
||||
// Cache writes are held back briefly; get the signed-out data off disk now.
|
||||
void flushPersistedWrites();
|
||||
}
|
||||
|
||||
// Kept across accounts, not per account, so they go only once nobody is
|
||||
// signed in (webmail lib/sign-out-cleanup.ts): the recent searches name what
|
||||
// the accounts' mail is about, and calendar feed URLs are often secret.
|
||||
// Templates and tag names stay, as in the webmail: they are the user's own
|
||||
// work on this device, not a copy of an account's data.
|
||||
function clearDeviceWideAccountData(): void {
|
||||
useSearchHistoryStore.getState().clearRecentSearches();
|
||||
useCalendarSubscriptionsStore.getState().clearAll();
|
||||
}
|
||||
|
||||
// Drop the named account from the email cache, then reset the (per-session,
|
||||
// not yet per-account) contacts and calendar stores. Used by logout when
|
||||
// signing one account out while others remain.
|
||||
@@ -746,6 +759,8 @@ export const useAuthStore = create<AuthState>((set, get) => ({
|
||||
return;
|
||||
}
|
||||
|
||||
// That was the last account.
|
||||
if (useAccountStore.getState().accounts.length === 0) clearDeviceWideAccountData();
|
||||
set({
|
||||
isAuthenticated: false,
|
||||
isLoading: false,
|
||||
|
||||
@@ -80,6 +80,11 @@ interface SubscriptionsState {
|
||||
* account has that id.
|
||||
*/
|
||||
unlinkAccount: (localAccountId: string, jmapAccountIds: { previous: string | null; next: string | null }) => Promise<void>;
|
||||
/**
|
||||
* Everyone signed out: forget every subscription on this device (feed URLs
|
||||
* are often secret). The mirror calendars stay on the server.
|
||||
*/
|
||||
clearAll: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -417,6 +422,8 @@ export const useCalendarSubscriptionsStore = create<SubscriptionsState>()(
|
||||
: s)),
|
||||
});
|
||||
},
|
||||
|
||||
clearAll: () => set({ subscriptions: [], syncing: {} }),
|
||||
}),
|
||||
{
|
||||
name: 'calendar-subscriptions',
|
||||
|
||||
Reference in New Issue
Block a user