From fce4c1bbcd13938781ffda2dd95ef27427f49c1d Mon Sep 17 00:00:00 2001 From: reya Date: Thu, 5 Feb 2026 09:29:47 +0700 Subject: [PATCH] wip: update nostr sdk --- Cargo.lock | 63 +++--- Cargo.toml | 1 + crates/auto_update/src/lib.rs | 12 +- crates/chat/src/lib.rs | 10 +- crates/chat/src/room.rs | 18 +- crates/common/src/nip96.rs | 9 +- crates/coop/src/dialogs/screening.rs | 20 +- crates/coop/src/panels/messaging_relays.rs | 13 +- crates/coop/src/panels/relay_list.rs | 10 +- crates/device/src/lib.rs | 106 ++++++---- crates/person/src/lib.rs | 20 +- crates/relay_auth/src/lib.rs | 24 +-- crates/settings/src/lib.rs | 20 +- crates/state/Cargo.toml | 1 + crates/state/src/identity.rs | 4 +- crates/state/src/lib.rs | 221 ++++++++++++--------- crates/state/src/signer.rs | 88 ++++++++ crates/ui/src/menu/context_menu.rs | 3 +- 18 files changed, 391 insertions(+), 252 deletions(-) create mode 100644 crates/state/src/signer.rs diff --git a/Cargo.lock b/Cargo.lock index f9f00bf..eb12681 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1264,7 +1264,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "indexmap", "rustc-hash 2.1.1", @@ -1709,7 +1709,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "proc-macro2", "quote", @@ -2632,7 +2632,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.2.2" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "anyhow", "as-raw-xcb-connection", @@ -2734,7 +2734,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -2745,7 +2745,7 @@ dependencies = [ [[package]] name = "gpui_tokio" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "anyhow", "gpui", @@ -2967,7 +2967,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "anyhow", "async-compression", @@ -2992,7 +2992,7 @@ dependencies = [ [[package]] name = "http_client_tls" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "rustls", "rustls-platform-verifier", @@ -3742,7 +3742,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "anyhow", "bindgen", @@ -3982,7 +3982,7 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" [[package]] name = "nostr" version = "0.44.1" -source = "git+https://github.com/rust-nostr/nostr#c25727493b52af561ed6b6aca43cf866ef7aeb16" +source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd" dependencies = [ "aes", "base64", @@ -4007,7 +4007,7 @@ dependencies = [ [[package]] name = "nostr-connect" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#c25727493b52af561ed6b6aca43cf866ef7aeb16" +source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd" dependencies = [ "async-utility", "futures-core", @@ -4020,7 +4020,7 @@ dependencies = [ [[package]] name = "nostr-database" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#c25727493b52af561ed6b6aca43cf866ef7aeb16" +source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd" dependencies = [ "btreecap", "flatbuffers", @@ -4032,15 +4032,27 @@ dependencies = [ [[package]] name = "nostr-gossip" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#c25727493b52af561ed6b6aca43cf866ef7aeb16" +source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd" dependencies = [ "nostr", ] +[[package]] +name = "nostr-gossip-memory" +version = "0.44.0" +source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd" +dependencies = [ + "indexmap", + "lru", + "nostr", + "nostr-gossip", + "tokio", +] + [[package]] name = "nostr-lmdb" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#c25727493b52af561ed6b6aca43cf866ef7aeb16" +source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd" dependencies = [ "async-utility", "flume", @@ -4054,7 +4066,7 @@ dependencies = [ [[package]] name = "nostr-sdk" version = "0.44.1" -source = "git+https://github.com/rust-nostr/nostr#c25727493b52af561ed6b6aca43cf866ef7aeb16" +source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd" dependencies = [ "async-utility", "async-wsocket", @@ -4588,7 +4600,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "collections", "serde", @@ -5237,7 +5249,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "derive_refineable", ] @@ -5336,7 +5348,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "anyhow", "bytes", @@ -5391,7 +5403,7 @@ dependencies = [ [[package]] name = "rope" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "arrayvec", "log", @@ -5670,7 +5682,7 @@ dependencies = [ [[package]] name = "scheduler" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "async-task", "backtrace", @@ -6189,6 +6201,7 @@ dependencies = [ "gpui_tokio", "log", "nostr-connect", + "nostr-gossip-memory", "nostr-lmdb", "nostr-sdk", "petname", @@ -6273,7 +6286,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "arrayvec", "log", @@ -7258,7 +7271,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "anyhow", "async-fs", @@ -7296,7 +7309,7 @@ dependencies = [ [[package]] name = "util_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "perf", "quote", @@ -8772,7 +8785,7 @@ dependencies = [ [[package]] name = "zlog" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "anyhow", "chrono", @@ -8789,7 +8802,7 @@ checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445" [[package]] name = "ztracing" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" dependencies = [ "tracing", "tracing-subscriber", @@ -8800,7 +8813,7 @@ dependencies = [ [[package]] name = "ztracing_macro" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd" +source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667" [[package]] name = "zune-core" diff --git a/Cargo.toml b/Cargo.toml index 8ecb3fe..5d2a2da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ reqwest_client = { git = "https://github.com/zed-industries/zed" } 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 anyhow = "1.0.44" diff --git a/crates/auto_update/src/lib.rs b/crates/auto_update/src/lib.rs index de57b71..a525f40 100644 --- a/crates/auto_update/src/lib.rs +++ b/crates/auto_update/src/lib.rs @@ -243,12 +243,7 @@ impl AutoUpdater { .author(app_pubkey) .limit(1); - if let Err(e) = client - .subscribe_to(BOOTSTRAP_RELAYS, filter, Some(opts)) - .await - { - log::error!("Failed to subscribe to updates: {e}"); - }; + // TODO }) } @@ -285,10 +280,7 @@ impl AutoUpdater { .author(app_pubkey) .ids(ids.clone()); - // Get all files for this release - client - .subscribe_to(BOOTSTRAP_RELAYS, filter, Some(opts)) - .await?; + // TODO Ok(ids) } else { diff --git a/crates/chat/src/lib.rs b/crates/chat/src/lib.rs index ea02060..788547a 100644 --- a/crates/chat/src/lib.rs +++ b/crates/chat/src/lib.rs @@ -195,8 +195,8 @@ impl ChatRegistry { let mut notifications = client.notifications(); let mut processed_events = HashSet::new(); - while let Ok(notification) = notifications.recv().await { - let RelayPoolNotification::Message { message, .. } = notification else { + while let Some(notification) = notifications.next().await { + let ClientNotification::Message { message, .. } = notification else { // Skip non-message notifications continue; }; @@ -432,7 +432,7 @@ impl ChatRegistry { let client = nostr.read(cx).client(); cx.background_spawn(async move { - let signer = client.signer().await?; + let signer = client.signer().context("Signer not found")?; let public_key = signer.get_public_key().await?; let contacts = client.database().contacts_public_keys(public_key).await?; @@ -597,8 +597,8 @@ impl ChatRegistry { }; // Try with the user's signer - let user_signer = client.signer().await?; - let unwrapped = UnwrappedGift::from_gift_wrap(&user_signer, gift_wrap).await?; + let user_signer = client.signer().context("Signer not found")?; + let unwrapped = UnwrappedGift::from_gift_wrap(user_signer, gift_wrap).await?; Ok(unwrapped) } diff --git a/crates/chat/src/room.rs b/crates/chat/src/room.rs index e92d4be..ec55efc 100644 --- a/crates/chat/src/room.rs +++ b/crates/chat/src/room.rs @@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet}; use std::hash::{Hash, Hasher}; use std::time::Duration; -use anyhow::Error; +use anyhow::{Context as AnyhowContext, Error}; use common::EventUtils; use gpui::{App, AppContext, Context, EventEmitter, SharedString, Task}; use itertools::Itertools; @@ -325,7 +325,7 @@ impl Room { let id = SubscriptionId::new(format!("room-{}", self.id)); cx.background_spawn(async move { - let signer = client.signer().await?; + let signer = client.signer().context("Signer not found")?; let public_key = signer.get_public_key().await?; // Subscription options @@ -343,7 +343,9 @@ impl Room { // Subscribe to get member's gossip relays client - .subscribe_with_id(id.clone(), filter, Some(opts)) + .subscribe(filter) + .close_on(opts) + .with_id(id.clone()) .await?; } @@ -458,7 +460,7 @@ impl Room { let task = self.members_with_relays(cx); cx.background_spawn(async move { - let signer = client.signer().await?; + let signer = client.signer().context("Signer not found")?; let current_user_relays = current_user_relays.await; let mut members = task.await; @@ -484,10 +486,10 @@ impl Room { // Construct the gift wrap event let event = - EventBuilder::gift_wrap(&signer, &receiver, rumor.clone(), vec![]).await?; + EventBuilder::gift_wrap(signer, &receiver, rumor.clone(), vec![]).await?; // Send the gift wrap event to the messaging relays - match client.send_event_to(relays, &event).await { + match client.send_event(&event).to(&relays).await { Ok(output) => { let id = output.id().to_owned(); let auth = output.failed.iter().any(|(_, s)| s.starts_with("auth-")); @@ -525,7 +527,7 @@ impl Room { // Construct the gift-wrapped event let event = - EventBuilder::gift_wrap(&signer, ¤t_user, rumor.clone(), vec![]).await?; + EventBuilder::gift_wrap(signer, ¤t_user, rumor.clone(), vec![]).await?; // Only send a backup message to current user if sent successfully to others if reports.iter().all(|r| r.is_sent_success()) { @@ -580,7 +582,7 @@ impl Room { if let Some(event) = client.database().event_by_id(id).await? { for url in urls.into_iter() { - let relay = client.pool().relay(url).await?; + let relay = client.relay(url).await?.context("Relay not found")?; let id = relay.send_event(&event).await?; let resent: Output = Output { diff --git a/crates/common/src/nip96.rs b/crates/common/src/nip96.rs index 10d4e6f..c40cc89 100644 --- a/crates/common/src/nip96.rs +++ b/crates/common/src/nip96.rs @@ -72,11 +72,10 @@ pub async fn nip96_upload( let json: Value = res.json().await?; let config = nip96::ServerConfig::from_json(json.to_string())?; - let signer = if client.has_signer().await { - client.signer().await? - } else { - Keys::generate().into_nostr_signer() - }; + let signer = client + .signer() + .cloned() + .unwrap_or(Keys::generate().into_nostr_signer()); let url = upload(&signer, &config, file, None).await?; diff --git a/crates/coop/src/dialogs/screening.rs b/crates/coop/src/dialogs/screening.rs index 31b7629..4ca6ccf 100644 --- a/crates/coop/src/dialogs/screening.rs +++ b/crates/coop/src/dialogs/screening.rs @@ -1,6 +1,7 @@ +use std::collections::HashMap; use std::time::Duration; -use anyhow::Error; +use anyhow::{Context as AnyhowContext, Error}; use common::{shorten_pubkey, RenderedProfile, RenderedTimestamp, BOOTSTRAP_RELAYS}; use gpui::prelude::FluentBuilder; use gpui::{ @@ -45,7 +46,7 @@ impl Screening { let contact_check: Task), Error>> = cx.background_spawn({ let client = nostr.read(cx).client(); async move { - let signer = client.signer().await?; + let signer = client.signer().context("Signer not found")?; let signer_pubkey = signer.get_public_key().await?; // Check if user is in contact list @@ -74,8 +75,15 @@ impl Screening { let filter = Filter::new().author(public_key).limit(1); let mut activity: Option = None; + // Construct target for subscription + let target = BOOTSTRAP_RELAYS + .into_iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + if let Ok(mut stream) = client - .stream_events_from(BOOTSTRAP_RELAYS, filter, Duration::from_secs(2)) + .stream_events(target) + .timeout(Duration::from_secs(2)) .await { while let Some((_url, event)) = stream.next().await { @@ -162,12 +170,12 @@ impl Screening { let public_key = self.profile.public_key(); let task: Task> = cx.background_spawn(async move { - let signer = client.signer().await?; let tag = Tag::public_key_report(public_key, Report::Impersonation); - let event = EventBuilder::report(vec![tag], "").sign(&signer).await?; + let builder = EventBuilder::report(vec![tag], ""); + let event = client.sign_event_builder(builder).await?; // Send the report to the public relays - client.send_event_to(BOOTSTRAP_RELAYS, &event).await?; + client.send_event(&event).to(BOOTSTRAP_RELAYS).await?; Ok(()) }); diff --git a/crates/coop/src/panels/messaging_relays.rs b/crates/coop/src/panels/messaging_relays.rs index d1a5f4a..6388d7c 100644 --- a/crates/coop/src/panels/messaging_relays.rs +++ b/crates/coop/src/panels/messaging_relays.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use std::time::Duration; -use anyhow::{anyhow, Error}; +use anyhow::{anyhow, Context as AnyhowContext, Error}; use dock::panel::{Panel, PanelEvent}; use gpui::prelude::FluentBuilder; use gpui::{ @@ -89,7 +89,7 @@ impl MessagingRelayPanel { } async fn load(client: &Client) -> Result, Error> { - let signer = client.signer().await?; + let signer = client.signer().context("Signer not found")?; let public_key = signer.get_public_key().await?; let filter = Filter::new() @@ -162,20 +162,17 @@ impl MessagingRelayPanel { let task: Task> = cx.background_spawn(async move { let urls = write_relays.await; - let signer = client.signer().await?; let tags: Vec = relays .iter() .map(|relay| Tag::relay(relay.clone())) .collect(); - let event = EventBuilder::new(Kind::InboxRelays, "") - .tags(tags) - .sign(&signer) - .await?; + let builder = EventBuilder::new(Kind::InboxRelays, "").tags(tags); + let event = client.sign_event_builder(builder).await?; // Set messaging relays - client.send_event_to(urls, &event).await?; + client.send_event(&event).to(urls).await?; // Connect to messaging relays for relay in relays.iter() { diff --git a/crates/coop/src/panels/relay_list.rs b/crates/coop/src/panels/relay_list.rs index f7aef9c..262556b 100644 --- a/crates/coop/src/panels/relay_list.rs +++ b/crates/coop/src/panels/relay_list.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use std::time::Duration; -use anyhow::{anyhow, Error}; +use anyhow::{anyhow, Context as AnyhowContext, Error}; use common::BOOTSTRAP_RELAYS; use dock::panel::{Panel, PanelEvent}; use gpui::prelude::FluentBuilder; @@ -96,7 +96,7 @@ impl RelayListPanel { } async fn load(client: &Client) -> Result)>, Error> { - let signer = client.signer().await?; + let signer = client.signer().context("Signer not found")?; let public_key = signer.get_public_key().await?; let filter = Filter::new() @@ -167,11 +167,11 @@ impl RelayListPanel { let relays = self.relays.clone(); let task: Task> = cx.background_spawn(async move { - let signer = client.signer().await?; - let event = EventBuilder::relay_list(relays).sign(&signer).await?; + let builder = EventBuilder::relay_list(relays); + let event = client.sign_event_builder(builder).await?; // Set relay list for current user - client.send_event_to(BOOTSTRAP_RELAYS, &event).await?; + client.send_event(&event).to(BOOTSTRAP_RELAYS).await?; Ok(()) }); diff --git a/crates/device/src/lib.rs b/crates/device/src/lib.rs index f7ac1ee..e4d2ce8 100644 --- a/crates/device/src/lib.rs +++ b/crates/device/src/lib.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::sync::Arc; use std::time::Duration; @@ -130,8 +130,8 @@ impl DeviceRegistry { let mut notifications = client.notifications(); let mut processed_events = HashSet::new(); - while let Ok(notification) = notifications.recv().await { - if let RelayPoolNotification::Message { + while let Some(notification) = notifications.next().await { + if let ClientNotification::Message { message: RelayMessage::Event { event, .. }, .. } = notification @@ -162,7 +162,7 @@ impl DeviceRegistry { /// Verify the author of an event async fn verify_author(client: &Client, event: &Event) -> bool { - if let Ok(signer) = client.signer().await { + if let Some(signer) = client.signer() { if let Ok(public_key) = signer.get_public_key().await { return public_key == event.pubkey; } @@ -172,7 +172,7 @@ impl DeviceRegistry { /// Encrypt and store device keys in the local database. async fn set_keys(client: &Client, secret: &str) -> Result<(), Error> { - let signer = client.signer().await?; + let signer = client.signer().context("Signer not found")?; let public_key = signer.get_public_key().await?; // Encrypt the value @@ -193,7 +193,7 @@ impl DeviceRegistry { /// Get device keys from the local database. async fn get_keys(client: &Client) -> Result { - let signer = client.signer().await?; + let signer = client.signer().context("Signer not found")?; let public_key = signer.get_public_key().await?; let filter = Filter::new() @@ -278,10 +278,13 @@ impl DeviceRegistry { let filter = Filter::new().kind(Kind::GiftWrap).pubkey(pkey); let id = SubscriptionId::new(DEVICE_GIFTWRAP); - if let Err(e) = client - .subscribe_with_id_to(&urls, id, vec![filter], None) - .await - { + // Construct target for subscription + let target = urls + .iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + + if let Err(e) = client.subscribe(target).with_id(id).await { log::error!("Failed to subscribe to gift wrap events: {e}"); } } @@ -291,10 +294,13 @@ impl DeviceRegistry { let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key); let id = SubscriptionId::new(USER_GIFTWRAP); - if let Err(e) = client - .subscribe_with_id_to(urls, id, vec![filter], None) - .await - { + // Construct target for subscription + let target = urls + .iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + + if let Err(e) = client.subscribe(target).with_id(id).await { log::error!("Failed to subscribe to gift wrap events: {e}"); } }) @@ -318,8 +324,15 @@ impl DeviceRegistry { .author(public_key) .limit(1); + // Construct target for subscription + let target = urls + .iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + let mut stream = client - .stream_events_from(&urls, vec![filter], Duration::from_secs(TIMEOUT)) + .stream_events(target) + .timeout(Duration::from_secs(TIMEOUT)) .await?; while let Some((_url, res)) = stream.next().await { @@ -368,20 +381,17 @@ impl DeviceRegistry { let n = keys.public_key(); let task: Task> = cx.background_spawn(async move { - let signer = client.signer().await?; let urls = write_relays.await; // Construct an announcement event - let event = EventBuilder::new(Kind::Custom(10044), "") - .tags(vec![ - Tag::custom(TagKind::custom("n"), vec![n]), - Tag::client(app_name()), - ]) - .sign(&signer) - .await?; + let builder = EventBuilder::new(Kind::Custom(10044), "").tags(vec![ + Tag::custom(TagKind::custom("n"), vec![n]), + Tag::client(app_name()), + ]); + let event = client.sign_event_builder(builder).await?; // Publish announcement - client.send_event_to(&urls, &event).await?; + client.send_event(&event).to(urls).await?; // Save device keys to the database Self::set_keys(&client, &secret).await?; @@ -461,8 +471,14 @@ impl DeviceRegistry { .author(public_key) .since(Timestamp::now()); + // Construct target for subscription + let target = urls + .iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + // Subscribe to the device key requests on user's write relays - client.subscribe_to(&urls, vec![filter], None).await?; + client.subscribe(target).await?; Ok(()) }); @@ -487,8 +503,14 @@ impl DeviceRegistry { .author(public_key) .since(Timestamp::now()); + // Construct target for subscription + let target = urls + .iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + // Subscribe to the device key requests on user's write relays - client.subscribe_to(&urls, vec![filter], None).await?; + client.subscribe(target).await?; Ok(()) }); @@ -508,7 +530,7 @@ impl DeviceRegistry { let app_pubkey = app_keys.public_key(); let task: Task, Error>> = cx.background_spawn(async move { - let signer = client.signer().await?; + let signer = client.signer().context("Signer not found")?; let public_key = signer.get_public_key().await?; let filter = Filter::new() @@ -538,16 +560,14 @@ impl DeviceRegistry { let urls = write_relays.await; // Construct an event for device key request - let event = EventBuilder::new(Kind::Custom(4454), "") - .tags(vec![ - Tag::client(app_name()), - Tag::custom(TagKind::custom("P"), vec![app_pubkey]), - ]) - .sign(&signer) - .await?; + let builder = EventBuilder::new(Kind::Custom(4454), "").tags(vec![ + Tag::client(app_name()), + Tag::custom(TagKind::custom("P"), vec![app_pubkey]), + ]); + let event = client.sign_event_builder(builder).await?; // Send the event to write relays - client.send_event_to(&urls, &event).await?; + client.send_event(&event).to(urls).await?; Ok(None) } @@ -625,7 +645,7 @@ impl DeviceRegistry { let task: Task> = cx.background_spawn(async move { let urls = write_relays.await; - let signer = client.signer().await?; + let signer = client.signer().context("Signer not found")?; // Get device keys let keys = Self::get_keys(&client).await?; @@ -646,16 +666,14 @@ impl DeviceRegistry { // // P tag: the current device's public key // p tag: the requester's public key - let event = EventBuilder::new(Kind::Custom(4455), payload) - .tags(vec![ - Tag::custom(TagKind::custom("P"), vec![keys.public_key()]), - Tag::public_key(target), - ]) - .sign(&signer) - .await?; + let builder = EventBuilder::new(Kind::Custom(4455), payload).tags(vec![ + Tag::custom(TagKind::custom("P"), vec![keys.public_key()]), + Tag::public_key(target), + ]); + let event = client.sign_event_builder(builder).await?; // Send the response event to the user's relay list - client.send_event_to(&urls, &event).await?; + client.send_event(&event).to(urls).await?; Ok(()) }); diff --git a/crates/person/src/lib.rs b/crates/person/src/lib.rs index ab20aec..3e1ef3e 100644 --- a/crates/person/src/lib.rs +++ b/crates/person/src/lib.rs @@ -139,20 +139,14 @@ impl PersonRegistry { /// Handle nostr notifications async fn handle_notifications(client: &Client, tx: &flume::Sender) { let mut notifications = client.notifications(); - let mut processed_events = HashSet::new(); - while let Ok(notification) = notifications.recv().await { - let RelayPoolNotification::Message { message, .. } = notification else { + while let Some(notification) = notifications.next().await { + let ClientNotification::Message { message, .. } = notification else { // Skip if the notification is not a message continue; }; if let RelayMessage::Event { event, .. } = message { - if !processed_events.insert(event.id) { - // Skip if the event has already been processed - continue; - } - match event.kind { Kind::Metadata => { let metadata = Metadata::from_json(&event.content).unwrap_or_default(); @@ -230,9 +224,13 @@ impl PersonRegistry { .authors(authors) .limit(limit); - client - .subscribe_to(BOOTSTRAP_RELAYS, filter, Some(opts)) - .await?; + // Construct target for subscription + let target = BOOTSTRAP_RELAYS + .into_iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + + client.subscribe(target).close_on(opts).await?; Ok(()) } diff --git a/crates/relay_auth/src/lib.rs b/crates/relay_auth/src/lib.rs index 99aa2f1..6a88e0c 100644 --- a/crates/relay_auth/src/lib.rs +++ b/crates/relay_auth/src/lib.rs @@ -4,7 +4,7 @@ use std::collections::HashSet; use std::hash::{Hash, Hasher}; use std::rc::Rc; -use anyhow::{anyhow, Error}; +use anyhow::{anyhow, Context as AnyhowContext, Error}; use gpui::{ App, AppContext, Context, Entity, Global, IntoElement, ParentElement, SharedString, Styled, Subscription, Task, Window, @@ -137,8 +137,8 @@ impl RelayAuth { async fn handle_notifications(client: &Client, tx: &flume::Sender) { let mut notifications = client.notifications(); - while let Ok(notification) = notifications.recv().await { - if let RelayPoolNotification::Message { + while let Some(notification) = notifications.next().await { + if let ClientNotification::Message { message: RelayMessage::Auth { challenge }, relay_url, } = notification @@ -184,34 +184,33 @@ impl RelayAuth { let url_clone = url.clone(); let task: Task> = cx.background_spawn(async move { - let signer = client.signer().await?; - // Construct event - let event: Event = EventBuilder::auth(challenge_clone, url_clone.clone()) - .sign(&signer) - .await?; + let builder = EventBuilder::auth(challenge_clone, url_clone.clone()); + let event = client.sign_event_builder(builder).await?; // Get the event ID let id = event.id; // Get the relay - let relay = client.pool().relay(url_clone).await?; + let relay = client.relay(url_clone).await?.context("Relay not found")?; let relay_url = relay.url(); // Subscribe to notifications let mut notifications = relay.notifications(); // Send the AUTH message - relay.send_msg(ClientMessage::Auth(Cow::Borrowed(&event)))?; + relay + .send_msg(ClientMessage::Auth(Cow::Borrowed(&event))) + .await?; - while let Ok(notification) = notifications.recv().await { + while let Some(notification) = notifications.next().await { match notification { RelayNotification::Message { message: RelayMessage::Ok { event_id, .. }, } => { if id == event_id { // Re-subscribe to previous subscription - relay.resubscribe().await?; + // relay.resubscribe().await?; // Get all pending events that need to be resent let mut tracker = tracker().write().await; @@ -228,7 +227,6 @@ impl RelayAuth { } } RelayNotification::AuthenticationFailed => break, - RelayNotification::Shutdown => break, _ => {} } } diff --git a/crates/settings/src/lib.rs b/crates/settings/src/lib.rs index 013880c..9781b8d 100644 --- a/crates/settings/src/lib.rs +++ b/crates/settings/src/lib.rs @@ -136,7 +136,7 @@ impl AppSettings { } fn new(cx: &mut Context) -> Self { - let load_settings = Self::get_from_database(false, cx); + let load_settings = Self::get_from_database(cx); let mut tasks = smallvec![]; let mut subscriptions = smallvec![]; @@ -169,12 +169,10 @@ impl AppSettings { } /// Get settings from the database - /// - /// If `current_user` is true, the settings will be retrieved for current user. - /// Otherwise, Coop will load the latest settings from the database. - fn get_from_database(current_user: bool, cx: &App) -> Task> { + fn get_from_database(cx: &App) -> Task> { let nostr = NostrRegistry::global(cx); let client = nostr.read(cx).client(); + let current_user = nostr.read(cx).identity().read(cx).public_key; cx.background_spawn(async move { // Construct a filter to get the latest settings @@ -183,10 +181,7 @@ impl AppSettings { .identifier(SETTINGS_IDENTIFIER) .limit(1); - if current_user { - let signer = client.signer().await?; - let public_key = signer.get_public_key().await?; - + if let Some(public_key) = current_user { // Push author to the filter filter = filter.author(public_key); } @@ -201,7 +196,7 @@ impl AppSettings { /// Load settings pub fn load(&mut self, cx: &mut Context) { - let task = Self::get_from_database(true, cx); + let task = Self::get_from_database(cx); self._tasks.push( // Run task in the background @@ -221,18 +216,17 @@ impl AppSettings { pub fn save(&mut self, cx: &mut Context) { let nostr = NostrRegistry::global(cx); let client = nostr.read(cx).client(); + let public_key = nostr.read(cx).identity().read(cx).public_key(); if let Ok(content) = serde_json::to_string(&self.values) { let task: Task> = cx.background_spawn(async move { - let signer = client.signer().await?; - let public_key = signer.get_public_key().await?; - let event = EventBuilder::new(Kind::ApplicationSpecificData, content) .tag(Tag::identifier(SETTINGS_IDENTIFIER)) .build(public_key) .sign(&Keys::generate()) .await?; + // Save event to the local database without sending to relays client.database().save_event(&event).await?; Ok(()) diff --git a/crates/state/Cargo.toml b/crates/state/Cargo.toml index abeea36..2d04fc2 100644 --- a/crates/state/Cargo.toml +++ b/crates/state/Cargo.toml @@ -10,6 +10,7 @@ common = { path = "../common" } nostr-sdk.workspace = true nostr-lmdb.workspace = true nostr-connect.workspace = true +nostr-gossip-memory.workspace = true gpui.workspace = true gpui_tokio.workspace = true diff --git a/crates/state/src/identity.rs b/crates/state/src/identity.rs index 81a652b..31bb690 100644 --- a/crates/state/src/identity.rs +++ b/crates/state/src/identity.rs @@ -15,9 +15,9 @@ impl RelayState { } /// Identity -#[derive(Debug, Clone, Default)] +#[derive(Debug, Default)] pub struct Identity { - /// The public key of the account + /// Signer's public key pub public_key: Option, /// Whether the identity is owned by the user diff --git a/crates/state/src/lib.rs b/crates/state/src/lib.rs index 6e34fe4..5fb7df6 100644 --- a/crates/state/src/lib.rs +++ b/crates/state/src/lib.rs @@ -1,5 +1,6 @@ -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::os::unix::fs::PermissionsExt; +use std::sync::Arc; use std::time::Duration; use anyhow::{anyhow, Error}; @@ -14,12 +15,14 @@ mod event; mod gossip; mod identity; mod nip05; +mod signer; pub use device::*; pub use event::*; pub use gossip::*; pub use identity::*; pub use nip05::*; +pub use signer::*; use crate::identity::Identity; @@ -68,6 +71,9 @@ pub struct NostrRegistry { /// Nostr client client: Client, + /// Nostr signer + signer: Arc, + /// App keys /// /// Used for Nostr Connect and NIP-4e operations @@ -108,14 +114,6 @@ impl NostrRegistry { .install_default() .ok(); - // Construct the nostr client options - let opts = ClientOptions::new() - .automatic_authentication(false) - .verify_subscriptions(false) - .sleep_when_idle(SleepWhenIdle::Enabled { - timeout: Duration::from_secs(600), - }); - // Construct the lmdb let lmdb = cx.foreground_executor().block_on(async move { NostrLmdb::open(config_dir().join("nostr")) @@ -123,9 +121,23 @@ impl NostrRegistry { .expect("Failed to initialize database") }); + // Construct the nostr signer + let keys = Keys::generate(); + let signer = Arc::new(CoopSigner::new(keys)); + // Construct the nostr client - let client = ClientBuilder::default().database(lmdb).opts(opts).build(); - let _ = tracker(); + let client = ClientBuilder::default() + .signer(signer.clone()) + .database(lmdb) + .automatic_authentication(false) + .verify_subscriptions(false) + .sleep_when_idle(SleepWhenIdle::Enabled { + timeout: Duration::from_secs(600), + }) + .build(); + + // Construct the event tracker + let _tracker = tracker(); // Get the app keys let app_keys = Self::create_or_init_app_keys().unwrap(); @@ -135,7 +147,7 @@ impl NostrRegistry { let async_gossip = gossip.downgrade(); // Construct the identity entity - let identity = cx.new(|_| Identity::default()); + let identity = cx.new(|_| Identity::new()); // Channel for communication between nostr and gpui let (tx, rx) = flume::bounded::(2048); @@ -207,6 +219,7 @@ impl NostrRegistry { Self { client, + signer, app_keys, identity, gossip, @@ -239,8 +252,8 @@ impl NostrRegistry { let mut notifications = client.notifications(); let mut processed_events = HashSet::new(); - while let Ok(notification) = notifications.recv().await { - if let RelayPoolNotification::Message { message, relay_url } = notification { + while let Some(notification) = notifications.next().await { + if let ClientNotification::Message { message, relay_url } = notification { match message { RelayMessage::Event { event, @@ -325,9 +338,13 @@ impl NostrRegistry { .author(event.pubkey) .limit(1); - client - .subscribe_to(write_relays, vec![inbox, announcement], Some(opts)) - .await?; + // Construct target for subscription + let target = write_relays + .into_iter() + .map(|relay| (relay, vec![inbox.clone(), announcement.clone()])) + .collect::>(); + + client.subscribe(target).close_on(opts).await?; Ok(()) } @@ -433,21 +450,21 @@ impl NostrRegistry { } /// Set the signer for the nostr client and verify the public key - pub fn set_signer(&mut self, signer: T, owned: bool, cx: &mut Context) + pub fn set_signer(&mut self, new: T, owned: bool, cx: &mut Context) where T: NostrSigner + 'static, { - let client = self.client(); let identity = self.identity.downgrade(); + let signer = self.signer.clone(); // Create a task to update the signer and verify the public key let task: Task> = cx.background_spawn(async move { // Update signer - client.set_signer(signer).await; + signer.switch(new).await; // Verify signer - let signer = client.signer().await?; let public_key = signer.get_public_key().await?; + log::info!("test: {public_key:?}"); Ok(public_key) }); @@ -471,31 +488,6 @@ impl NostrRegistry { })); } - /// Unset the current signer - pub fn unset_signer(&mut self, cx: &mut Context) { - let client = self.client(); - let async_identity = self.identity.downgrade(); - - self.tasks.push(cx.spawn(async move |_this, cx| { - // Unset the signer from nostr client - cx.background_executor() - .await_on_background(async move { - client.unset_signer().await; - }) - .await; - - // Unset the current identity - async_identity - .update(cx, |this, cx| { - this.unset_public_key(); - cx.notify(); - }) - .ok(); - - Ok(()) - })); - } - // Get relay list for current user fn get_relay_list(&mut self, cx: &mut Context) { let client = self.client(); @@ -508,8 +500,15 @@ impl NostrRegistry { .author(public_key) .limit(1); + // Construct targets for subscription + let target = BOOTSTRAP_RELAYS + .into_iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + let mut stream = client - .stream_events_from(BOOTSTRAP_RELAYS, vec![filter], Duration::from_secs(TIMEOUT)) + .stream_events(target) + .timeout(Duration::from_secs(TIMEOUT)) .await?; while let Some((_url, res)) = stream.next().await { @@ -523,10 +522,14 @@ impl NostrRegistry { .author(public_key) .since(Timestamp::now()); + // Construct targets for subscription + let target = BOOTSTRAP_RELAYS + .into_iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + // Subscribe to the relay list events - client - .subscribe_to(BOOTSTRAP_RELAYS, vec![filter], None) - .await?; + client.subscribe(target).await?; return Ok(RelayState::Set); } @@ -565,8 +568,7 @@ impl NostrRegistry { let write_relays = self.write_relays(&public_key, cx); let task: Task> = cx.background_spawn(async move { - let mut urls = vec![]; - urls.extend(write_relays.await); + let mut urls = write_relays.await; urls.extend( BOOTSTRAP_RELAYS .iter() @@ -590,9 +592,13 @@ impl NostrRegistry { .limit(1) .author(public_key); - client - .subscribe_to(urls, vec![metadata, contact_list], Some(opts)) - .await?; + // Construct targets for subscription + let target = urls + .into_iter() + .map(|relay| (relay, vec![metadata.clone(), contact_list.clone()])) + .collect::>(); + + client.subscribe(target).close_on(opts).await?; Ok(()) }); @@ -616,9 +622,16 @@ impl NostrRegistry { .author(public_key) .limit(1); + // Construct targets for subscription + let target = urls + .iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + // Stream events from the write relays let mut stream = client - .stream_events_from(&urls, vec![filter], Duration::from_secs(TIMEOUT)) + .stream_events(target) + .timeout(Duration::from_secs(TIMEOUT)) .await?; while let Some((_url, res)) = stream.next().await { @@ -632,8 +645,14 @@ impl NostrRegistry { .author(public_key) .since(Timestamp::now()); + // Construct targets for subscription + let target = urls + .iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + // Subscribe to the relay list events - client.subscribe_to(&urls, vec![filter], None).await?; + client.subscribe(target).await?; return Ok(RelayState::Set); } @@ -687,13 +706,13 @@ impl NostrRegistry { cx.background_spawn(async move { let urls = write_relays.await; - let signer = client.signer().await?; - // Sign the new metadata event - let event = EventBuilder::metadata(&metadata).sign(&signer).await?; + // Build and sign the metadata event + let builder = EventBuilder::metadata(&metadata); + let event = client.sign_event_builder(builder).await?; // Send event to user's write relayss - client.send_event_to(urls, &event).await?; + client.send_event(&event).to(urls).await?; Ok(()) }) @@ -728,9 +747,6 @@ impl NostrRegistry { /// Create a new identity fn create_identity(&mut self, cx: &mut Context) { - let client = self.client(); - - // Generate new keys let keys = Keys::generate(); // Get write credential task @@ -743,23 +759,25 @@ impl NostrRegistry { // Update the signer self.set_signer(keys, false, cx); - // Spawn a task to set metadata and write the credentials + // Generate a unique name and avatar for the identity + let name = petname::petname(2, "-").unwrap_or("Cooper".to_string()); + let avatar = Url::parse(DEFAULT_AVATAR).unwrap(); + + // Construct metadata for the identity + let metadata = Metadata::new() + .display_name(&name) + .name(&name) + .picture(avatar); + + // Update user's metadata + let task = self.set_metadata(&metadata, cx); + + // Spawn a task to write the credentials cx.background_spawn(async move { - let name = petname::petname(2, "-").unwrap_or("Cooper".to_string()); - let avatar = Url::parse(DEFAULT_AVATAR).unwrap(); - - // Construct metadata for the identity - let metadata = Metadata::new() - .display_name(&name) - .name(&name) - .picture(avatar); - - // Set metadata for the identity - if let Err(e) = client.set_metadata(&metadata).await { - log::error!("Failed to set metadata: {}", e); + if let Err(e) = task.await { + log::error!("Failed to update metadata: {}", e); } - // Write the credentials if let Err(e) = write_credential.await { log::error!("Failed to write credentials: {}", e); } @@ -874,10 +892,13 @@ impl NostrRegistry { .author(public_key) .limit(1); - // Subscribe to bootstrap relays - client - .subscribe_to(BOOTSTRAP_RELAYS, vec![filter], Some(opts)) - .await?; + // Construct target for subscription + let target = BOOTSTRAP_RELAYS + .into_iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + + client.subscribe(target).close_on(opts).await?; Ok(public_key) }) @@ -897,9 +918,16 @@ impl NostrRegistry { .kind(Kind::Metadata) .limit(FIND_LIMIT); + // Construct target for subscription + let target = SEARCH_RELAYS + .into_iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + // Stream events from the search relays let mut stream = client - .stream_events_from(SEARCH_RELAYS, vec![filter], Duration::from_secs(3)) + .stream_events(target) + .timeout(Duration::from_secs(TIMEOUT)) .await?; // Collect the results @@ -923,28 +951,31 @@ impl NostrRegistry { let query = query.to_string(); cx.background_spawn(async move { - let signer = client.signer().await?; - // Construct a vertex request event - let event = EventBuilder::new(Kind::Custom(5315), "") - .tags(vec![ - Tag::custom(TagKind::custom("param"), vec!["search", &query]), - Tag::custom(TagKind::custom("param"), vec!["limit", "10"]), - ]) - .sign(&signer) - .await?; + let builder = EventBuilder::new(Kind::Custom(5315), "").tags(vec![ + Tag::custom(TagKind::custom("param"), vec!["search", &query]), + Tag::custom(TagKind::custom("param"), vec!["limit", "10"]), + ]); + let event = client.sign_event_builder(builder).await?; // Send the event to vertex relays - let output = client.send_event_to(WOT_RELAYS, &event).await?; + let output = client.send_event(&event).to(WOT_RELAYS).await?; // Construct a filter to get the response or error from vertex let filter = Filter::new() .kinds(vec![Kind::Custom(6315), Kind::Custom(7000)]) .event(output.id().to_owned()); - // Stream events from the search relays + // Construct target for subscription + let target = WOT_RELAYS + .into_iter() + .map(|relay| (relay, vec![filter.clone()])) + .collect::>(); + + // Stream events from the wot relays let mut stream = client - .stream_events_from(WOT_RELAYS, vec![filter], Duration::from_secs(3)) + .stream_events(target) + .timeout(Duration::from_secs(TIMEOUT)) .await?; while let Some((_url, res)) = stream.next().await { diff --git a/crates/state/src/signer.rs b/crates/state/src/signer.rs new file mode 100644 index 0000000..618df95 --- /dev/null +++ b/crates/state/src/signer.rs @@ -0,0 +1,88 @@ +use std::borrow::Cow; +use std::sync::Arc; + +use nostr_sdk::prelude::*; +use smol::lock::RwLock; + +#[derive(Debug)] +pub struct CoopSigner { + signer: RwLock>, +} + +impl CoopSigner { + pub fn new(signer: T) -> Self + where + T: IntoNostrSigner, + { + Self { + signer: RwLock::new(signer.into_nostr_signer()), + } + } + + async fn get(&self) -> Arc { + self.signer.read().await.clone() + } + + pub async fn switch(&self, new: T) + where + T: IntoNostrSigner, + { + let mut signer = self.signer.write().await; + *signer = new.into_nostr_signer(); + } +} + +impl NostrSigner for CoopSigner { + fn backend(&self) -> SignerBackend { + SignerBackend::Custom(Cow::Borrowed("custom")) + } + + fn get_public_key(&self) -> BoxedFuture> { + Box::pin(async move { Ok(self.get().await.get_public_key().await?) }) + } + + fn sign_event( + &self, + unsigned: UnsignedEvent, + ) -> BoxedFuture> { + Box::pin(async move { Ok(self.get().await.sign_event(unsigned).await?) }) + } + + fn nip04_encrypt<'a>( + &'a self, + public_key: &'a PublicKey, + content: &'a str, + ) -> BoxedFuture<'a, std::result::Result> { + Box::pin(async move { Ok(self.get().await.nip04_encrypt(public_key, content).await?) }) + } + + fn nip04_decrypt<'a>( + &'a self, + public_key: &'a PublicKey, + encrypted_content: &'a str, + ) -> BoxedFuture<'a, std::result::Result> { + Box::pin(async move { + Ok(self + .get() + .await + .nip04_decrypt(public_key, encrypted_content) + .await?) + }) + } + + fn nip44_encrypt<'a>( + &'a self, + public_key: &'a PublicKey, + content: &'a str, + ) -> BoxedFuture<'a, std::result::Result> { + Box::pin(async move { Ok(self.get().await.nip44_encrypt(public_key, content).await?) }) + } + + fn nip44_decrypt<'a>( + &'a self, + public_key: &'a PublicKey, + payload: &'a str, + ) -> BoxedFuture<'a, std::result::Result> { + Box::pin(async move { Ok(self.get().await.nip44_decrypt(public_key, payload).await?) }) + } +} diff --git a/crates/ui/src/menu/context_menu.rs b/crates/ui/src/menu/context_menu.rs index a371e84..2984adc 100644 --- a/crates/ui/src/menu/context_menu.rs +++ b/crates/ui/src/menu/context_menu.rs @@ -246,8 +246,7 @@ impl Element for ContextMenu { let menu = PopupMenu::build(window, cx, |menu, window, cx| { (builder)(menu, window, cx) - }) - .into_element(); + }); let _subscription = window.subscribe(&menu, cx, { let shared_state = shared_state.clone();