Skip to content

feat(updates): update modal, auto-download setting, changelog#2818

Open
charlesvien wants to merge 1 commit into
chore/dependency-upgradesfrom
feat/auto-update-ux
Open

feat(updates): update modal, auto-download setting, changelog#2818
charlesvien wants to merge 1 commit into
chore/dependency-upgradesfrom
feat/auto-update-ux

Conversation

@charlesvien

@charlesvien charlesvien commented Jun 21, 2026

Copy link
Copy Markdown
Member

Problem

The move to electron-updater gave us a new set of primitives that were previously unavailable to us: checking for an update without downloading it, byte-level download progress, an explicit download trigger, install-on-quit and release notes at check time. The old experience used none of them. Updates downloaded silently, and the only UI was a bottom-left banner with a "Restart" button that appeared once the download had already finished. There was no opt-in download, no release notes, no progress and no way to browse the changelog.

Changes

Three user-facing additions, built on those primitives:

  • Update modal. Updates no longer auto-download by default. The bottom-left banner becomes a passive "Update available" that opens a modal showing the release notes (rendered markdown), your current version and a Download button with a live progress bar (percent and MB/s). You can close the modal while the download runs and the banner mirrors the progress; when it finishes the action becomes "Restart to update".
  • "Download updates automatically" setting in General (default off). When on, updates download in the background and install on the next quit, with the Restart button still available for an immediate install. The renderer pushes the preference to the core updater through a new updates.setAutoDownload procedure.
  • "View changelog" in the project switcher opens a "What's New" modal: a timeline of GitHub releases fetched by a new GitHubReleasesService (in workspace-server, public GET /repos/PostHog/code/releases, Zod-parsed and cached). The current version is marked, and the modal auto-shows once on the first launch after an update installs (tracked with a persisted lastSeenChangelogVersion).

Plumbing: IUpdater gains download(), onDownloadProgress() and setAutoDownload(), and onUpdateAvailable now carries the release notes. The electron-updater adapter sets autoDownload to false and autoInstallOnAppQuit to true and wires the download-progress event. UpdatesService gains an "available" state, download progress and the auto-download decision, and the status payload plus the renderer stores carry the new fields.

Auto-update stays macOS and Windows only; the What's New changelog works on every platform.

Stacked on #2817.

How did you test this?

  • pnpm typecheck (22 of 22), pnpm lint and pnpm build all pass.
  • pnpm test passes. New unit tests cover the UpdatesService transitions (available, then requestDownload, then downloading, then ready, plus the auto-download branch) with a faked IUpdater, and the GitHubReleasesService mapping, caching and error handling with a faked fetch.
  • Driving the live modal and banner states in the packaged app over CDP is a follow-up.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

@charlesvien charlesvien changed the title add update modal, auto-download, changelog modal feat(updates): update modal, auto-download setting, changelog Jun 21, 2026
@charlesvien charlesvien marked this pull request as ready for review June 21, 2026 02:17

Copy link
Copy Markdown
Member Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions

Copy link
Copy Markdown

React Doctor found 1 issue in 1 file · 1 warning.

1 warning

src/features/sidebar/components/ProjectSwitcher.tsx

Reviewed by React Doctor for commit 4034c0f.

@greptile-apps

