diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index c6102272b..d1c3613c7 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -160,6 +160,7 @@ import { scheduledMessagesAtom } from '../../state/scheduledMessages'; import { createErrorToast, toastQueueAtom } from '../../state/toast'; import { getThreadDraftKey } from '../../state/room/thread'; import { stripTrackingParamsInHtml, stripTrackingParamsInText } from '../../utils/urlTracking'; +import { plaintextCachesWiped } from '../../state/plaintextCaches'; const GifPicker = React.lazy(() => import('../../components/GifPicker').then((m) => ({ default: m.GifPicker })), @@ -562,6 +563,8 @@ export const RoomInput = forwardRef( // Persist the current editor state to the draft atom + localStorage; an empty // editor clears both so the draft indicators never claim a draft that isn't there. const persistDraft = useCallback(() => { + // [Gitea #319] Logout already wiped the drafts; don't write this one back. + if (plaintextCachesWiped()) return; if (!isEmptyEditor(editor)) { const parsedDraft = JSON.parse(JSON.stringify(editor.children)); setMsgDraft(parsedDraft); diff --git a/src/app/state/plaintextCaches.test.ts b/src/app/state/plaintextCaches.test.ts index 8e72c6392..869ed6bda 100644 --- a/src/app/state/plaintextCaches.test.ts +++ b/src/app/state/plaintextCaches.test.ts @@ -26,7 +26,7 @@ const api = { : Object.getOwnPropertyDescriptor(target, key), }); -const { clearPlaintextCaches } = await import('./plaintextCaches'); +const { clearPlaintextCaches, plaintextCachesWiped } = await import('./plaintextCaches'); test('clearPlaintextCaches removes every plaintext/PII localStorage key', () => { store.clear(); @@ -94,3 +94,13 @@ test('clearPlaintextCaches removes the call-volume list and the settings-sync st assert.equal(store.has('settings-sync-meta:@me:server'), false); assert.equal(store.has('settings'), true, 'device preferences are kept'); }); + +test('clearPlaintextCaches clears the encrypted-search opt-in and flags the wipe (#296, #319)', () => { + store.clear(); + store.set('searchCacheEnabled', 'true'); + store.set('mxjssdk_memory_filter_FILTER_SYNC_@me:server', '0'); + clearPlaintextCaches('@me:server'); + assert.equal(store.has('searchCacheEnabled'), false); + assert.equal(store.has('mxjssdk_memory_filter_FILTER_SYNC_@me:server'), false); + assert.equal(plaintextCachesWiped(), true, 'draft writes stop until the page reloads'); +}); diff --git a/src/app/state/plaintextCaches.ts b/src/app/state/plaintextCaches.ts index cf9b510fd..6e66bc864 100644 --- a/src/app/state/plaintextCaches.ts +++ b/src/app/state/plaintextCaches.ts @@ -112,7 +112,34 @@ const clearCallVolumesAndSyncMeta = (userId?: string): void => { }); }; +// [Gitea #319] Set once a logout has wiped the caches. Logout then reloads the +// page, and the reload's `pagehide` made RoomInput flush the open composer's +// draft straight back to localStorage; draft writes check this and stop. +// Module state, so it resets with the new page. +let wiped = false; +export const plaintextCachesWiped = (): boolean => wiped; + +/** + * [Gitea #296] Device-wide flags/keys that carry the previous account's choice + * or id: the encrypted-search opt-in (the next account would otherwise start + * indexing decrypted text without opting in) and the SDK's per-user filter key. + */ +const clearAccountScopedFlags = (userId?: string): void => { + const keys = [ + 'searchCacheEnabled', + ...(userId ? [`mxjssdk_memory_filter_FILTER_SYNC_${userId}`] : []), + ]; + keys.forEach((key) => { + try { + localStorage.removeItem(key); + } catch { + // best-effort + } + }); +}; + export const clearPlaintextCaches = (userId?: string): void => { + wiped = true; clearTranslationCache(); clearScheduledMessages(); clearRecentSearches(); @@ -124,5 +151,6 @@ export const clearPlaintextCaches = (userId?: string): void => { clearCallSession(); clearStatusMessage(); clearCallVolumesAndSyncMeta(userId); + clearAccountScopedFlags(userId); if (userId) clearNavToActivePathStore(userId); };