8e6d7effbebe
hide the secure reset panel once the device is on the settled generation (v5.2.50)
- time
- 2026-09-11 23:56 UTC · 1 month ago
- machine
- koh2
- agent
- deepseek/deepseek-v4-flash
- session
- sync screen polish
- parent
- b1c0079c5bff
↓ peach-browser-8e6d7eff.tar.gzatlas at this save
files changed2 files
src/popup/views/Settings.tsx+62 −57src/popup/views/contracts.test.tsx+31 −0src/popup/views/Settings.tsx+62 −57
@@ -1755,66 +1755,71 @@17551755 </div>17561756 </Show>17571757 1758- <p class="peach-settings-sub-label">SECURE SYNC RESET</p>1759- <div class="peach-settings-row peach-settings-row--top">1760- <div class="peach-settings-row-main">1761- <span class="peach-settings-value" style="color:var(--peach-t1)">1762- {secureResetHeadline(generation()?.state)}1763- </span>1764- <span class="peach-settings-value" style="font-size:11px;line-height:1.5;color:var(--peach-t2)">1765- {secureResetDetail(generation()?.state)}1766- </span>1767- </div>1768- <Show1769- when={1770- generation()?.state === "LEGACY_ACTIVE" ||1771- generation()?.state === "RESET_REQUIRED" ||1772- generation()?.state === "RESET_CONFIRMED"1773- }1774- >1775- <button1776- class="peach-btn peach-btn--subtle peach-btn--sm"1777- onClick={() => {1778- setResetError(null);1779- setResetPassword("");1780- setResetOpen(true);1781- }}1782- >reset sync</button>1783- </Show>1784- </div>1785- <Show when={resetError()}>1786- <p1787- class="peach-settings-value"1788- role="alert"1789- style="color:var(--peach-danger,#e5805c);font-size:11px;line-height:1.5"1790- >{resetError()}</p>1791- </Show>1792- <Show when={resetOpen()}>1793- <div class="peach-settings-stack" style="padding: 4px 0 2px; gap: 6px;">1794- <p class="peach-settings-value" style="font-size:11px;line-height:1.5;color:var(--peach-t2)">1795- {SECURE_RESET_DISCLOSURE}1796- </p>1797- <input1798- class="peach-settings-input"1799- type="password"1800- placeholder="master password"1801- value={resetPassword()}1802- onInput={(e) => setResetPassword((e.target as HTMLInputElement).value)}1803- autocomplete="current-password"1804- />1805- <div style="display:flex;gap:4px">1758+ {/* §11: this panel is a migration call to action, not a permanent1759+ label. On the settled new generation there is nothing left to do1760+ here, so it goes away entirely — Android applies the same rule. */}1761+ <Show when={generation()?.state && generation()?.state !== "V2_ACTIVE"}>1762+ <p class="peach-settings-sub-label">SECURE SYNC RESET</p>1763+ <div class="peach-settings-row peach-settings-row--top">1764+ <div class="peach-settings-row-main">1765+ <span class="peach-settings-value" style="color:var(--peach-t1)">1766+ {secureResetHeadline(generation()?.state)}1767+ </span>1768+ <span class="peach-settings-value" style="font-size:11px;line-height:1.5;color:var(--peach-t2)">1769+ {secureResetDetail(generation()?.state)}1770+ </span>1771+ </div>1772+ <Show1773+ when={1774+ generation()?.state === "LEGACY_ACTIVE" ||1775+ generation()?.state === "RESET_REQUIRED" ||1776+ generation()?.state === "RESET_CONFIRMED"1777+ }1778+ >18061779 <button18071780 class="peach-btn peach-btn--subtle peach-btn--sm"1808- disabled={resetting()}1809- onClick={() => void runSecureReset()}1810- >{resetting() ? "resetting…" : "reset"}</button>1811- <button1812- class="peach-btn peach-btn--subtle peach-btn--sm"1813- disabled={resetting()}1814- onClick={() => setResetOpen(false)}1815- >cancel</button>1816- </div>1781+ onClick={() => {1782+ setResetError(null);1783+ setResetPassword("");1784+ setResetOpen(true);1785+ }}1786+ >reset sync</button>1787+ </Show>18171788 </div>1789+ <Show when={resetError()}>1790+ <p1791+ class="peach-settings-value"1792+ role="alert"1793+ style="color:var(--peach-danger,#e5805c);font-size:11px;line-height:1.5"1794+ >{resetError()}</p>1795+ </Show>1796+ <Show when={resetOpen()}>1797+ <div class="peach-settings-stack" style="padding: 4px 0 2px; gap: 6px;">1798+ <p class="peach-settings-value" style="font-size:11px;line-height:1.5;color:var(--peach-t2)">1799+ {SECURE_RESET_DISCLOSURE}1800+ </p>1801+ <input1802+ class="peach-settings-input"1803+ type="password"1804+ placeholder="master password"1805+ value={resetPassword()}1806+ onInput={(e) => setResetPassword((e.target as HTMLInputElement).value)}1807+ autocomplete="current-password"1808+ />1809+ <div style="display:flex;gap:4px">1810+ <button1811+ class="peach-btn peach-btn--subtle peach-btn--sm"1812+ disabled={resetting()}1813+ onClick={() => void runSecureReset()}1814+ >{resetting() ? "resetting…" : "reset"}</button>1815+ <button1816+ class="peach-btn peach-btn--subtle peach-btn--sm"1817+ disabled={resetting()}1818+ onClick={() => setResetOpen(false)}1819+ >cancel</button>1820+ </div>1821+ </div>1822+ </Show>18181823 </Show>18191824 18201825 <p class="peach-settings-sub-label">ADD A DEVICE</p>src/popup/views/contracts.test.tsx+31 −0
@@ -872,6 +872,37 @@872872 expect(container.textContent).toContain("June's Phone");873873 });874874 875+ it("Settings drops the secure-reset panel once the device is on the settled generation", async () => {876+ const openSyncCard = async (state: "V2_ACTIVE" | "V2_ANCHOR" | "LEGACY_ACTIVE") => {877+ sendMessageMock.mockImplementation(async (msg: unknown) => {878+ const message = msg as { type?: string };879+ if (message.type === "GET_SYNC_GENERATION") {880+ return { ok: true, data: { schemaVersion: 1, generation: 2, state, createdAt: 1 } };881+ }882+ return { ok: true, data: null };883+ });884+ const container = mount(<SettingsView {...settingsProps({ isPro: true })} />);885+ for (let i = 0; i < 30; i++) await Promise.resolve();886+ Array.from(container.querySelectorAll("button")).find((b) => /sync/i.test(b.textContent ?? ""))?.click();887+ for (let i = 0; i < 30; i++) await Promise.resolve();888+ return container;889+ };890+891+ // Settled: the migration is over, so the panel must not linger as a label.892+ const settled = await openSyncCard("V2_ACTIVE");893+ expect(settled.textContent).not.toContain("SECURE SYNC RESET");894+ expect(settled.textContent).toContain("ADD A DEVICE");895+896+ // Still actionable states keep the panel and its explanation.897+ const anchored = await openSyncCard("V2_ANCHOR");898+ expect(anchored.textContent).toContain("SECURE SYNC RESET");899+ expect(anchored.textContent).toContain("Not re-paired");900+901+ const legacy = await openSyncCard("LEGACY_ACTIVE");902+ expect(legacy.textContent).toContain("SECURE SYNC RESET");903+ expect(legacy.textContent).toContain("Legacy Sync generation");904+ });905+875906 it("Settings keeps connection and recovery details collapsed until requested", async () => {876907 sendMessageMock.mockImplementation(async (msg: any) => {877908 if (msg?.type === "LIST_PAIRED_DEVICES") {