chore: refactor auto updater
This commit is contained in:
153
Cargo.lock
generated
153
Cargo.lock
generated
@@ -420,16 +420,14 @@ name = "auto_update"
|
|||||||
version = "1.0.0-beta"
|
version = "1.0.0-beta"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"cargo-packager-updater",
|
||||||
"common",
|
"common",
|
||||||
"global",
|
"global",
|
||||||
"gpui",
|
"gpui",
|
||||||
"i18n",
|
|
||||||
"log",
|
"log",
|
||||||
"nostr-sdk",
|
"nostr-sdk",
|
||||||
"reqwest 0.12.22",
|
"smallvec",
|
||||||
"rust-i18n",
|
|
||||||
"smol",
|
"smol",
|
||||||
"tempfile",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -841,6 +839,42 @@ dependencies = [
|
|||||||
"wayland-client",
|
"wayland-client",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo-packager-updater"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eec09acab5c2227aba2e592d431708305bdeb6d507703f6cd8983fb57b6c5ef7"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"cargo-packager-utils",
|
||||||
|
"ctor 0.2.9",
|
||||||
|
"dirs 5.0.1",
|
||||||
|
"dunce",
|
||||||
|
"flate2",
|
||||||
|
"http",
|
||||||
|
"log",
|
||||||
|
"minisign-verify",
|
||||||
|
"percent-encoding",
|
||||||
|
"reqwest 0.12.22",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tar",
|
||||||
|
"tempfile",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"time",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo-packager-utils"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b43458dd2ee3cdab3f5b105acd80791383b730380c929018701313d7d299d4e8"
|
||||||
|
dependencies = [
|
||||||
|
"ctor 0.2.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cbc"
|
name = "cbc"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -1441,6 +1475,16 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctor"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.104",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctor"
|
name = "ctor"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@@ -1475,6 +1519,15 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b"
|
checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deranged"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
|
||||||
|
dependencies = [
|
||||||
|
"powerfmt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "0.99.20"
|
version = "0.99.20"
|
||||||
@@ -1860,6 +1913,18 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filetime"
|
||||||
|
version = "0.2.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"libredox",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flatbuffers"
|
name = "flatbuffers"
|
||||||
version = "25.2.10"
|
version = "25.2.10"
|
||||||
@@ -2394,7 +2459,7 @@ dependencies = [
|
|||||||
"core-text",
|
"core-text",
|
||||||
"core-video",
|
"core-video",
|
||||||
"cosmic-text",
|
"cosmic-text",
|
||||||
"ctor",
|
"ctor 0.4.3",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"embed-resource",
|
"embed-resource",
|
||||||
"etagere",
|
"etagere",
|
||||||
@@ -3333,6 +3398,7 @@ checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
"libc",
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3533,7 +3599,7 @@ dependencies = [
|
|||||||
"bindgen 0.71.1",
|
"bindgen 0.71.1",
|
||||||
"core-foundation 0.10.1",
|
"core-foundation 0.10.1",
|
||||||
"core-video",
|
"core-video",
|
||||||
"ctor",
|
"ctor 0.4.3",
|
||||||
"foreign-types 0.5.0",
|
"foreign-types 0.5.0",
|
||||||
"metal",
|
"metal",
|
||||||
"objc",
|
"objc",
|
||||||
@@ -3610,6 +3676,12 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minisign-verify"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e856fdd13623a2f5f2f54676a4ee49502a96a80ef4a62bcedd23d52427c44d43"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.9"
|
version = "0.8.9"
|
||||||
@@ -3915,6 +3987,12 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-conv"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-derive"
|
name = "num-derive"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
@@ -4517,6 +4595,12 @@ dependencies = [
|
|||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "powerfmt"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.21"
|
version = "0.2.21"
|
||||||
@@ -5013,6 +5097,7 @@ dependencies = [
|
|||||||
"base64",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2",
|
"h2",
|
||||||
@@ -5030,6 +5115,9 @@ dependencies = [
|
|||||||
"native-tls",
|
"native-tls",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"quinn",
|
||||||
|
"rustls",
|
||||||
|
"rustls-native-certs",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -5037,6 +5125,7 @@ dependencies = [
|
|||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
|
"tokio-rustls",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
@@ -6233,6 +6322,17 @@ dependencies = [
|
|||||||
"objc",
|
"objc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tar"
|
||||||
|
version = "0.4.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a"
|
||||||
|
dependencies = [
|
||||||
|
"filetime",
|
||||||
|
"libc",
|
||||||
|
"xattr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "target-lexicon"
|
name = "target-lexicon"
|
||||||
version = "0.12.16"
|
version = "0.12.16"
|
||||||
@@ -6341,6 +6441,37 @@ dependencies = [
|
|||||||
"weezl",
|
"weezl",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.41"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
|
||||||
|
dependencies = [
|
||||||
|
"deranged",
|
||||||
|
"itoa",
|
||||||
|
"num-conv",
|
||||||
|
"powerfmt",
|
||||||
|
"serde",
|
||||||
|
"time-core",
|
||||||
|
"time-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-core"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-macros"
|
||||||
|
version = "0.2.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
|
||||||
|
dependencies = [
|
||||||
|
"num-conv",
|
||||||
|
"time-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiny-keccak"
|
name = "tiny-keccak"
|
||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
@@ -8041,6 +8172,16 @@ version = "0.13.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d"
|
checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xattr"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rustix 1.0.8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xcb"
|
name = "xcb"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
|||||||
@@ -8,13 +8,11 @@ publish.workspace = true
|
|||||||
common = { path = "../common" }
|
common = { path = "../common" }
|
||||||
global = { path = "../global" }
|
global = { path = "../global" }
|
||||||
|
|
||||||
rust-i18n.workspace = true
|
|
||||||
i18n.workspace = true
|
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
nostr-sdk.workspace = true
|
nostr-sdk.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
reqwest.workspace = true
|
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
smallvec.workspace = true
|
||||||
|
|
||||||
tempfile = "3.19.1"
|
cargo-packager-updater = "0.2.3"
|
||||||
|
|||||||
@@ -1,348 +1,160 @@
|
|||||||
use std::env::consts::OS;
|
use anyhow::Error;
|
||||||
use std::env::{self};
|
use cargo_packager_updater::semver::Version;
|
||||||
use std::ffi::OsString;
|
use cargo_packager_updater::{check_update, Config, Update};
|
||||||
use std::path::PathBuf;
|
use global::constants::{APP_PUBKEY, APP_UPDATER_ENDPOINT};
|
||||||
|
use gpui::http_client::Url;
|
||||||
use anyhow::{anyhow, Context as _, Error};
|
use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task, Window};
|
||||||
use global::nostr_client;
|
use smallvec::{smallvec, SmallVec};
|
||||||
use gpui::{App, AppContext, Context, Entity, Global, SemanticVersion, Task};
|
|
||||||
use nostr_sdk::prelude::*;
|
|
||||||
use smol::fs::{self, File};
|
|
||||||
use smol::io::AsyncWriteExt;
|
|
||||||
use smol::process::Command;
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
i18n::init!();
|
|
||||||
|
|
||||||
struct GlobalAutoUpdate(Entity<AutoUpdater>);
|
|
||||||
|
|
||||||
impl Global for GlobalAutoUpdate {}
|
|
||||||
|
|
||||||
pub fn init(cx: &mut App) {
|
pub fn init(cx: &mut App) {
|
||||||
let env = env!("CARGO_PKG_VERSION");
|
AutoUpdater::set_global(cx.new(AutoUpdater::new), cx);
|
||||||
let current_version: SemanticVersion = env.parse().expect("Invalid version in Cargo.toml");
|
|
||||||
|
|
||||||
AutoUpdater::set_global(
|
|
||||||
cx.new(|_| AutoUpdater {
|
|
||||||
current_version,
|
|
||||||
status: AutoUpdateStatus::Idle,
|
|
||||||
}),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MacOsUnmounter {
|
struct GlobalAutoUpdater(Entity<AutoUpdater>);
|
||||||
mount_path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for MacOsUnmounter {
|
impl Global for GlobalAutoUpdater {}
|
||||||
fn drop(&mut self) {
|
|
||||||
let unmount_output = std::process::Command::new("hdiutil")
|
|
||||||
.args(["detach", "-force"])
|
|
||||||
.arg(&self.mount_path)
|
|
||||||
.output();
|
|
||||||
|
|
||||||
match unmount_output {
|
#[derive(Debug, Clone)]
|
||||||
Ok(output) if output.status.success() => {
|
|
||||||
log::info!("Successfully unmounted the disk image");
|
|
||||||
}
|
|
||||||
Ok(output) => {
|
|
||||||
log::error!(
|
|
||||||
"Failed to unmount disk image: {:?}",
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
log::error!("Error while trying to unmount disk image: {error:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub enum AutoUpdateStatus {
|
pub enum AutoUpdateStatus {
|
||||||
Idle,
|
Idle,
|
||||||
Checking,
|
Checking,
|
||||||
Downloading,
|
Checked { update: Box<Update> },
|
||||||
Installing,
|
Installing,
|
||||||
Updated { binary_path: PathBuf },
|
Updated,
|
||||||
Errored,
|
Errored { msg: Box<String> },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AutoUpdateStatus {
|
impl AutoUpdateStatus {
|
||||||
|
pub fn is_updating(&self) -> bool {
|
||||||
|
matches!(self, Self::Checked { .. } | Self::Installing)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_updated(&self) -> bool {
|
pub fn is_updated(&self) -> bool {
|
||||||
matches!(self, Self::Updated { .. })
|
matches!(self, Self::Updated)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checked(update: Update) -> Self {
|
||||||
|
Self::Checked {
|
||||||
|
update: Box::new(update),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error(e: String) -> Self {
|
||||||
|
Self::Errored { msg: Box::new(e) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AutoUpdater {
|
pub struct AutoUpdater {
|
||||||
status: AutoUpdateStatus,
|
pub status: AutoUpdateStatus,
|
||||||
current_version: SemanticVersion,
|
config: Config,
|
||||||
|
version: Version,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
subscriptions: SmallVec<[Subscription; 1]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AutoUpdater {
|
impl AutoUpdater {
|
||||||
|
/// Retrieve the Global Auto Updater instance
|
||||||
pub fn global(cx: &App) -> Entity<Self> {
|
pub fn global(cx: &App) -> Entity<Self> {
|
||||||
cx.global::<GlobalAutoUpdate>().0.clone()
|
cx.global::<GlobalAutoUpdater>().0.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_global(auto_updater: Entity<Self>, cx: &mut App) {
|
/// Retrieve the Auto Updater instance
|
||||||
cx.set_global(GlobalAutoUpdate(auto_updater));
|
pub fn read_global(cx: &App) -> &Self {
|
||||||
|
cx.global::<GlobalAutoUpdater>().0.read(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_version(&self) -> SemanticVersion {
|
/// Set the Global Auto Updater instance
|
||||||
self.current_version
|
pub(crate) fn set_global(state: Entity<Self>, cx: &mut App) {
|
||||||
|
cx.set_global(GlobalAutoUpdater(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn status(&self) -> AutoUpdateStatus {
|
pub(crate) fn new(cx: &mut Context<Self>) -> Self {
|
||||||
self.status.clone()
|
let config = cargo_packager_updater::Config {
|
||||||
|
endpoints: vec![Url::parse(APP_UPDATER_ENDPOINT).expect("Endpoint is not valid")],
|
||||||
|
pubkey: String::from(APP_PUBKEY),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let version = Version::parse(env!("CARGO_PKG_VERSION")).expect("Failed to parse version");
|
||||||
|
let mut subscriptions = smallvec![];
|
||||||
|
|
||||||
|
subscriptions.push(cx.observe_new::<Self>(|this, window, cx| {
|
||||||
|
if let Some(window) = window {
|
||||||
|
this.check_for_updates(window, cx);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
status: AutoUpdateStatus::Idle,
|
||||||
|
version,
|
||||||
|
config,
|
||||||
|
subscriptions,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_status(&mut self, status: AutoUpdateStatus, cx: &mut Context<Self>) {
|
pub fn check_for_updates(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
let config = self.config.clone();
|
||||||
|
let current_version = self.version.clone();
|
||||||
|
|
||||||
|
log::info!("Checking for updates...");
|
||||||
|
self.set_status(AutoUpdateStatus::Checking, cx);
|
||||||
|
|
||||||
|
let checking: Task<Result<Option<Update>, Error>> = cx.background_spawn(async move {
|
||||||
|
if let Some(update) = check_update(current_version, config)? {
|
||||||
|
Ok(Some(update))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
|
if let Ok(Some(update)) = checking.await {
|
||||||
|
cx.update(|window, cx| {
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.set_status(AutoUpdateStatus::checked(update), cx);
|
||||||
|
this.install_update(window, cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
} else {
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.set_status(AutoUpdateStatus::Idle, cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn install_update(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.set_status(AutoUpdateStatus::Installing, cx);
|
||||||
|
|
||||||
|
if let AutoUpdateStatus::Checked { update } = self.status.clone() {
|
||||||
|
let install: Task<Result<(), Error>> =
|
||||||
|
cx.background_spawn(async move { Ok(update.download_and_install()?) });
|
||||||
|
|
||||||
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
|
match install.await {
|
||||||
|
Ok(_) => {
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.set_status(AutoUpdateStatus::Updated, cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.set_status(AutoUpdateStatus::error(e.to_string()), cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_status(&mut self, status: AutoUpdateStatus, cx: &mut Context<Self>) {
|
||||||
self.status = status;
|
self.status = status;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, event: Event, cx: &mut Context<Self>) {
|
|
||||||
self.set_status(AutoUpdateStatus::Checking, cx);
|
|
||||||
|
|
||||||
// Extract the version from the identifier tag
|
|
||||||
let ident = match event.tags.identifier() {
|
|
||||||
Some(i) => match i.split('@').next_back() {
|
|
||||||
Some(i) => i,
|
|
||||||
None => return,
|
|
||||||
},
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convert the version string to a SemanticVersion
|
|
||||||
let new_version: SemanticVersion = ident.parse().expect("Invalid version");
|
|
||||||
|
|
||||||
// Check if the new version is the same as the current version
|
|
||||||
if self.current_version == new_version {
|
|
||||||
self.set_status(AutoUpdateStatus::Idle, cx);
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Download the new version
|
|
||||||
self.set_status(AutoUpdateStatus::Downloading, cx);
|
|
||||||
|
|
||||||
let task: Task<Result<(TempDir, PathBuf), Error>> = cx.background_spawn(async move {
|
|
||||||
let ids = event.tags.event_ids().copied();
|
|
||||||
let filter = Filter::new().ids(ids).kind(Kind::FileMetadata);
|
|
||||||
let events = nostr_client().database().query(filter).await?;
|
|
||||||
|
|
||||||
if let Some(event) = events.into_iter().find(|event| event.content == OS) {
|
|
||||||
let tag = event.tags.find(TagKind::Url).context("url not found")?;
|
|
||||||
let url = Url::parse(tag.content().context("invalid")?)?;
|
|
||||||
|
|
||||||
let temp_dir = tempfile::Builder::new().prefix("coop-update").tempdir()?;
|
|
||||||
let filename = match OS {
|
|
||||||
"macos" => Ok("Coop.dmg"),
|
|
||||||
"linux" => Ok("Coop.tar.gz"),
|
|
||||||
"windows" => Ok("CoopUpdateInstaller.exe"),
|
|
||||||
_ => Err(anyhow!("not supported: {:?}", OS)),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
let downloaded_asset = temp_dir.path().join(filename);
|
|
||||||
let mut target_file = File::create(&downloaded_asset).await?;
|
|
||||||
|
|
||||||
let response = reqwest::get(url).await?;
|
|
||||||
let mut stream = response.bytes_stream();
|
|
||||||
|
|
||||||
while let Some(item) = stream.next().await {
|
|
||||||
let chunk = item?;
|
|
||||||
target_file.write_all(&chunk).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
log::info!("downloaded update. path: {downloaded_asset:?}");
|
|
||||||
|
|
||||||
Ok((temp_dir, downloaded_asset))
|
|
||||||
} else {
|
|
||||||
Err(anyhow!("Not found"))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
|
||||||
if let Ok((temp_dir, downloaded_asset)) = task.await {
|
|
||||||
cx.update(|cx| {
|
|
||||||
this.update(cx, |this, cx| {
|
|
||||||
this.set_status(AutoUpdateStatus::Installing, cx);
|
|
||||||
|
|
||||||
match OS {
|
|
||||||
"macos" => this.install_release_macos(temp_dir, downloaded_asset, cx),
|
|
||||||
"linux" => this.install_release_linux(temp_dir, downloaded_asset, cx),
|
|
||||||
"windows" => this.install_release_windows(downloaded_asset, cx),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn install_release_macos(&mut self, temp_dir: TempDir, asset: PathBuf, cx: &mut Context<Self>) {
|
|
||||||
let running_app_path = cx.app_path().unwrap();
|
|
||||||
let running_app_filename = running_app_path.file_name().unwrap();
|
|
||||||
|
|
||||||
let mount_path = temp_dir.path().join("Coop");
|
|
||||||
|
|
||||||
let mut mounted_app_path: OsString = mount_path.join(running_app_filename).into();
|
|
||||||
mounted_app_path.push("/");
|
|
||||||
|
|
||||||
let task: Task<Result<PathBuf, Error>> = cx.background_spawn(async move {
|
|
||||||
let output = Command::new("hdiutil")
|
|
||||||
.args(["attach", "-nobrowse"])
|
|
||||||
.arg(&asset)
|
|
||||||
.arg("-mountroot")
|
|
||||||
.arg(temp_dir.path())
|
|
||||||
.output()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
anyhow::ensure!(
|
|
||||||
output.status.success(),
|
|
||||||
"failed to mount: {:?}",
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create an MacOsUnmounter that will be dropped (and thus unmount the disk) when this function exits
|
|
||||||
let _unmounter = MacOsUnmounter {
|
|
||||||
mount_path: mount_path.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let output = Command::new("rsync")
|
|
||||||
.args(["-av", "--delete"])
|
|
||||||
.arg(&mounted_app_path)
|
|
||||||
.arg(&running_app_path)
|
|
||||||
.output()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
anyhow::ensure!(
|
|
||||||
output.status.success(),
|
|
||||||
"failed to copy app: {:?}",
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(running_app_path)
|
|
||||||
});
|
|
||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
|
||||||
if let Ok(binary_path) = task.await {
|
|
||||||
cx.update(|cx| {
|
|
||||||
this.update(cx, |this, cx| {
|
|
||||||
this.status = AutoUpdateStatus::Updated { binary_path };
|
|
||||||
cx.notify();
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn install_release_linux(&mut self, temp_dir: TempDir, asset: PathBuf, cx: &mut Context<Self>) {
|
|
||||||
let home_dir = PathBuf::from(env::var("HOME").unwrap());
|
|
||||||
let running_app_path = cx.app_path().unwrap();
|
|
||||||
let extracted = temp_dir.path().join("coop");
|
|
||||||
|
|
||||||
let task: Task<Result<PathBuf, Error>> = cx.background_spawn(async move {
|
|
||||||
fs::create_dir_all(&extracted).await?;
|
|
||||||
|
|
||||||
let output = Command::new("tar")
|
|
||||||
.arg("-xzf")
|
|
||||||
.arg(&asset)
|
|
||||||
.arg("-C")
|
|
||||||
.arg(&extracted)
|
|
||||||
.output()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
anyhow::ensure!(
|
|
||||||
output.status.success(),
|
|
||||||
"failed to extract {:?} to {:?}: {:?}",
|
|
||||||
asset,
|
|
||||||
extracted,
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
);
|
|
||||||
|
|
||||||
let app_folder_name: String = "coop.app".into();
|
|
||||||
let from = extracted.join(&app_folder_name);
|
|
||||||
let mut to = home_dir.join(".local");
|
|
||||||
|
|
||||||
let expected_suffix = format!("{app_folder_name}/libexec/coop");
|
|
||||||
|
|
||||||
if let Some(prefix) = running_app_path
|
|
||||||
.to_str()
|
|
||||||
.and_then(|str| str.strip_suffix(&expected_suffix))
|
|
||||||
{
|
|
||||||
to = PathBuf::from(prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = Command::new("rsync")
|
|
||||||
.args(["-av", "--delete"])
|
|
||||||
.arg(&from)
|
|
||||||
.arg(&to)
|
|
||||||
.output()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
anyhow::ensure!(
|
|
||||||
output.status.success(),
|
|
||||||
"failed to copy Coop update from {:?} to {:?}: {:?}",
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(to.join(expected_suffix))
|
|
||||||
});
|
|
||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
|
||||||
if let Ok(binary_path) = task.await {
|
|
||||||
cx.update(|cx| {
|
|
||||||
this.update(cx, |this, cx| {
|
|
||||||
this.status = AutoUpdateStatus::Updated { binary_path };
|
|
||||||
cx.notify();
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn install_release_windows(&mut self, asset: PathBuf, cx: &mut Context<Self>) {
|
|
||||||
let task: Task<Result<PathBuf, Error>> = cx.background_spawn(async move {
|
|
||||||
let output = Command::new(asset)
|
|
||||||
.arg("/verysilent")
|
|
||||||
.arg("/update=true")
|
|
||||||
.arg("!desktopicon")
|
|
||||||
.arg("!quicklaunchicon")
|
|
||||||
.output()
|
|
||||||
.await?;
|
|
||||||
anyhow::ensure!(
|
|
||||||
output.status.success(),
|
|
||||||
"failed to start installer: {:?}",
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
);
|
|
||||||
Ok(std::env::current_exe()?)
|
|
||||||
});
|
|
||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
|
||||||
if let Ok(binary_path) = task.await {
|
|
||||||
cx.update(|cx| {
|
|
||||||
this.update(cx, |this, cx| {
|
|
||||||
this.status = AutoUpdateStatus::Updated { binary_path };
|
|
||||||
cx.notify();
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use auto_update::AutoUpdater;
|
||||||
use client_keys::ClientKeys;
|
use client_keys::ClientKeys;
|
||||||
use common::display::DisplayProfile;
|
use common::display::DisplayProfile;
|
||||||
use global::constants::DEFAULT_SIDEBAR_WIDTH;
|
use global::constants::DEFAULT_SIDEBAR_WIDTH;
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, div, px, rems, Action, App, AppContext, Axis, Context, Entity, InteractiveElement,
|
actions, div, px, rems, Action, App, AppContext, Axis, Context, Entity, InteractiveElement,
|
||||||
IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Window,
|
IntoElement, ParentElement, Render, SharedString, StatefulInteractiveElement, Styled,
|
||||||
|
Subscription, Window,
|
||||||
};
|
};
|
||||||
use i18n::t;
|
use i18n::{shared_t, t};
|
||||||
use identity::Identity;
|
use identity::Identity;
|
||||||
use nostr_connect::prelude::*;
|
use nostr_connect::prelude::*;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
@@ -275,7 +277,7 @@ impl ChatSpace {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_settings(&mut self, _ev: &Settings, window: &mut Window, cx: &mut Context<Self>) {
|
fn on_settings(&mut self, _ev: &Settings, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let view = preferences::init(window, cx);
|
let view = preferences::init(window, cx);
|
||||||
let title = SharedString::new(t!("common.preferences"));
|
let title = SharedString::new(t!("common.preferences"));
|
||||||
|
|
||||||
@@ -341,8 +343,39 @@ impl ChatSpace {
|
|||||||
let need_backup = Identity::read_global(cx).need_backup();
|
let need_backup = Identity::read_global(cx).need_backup();
|
||||||
let relay_ready = Identity::read_global(cx).relay_ready();
|
let relay_ready = Identity::read_global(cx).relay_ready();
|
||||||
|
|
||||||
|
let updating = AutoUpdater::read_global(cx).status.is_updating();
|
||||||
|
let updated = AutoUpdater::read_global(cx).status.is_updated();
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
|
.when(updating, |this| {
|
||||||
|
this.child(
|
||||||
|
h_flex()
|
||||||
|
.h_6()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.text_xs()
|
||||||
|
.bg(cx.theme().ghost_element_background_alt)
|
||||||
|
.child(shared_t!("auto_update.updating")),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.when(updated, |this| {
|
||||||
|
this.child(
|
||||||
|
h_flex()
|
||||||
|
.id("updated")
|
||||||
|
.h_6()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.text_xs()
|
||||||
|
.bg(cx.theme().ghost_element_background_alt)
|
||||||
|
.hover(|this| this.bg(cx.theme().ghost_element_hover))
|
||||||
|
.active(|this| this.bg(cx.theme().ghost_element_active))
|
||||||
|
.child(shared_t!("auto_update.updated"))
|
||||||
|
.on_click(|_, _window, cx| {
|
||||||
|
cx.restart(None);
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
.when_some(relay_ready, |this, status| {
|
.when_some(relay_ready, |this, status| {
|
||||||
this.when(!status, |this| this.child(messaging_relays::relay_button()))
|
this.when(!status, |this| this.child(messaging_relays::relay_button()))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use assets::Assets;
|
use assets::Assets;
|
||||||
use auto_update::AutoUpdater;
|
|
||||||
use global::constants::{
|
use global::constants::{
|
||||||
ALL_MESSAGES_SUB_ID, APP_ID, APP_NAME, APP_PUBKEY, BOOTSTRAP_RELAYS, METADATA_BATCH_LIMIT,
|
ALL_MESSAGES_SUB_ID, APP_ID, APP_NAME, BOOTSTRAP_RELAYS, METADATA_BATCH_LIMIT,
|
||||||
METADATA_BATCH_TIMEOUT, NEW_MESSAGE_SUB_ID, SEARCH_RELAYS,
|
METADATA_BATCH_TIMEOUT, NEW_MESSAGE_SUB_ID, SEARCH_RELAYS,
|
||||||
};
|
};
|
||||||
use global::{nostr_client, NostrSignal};
|
use global::{nostr_client, NostrSignal};
|
||||||
@@ -55,11 +54,6 @@ fn main() {
|
|||||||
log::error!("Failed to connect to bootstrap relays: {e}");
|
log::error!("Failed to connect to bootstrap relays: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to bootstrap relays.
|
|
||||||
if let Err(e) = subscribe_for_app_updates(client).await {
|
|
||||||
log::error!("Failed to subscribe for app updates: {e}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Nostr notifications.
|
// Handle Nostr notifications.
|
||||||
//
|
//
|
||||||
// Send the redefined signal back to GPUI via channel.
|
// Send the redefined signal back to GPUI via channel.
|
||||||
@@ -250,7 +244,6 @@ fn main() {
|
|||||||
while let Ok(signal) = signal_rx.recv().await {
|
while let Ok(signal) = signal_rx.recv().await {
|
||||||
cx.update(|window, cx| {
|
cx.update(|window, cx| {
|
||||||
let registry = Registry::global(cx);
|
let registry = Registry::global(cx);
|
||||||
let auto_updater = AutoUpdater::global(cx);
|
|
||||||
let identity = Identity::read_global(cx);
|
let identity = Identity::read_global(cx);
|
||||||
|
|
||||||
match signal {
|
match signal {
|
||||||
@@ -293,11 +286,6 @@ fn main() {
|
|||||||
NostrSignal::Notice(_msg) => {
|
NostrSignal::Notice(_msg) => {
|
||||||
// window.push_notification(msg, cx);
|
// window.push_notification(msg, cx);
|
||||||
}
|
}
|
||||||
NostrSignal::AppUpdate(event) => {
|
|
||||||
auto_updater.update(cx, |this, cx| {
|
|
||||||
this.update(event, cx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
@@ -431,20 +419,6 @@ async fn handle_nostr_notifications(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Kind::ReleaseArtifactSet => {
|
|
||||||
let ids = event.tags.event_ids().copied();
|
|
||||||
let filter = Filter::new().ids(ids).kind(Kind::FileMetadata);
|
|
||||||
|
|
||||||
client
|
|
||||||
.subscribe_to(BOOTSTRAP_RELAYS, filter, Some(opts))
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
signal_tx
|
|
||||||
.send(NostrSignal::AppUpdate(event.into_owned()))
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -460,27 +434,6 @@ async fn handle_nostr_notifications(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn subscribe_for_app_updates(client: &Client) -> Result<(), Error> {
|
|
||||||
let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE);
|
|
||||||
|
|
||||||
let coordinate = Coordinate {
|
|
||||||
kind: Kind::Custom(32267),
|
|
||||||
public_key: PublicKey::from_hex(APP_PUBKEY).expect("App Pubkey is invalid"),
|
|
||||||
identifier: APP_ID.into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let filter = Filter::new()
|
|
||||||
.kind(Kind::ReleaseArtifactSet)
|
|
||||||
.coordinate(&coordinate)
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
client
|
|
||||||
.subscribe_to(BOOTSTRAP_RELAYS, filter, Some(opts))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn check_author(client: &Client, event: &Event) -> Result<bool, Error> {
|
async fn check_author(client: &Client, event: &Event) -> Result<bool, Error> {
|
||||||
let signer = client.signer().await?;
|
let signer = client.signer().await?;
|
||||||
let public_key = signer.get_public_key().await?;
|
let public_key = signer.get_public_key().await?;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
pub const APP_NAME: &str = "Coop";
|
pub const APP_NAME: &str = "Coop";
|
||||||
pub const APP_ID: &str = "su.reya.coop";
|
pub const APP_ID: &str = "su.reya.coop";
|
||||||
pub const APP_PUBKEY: &str = "b1813fb01274b32cc5db6d1198e7c79dda0fb430899f63c7064f651a41d44f2b";
|
pub const APP_PUBKEY: &str = "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDc4MkNFRkQ2RkVGQURGNzUKUldSMTMvcisxdThzZUZraHc4Vno3NVNJek81VkJFUEV3MkJweGFxQXhpekdSU1JIekpqMG4yemMK";
|
||||||
|
pub const APP_UPDATER_ENDPOINT: &str = "https://coop-updater.reya.su/";
|
||||||
pub const KEYRING_URL: &str = "Coop Safe Storage";
|
pub const KEYRING_URL: &str = "Coop Safe Storage";
|
||||||
|
|
||||||
pub const ACCOUNT_D: &str = "coop:account";
|
pub const ACCOUNT_D: &str = "coop:account";
|
||||||
|
|||||||
@@ -31,9 +31,6 @@ pub enum NostrSignal {
|
|||||||
|
|
||||||
/// Notice from Relay Pool
|
/// Notice from Relay Pool
|
||||||
Notice(String),
|
Notice(String),
|
||||||
|
|
||||||
/// Application update event received
|
|
||||||
AppUpdate(Event),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static NOSTR_CLIENT: OnceLock<Client> = OnceLock::new();
|
static NOSTR_CLIENT: OnceLock<Client> = OnceLock::new();
|
||||||
|
|||||||
@@ -581,7 +581,6 @@ impl Identity {
|
|||||||
|
|
||||||
cx.spawn_in(window, async move |this, cx| {
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
let result = task.await;
|
let result = task.await;
|
||||||
log::info!("result: {result}");
|
|
||||||
|
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
this.relay_ready = Some(result);
|
this.relay_ready = Some(result);
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ common:
|
|||||||
clear:
|
clear:
|
||||||
en: "Clear"
|
en: "Clear"
|
||||||
|
|
||||||
|
auto_update:
|
||||||
|
updating:
|
||||||
|
en: "Installing the new update..."
|
||||||
|
updated:
|
||||||
|
en: "Restart to apply the new update"
|
||||||
|
|
||||||
user:
|
user:
|
||||||
dark_mode:
|
dark_mode:
|
||||||
en: "Dark Mode"
|
en: "Dark Mode"
|
||||||
|
|||||||
Reference in New Issue
Block a user