greptile-apps Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Comments Outside Diff (3)

  1. packages/ui/src/features/settings/sections/GeneralSettings.tsx, line 798-810 (link)

    P2 Redundant server call when toggling auto-download

    handleAutoDownloadChange calls both setDownloadUpdatesAutomatically (which triggers the module-level syncAutoDownload subscription in updates.ts) AND autoDownloadMutation.mutate. The subscription in updates.ts already forwards every settings change to the host via hostTrpcClient.updates.setAutoDownload.mutate, so each toggle sends two setAutoDownload calls to the server. Since the mutations are idempotent the result is correct, but autoDownloadMutation here is dead weight. The autoDownloadMutation declaration and its use in handleAutoDownloadChange can be removed.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/ui/src/features/settings/sections/GeneralSettings.tsx
    Line: 798-810
    
    Comment:
    **Redundant server call when toggling auto-download**
    
    `handleAutoDownloadChange` calls both `setDownloadUpdatesAutomatically` (which triggers the module-level `syncAutoDownload` subscription in `updates.ts`) AND `autoDownloadMutation.mutate`. The subscription in `updates.ts` already forwards every settings change to the host via `hostTrpcClient.updates.setAutoDownload.mutate`, so each toggle sends two `setAutoDownload` calls to the server. Since the mutations are idempotent the result is correct, but `autoDownloadMutation` here is dead weight. The `autoDownloadMutation` declaration and its use in `handleAutoDownloadChange` can be removed.
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. packages/ui/src/features/updates/WhatsNewModal.tsx, line 1403 (link)

    P2 "Latest" badge can land on a pre-release

    GitHubReleasesService.listReleases filters out drafts but keeps pre-releases, and the GitHub API returns releases newest-first. If the most recent published release is a pre-release, index === 0 will be a pre-release entry and receive the Latest badge. Consider either filtering pre-releases out of the list, or basing the badge on !release.isPrerelease rather than position.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/ui/src/features/updates/WhatsNewModal.tsx
    Line: 1403
    
    Comment:
    **"Latest" badge can land on a pre-release**
    
    `GitHubReleasesService.listReleases` filters out drafts but keeps pre-releases, and the GitHub API returns releases newest-first. If the most recent published release is a pre-release, `index === 0` will be a pre-release entry and receive the `Latest` badge. Consider either filtering pre-releases out of the list, or basing the badge on `!release.isPrerelease` rather than position.
    
    How can I resolve this? If you propose a fix, please make it concise.
  3. packages/core/src/updates/updateStore.test.ts, line 244-257 (link)

    P2 No direct unit test for the new available branch in deriveUpdateUiStatus

    updateStore.test.ts now covers the downloading case, and the available path is exercised indirectly via updates.test.ts, but there is no direct unit test for deriveUpdateUiStatus({ available: true, availableVersion: "...", ... }). Prefer a parameterised test alongside the existing cases here so the mapping is documented and protected at the unit level.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/core/src/updates/updateStore.test.ts
    Line: 244-257
    
    Comment:
    **No direct unit test for the new `available` branch in `deriveUpdateUiStatus`**
    
    `updateStore.test.ts` now covers the `downloading` case, and the `available` path is exercised indirectly via `updates.test.ts`, but there is no direct unit test for `deriveUpdateUiStatus({ available: true, availableVersion: "...", ... })`. Prefer a parameterised test alongside the existing cases here so the mapping is documented and protected at the unit level.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix All With AI
Fix the following 4 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 4
packages/ui/src/features/sidebar/components/UpdateBanner.tsx:15-17
`useUpdateView()` is called twice in the same component, creating two separate store subscriptions and violating OnceAndOnlyOnce. `isEnabled` should simply be added to the first destructure.

```suggestion
  const { status, version, availableVersion, downloadPercent, isEnabled } =
    useUpdateView();
```

### Issue 2 of 4
packages/ui/src/features/settings/sections/GeneralSettings.tsx:798-810
**Redundant server call when toggling auto-download**

`handleAutoDownloadChange` calls both `setDownloadUpdatesAutomatically` (which triggers the module-level `syncAutoDownload` subscription in `updates.ts`) AND `autoDownloadMutation.mutate`. The subscription in `updates.ts` already forwards every settings change to the host via `hostTrpcClient.updates.setAutoDownload.mutate`, so each toggle sends two `setAutoDownload` calls to the server. Since the mutations are idempotent the result is correct, but `autoDownloadMutation` here is dead weight. The `autoDownloadMutation` declaration and its use in `handleAutoDownloadChange` can be removed.

### Issue 3 of 4
packages/ui/src/features/updates/WhatsNewModal.tsx:1403
**"Latest" badge can land on a pre-release**

`GitHubReleasesService.listReleases` filters out drafts but keeps pre-releases, and the GitHub API returns releases newest-first. If the most recent published release is a pre-release, `index === 0` will be a pre-release entry and receive the `Latest` badge. Consider either filtering pre-releases out of the list, or basing the badge on `!release.isPrerelease` rather than position.

### Issue 4 of 4
packages/core/src/updates/updateStore.test.ts:244-257
**No direct unit test for the new `available` branch in `deriveUpdateUiStatus`**

`updateStore.test.ts` now covers the `downloading` case, and the `available` path is exercised indirectly via `updates.test.ts`, but there is no direct unit test for `deriveUpdateUiStatus({ available: true, availableVersion: "...", ... })`. Prefer a parameterised test alongside the existing cases here so the mapping is documented and protected at the unit level.

Reviews (1): Last reviewed commit: "add update modal, auto-download, changel..." | Re-trigger Greptile

Comment on lines +15 to +17
const { status, version, availableVersion, downloadPercent } =
useUpdateView();
const { isEnabled } = useUpdateView();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 useUpdateView() is called twice in the same component, creating two separate store subscriptions and violating OnceAndOnlyOnce. isEnabled should simply be added to the first destructure.

Suggested change
const { status, version, availableVersion, downloadPercent } =
useUpdateView();
const { isEnabled } = useUpdateView();
const { status, version, availableVersion, downloadPercent, isEnabled } =
useUpdateView();
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/features/sidebar/components/UpdateBanner.tsx
Line: 15-17

Comment:
`useUpdateView()` is called twice in the same component, creating two separate store subscriptions and violating OnceAndOnlyOnce. `isEnabled` should simply be added to the first destructure.

```suggestion
  const { status, version, availableVersion, downloadPercent, isEnabled } =
    useUpdateView();
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant