From b7ffdc8431bf582acffb07c619c96a45a8570e56 Mon Sep 17 00:00:00 2001 From: reya Date: Thu, 26 Feb 2026 18:28:24 +0700 Subject: [PATCH] . --- Cargo.lock | 90 +++++++++++++- Cargo.toml | 1 + crates/chat/src/lib.rs | 9 +- crates/chat/src/room.rs | 29 ++--- crates/coop/src/panels/messaging_relays.rs | 19 +-- crates/coop/src/workspace.rs | 41 ------- crates/device/src/lib.rs | 72 ++++------- crates/state/Cargo.toml | 1 + crates/state/src/constants.rs | 2 +- crates/state/src/gossip.rs | 83 ------------- crates/state/src/lib.rs | 131 ++++----------------- 11 files changed, 144 insertions(+), 334 deletions(-) delete mode 100644 crates/state/src/gossip.rs diff --git a/Cargo.lock b/Cargo.lock index bdd5d5f..8270456 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1912,7 +1912,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1976,6 +1976,18 @@ dependencies = [ "zune-inflate", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fastrand" version = "1.9.0" @@ -3589,6 +3601,17 @@ dependencies = [ "redox_syscall 0.7.2", ] +[[package]] +name = "libsqlite3-sys" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b4103cffefa72eb8428cb6b47d6627161e51c2739fc5e3b734584157bc642a" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linebender_resource_handle" version = "0.1.1" @@ -4095,6 +4118,18 @@ dependencies = [ "nostr", ] +[[package]] +name = "nostr-gossip-sqlite" +version = "0.44.0" +source = "git+https://github.com/rust-nostr/nostr#b1ac65997d05424ad7b888dbdb5214b8999924ff" +dependencies = [ + "async-utility", + "nostr", + "nostr-gossip", + "rusqlite", + "tokio", +] + [[package]] name = "nostr-lmdb" version = "0.44.0" @@ -4144,7 +4179,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -4977,7 +5012,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -5396,6 +5431,30 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" +[[package]] +name = "rsqlite-vfs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a1f2315036ef6b1fbacd1972e8ee7688030b0a2121edfc2a6550febd41574d" +dependencies = [ + "hashbrown 0.16.1", + "thiserror 2.0.18", +] + +[[package]] +name = "rusqlite" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1c93dd1c9683b438c392c492109cb702b8090b2bfc8fed6f6e4eb4523f17af3" +dependencies = [ + "bitflags 2.11.0", + "fallible-iterator", + "fallible-streaming-iterator", + "libsqlite3-sys", + "smallvec", + "sqlite-wasm-rs", +] + [[package]] name = "rust-embed" version = "8.11.0" @@ -5491,7 +5550,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -6117,6 +6176,18 @@ dependencies = [ "bitflags 2.11.0", ] +[[package]] +name = "sqlite-wasm-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4206ed3a67690b9c29b77d728f6acc3ce78f16bf846d83c94f76400320181b" +dependencies = [ + "cc", + "js-sys", + "rsqlite-vfs", + "wasm-bindgen", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -6171,6 +6242,7 @@ dependencies = [ "nostr", "nostr-blossom", "nostr-connect", + "nostr-gossip-sqlite", "nostr-lmdb", "nostr-sdk", "petname", @@ -6471,7 +6543,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -7287,6 +7359,12 @@ dependencies = [ "sval_serde", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -7845,7 +7923,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 337a929..06664d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,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-blossom = { git = "https://github.com/rust-nostr/nostr" } +nostr-gossip-sqlite = { git = "https://github.com/rust-nostr/nostr" } nostr-sdk = { git = "https://github.com/rust-nostr/nostr" } nostr = { git = "https://github.com/rust-nostr/nostr", features = [ "nip96", "nip59", "nip49", "nip44" ] } diff --git a/crates/chat/src/lib.rs b/crates/chat/src/lib.rs index 65c2cbb..e44e36f 100644 --- a/crates/chat/src/lib.rs +++ b/crates/chat/src/lib.rs @@ -285,24 +285,17 @@ impl ChatRegistry { let signer = nostr.read(cx).signer(); let public_key = signer.public_key().unwrap(); - let write_relays = nostr.read(cx).write_relays(&public_key, cx); cx.background_spawn(async move { - let urls = write_relays.await; - // Construct filter for inbox relays let filter = Filter::new() .kind(Kind::InboxRelays) .author(public_key) .limit(1); - // Construct target for subscription - let target: HashMap<&RelayUrl, Filter> = - urls.iter().map(|relay| (relay, filter.clone())).collect(); - // Stream events from user's write relays let mut stream = client - .stream_events(target) + .stream_events(filter) .timeout(Duration::from_secs(TIMEOUT)) .await?; diff --git a/crates/chat/src/room.rs b/crates/chat/src/room.rs index 55a1915..ca34ad7 100644 --- a/crates/chat/src/room.rs +++ b/crates/chat/src/room.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::hash::{Hash, Hasher}; use std::time::Duration; -use anyhow::{anyhow, Error}; +use anyhow::Error; use common::EventUtils; use gpui::{App, AppContext, Context, EventEmitter, SharedString, Task}; use itertools::Itertools; @@ -323,6 +323,7 @@ impl Room { /// Get gossip relays for each member pub fn connect(&self, cx: &App) -> HashMap>> { let nostr = NostrRegistry::global(cx); + let signer = nostr.read(cx).signer(); let public_key = signer.public_key().unwrap(); @@ -330,25 +331,18 @@ impl Room { let mut tasks = HashMap::new(); for member in members.into_iter() { + let client = nostr.read(cx).client(); + // Skip if member is the current user if member == public_key { continue; } - let client = nostr.read(cx).client(); - let write_relays = nostr.read(cx).write_relays(&member, cx); - tasks.insert( member, cx.background_spawn(async move { - let urls = write_relays.await; - - // Return if no relays are available - if urls.is_empty() { - return Err(anyhow!( - "User has not set up any relays. You cannot send messages to them." - )); - } + let mut has_inbox = false; + let mut has_announcement = false; // Construct filters for inbox let inbox = Filter::new() @@ -362,21 +356,12 @@ impl Room { .author(member) .limit(1); - // Create subscription targets - let target: HashMap> = urls - .into_iter() - .map(|relay| (relay, vec![inbox.clone(), announcement.clone()])) - .collect(); - // Stream events from user's write relays let mut stream = client - .stream_events(target) + .stream_events(vec![inbox.clone(), announcement.clone()]) .timeout(Duration::from_secs(TIMEOUT)) .await?; - let mut has_inbox = false; - let mut has_announcement = false; - while let Some((_url, res)) = stream.next().await { let event = res?; diff --git a/crates/coop/src/panels/messaging_relays.rs b/crates/coop/src/panels/messaging_relays.rs index ad88825..caad3df 100644 --- a/crates/coop/src/panels/messaging_relays.rs +++ b/crates/coop/src/panels/messaging_relays.rs @@ -10,7 +10,7 @@ use gpui::{ }; use nostr_sdk::prelude::*; use smallvec::{smallvec, SmallVec}; -use state::NostrRegistry; +use state::{NostrRegistry, TIMEOUT}; use theme::ActiveTheme; use ui::button::{Button, ButtonVariants}; use ui::dock_area::panel::{Panel, PanelEvent}; @@ -170,15 +170,6 @@ impl MessagingRelayPanel { let nostr = NostrRegistry::global(cx); let client = nostr.read(cx).client(); - let signer = nostr.read(cx).signer(); - - let Some(public_key) = signer.public_key() else { - window.push_notification("Public Key not found", cx); - return; - }; - - // Get user's write relays - let write_relays = nostr.read(cx).write_relays(&public_key, cx); // Construct event tags let tags: Vec = self @@ -191,14 +182,16 @@ impl MessagingRelayPanel { self.set_updating(true, cx); let task: Task> = cx.background_spawn(async move { - let urls = write_relays.await; - // Construct nip17 event builder let builder = EventBuilder::new(Kind::InboxRelays, "").tags(tags); let event = client.sign_event_builder(builder).await?; // Set messaging relays - client.send_event(&event).to(urls).await?; + client + .send_event(&event) + .to_nip65() + .ok_timeout(Duration::from_secs(TIMEOUT)) + .await?; Ok(()) }); diff --git a/crates/coop/src/workspace.rs b/crates/coop/src/workspace.rs index fb887a1..e52bdee 100644 --- a/crates/coop/src/workspace.rs +++ b/crates/coop/src/workspace.rs @@ -582,47 +582,6 @@ impl Workspace { .dropdown_menu(move |this, _window, _cx| { this.min_w(px(260.)) .label("Relays") - .menu_element_with_disabled( - Box::new(Command::ShowRelayList), - true, - move |_window, cx| { - let nostr = NostrRegistry::global(cx); - let urls = nostr.read(cx).read_only_relays(&pkey, cx); - - v_flex() - .gap_1() - .w_full() - .items_start() - .justify_start() - .children({ - let mut items = vec![]; - - for url in urls.into_iter() { - items.push( - h_flex() - .h_6() - .w_full() - .gap_2() - .px_2() - .text_xs() - .bg(cx - .theme() - .elevated_surface_background) - .rounded(cx.theme().radius) - .child( - div() - .size_1() - .rounded_full() - .bg(gpui::green()), - ) - .child(url), - ); - } - - items - }) - }, - ) .separator() .menu_with_icon( "Reload", diff --git a/crates/device/src/lib.rs b/crates/device/src/lib.rs index 876e1a8..c0eb0b9 100644 --- a/crates/device/src/lib.rs +++ b/crates/device/src/lib.rs @@ -255,24 +255,16 @@ impl DeviceRegistry { let signer = nostr.read(cx).signer(); let public_key = signer.public_key().unwrap(); - let write_relays = nostr.read(cx).write_relays(&public_key, cx); - let task: Task> = cx.background_spawn(async move { - let urls = write_relays.await; - // Construct the filter for the device announcement event let filter = Filter::new() .kind(Kind::Custom(10044)) .author(public_key) .limit(1); - // Construct target for subscription - let target: HashMap<&RelayUrl, Filter> = - urls.iter().map(|relay| (relay, filter.clone())).collect(); - // Stream events from user's write relays let mut stream = client - .stream_events(target) + .stream_events(filter) .timeout(Duration::from_secs(TIMEOUT)) .await?; @@ -313,20 +305,12 @@ impl DeviceRegistry { let nostr = NostrRegistry::global(cx); let client = nostr.read(cx).client(); - // Get current user - let signer = nostr.read(cx).signer(); - let public_key = signer.public_key().unwrap(); - - // Get user's write relays - let write_relays = nostr.read(cx).write_relays(&public_key, cx); - + // Generate encryption keys let keys = Keys::generate(); let secret = keys.secret_key().to_secret_hex(); let n = keys.public_key(); cx.background_spawn(async move { - let urls = write_relays.await; - // Construct an announcement event let event = client .sign_event_builder(EventBuilder::new(Kind::Custom(10044), "").tags(vec![ @@ -336,7 +320,11 @@ impl DeviceRegistry { .await?; // Publish announcement - client.send_event(&event).to(urls).await?; + client + .send_event(&event) + .to_nip65() + .ok_timeout(Duration::from_secs(TIMEOUT)) + .await?; // Save device keys to the database set_keys(&client, &secret).await?; @@ -416,23 +404,15 @@ impl DeviceRegistry { let signer = nostr.read(cx).signer(); let public_key = signer.public_key().unwrap(); - let write_relays = nostr.read(cx).write_relays(&public_key, cx); - let task: Task> = cx.background_spawn(async move { - let urls = write_relays.await; - // Construct a filter for device key requests let filter = Filter::new() .kind(Kind::Custom(4454)) .author(public_key) .since(Timestamp::now()); - // Construct target for subscription - let target: HashMap<&RelayUrl, Filter> = - urls.iter().map(|relay| (relay, filter.clone())).collect(); - // Subscribe to the device key requests on user's write relays - client.subscribe(target).await?; + client.subscribe(filter).await?; Ok(()) }); @@ -448,23 +428,15 @@ impl DeviceRegistry { let signer = nostr.read(cx).signer(); let public_key = signer.public_key().unwrap(); - let write_relays = nostr.read(cx).write_relays(&public_key, cx); - self.tasks.push(cx.background_spawn(async move { - let urls = write_relays.await; - // Construct a filter for device key requests let filter = Filter::new() .kind(Kind::Custom(4455)) .author(public_key) .since(Timestamp::now()); - // Construct target for subscription - let target: HashMap<&RelayUrl, Filter> = - urls.iter().map(|relay| (relay, filter.clone())).collect(); - // Subscribe to the device key requests on user's write relays - client.subscribe(target).await?; + client.subscribe(filter).await?; Ok(()) })); @@ -474,11 +446,7 @@ impl DeviceRegistry { fn request(&mut self, cx: &mut Context) { let nostr = NostrRegistry::global(cx); let client = nostr.read(cx).client(); - let signer = nostr.read(cx).signer(); - let public_key = signer.public_key().unwrap(); - - let write_relays = nostr.read(cx).write_relays(&public_key, cx); let app_keys = nostr.read(cx).app_keys().clone(); let app_pubkey = app_keys.public_key(); @@ -510,18 +478,20 @@ impl DeviceRegistry { Ok(Some(keys)) } None => { - let urls = write_relays.await; - // Construct an event for device key request let event = client .sign_event_builder(EventBuilder::new(Kind::Custom(4454), "").tags(vec![ - Tag::client(app_name()), Tag::custom(TagKind::custom("P"), vec![app_pubkey]), + Tag::client(app_name()), ])) .await?; // Send the event to write relays - client.send_event(&event).to(urls).await?; + client + .send_event(&event) + .to_nip65() + .ok_timeout(Duration::from_secs(TIMEOUT)) + .await?; Ok(None) } @@ -588,18 +558,12 @@ impl DeviceRegistry { pub fn approve(&self, event: &Event, cx: &App) -> Task> { let nostr = NostrRegistry::global(cx); let client = nostr.read(cx).client(); - - // Get current user let signer = nostr.read(cx).signer(); - let public_key = signer.public_key().unwrap(); // Get user's write relays - let write_relays = nostr.read(cx).write_relays(&public_key, cx); let event = event.clone(); cx.background_spawn(async move { - let urls = write_relays.await; - // Get device keys let keys = get_keys(&client).await?; let secret = keys.secret_key().to_secret_hex(); @@ -627,7 +591,11 @@ impl DeviceRegistry { .await?; // Send the response event to the user's relay list - client.send_event(&event).to(urls).await?; + client + .send_event(&event) + .to_nip65() + .ok_timeout(Duration::from_secs(TIMEOUT)) + .await?; Ok(()) }) diff --git a/crates/state/Cargo.toml b/crates/state/Cargo.toml index fcedd0d..06bb079 100644 --- a/crates/state/Cargo.toml +++ b/crates/state/Cargo.toml @@ -11,6 +11,7 @@ nostr.workspace = true nostr-sdk.workspace = true nostr-lmdb.workspace = true nostr-connect.workspace = true +nostr-gossip-sqlite.workspace = true nostr-blossom.workspace = true gpui.workspace = true diff --git a/crates/state/src/constants.rs b/crates/state/src/constants.rs index 3357b2d..d4db28d 100644 --- a/crates/state/src/constants.rs +++ b/crates/state/src/constants.rs @@ -12,7 +12,7 @@ pub const APP_ID: &str = "su.reya.coop"; /// Keyring name pub const KEYRING: &str = "Coop Safe Storage"; -/// Default timeout for subscription +/// Default timeout in second for subscription pub const TIMEOUT: u64 = 2; /// Default delay for searching diff --git a/crates/state/src/gossip.rs b/crates/state/src/gossip.rs deleted file mode 100644 index 805a1ac..0000000 --- a/crates/state/src/gossip.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use gpui::SharedString; -use nostr_sdk::prelude::*; - -/// Gossip -#[derive(Debug, Clone, Default)] -pub struct Gossip { - relays: HashMap)>>, -} - -impl Gossip { - pub fn read_only_relays(&self, public_key: &PublicKey) -> Vec { - self.relays - .get(public_key) - .map(|relays| { - relays - .iter() - .map(|(url, _)| url.to_string().into()) - .collect() - }) - .unwrap_or_default() - } - - /// Get read relays for a given public key - pub fn read_relays(&self, public_key: &PublicKey) -> Vec { - self.relays - .get(public_key) - .map(|relays| { - relays - .iter() - .filter_map(|(url, metadata)| { - if metadata.is_none() || metadata == &Some(RelayMetadata::Read) { - Some(url.to_owned()) - } else { - None - } - }) - .collect() - }) - .unwrap_or_default() - } - - /// Get write relays for a given public key - pub fn write_relays(&self, public_key: &PublicKey) -> Vec { - self.relays - .get(public_key) - .map(|relays| { - relays - .iter() - .filter_map(|(url, metadata)| { - if metadata.is_none() || metadata == &Some(RelayMetadata::Write) { - Some(url.to_owned()) - } else { - None - } - }) - .collect() - }) - .unwrap_or_default() - } - - /// Insert gossip relays for a public key - pub fn insert_relays(&mut self, event: &Event) { - self.relays.entry(event.pubkey).or_default().extend( - event - .tags - .iter() - .filter_map(|tag| { - if let Some(TagStandard::RelayMetadata { - relay_url, - metadata, - }) = tag.clone().to_standardized() - { - Some((relay_url, metadata)) - } else { - None - } - }) - .take(3), - ); - } -} diff --git a/crates/state/src/lib.rs b/crates/state/src/lib.rs index fb5466c..1c97311 100644 --- a/crates/state/src/lib.rs +++ b/crates/state/src/lib.rs @@ -1,26 +1,25 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::os::unix::fs::PermissionsExt; use std::sync::Arc; use std::time::Duration; use anyhow::{anyhow, Context as AnyhowContext, Error}; use common::config_dir; -use gpui::{App, AppContext, Context, Entity, Global, SharedString, Task, Window}; +use gpui::{App, AppContext, Context, Entity, Global, Task, Window}; use nostr_connect::prelude::*; +use nostr_gossip_sqlite::prelude::*; use nostr_lmdb::prelude::*; use nostr_sdk::prelude::*; mod blossom; mod constants; mod device; -mod gossip; mod nip05; mod signer; pub use blossom::*; pub use constants::*; pub use device::*; -pub use gossip::*; pub use nip05::*; pub use signer::*; @@ -56,9 +55,6 @@ pub struct NostrRegistry { /// Used for Nostr Connect and NIP-4e operations app_keys: Keys, - /// Custom gossip implementation - gossip: Entity, - /// Relay list state relay_list_state: RelayState, @@ -89,9 +85,6 @@ impl NostrRegistry { let app_keys = get_or_init_app_keys().unwrap_or(Keys::generate()); let signer = Arc::new(CoopSigner::new(app_keys.clone())); - // Construct the gossip entity - let gossip = cx.new(|_| Gossip::default()); - // Construct the nostr lmdb instance let lmdb = cx.foreground_executor().block_on(async move { NostrLmdb::open(config_dir().join("nostr")) @@ -99,13 +92,21 @@ impl NostrRegistry { .expect("Failed to initialize database") }); + // Construct the nostr gossip instance + let gossip = cx.foreground_executor().block_on(async move { + NostrGossipSqlite::open(config_dir().join("gossip")) + .await + .expect("Failed to initialize gossip database") + }); + // Construct the nostr client let client = ClientBuilder::default() .signer(signer.clone()) .database(lmdb) + .gossip(gossip) .automatic_authentication(false) .verify_subscriptions(false) - .connect_timeout(Duration::from_secs(TIMEOUT)) + .connect_timeout(Duration::from_secs(5)) .sleep_when_idle(SleepWhenIdle::Enabled { timeout: Duration::from_secs(600), }) @@ -114,14 +115,12 @@ impl NostrRegistry { // Run at the end of current cycle cx.defer_in(window, |this, _window, cx| { this.connect(cx); - this.handle_notifications(cx); }); Self { client, signer, app_keys, - gossip, relay_list_state: RelayState::Idle, connected: false, creating: false, @@ -146,10 +145,7 @@ impl NostrRegistry { } // Connect to all added relays - client - .connect() - .and_wait(Duration::from_secs(TIMEOUT)) - .await; + client.connect().and_wait(Duration::from_secs(5)).await; }) .await; @@ -163,60 +159,6 @@ impl NostrRegistry { })); } - /// Handle nostr notifications - fn handle_notifications(&mut self, cx: &mut Context) { - let client = self.client(); - let gossip = self.gossip.downgrade(); - - // Channel for communication between nostr and gpui - let (tx, rx) = flume::bounded::(2048); - - let task: Task> = cx.background_spawn(async move { - // Handle nostr notifications - let mut notifications = client.notifications(); - let mut processed_events = HashSet::new(); - - while let Some(notification) = notifications.next().await { - if let ClientNotification::Message { - message: RelayMessage::Event { event, .. }, - .. - } = notification - { - // Skip if the event has already been processed - if processed_events.insert(event.id) { - match event.kind { - Kind::RelayList => { - tx.send_async(event.into_owned()).await?; - } - Kind::InboxRelays => { - tx.send_async(event.into_owned()).await?; - } - _ => {} - } - } - } - } - - Ok(()) - }); - - // Run task in the background - task.detach(); - - self.tasks.push(cx.spawn(async move |_this, cx| { - while let Ok(event) = rx.recv_async().await { - if let Kind::RelayList = event.kind { - gossip.update(cx, |this, cx| { - this.insert_relays(&event); - cx.notify(); - })?; - } - } - - Ok(()) - })); - } - /// Get the nostr client pub fn client(&self) -> Client { self.client.clone() @@ -247,41 +189,6 @@ impl NostrRegistry { self.relay_list_state.clone() } - /// Get all relays for a given public key without ensuring connections - pub fn read_only_relays(&self, public_key: &PublicKey, cx: &App) -> Vec { - self.gossip.read(cx).read_only_relays(public_key) - } - - /// Get a list of write relays for a given public key - pub fn write_relays(&self, public_key: &PublicKey, cx: &App) -> Task> { - let client = self.client(); - let relays = self.gossip.read(cx).write_relays(public_key); - - cx.background_spawn(async move { - // Ensure relay connections - for url in relays.iter() { - client.add_relay(url).and_connect().await.ok(); - } - - relays - }) - } - - /// Get a list of read relays for a given public key - pub fn read_relays(&self, public_key: &PublicKey, cx: &App) -> Task> { - let client = self.client(); - let relays = self.gossip.read(cx).read_relays(public_key); - - cx.background_spawn(async move { - // Ensure relay connections - for url in relays.iter() { - client.add_relay(url).and_connect().await.ok(); - } - - relays - }) - } - /// Set the connected status of the client fn set_connected(&mut self, cx: &mut Context) { self.connected = true; @@ -488,16 +395,24 @@ impl NostrRegistry { cx.notify(); } + /// Set relay list state + fn set_relay_list_state(&mut self, state: RelayState, cx: &mut Context) { + self.relay_list_state = state; + cx.notify(); + } + pub fn ensure_relay_list(&mut self, cx: &mut Context) { let task = self.verify_relay_list(cx); + // Reset state + self.set_relay_list_state(RelayState::default(), cx); + self.tasks.push(cx.spawn(async move |this, cx| { let result = task.await?; // Update state this.update(cx, |this, cx| { - this.relay_list_state = result; - cx.notify(); + this.set_relay_list_state(result, cx); })?; Ok(())