Skip to content

fix(updater): open the release-notes tab in the background - #1898

Merged
h4yfans merged 4 commits into
mainfrom
update-notice-tab-focus
Aug 30, 2026
Merged

fix(updater): open the release-notes tab in the background#1898
h4yfans merged 4 commits into
mainfrom
update-notice-tab-focus

Conversation

@h4yfans

@h4yfans h4yfans commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

What was broken

A customer on v2026-08-23.2 (Win32) lost a template body: "As I was creating a Template I received an update notice, which resulted in my template being lost because the update note took its place. I was able to get my template tab back, but all my work was gone so it was just the title of the template."

Root cause

UpdateReleaseNotesTabOpener opens the release-notes tab the moment a release surfaces, and it passed no open options. OPEN_TAB therefore took the foreground branch and made the new tab active. TabPane mounts only the active tab (tab-pane.tsx:57 renders activeTab alone), so the template editor unmounted mid-edit. Its body lives in component state behind an 800ms debounced save whose cleanup is a bare clearTimeout, so the pending write was discarded and the template kept only the name written when Create was pressed. That is exactly the title-only row the customer found. Introduced by #817.

What changed

The opener now passes { background: true }. openTab already forwarded that flag and the reducer already honoured it on every branch; the caller simply never set it. An automatic, non-user-initiated tab no longer takes focus. The two other automatic-looking openTab callers (use-reminder-notifications) are both driven by a real click, so they are untouched.

Verification

Unit, apps/desktop/src/renderer/src/components/updater/update-release-notes-tab-focus.test.tsx, driving the real TabProvider, the real reducer and the real useTemplateDraft rather than a mocked useTabs. Committed failing first, then the fix.

Before the fix, 3 failed / 3 passed:

× release-notes tab focus > leaves the tab the user is working in active when an update surfaces
  Expected the element to have attribute: aria-current="false"
  Received: aria-current="true"
× release-notes tab focus > lets the debounced template save land instead of unmounting it away
  expected "vi.fn()" to be called with arguments: [ ObjectContaining{…} ]
  Number of calls: 0
× UpdateReleaseNotesTabOpener > opens a read-only release-notes tab once when an update surfaces
  expected undefined to match object { background: true }

After: Test Files 5 passed (5) / Tests 27 passed (27).

E2E, apps/desktop/tests/e2e/update-notice-tab-focus.e2e.ts, on the real path. It creates a template through Settings, types a body, then broadcasts a downloading release from the main process on updater:state-changed (the silent auto-download phase, which is not promptable, so no modal covers the app). No test-only production code was added.

Without the fix the spec fails on the focus assertion, and a probe on the same build showed the persisted body was "" while the editor had unmounted:

✘ a surfaced release does not steal focus from, or discard, a template being written
  expect(locator).toHaveAttribute(expected) failed
  Expected: "false"   Received: "true"

With the fix, 1 passed, asserting the release-notes tab is present but aria-selected="false", the template tab is still the active tab, and the persisted template body contains the typed text.

Also green: pnpm lint (0 errors), pnpm typecheck (19/19), pnpm check:architecture, git diff --check, pnpm docs:impact --strict (covered).

pnpm --filter @memry/desktop test:renderer reports notes-tree-folder-rename-duplicate.test.tsx failing on timeouts; it passes 14/14 in isolation, reproduces on clean origin/main in a separate worktree, and is machine load from several worktrees running at once, not this diff.

Not fixed here

Two latent defects this change does not address, deliberately kept out of scope:

  • use-template-draft.ts:168-178 clears its debounce on unmount without flushing, so up to 800ms of template edits are still lost on any ordinary tab switch.
  • use-editor-teardown.ts:72-91 defers the beforeDestroy markdown flush into a queueMicrotask, so it runs after the owning component has already unmounted.

Both get their own issues.

…als focus

A surfaced release opens its notes tab in the foreground. Only the active tab
is mounted, so the template the user is writing unmounts and the 800ms
debounced template save is cleared without ever running.

Three failing assertions: the opener passes no open options, the release-notes
tab takes aria-current from the template tab, and updateTemplate is never
called with the typed body.
The release poller opened the notes tab in the foreground. Only the active tab
is mounted, so the surfaced update unmounted whatever the user was writing.
For a template that meant the 800ms debounced save was cleared before it ran
and the body was never written, leaving the name-only row the user found on
reopening.

openTab already forwards a background option to the reducer; the opener just
never passed it.
@github-actions

Copy link
Copy Markdown

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 811a354.

@github-actions github-actions Bot added bug Something isn't working documentation Improvements or additions to documentation test labels Aug 30, 2026
@codecov

codecov Bot commented Aug 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@h4yfans
h4yfans marked this pull request as ready for review August 30, 2026 18:11
@h4yfans
h4yfans merged commit 7ee5192 into main Aug 30, 2026
24 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant