auto_update: Add title bar feedback when Zed is already up to date (#64106)

# Objective

A manual "check for updates" that finds no newer version left the
updater back at `Idle`, so the "Checking for Zed Updates…" indicator in
the title bar simply vanished with no confirmation that the check had
succeeded.

## Solution

Track that transition in `UpdateVersion` and show an "Up to Date" button
in its place for a few seconds. The timer is stored as a task on the
view, so it is cancelled when the view is dropped or a new update state
arrives.

`restart_after_wake` passed through `Idle` before re-polling, which
would read as a completed check; it now stays in `Checking`.

## Testing

- Did you test these changes? If so, how? **Tested by running Zed in
preview channel.**
- Are there any parts that need more testing? **No**
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?

**Use `ZED_RELEASE_CHANNEL=preview cargo run` to open Zed. Then manually
trigger a check within menu `Zed -> Check for Updates`.**

- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?

**Tested only on MacOS Tahoe**

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

## Showcase

A visual demo of what happens in title_bar when a manual check for
updates is triggered.


https://github.com/user-attachments/assets/23b72bf2-6188-4eae-82b9-1c5b70782bb4

---

Release Notes:

- Added confirmation in the title bar when a manual update check finds
Zed is already up to date
This commit is contained in:
Taha authored and GitHub committed 2026-09-16 21:41:45 +00:00
1 parent b664a40042
commit e57126dcd4
3 files changed
+62 -7

No files matched your search

+16 -5
View File
@@ -3,8 +3,8 @@ use client::Client;
use db::kvp::KeyValueStore;
use futures_lite::StreamExt;
use gpui::{
App, AppContext as _, AsyncApp, BackgroundExecutor, Context, Entity, Global, Task, TaskExt,
Window, actions,
App, AppContext as _, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Global,
Task, TaskExt, Window, actions,
};
use http_client::{HttpClient, HttpClientWithUrl};
use paths::remote_servers_dir;
@@ -173,6 +173,11 @@ impl AutoUpdateStatus {
}
}
pub enum AutoUpdateEvent {
/// A manual check received a release response and found no newer version.
UpToDate,
}
pub struct AutoUpdater {
status: AutoUpdateStatus,
current_version: Version,
@@ -419,6 +424,8 @@ impl UpdateCheckType {
}
}
impl EventEmitter<AutoUpdateEvent> for AutoUpdater {}
impl AutoUpdater {
pub fn get(cx: &mut App) -> Option<Entity<Self>> {
cx.default_global::<GlobalAutoUpdate>().0.clone()
@@ -763,11 +770,15 @@ impl AutoUpdater {
let Some(newer_version) = newer_version else {
this.update(cx, |this, cx| {
let status = match previous_status {
this.status = match previous_status {
AutoUpdateStatus::Updated { .. } => previous_status,
_ => AutoUpdateStatus::Idle,
_ => {
if this.update_check_type.is_manual() {
cx.emit(AutoUpdateEvent::UpToDate);
}
AutoUpdateStatus::Idle
}
};
this.status = status;
cx.notify();
});
return Ok(());
+41 -2
View File
@@ -1,15 +1,20 @@
use std::sync::Arc;
use std::time::Duration;
use anyhow::anyhow;
use auto_update::{AutoUpdateStatus, AutoUpdater, UpdateCheckType};
use gpui::{Empty, Render};
use auto_update::{AutoUpdateEvent, AutoUpdateStatus, AutoUpdater, UpdateCheckType};
use gpui::{Empty, Render, Task};
use semver::Version;
use ui::{Tooltip, UpdateButton, prelude::*};
const UP_TO_DATE_DISPLAY_DURATION: Duration = Duration::from_secs(4);
pub struct UpdateVersion {
status: AutoUpdateStatus,
update_check_type: UpdateCheckType,
dismissed_status: Option<AutoUpdateStatus>,
showing_up_to_date: bool,
_up_to_date_timer: Option<Task<()>>,
}
impl UpdateVersion {
@@ -20,23 +25,53 @@ impl UpdateVersion {
this.status = auto_update.status();
this.update_check_type = auto_update.update_check_type();
this.dismissed_status = auto_update.dismissed_status();
if !matches!(this.status, AutoUpdateStatus::Idle) {
this.hide_up_to_date();
}
cx.notify();
})
.detach();
cx.subscribe(&auto_updater, |this, _, event, cx| match event {
AutoUpdateEvent::UpToDate => this.show_up_to_date(cx),
})
.detach();
Self {
status: auto_updater.read(cx).status(),
update_check_type: UpdateCheckType::Automatic,
dismissed_status: auto_updater.read(cx).dismissed_status(),
showing_up_to_date: false,
_up_to_date_timer: None,
}
} else {
Self {
status: AutoUpdateStatus::Idle,
update_check_type: UpdateCheckType::Automatic,
dismissed_status: None,
showing_up_to_date: false,
_up_to_date_timer: None,
}
}
}
fn show_up_to_date(&mut self, cx: &mut Context<Self>) {
self.showing_up_to_date = true;
self._up_to_date_timer = Some(cx.spawn(async move |this, cx| {
cx.background_executor()
.timer(UP_TO_DATE_DISPLAY_DURATION)
.await;
this.update(cx, |this, cx| {
this.hide_up_to_date();
cx.notify();
})
.ok();
}));
}
fn hide_up_to_date(&mut self) {
self.showing_up_to_date = false;
self._up_to_date_timer = None;
}
pub fn update_simulation(&mut self, cx: &mut Context<Self>) {
let next_state = match self.status {
AutoUpdateStatus::Idle => AutoUpdateStatus::Checking,
@@ -59,6 +94,7 @@ impl UpdateVersion {
self.status = next_state;
self.update_check_type = UpdateCheckType::Manual;
self.dismissed_status = None;
self.hide_up_to_date();
cx.notify()
}
@@ -91,6 +127,9 @@ impl Render for UpdateVersion {
if self.is_dismissed() {
return Empty.into_any_element();
}
if self.showing_up_to_date {
return UpdateButton::up_to_date().into_any_element();
}
match &self.status {
AutoUpdateStatus::Checking if self.update_check_type.is_manual() => {
UpdateButton::checking().into_any_element()
@@ -118,6 +118,10 @@ impl UpdateButton {
.disabled(true)
}
pub fn up_to_date() -> Self {
Self::new(IconName::Check, "Up to Date").disabled(true)
}
pub fn updated(version: impl Into<SharedString>) -> Self {
Self::new(IconName::Download, "Restart to Update")
.tooltip(version)
@@ -254,6 +258,7 @@ impl Component for UpdateButton {
"Installing",
UpdateButton::installing(version).into_any_element(),
),
single_example("Up to Date", UpdateButton::up_to_date().into_any_element()),
],
),
example_group_with_title(