chore: migrate to gpui updater #42

Merged
reya merged 2 commits from feat/auto-update into master 2026-08-03 01:54:25 +00:00
5 changed files with 82 additions and 8 deletions
Showing only changes of commit c3b5bbc1b2 - Show all commits

1
Cargo.lock generated
View File

@@ -9117,6 +9117,7 @@ name = "workspace"
version = "1.0.0-beta5"
dependencies = [
"anyhow",
"auto_update",
"browser-signer-proxy",
"chat",
"chat_ui",

View File

@@ -1,8 +1,8 @@
#![cfg(not(target_arch = "wasm32"))]
use gpui::{App, AppContext, Context, Entity, Global, Subscription, Window};
use gpui::{App, AppContext, Context, Entity, Global, SharedString, Subscription, Window};
use gpui_updater::{EngineConfig, GitHubSource, UpdateStatus, Updater, Version};
use instant::Duration;
use instant::{Duration, Instant};
const COOP_UPDATE_EXPLANATION: &str = "COOP_UPDATE_EXPLANATION";
@@ -52,6 +52,8 @@ pub struct AutoUpdater {
pub version: Version,
/// Keeps the observer subscription alive.
_subscription: Subscription,
/// When the last error was recorded, so we can reset to idle after 5s.
error_time: Option<Instant>,
}
impl AutoUpdater {
@@ -83,11 +85,24 @@ impl AutoUpdater {
// When an update becomes available, automatically download and install it.
let subscription = cx.observe(&updater, |this: &mut AutoUpdater, _updater, cx| {
let status = this.updater.read(cx).status().clone();
if matches!(status, UpdateStatus::Available(_)) {
this.updater.update(cx, |updater, cx| {
updater.download_and_install(cx);
});
}
if matches!(status, UpdateStatus::Errored(_)) {
this.error_time = Some(Instant::now());
cx.spawn(async move |this, cx| {
cx.background_executor().timer(Duration::from_secs(5)).await;
this.update(cx, |_this, cx| cx.notify()).ok();
})
.detach();
} else {
this.error_time = None;
}
cx.notify();
});
@@ -111,6 +126,55 @@ impl AutoUpdater {
updater,
version,
_subscription: subscription,
error_time: None,
}
}
pub fn idle(&self, cx: &App) -> bool {
let status = self.updater.read(cx).status();
if status == &UpdateStatus::Idle {
return true;
}
if matches!(status, UpdateStatus::Errored(_))
&& self
.error_time
.is_some_and(|t| t.elapsed() >= Duration::from_secs(5))
{
return true;
}
false
}
pub fn status(&self, cx: &App) -> SharedString {
let status = self.updater.read(cx).status();
match status {
UpdateStatus::Idle => "Up to date".into(),
UpdateStatus::Checking => "Checking for updates…".into(),
UpdateStatus::UpToDate => "Up to date".into(),
UpdateStatus::Available(version) => format!("Version {version} available").into(),
UpdateStatus::Downloading { downloaded, total } => {
let total_mb = total.map(|t| t as f64 / 1_048_576.0);
let downloaded_mb = *downloaded as f64 / 1_048_576.0;
match total_mb {
Some(t) => format!("Downloading {downloaded_mb:.1} / {t:.1} MB").into(),
None => format!("Downloading {downloaded_mb:.1} MB").into(),
}
}
UpdateStatus::Installing => "Installing update…".into(),
UpdateStatus::Staged(version) => {
format!("Version {version} ready — restart to apply").into()
}
UpdateStatus::Errored(msg) => {
if self
.error_time
.is_some_and(|t| t.elapsed() >= Duration::from_secs(5))
{
"Up to date".into()
} else {
format!("Update failed: {msg}").into()
}
}
}
}
}

View File

@@ -164,12 +164,8 @@
</head>
<body>
<div class="card">
<div class="logo">
<div class="logo__mark">C</div>
<span class="logo__text">Coop</span>
</div>
<h1 class="heading">Web Signer</h1>
<p class="subtitle">
This page connects the app to your Nostr Web Signer extension so you can sign in and use Coop securely.
</p>

View File

@@ -14,6 +14,7 @@ chat = { path = "../chat" }
chat_ui = { path = "../chat_ui" }
settings = { path = "../settings" }
person = { path = "../person" }
auto_update = { path = "../auto_update" }
gpui.workspace = true
nostr-sdk.workspace = true

View File

@@ -2,6 +2,7 @@ use std::sync::Arc;
use ::settings::AppSettings;
use anyhow::Error;
use auto_update::AutoUpdater;
use chat::{ChatEvent, ChatRegistry};
use common::{CoopImageCache, download_dir};
use device::{DeviceEvent, DeviceRegistry};
@@ -379,7 +380,12 @@ impl Workspace {
self.import_encryption(window, cx);
}
Command::Update => {
//
let auto_updater = AutoUpdater::global(cx);
auto_updater.update(cx, |this, cx| {
this.updater.update(cx, |updater, cx| {
updater.check(cx);
});
});
}
}
}
@@ -604,6 +610,7 @@ impl Workspace {
}
fn titlebar_right(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
let updater = AutoUpdater::global(cx);
let chat = ChatRegistry::global(cx);
let nip4e_enabled = AppSettings::get_nip4e(cx);
let nostr = NostrRegistry::global(cx);
@@ -615,10 +622,15 @@ impl Workspace {
let persons = PersonRegistry::global(cx);
let profile = persons.read(cx).get(&public_key, cx);
let announcement = profile.announcement();
let updater_idle = updater.read(cx).idle(cx);
h_flex()
.when(!cx.theme().platform.is_mac(), |this| this.pr_2())
.gap_2()
.when(!updater_idle, |this| {
let status = updater.read(cx).status(cx);
this.child(div().text_xs().italic().child(status))
})
.when(nip4e_enabled, |this| {
this.child(
Button::new("key")