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
2 files src/popup/views/Settings.tsx+62 −57src/popup/views/contracts.test.tsx+31 −0
src/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>

view file

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") {

view file