From 739088b669f642c1cf51bb9e3529461629dc1617 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Sat, 14 Mar 2026 15:17:11 +0700 Subject: [PATCH] ensure relay connection --- crates/chat/src/lib.rs | 18 +++++--- crates/coop/src/workspace.rs | 8 ++++ crates/device/src/lib.rs | 25 +++++----- crates/state/src/lib.rs | 90 ++++++++++++++++++++++++++++++++---- 4 files changed, 115 insertions(+), 26 deletions(-) diff --git a/crates/chat/src/lib.rs b/crates/chat/src/lib.rs index 6c90ea0..361dd96 100644 --- a/crates/chat/src/lib.rs +++ b/crates/chat/src/lib.rs @@ -105,12 +105,17 @@ impl ChatRegistry { subscriptions.push( // Subscribe to the signer event cx.subscribe(&nostr, |this, _state, event, cx| { - if let StateEvent::SignerSet = event { - this.reset(cx); - this.get_rooms(cx); - this.get_contact_list(cx); - this.get_messages(cx) - } + match event { + StateEvent::SignerSet => { + this.reset(cx); + this.get_rooms(cx); + } + StateEvent::RelayConnected => { + this.get_contact_list(cx); + this.get_messages(cx) + } + _ => {} + }; }), ); @@ -332,6 +337,7 @@ impl ChatRegistry { while let Some((_url, res)) = stream.next().await { if let Ok(event) = res { + log::debug!("Got event: {:?}", event); let urls: Vec = nip17::extract_owned_relay_list(event).collect(); return Ok(urls); } diff --git a/crates/coop/src/workspace.rs b/crates/coop/src/workspace.rs index 3bcff2e..4071dd9 100644 --- a/crates/coop/src/workspace.rs +++ b/crates/coop/src/workspace.rs @@ -134,6 +134,14 @@ impl Workspace { window.push_notification(note, cx); } + StateEvent::FetchingRelayList => { + let note = Notification::new() + .id::() + .message("Getting relay list...") + .with_kind(NotificationKind::Info); + + window.push_notification(note, cx); + } StateEvent::RelayNotConfigured => { this.relay_notification(window, cx); } diff --git a/crates/device/src/lib.rs b/crates/device/src/lib.rs index 684b413..9a6fe2d 100644 --- a/crates/device/src/lib.rs +++ b/crates/device/src/lib.rs @@ -73,14 +73,19 @@ impl DeviceRegistry { let nostr = NostrRegistry::global(cx); let state = DeviceState::default(); - let subscription = - Some( - cx.subscribe_in(&nostr, window, |this, _state, ev, _window, cx| { - if let StateEvent::SignerSet = ev { - this.get_announcement(cx); - } - }), - ); + let subscription = Some(cx.subscribe_in( + &nostr, + window, + |this, _state, event, _window, cx| match event { + StateEvent::SignerSet => { + this.reset(cx); + } + StateEvent::RelayConnected => { + this.get_announcement(cx); + } + _ => {} + }, + )); cx.defer_in(window, |this, window, cx| { this.handle_notifications(window, cx); @@ -269,9 +274,6 @@ impl DeviceRegistry { let nostr = NostrRegistry::global(cx); let client = nostr.read(cx).client(); - // Reset state before fetching announcement - self.reset(cx); - let task: Task> = cx.background_spawn(async move { let signer = client.signer().context("Signer not found")?; let public_key = signer.get_public_key().await?; @@ -373,7 +375,6 @@ impl DeviceRegistry { if keys.public_key() != device_pubkey { return Err(anyhow!("Key mismatch")); }; - Ok(keys) } else { Err(anyhow!("Key not found")) diff --git a/crates/state/src/lib.rs b/crates/state/src/lib.rs index 4702e96..0a474e7 100644 --- a/crates/state/src/lib.rs +++ b/crates/state/src/lib.rs @@ -50,6 +50,8 @@ pub enum StateEvent { Connecting, /// Connected to the bootstrapping relay Connected, + /// Fetching the relay list + FetchingRelayList, /// User has not set up NIP-65 relays RelayNotConfigured, /// Connected to NIP-65 relays @@ -60,6 +62,15 @@ pub enum StateEvent { Error(SharedString), } +impl StateEvent { + pub fn error(error: T) -> Self + where + T: Into, + { + Self::Error(error.into()) + } +} + /// Nostr Registry #[derive(Debug)] pub struct NostrRegistry { @@ -206,7 +217,7 @@ impl NostrRegistry { } Err(e) => { this.update(cx, |_this, cx| { - cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); + cx.emit(StateEvent::error(e.to_string())); }) .ok(); } @@ -271,7 +282,7 @@ impl NostrRegistry { }, Err(e) => { this.update(cx, |_this, cx| { - cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); + cx.emit(StateEvent::error(e.to_string())); }) .ok(); } @@ -360,7 +371,7 @@ impl NostrRegistry { } Err(e) => { this.update(cx, |_this, cx| { - cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); + cx.emit(StateEvent::error(e.to_string())); }) .ok(); } @@ -458,7 +469,7 @@ impl NostrRegistry { } Err(e) => { this.update(cx, |_this, cx| { - cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); + cx.emit(StateEvent::error(e.to_string())); }) .ok(); } @@ -505,7 +516,7 @@ impl NostrRegistry { } Err(e) => { this.update(cx, |_this, cx| { - cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); + cx.emit(StateEvent::error(e.to_string())); }) .ok(); } @@ -550,7 +561,7 @@ impl NostrRegistry { } Err(e) => { this.update(cx, |_this, cx| { - cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); + cx.emit(StateEvent::error(e.to_string())); }) .ok(); } @@ -558,7 +569,7 @@ impl NostrRegistry { } Err(e) => { this.update(cx, |_this, cx| { - cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); + cx.emit(StateEvent::error(e.to_string())); }) .ok(); } @@ -566,9 +577,72 @@ impl NostrRegistry { })); } + /// Ensure the relay list is fetched for the given public key pub fn ensure_relay_list(&mut self, public_key: &PublicKey, cx: &mut Context) { let task = self.get_event(public_key, Kind::RelayList, cx); + // Emit a fetching event before starting the task + cx.emit(StateEvent::FetchingRelayList); + + self.tasks.push(cx.spawn(async move |this, cx| { + match task.await { + Ok(event) => { + this.update(cx, |this, cx| { + this.ensure_connection(&event, cx); + }) + .ok(); + } + Err(e) => { + this.update(cx, |_this, cx| { + cx.emit(StateEvent::RelayNotConfigured); + cx.emit(StateEvent::error(e.to_string())); + }) + .ok(); + } + }; + })); + } + + /// Ensure that the user is connected to the relay specified in the NIP-65 event. + pub fn ensure_connection(&mut self, event: &Event, cx: &mut Context) { + let client = self.client(); + // Extract the relay list from the event + let relays: Vec<(RelayUrl, Option)> = nip65::extract_relay_list(event) + .map(|(url, metadata)| (url.to_owned(), metadata.to_owned())) + .collect(); + + let task: Task> = cx.background_spawn(async move { + for (url, metadata) in relays.into_iter() { + match metadata { + Some(RelayMetadata::Read) => { + client + .add_relay(url) + .capabilities(RelayCapabilities::READ) + .connect_timeout(Duration::from_secs(TIMEOUT)) + .and_connect() + .await?; + } + Some(RelayMetadata::Write) => { + client + .add_relay(url) + .capabilities(RelayCapabilities::WRITE) + .connect_timeout(Duration::from_secs(TIMEOUT)) + .and_connect() + .await?; + } + None => { + client + .add_relay(url) + .capabilities(RelayCapabilities::NONE) + .connect_timeout(Duration::from_secs(TIMEOUT)) + .and_connect() + .await?; + } + } + } + Ok(()) + }); + self.tasks.push(cx.spawn(async move |this, cx| { match task.await { Ok(_) => { @@ -580,7 +654,7 @@ impl NostrRegistry { Err(e) => { this.update(cx, |_this, cx| { cx.emit(StateEvent::RelayNotConfigured); - cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); + cx.emit(StateEvent::error(e.to_string())); }) .ok(); }