feat: add relay tracking for gift wrap events #21

Merged
reya merged 5 commits from chat-patch into master 2026-03-14 08:18:20 +00:00
4 changed files with 115 additions and 26 deletions
Showing only changes of commit 739088b669 - Show all commits

View File

@@ -105,12 +105,17 @@ impl ChatRegistry {
subscriptions.push( subscriptions.push(
// Subscribe to the signer event // Subscribe to the signer event
cx.subscribe(&nostr, |this, _state, event, cx| { cx.subscribe(&nostr, |this, _state, event, cx| {
if let StateEvent::SignerSet = event { match event {
StateEvent::SignerSet => {
this.reset(cx); this.reset(cx);
this.get_rooms(cx); this.get_rooms(cx);
}
StateEvent::RelayConnected => {
this.get_contact_list(cx); this.get_contact_list(cx);
this.get_messages(cx) this.get_messages(cx)
} }
_ => {}
};
}), }),
); );
@@ -332,6 +337,7 @@ impl ChatRegistry {
while let Some((_url, res)) = stream.next().await { while let Some((_url, res)) = stream.next().await {
if let Ok(event) = res { if let Ok(event) = res {
log::debug!("Got event: {:?}", event);
let urls: Vec<RelayUrl> = nip17::extract_owned_relay_list(event).collect(); let urls: Vec<RelayUrl> = nip17::extract_owned_relay_list(event).collect();
return Ok(urls); return Ok(urls);
} }

View File

@@ -134,6 +134,14 @@ impl Workspace {
window.push_notification(note, cx); window.push_notification(note, cx);
} }
StateEvent::FetchingRelayList => {
let note = Notification::new()
.id::<RelayNotifcation>()
.message("Getting relay list...")
.with_kind(NotificationKind::Info);
window.push_notification(note, cx);
}
StateEvent::RelayNotConfigured => { StateEvent::RelayNotConfigured => {
this.relay_notification(window, cx); this.relay_notification(window, cx);
} }

View File

@@ -73,14 +73,19 @@ impl DeviceRegistry {
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
let state = DeviceState::default(); let state = DeviceState::default();
let subscription = let subscription = Some(cx.subscribe_in(
Some( &nostr,
cx.subscribe_in(&nostr, window, |this, _state, ev, _window, cx| { window,
if let StateEvent::SignerSet = ev { |this, _state, event, _window, cx| match event {
StateEvent::SignerSet => {
this.reset(cx);
}
StateEvent::RelayConnected => {
this.get_announcement(cx); this.get_announcement(cx);
} }
}), _ => {}
); },
));
cx.defer_in(window, |this, window, cx| { cx.defer_in(window, |this, window, cx| {
this.handle_notifications(window, cx); this.handle_notifications(window, cx);
@@ -269,9 +274,6 @@ impl DeviceRegistry {
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client(); let client = nostr.read(cx).client();
// Reset state before fetching announcement
self.reset(cx);
let task: Task<Result<Event, Error>> = cx.background_spawn(async move { let task: Task<Result<Event, Error>> = cx.background_spawn(async move {
let signer = client.signer().context("Signer not found")?; let signer = client.signer().context("Signer not found")?;
let public_key = signer.get_public_key().await?; let public_key = signer.get_public_key().await?;
@@ -373,7 +375,6 @@ impl DeviceRegistry {
if keys.public_key() != device_pubkey { if keys.public_key() != device_pubkey {
return Err(anyhow!("Key mismatch")); return Err(anyhow!("Key mismatch"));
}; };
Ok(keys) Ok(keys)
} else { } else {
Err(anyhow!("Key not found")) Err(anyhow!("Key not found"))

View File

@@ -50,6 +50,8 @@ pub enum StateEvent {
Connecting, Connecting,
/// Connected to the bootstrapping relay /// Connected to the bootstrapping relay
Connected, Connected,
/// Fetching the relay list
FetchingRelayList,
/// User has not set up NIP-65 relays /// User has not set up NIP-65 relays
RelayNotConfigured, RelayNotConfigured,
/// Connected to NIP-65 relays /// Connected to NIP-65 relays
@@ -60,6 +62,15 @@ pub enum StateEvent {
Error(SharedString), Error(SharedString),
} }
impl StateEvent {
pub fn error<T>(error: T) -> Self
where
T: Into<SharedString>,
{
Self::Error(error.into())
}
}
/// Nostr Registry /// Nostr Registry
#[derive(Debug)] #[derive(Debug)]
pub struct NostrRegistry { pub struct NostrRegistry {
@@ -206,7 +217,7 @@ impl NostrRegistry {
} }
Err(e) => { Err(e) => {
this.update(cx, |_this, cx| { this.update(cx, |_this, cx| {
cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); cx.emit(StateEvent::error(e.to_string()));
}) })
.ok(); .ok();
} }
@@ -271,7 +282,7 @@ impl NostrRegistry {
}, },
Err(e) => { Err(e) => {
this.update(cx, |_this, cx| { this.update(cx, |_this, cx| {
cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); cx.emit(StateEvent::error(e.to_string()));
}) })
.ok(); .ok();
} }
@@ -360,7 +371,7 @@ impl NostrRegistry {
} }
Err(e) => { Err(e) => {
this.update(cx, |_this, cx| { this.update(cx, |_this, cx| {
cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); cx.emit(StateEvent::error(e.to_string()));
}) })
.ok(); .ok();
} }
@@ -458,7 +469,7 @@ impl NostrRegistry {
} }
Err(e) => { Err(e) => {
this.update(cx, |_this, cx| { this.update(cx, |_this, cx| {
cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); cx.emit(StateEvent::error(e.to_string()));
}) })
.ok(); .ok();
} }
@@ -505,7 +516,7 @@ impl NostrRegistry {
} }
Err(e) => { Err(e) => {
this.update(cx, |_this, cx| { this.update(cx, |_this, cx| {
cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); cx.emit(StateEvent::error(e.to_string()));
}) })
.ok(); .ok();
} }
@@ -550,7 +561,7 @@ impl NostrRegistry {
} }
Err(e) => { Err(e) => {
this.update(cx, |_this, cx| { this.update(cx, |_this, cx| {
cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); cx.emit(StateEvent::error(e.to_string()));
}) })
.ok(); .ok();
} }
@@ -558,7 +569,7 @@ impl NostrRegistry {
} }
Err(e) => { Err(e) => {
this.update(cx, |_this, cx| { this.update(cx, |_this, cx| {
cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); cx.emit(StateEvent::error(e.to_string()));
}) })
.ok(); .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<Self>) { pub fn ensure_relay_list(&mut self, public_key: &PublicKey, cx: &mut Context<Self>) {
let task = self.get_event(public_key, Kind::RelayList, cx); 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<Self>) {
let client = self.client();
// Extract the relay list from the event
let relays: Vec<(RelayUrl, Option<RelayMetadata>)> = nip65::extract_relay_list(event)
.map(|(url, metadata)| (url.to_owned(), metadata.to_owned()))
.collect();
let task: Task<Result<(), Error>> = 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| { self.tasks.push(cx.spawn(async move |this, cx| {
match task.await { match task.await {
Ok(_) => { Ok(_) => {
@@ -580,7 +654,7 @@ impl NostrRegistry {
Err(e) => { Err(e) => {
this.update(cx, |_this, cx| { this.update(cx, |_this, cx| {
cx.emit(StateEvent::RelayNotConfigured); cx.emit(StateEvent::RelayNotConfigured);
cx.emit(StateEvent::Error(SharedString::from(e.to_string()))); cx.emit(StateEvent::error(e.to_string()));
}) })
.ok(); .ok();
} }