diff --git a/Cargo.lock b/Cargo.lock index 17071ae..f7e54a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -601,9 +601,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" +checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" dependencies = [ "arrayvec", ] @@ -620,9 +620,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.37.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a" +checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" dependencies = [ "cc", "cmake", @@ -1142,9 +1142,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.57" +version = "4.5.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6899ea499e3fb9305a65d5ebf6e3d2248c5fab291f300ad0a704fbe142eae31a" +checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806" dependencies = [ "clap_builder", "clap_derive", @@ -1152,9 +1152,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.57" +version = "4.5.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b12c8b680195a62a8364d16b8447b01b6c2c8f9aaf68bee653be34d4245e238" +checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2" dependencies = [ "anstream", "anstyle", @@ -1176,9 +1176,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" [[package]] name = "cmake" @@ -1263,7 +1263,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "indexmap", "rustc-hash 2.1.1", @@ -1708,7 +1708,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "proc-macro2", "quote", @@ -2639,7 +2639,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.2.2" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "anyhow", "as-raw-xcb-connection", @@ -2741,7 +2741,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -2752,7 +2752,7 @@ dependencies = [ [[package]] name = "gpui_tokio" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "anyhow", "gpui", @@ -2987,7 +2987,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "anyhow", "async-compression", @@ -3012,7 +3012,7 @@ dependencies = [ [[package]] name = "http_client_tls" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "rustls", "rustls-platform-verifier", @@ -3567,7 +3567,7 @@ checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags 2.10.0", "libc", - "redox_syscall 0.7.0", + "redox_syscall 0.7.1", ] [[package]] @@ -3780,7 +3780,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "anyhow", "bindgen", @@ -4020,7 +4020,7 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" [[package]] name = "nostr" version = "0.44.1" -source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" +source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6" dependencies = [ "aes", "base64", @@ -4045,7 +4045,7 @@ dependencies = [ [[package]] name = "nostr-connect" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" +source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6" dependencies = [ "async-utility", "futures-core", @@ -4058,7 +4058,7 @@ dependencies = [ [[package]] name = "nostr-database" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" +source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6" dependencies = [ "btreecap", "flatbuffers", @@ -4070,7 +4070,7 @@ dependencies = [ [[package]] name = "nostr-gossip" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" +source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6" dependencies = [ "nostr", ] @@ -4078,7 +4078,7 @@ dependencies = [ [[package]] name = "nostr-gossip-memory" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" +source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6" dependencies = [ "indexmap", "lru", @@ -4090,7 +4090,7 @@ dependencies = [ [[package]] name = "nostr-lmdb" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" +source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6" dependencies = [ "async-utility", "flume", @@ -4104,7 +4104,7 @@ dependencies = [ [[package]] name = "nostr-sdk" version = "0.44.1" -source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" +source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6" dependencies = [ "async-utility", "async-wsocket", @@ -4638,7 +4638,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "collections", "serde", @@ -5258,9 +5258,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" dependencies = [ "bitflags 2.10.0", ] @@ -5299,7 +5299,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "derive_refineable", ] @@ -5398,7 +5398,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "anyhow", "bytes", @@ -5453,7 +5453,7 @@ dependencies = [ [[package]] name = "rope" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "arrayvec", "log", @@ -5715,7 +5715,7 @@ dependencies = [ [[package]] name = "scheduler" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "async-task", "backtrace", @@ -6330,7 +6330,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "arrayvec", "log", @@ -7297,7 +7297,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "anyhow", "async-fs", @@ -7335,7 +7335,7 @@ dependencies = [ [[package]] name = "util_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "perf", "quote", @@ -8936,7 +8936,7 @@ dependencies = [ [[package]] name = "zlog" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "anyhow", "chrono", @@ -8953,7 +8953,7 @@ checksum = "4de98dfa5d5b7fef4ee834d0073d560c9ca7b6c46a71d058c48db7960f8cfaf7" [[package]] name = "ztracing" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" dependencies = [ "tracing", "tracing-subscriber", @@ -8964,7 +8964,7 @@ dependencies = [ [[package]] name = "ztracing_macro" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" +source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca" [[package]] name = "zune-core" diff --git a/Cargo.toml b/Cargo.toml index 5d2a2da..8911aac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,10 @@ gpui_tokio = { git = "https://github.com/zed-industries/zed" } reqwest_client = { git = "https://github.com/zed-industries/zed" } # Nostr +nostr = { git = "https://github.com/rust-nostr/nostr", features = [ "nip96", "nip59", "nip49", "nip44" ] } +nostr-sdk = { git = "https://github.com/rust-nostr/nostr" } nostr-lmdb = { git = "https://github.com/rust-nostr/nostr" } nostr-connect = { git = "https://github.com/rust-nostr/nostr" } -nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = [ "nip96", "nip59", "nip49", "nip44" ] } nostr-gossip-memory = { git = "https://github.com/rust-nostr/nostr" } # Others diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 3fd083a..44b7514 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -6,6 +6,7 @@ publish.workspace = true [dependencies] gpui.workspace = true +nostr.workspace = true nostr-sdk.workspace = true anyhow.workspace = true @@ -19,4 +20,3 @@ log.workspace = true dirs = "5.0" qrcode = "0.14.1" -nostr = { git = "https://github.com/rust-nostr/nostr" } diff --git a/crates/coop/src/panels/greeter.rs b/crates/coop/src/panels/greeter.rs index 7b0dce1..961bc17 100644 --- a/crates/coop/src/panels/greeter.rs +++ b/crates/coop/src/panels/greeter.rs @@ -32,10 +32,10 @@ impl GreeterPanel { fn add_profile_panel(&mut self, window: &mut Window, cx: &mut Context) { let nostr = NostrRegistry::global(cx); - let signer_pkey = nostr.read(cx).signer_pkey(cx); + let signer = nostr.read(cx).signer(); - cx.spawn_in(window, async move |_this, cx| { - if let Ok(public_key) = signer_pkey.await { + if let Some(public_key) = signer.public_key() { + cx.spawn_in(window, async move |_this, cx| { cx.update(|window, cx| { Workspace::add_panel( profile::init(public_key, window, cx), @@ -45,9 +45,9 @@ impl GreeterPanel { ); }) .ok(); - } - }) - .detach(); + }) + .detach(); + } } } @@ -84,6 +84,8 @@ impl Render for GreeterPanel { let nostr = NostrRegistry::global(cx); let nip65_state = nostr.read(cx).nip65_state(); let nip17_state = nostr.read(cx).nip17_state(); + let signer = nostr.read(cx).signer(); + let owned = signer.owned(); let required_actions = nip65_state.read(cx) == &RelayState::NotConfigured || nip17_state.read(cx) == &RelayState::NotConfigured; @@ -186,7 +188,7 @@ impl Render for GreeterPanel { ), ) }) - .when(!nostr.read(cx).owned_signer(), |this| { + .when(!owned, |this| { this.child( v_flex() .gap_2() diff --git a/crates/coop/src/sidebar/mod.rs b/crates/coop/src/sidebar/mod.rs index fa8d81c..9ea5e6f 100644 --- a/crates/coop/src/sidebar/mod.rs +++ b/crates/coop/src/sidebar/mod.rs @@ -321,13 +321,13 @@ impl Sidebar { let async_chat = chat.downgrade(); let nostr = NostrRegistry::global(cx); - let signer_pkey = nostr.read(cx).signer_pkey(cx); + let signer = nostr.read(cx).signer(); // Get all selected public keys let receivers = self.get_selected(cx); self.tasks.push(cx.spawn_in(window, async move |this, cx| { - let public_key = signer_pkey.await?; + let public_key = signer.get_public_key().await?; // Create a new room and emit it async_chat.update_in(cx, |this, _window, cx| { diff --git a/crates/coop/src/workspace.rs b/crates/coop/src/workspace.rs index 272c9cf..e2ee5e3 100644 --- a/crates/coop/src/workspace.rs +++ b/crates/coop/src/workspace.rs @@ -2,14 +2,13 @@ use std::sync::Arc; use chat::{ChatEvent, ChatRegistry}; use dock::dock::DockPlacement; -use dock::panel::PanelView; +use dock::panel::{PanelStyle, PanelView}; use dock::{ClosePanel, DockArea, DockItem}; use gpui::prelude::FluentBuilder; use gpui::{ div, rems, App, AppContext, Axis, Context, Entity, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Window, }; -use nostr_sdk::prelude::*; use person::PersonRegistry; use smallvec::{smallvec, SmallVec}; use state::{NostrRegistry, RelayState}; @@ -34,24 +33,15 @@ pub struct Workspace { /// App's Dock Area dock: Entity, - /// Current User - current_user: Entity>, - /// Event subscriptions _subscriptions: SmallVec<[Subscription; 3]>, } impl Workspace { fn new(window: &mut Window, cx: &mut Context) -> Self { - let titlebar = cx.new(|_| TitleBar::new()); - let dock = - cx.new(|cx| DockArea::new(window, cx).panel_style(dock::panel::PanelStyle::TabBar)); - let chat = ChatRegistry::global(cx); - let current_user = cx.new(|_| None); - - let nostr = NostrRegistry::global(cx); - let nip65_state = nostr.read(cx).nip65_state(); + let titlebar = cx.new(|_| TitleBar::new()); + let dock = cx.new(|cx| DockArea::new(window, cx).style(PanelStyle::TabBar)); let mut subscriptions = smallvec![]; @@ -99,15 +89,6 @@ impl Workspace { }), ); - subscriptions.push( - // Observe the NIP-65 state - cx.observe(&nip65_state, move |this, state, cx| { - if state.read(cx).idle() || state.read(cx).checking() { - this.get_current_user(cx); - } - }), - ); - // Set the default layout for app's dock cx.defer_in(window, |this, window, cx| { this.set_layout(window, cx); @@ -116,7 +97,6 @@ impl Workspace { Self { titlebar, dock, - current_user, _subscriptions: subscriptions, } } @@ -181,37 +161,19 @@ impl Workspace { }); } - fn get_current_user(&self, cx: &mut Context) { - let nostr = NostrRegistry::global(cx); - let client = nostr.read(cx).client(); - let current_user = self.current_user.downgrade(); - - cx.spawn(async move |_this, cx| { - if let Some(signer) = client.signer() { - if let Ok(public_key) = signer.get_public_key().await { - current_user - .update(cx, |this, cx| { - *this = Some(public_key); - cx.notify(); - }) - .ok(); - } - } - }) - .detach(); - } - fn titlebar_left(&mut self, _window: &mut Window, cx: &Context) -> impl IntoElement { let nostr = NostrRegistry::global(cx); let nip65 = nostr.read(cx).nip65_state(); let nip17 = nostr.read(cx).nip17_state(); + let signer = nostr.read(cx).signer(); + let current_user = signer.public_key(); h_flex() .h(TITLEBAR_HEIGHT) .flex_shrink_0() .justify_between() .gap_2() - .when_some(self.current_user.read(cx).as_ref(), |this, public_key| { + .when_some(current_user.as_ref(), |this, public_key| { let persons = PersonRegistry::global(cx); let profile = persons.read(cx).get(public_key, cx); @@ -232,6 +194,19 @@ impl Workspace { }), ) }) + .when(nostr.read(cx).creating_signer(), |this| { + this.child(div().text_xs().text_color(cx.theme().text_muted).child( + SharedString::from("Coop is creating a new identity for you..."), + )) + }) + .when(!nostr.read(cx).connected(), |this| { + this.child( + div() + .text_xs() + .text_color(cx.theme().text_muted) + .child(SharedString::from("Connecting...")), + ) + }) .map(|this| match nip65.read(cx) { RelayState::Checking => this.child( div() @@ -247,7 +222,7 @@ impl Workspace { .text_xs() .text_color(cx.theme().warning_foreground) .bg(cx.theme().warning_background) - .rounded_xs() + .rounded_sm() .child(SharedString::from("User hasn't configured a relay list")), ), _ => this, @@ -266,7 +241,7 @@ impl Workspace { .text_xs() .text_color(cx.theme().warning_foreground) .bg(cx.theme().warning_background) - .rounded_xs() + .rounded_sm() .child(SharedString::from( "User hasn't configured a messaging relay list", )), diff --git a/crates/dock/src/lib.rs b/crates/dock/src/lib.rs index 972fb27..bb04b0f 100644 --- a/crates/dock/src/lib.rs +++ b/crates/dock/src/lib.rs @@ -351,7 +351,7 @@ impl DockArea { } /// Set the panel style of the dock area. - pub fn panel_style(mut self, style: PanelStyle) -> Self { + pub fn style(mut self, style: PanelStyle) -> Self { self.panel_style = style; self } diff --git a/crates/state/src/constants.rs b/crates/state/src/constants.rs index 90a1993..2c0d539 100644 --- a/crates/state/src/constants.rs +++ b/crates/state/src/constants.rs @@ -37,13 +37,12 @@ pub const USER_GIFTWRAP: &str = "user-gift-wraps"; pub const WOT_RELAYS: [&str; 1] = ["wss://relay.vertexlab.io"]; /// Default search relays -pub const SEARCH_RELAYS: [&str; 2] = ["wss://antiprimal.net", "wss://relay.noswhere.com"]; +pub const SEARCH_RELAYS: [&str; 1] = ["wss://antiprimal.net"]; /// Default bootstrap relays -pub const BOOTSTRAP_RELAYS: [&str; 4] = [ +pub const BOOTSTRAP_RELAYS: [&str; 3] = [ "wss://relay.damus.io", "wss://relay.primal.net", - "wss://relay.nos.social", "wss://user.kindpag.es", ]; diff --git a/crates/state/src/lib.rs b/crates/state/src/lib.rs index 553f86d..45031b2 100644 --- a/crates/state/src/lib.rs +++ b/crates/state/src/lib.rs @@ -48,14 +48,15 @@ pub struct NostrRegistry { /// Nostr client client: Client, + /// Whether the bootstrapping relays is connected + connected: bool, + + /// Whether coop is creating a default signer + creating_signer: bool, + /// Nostr signer signer: Arc, - /// By default, Coop generates a new signer for new users. - /// - /// This flag indicates whether the signer is user-owned or Coop-generated. - owned_signer: bool, - /// NIP-65 relay state nip65: Entity, @@ -129,16 +130,17 @@ impl NostrRegistry { cx.defer(|cx| { let nostr = NostrRegistry::global(cx); + // Connect to the bootstrapping relays nostr.update(cx, |this, cx| { this.connect(cx); - this.get_identity(cx); }); }); Self { client, + connected: false, + creating_signer: false, signer, - owned_signer: false, nip65, nip17, app_keys, @@ -151,19 +153,29 @@ impl NostrRegistry { fn connect(&mut self, cx: &mut Context) { let client = self.client(); - self.tasks.push(cx.background_spawn(async move { + let task: Task> = cx.background_spawn(async move { // Add search relay to the relay pool for url in SEARCH_RELAYS.into_iter() { - client.add_relay(url).await?; + client.add_relay(url).and_connect().await?; } // Add bootstrap relay to the relay pool for url in BOOTSTRAP_RELAYS.into_iter() { - client.add_relay(url).await?; + client.add_relay(url).and_connect().await?; } - // Connect to all added relays - client.connect().await; + Ok(()) + }); + + self.tasks.push(cx.spawn(async move |this, cx| { + // Wait for the task to complete + task.await?; + + // Update the state + this.update(cx, |this, cx| { + this.set_connected(cx); + this.get_signer(cx); + })?; Ok(()) })); @@ -174,22 +186,16 @@ impl NostrRegistry { self.client.clone() } + /// Get the nostr signer + pub fn signer(&self) -> Arc { + self.signer.clone() + } + /// Get the app keys pub fn app_keys(&self) -> &Keys { &self.app_keys } - /// Returns whether the current signer is owned by user - pub fn owned_signer(&self) -> bool { - self.owned_signer - } - - /// Set whether the current signer is owned by user - pub fn set_owned_signer(&mut self, owned: bool, cx: &mut Context) { - self.owned_signer = owned; - cx.notify(); - } - /// Get the NIP-65 state pub fn nip65_state(&self) -> Entity { self.nip65.clone() @@ -200,16 +206,26 @@ impl NostrRegistry { self.nip17.clone() } - /// Get current signer's public key - pub fn signer_pkey(&self, cx: &App) -> Task> { - let client = self.client(); + /// Get the connected state + pub fn connected(&self) -> bool { + self.connected + } - cx.background_spawn(async move { - let signer = client.signer().context("Signer not found")?; - let public_key = signer.get_public_key().await?; + /// Set the connected state + fn set_connected(&mut self, cx: &mut Context) { + self.connected = true; + cx.notify(); + } - Ok(public_key) - }) + /// Get the creating signer status + pub fn creating_signer(&self) -> bool { + self.creating_signer + } + + /// Set the creating signer status + fn set_creating_signer(&mut self, status: bool, cx: &mut Context) { + self.creating_signer = status; + cx.notify(); } /// Get a relay hint (messaging relay) for a given public key @@ -290,18 +306,17 @@ impl NostrRegistry { T: NostrSigner + 'static, { let client = self.client(); - let signer = self.signer.clone(); + let signer = self.signer(); // Create a task to update the signer and verify the public key let task: Task> = cx.background_spawn(async move { // Update signer - signer.switch(new).await; + signer.switch(new, owned).await; // Verify signer let signer = client.signer().context("Signer not found")?; let public_key = signer.get_public_key().await?; - - log::info!("Signer's public key: {public_key}"); + log::info!("Signer's public key: {}", public_key); Ok(()) }); @@ -313,8 +328,6 @@ impl NostrRegistry { // Update states this.update(cx, |this, cx| { this.reset_relay_states(cx); - this.get_relay_list(cx); - this.set_owned_signer(owned, cx); })?; Ok(()) @@ -615,83 +628,61 @@ impl NostrRegistry { }) } - /// Get local stored identity - fn get_identity(&mut self, cx: &mut Context) { - let read_credential = cx.read_credentials(KEYRING); - - self.tasks.push(cx.spawn(async move |this, cx| { - match read_credential.await { - Ok(Some((_, secret))) => { - let secret = SecretKey::from_slice(&secret)?; - let keys = Keys::new(secret); - - this.update(cx, |this, cx| { - this.set_signer(keys, false, cx); - }) - .ok(); - } - _ => { - this.update(cx, |this, cx| { - this.get_bunker(cx); - }) - .ok(); - } - } - - Ok(()) - })); - } - /// Create a new identity - fn create_identity(&mut self, cx: &mut Context) { + fn set_default_signer(&mut self, cx: &mut Context) { let client = self.client(); let keys = Keys::generate(); - let async_keys = keys.clone(); - // Get write credential task + // Create a write credential task let write_credential = cx.write_credentials( KEYRING, &keys.public_key().to_hex(), &keys.secret_key().to_secret_bytes(), ); + // Update the signer + self.set_signer(keys, false, cx); + + // Set the creating signer status + self.set_creating_signer(true, cx); + // Run async tasks in background let task: Task> = cx.background_spawn(async move { - // Build and sign the relay list event + let signer = client.signer().context("Signer not found")?; + + // Get default relay list let relay_list = default_relay_list(); - let event = EventBuilder::relay_list(relay_list) - .sign(&async_keys) - .await?; // Publish relay list event - client.send_event(&event).await?; + let event = EventBuilder::relay_list(relay_list).sign(signer).await?; + let output = client.send_event(&event).broadcast().await?; + log::info!("Published relay list event: {:?}", output.id()); - // Build and sign the metadata event + // Construct the default metadata let name = petname::petname(2, "-").unwrap_or("Cooper".to_string()); let avatar = Url::parse(&format!("https://avatar.vercel.sh/{name}")).unwrap(); let metadata = Metadata::new().display_name(&name).picture(avatar); - let event = EventBuilder::metadata(&metadata).sign(&async_keys).await?; // Publish metadata event - client.send_event(&event).await?; + let event = EventBuilder::metadata(&metadata).sign(signer).await?; + let output = client.send_event(&event).broadcast().await?; + log::info!("Published metadata event: {:?}", output.id()); - // Build and sign the contact list event + // Construct the default contact list let contacts = vec![Contact::new(PublicKey::parse(COOP_PUBKEY).unwrap())]; - let event = EventBuilder::contact_list(contacts) - .sign(&async_keys) - .await?; // Publish contact list event - client.send_event(&event).await?; + let event = EventBuilder::contact_list(contacts).sign(signer).await?; + let output = client.send_event(&event).broadcast().await?; + log::info!("Published contact list event: {:?}", output.id()); - // Build and sign the messaging relay list event + // Construct the default messaging relay list let relays = default_messaging_relays(); - let event = EventBuilder::nip17_relay_list(relays) - .sign(&async_keys) - .await?; // Publish messaging relay list event - client.send_event(&event).await?; + let event = EventBuilder::nip17_relay_list(relays).sign(signer).await?; + let output = client.send_event(&event).to_nip65().await?; + log::info!("Published messaging relay list event: {:?}", output.id()); // Write user's credentials to the system keyring write_credential.await?; @@ -703,15 +694,41 @@ impl NostrRegistry { // Wait for the task to complete task.await?; - // Update the signer this.update(cx, |this, cx| { - this.set_signer(keys, false, cx); + this.set_creating_signer(false, cx); + this.get_relay_list(cx); })?; Ok(()) })); } + /// Get local stored signer + fn get_signer(&mut self, cx: &mut Context) { + let read_credential = cx.read_credentials(KEYRING); + + self.tasks.push(cx.spawn(async move |this, cx| { + match read_credential.await { + Ok(Some((_user, secret))) => { + let secret = SecretKey::from_slice(&secret)?; + let keys = Keys::new(secret); + + this.update(cx, |this, cx| { + this.set_signer(keys, false, cx); + this.get_relay_list(cx); + })?; + } + _ => { + this.update(cx, |this, cx| { + this.get_bunker(cx); + })?; + } + } + + Ok(()) + })); + } + /// Get local stored bunker connection fn get_bunker(&mut self, cx: &mut Context) { let client = self.client(); @@ -741,6 +758,7 @@ impl NostrRegistry { Ok(signer) => { this.update(cx, |this, cx| { this.set_signer(signer, true, cx); + this.get_relay_list(cx); }) .ok(); } @@ -748,7 +766,7 @@ impl NostrRegistry { log::warn!("Failed to get bunker: {e}"); // Create a new identity if no stored bunker exists this.update(cx, |this, cx| { - this.create_identity(cx); + this.set_default_signer(cx); }) .ok(); } diff --git a/crates/state/src/signer.rs b/crates/state/src/signer.rs index afe26bf..6067fb6 100644 --- a/crates/state/src/signer.rs +++ b/crates/state/src/signer.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::result::Result; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use nostr_sdk::prelude::*; @@ -8,6 +9,17 @@ use smol::lock::RwLock; #[derive(Debug)] pub struct CoopSigner { signer: RwLock>, + + /// Signer's public key + signer_pkey: RwLock>, + + /// Whether coop is creating a new identity + creating: AtomicBool, + + /// By default, Coop generates a new signer for new users. + /// + /// This flag indicates whether the signer is user-owned or Coop-generated. + owned: AtomicBool, } impl CoopSigner { @@ -17,6 +29,9 @@ impl CoopSigner { { Self { signer: RwLock::new(signer.into_nostr_signer()), + signer_pkey: RwLock::new(None), + creating: AtomicBool::new(false), + owned: AtomicBool::new(false), } } @@ -25,13 +40,39 @@ impl CoopSigner { self.signer.read().await.clone() } + /// Get public key + pub fn public_key(&self) -> Option { + self.signer_pkey.read_blocking().to_owned() + } + + /// Get the flag indicating whether the signer is creating a new identity. + pub fn creating(&self) -> bool { + self.creating.load(Ordering::SeqCst) + } + + /// Get the flag indicating whether the signer is user-owned. + pub fn owned(&self) -> bool { + self.owned.load(Ordering::SeqCst) + } + /// Switch the current signer to a new signer. - pub async fn switch(&self, new: T) + pub async fn switch(&self, new: T, owned: bool) where T: IntoNostrSigner, { + let new_signer = new.into_nostr_signer(); + let public_key = new_signer.get_public_key().await.ok(); let mut signer = self.signer.write().await; - *signer = new.into_nostr_signer(); + let mut signer_pkey = self.signer_pkey.write().await; + + // Switch to the new signer + *signer = new_signer; + + // Update the public key + *signer_pkey = public_key; + + // Update the owned flag + self.owned.store(owned, Ordering::SeqCst); } }