Full document, spreadsheet, slideshow, and diagram tooling
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request 'fix: suppress E2EE key-loss modal on every doc for safe users (#684)' (#397) from fix/v0.51.1-key-warning-noise into main

scott 8640a0ea 05bd0f39

+50 -2
+3
CHANGELOG.md
··· 16 16 ### Removed 17 17 - 9 orphaned context-menu builder helpers (`buildDocsTextItems`, `buildDocsLinkItems`, `buildDocsImageItems`, `buildDocsTableItems`, `buildSheetsCellItems`, `buildSheetsColumnHeaderItems`, `buildSheetsRowHeaderItems`, `buildSheetsContextItems`, plus the `SheetsContextTarget` type): returned menu arrays with 45 no-op `() => {}` handlers and were only referenced by tests — never wired into a real editor. (#109) 18 18 19 + ### Fixed 20 + - E2EE key-loss warning no longer pops a "Your document is safely encrypted" modal on every document for users who are signed in with Tailscale and have their key already synced — the shield icon in the topbar still shows the status and can be clicked to view the explainer. At-risk (unsynced) and anonymous users still see the warning — one-time per doc — since the data-loss risk is real for them. (#684) 21 + 19 22 ## [0.49.0] — 2026-04-16 20 23 21 24 ### Added
+11 -2
src/lib/key-warning.ts
··· 155 155 156 156 function renderModal(docId: string, level: KeyLossRiskLevel, opts: MountKeyWarningOptions): void { 157 157 if (!isBrowser()) return; 158 - if (activeModal) return; // dedupe 158 + // Dedupe, but tolerate the case where the overlay was removed from the DOM 159 + // out-of-band (e.g. another component wiped the body, or a test harness). 160 + if (activeModal && !activeModal.isConnected) activeModal = null; 161 + if (activeModal) return; 159 162 160 163 const copy = copyForLevel(level); 161 164 ··· 309 312 * Show the key-loss warning for the current document, if the user hasn't seen it yet. 310 313 * 311 314 * Safe to call more than once per page; subsequent calls are no-ops unless `force: true`. 315 + * 316 + * #684 — skip the auto-mount entirely for "safe" users (authed + synced). The 317 + * shield icon in the topbar already communicates status for them, and popping a 318 + * modal on every doc is pure noise. Users can still re-open the modal manually 319 + * via the shield icon (which passes `force: true`). 312 320 */ 313 321 export function mountKeyWarning(opts: MountKeyWarningOptions): void { 314 322 if (!isBrowser()) return; 315 323 if (!opts.docId) return; 324 + const level = classifyKeyLossRisk({ user: opts.user, hasServerSyncedKey: opts.hasServerSyncedKey }); 325 + if (!opts.force && level === 'safe') return; 316 326 if (!opts.force && hasSeenKeyWarning(opts.docId)) return; 317 - const level = classifyKeyLossRisk({ user: opts.user, hasServerSyncedKey: opts.hasServerSyncedKey }); 318 327 renderModal(opts.docId, level, opts); 319 328 } 320 329
+36
tests/key-warning.test.ts
··· 28 28 hasSeenKeyWarning, 29 29 markKeyWarningSeen, 30 30 resetKeyWarningSeen, 31 + mountKeyWarning, 31 32 } = await import('../src/lib/key-warning.js'); 32 33 33 34 const authedUser: TailscaleUser = { login: 'scott@example.com', name: 'Scott', profilePic: null }; ··· 84 85 expect(hasSeenKeyWarning('')).toBe(false); 85 86 }); 86 87 }); 88 + 89 + // #684 — the "safe" variant should not pop a modal at all (it was noise on every doc). 90 + describe('mountKeyWarning — noise suppression (#684)', () => { 91 + beforeEach(() => { 92 + localStorage.clear(); 93 + // Clear any lingering overlay from a prior test 94 + document.querySelectorAll('.key-warning-overlay').forEach(n => n.remove()); 95 + }); 96 + 97 + const authedUser: TailscaleUser = { login: 'scott@example.com', name: 'Scott', profilePic: null }; 98 + 99 + it('does NOT show the modal for "safe" users (authed + synced)', () => { 100 + mountKeyWarning({ docId: 'doc-safe', user: authedUser, hasServerSyncedKey: true }); 101 + expect(document.querySelector('.key-warning-overlay')).toBeNull(); 102 + }); 103 + 104 + it('still shows the modal for anonymous users (genuine risk)', () => { 105 + mountKeyWarning({ docId: 'doc-anon', user: null, hasServerSyncedKey: false }); 106 + expect(document.querySelector('.key-warning-overlay')).not.toBeNull(); 107 + }); 108 + 109 + it('still shows the modal for at-risk users (authed, unsynced)', () => { 110 + mountKeyWarning({ docId: 'doc-at-risk', user: authedUser, hasServerSyncedKey: false }); 111 + expect(document.querySelector('.key-warning-overlay')).not.toBeNull(); 112 + }); 113 + 114 + it('does not show the modal twice for the same doc (per-doc dismissal still works)', () => { 115 + mountKeyWarning({ docId: 'doc-anon', user: null, hasServerSyncedKey: false }); 116 + const first = document.querySelector('.key-warning-overlay'); 117 + first?.remove(); 118 + markKeyWarningSeen('doc-anon'); 119 + mountKeyWarning({ docId: 'doc-anon', user: null, hasServerSyncedKey: false }); 120 + expect(document.querySelector('.key-warning-overlay')).toBeNull(); 121 + }); 122 + });