diff --git a/crates/nip29/src/lib.rs b/crates/nip29/src/lib.rs index 80dbae97..393706f0 100644 --- a/crates/nip29/src/lib.rs +++ b/crates/nip29/src/lib.rs @@ -90,7 +90,6 @@ fn is_own_subscription(id: &SubscriptionId) -> bool { || id.starts_with(PAGE_PREFIX) } -/// The group id, member count and timestamp of a group members event. fn parse_members(event: &Event) -> Option<(GroupId, Timestamp, usize)> { if event.kind != Kind::GroupMembers { return None; @@ -102,8 +101,6 @@ fn parse_members(event: &Event) -> Option<(GroupId, Timestamp, usize)> { Some((id, event.created_at, members)) } -/// Reconcile a filter with the relays via negentropy, returning the events -/// that were downloaded to the local database. async fn reconcile( client: &Client, filter: Filter, @@ -112,22 +109,19 @@ async fn reconcile( let output = match client.sync(filter).with(urls).await { Ok(output) => output, Err(error) => { - log::warn!("nip29: the reconciliation failed: {error}"); + log::warn!("the reconciliation failed: {error}"); return HashMap::new(); } }; for (relay, error) in &output.failed { - log::debug!("nip29: syncing with {relay} failed: {error}"); + log::debug!("syncing with {relay} failed: {error}"); } output.value.received } /// Pair the downloaded events with the relay that served them. -/// -/// The database does not keep relay associations, so the pairing has to -/// happen while the reconciliation still knows them. async fn pull_received( client: &Client, received: HashMap>, @@ -202,16 +196,15 @@ impl GroupsRegistry { })); cx.defer(move |cx| { - if let Err(error) = this.update(cx, |this, cx| { + this.update(cx, |this, cx| { this.handle_notifications(cx); this.load_cached(cx); if nostr.read(cx).current_user().is_some() { this.load(cx); } - }) { - log::warn!("nip29: initializing the registry failed: {error}"); - } + }) + .ok(); }); Self { @@ -278,7 +271,7 @@ impl GroupsRegistry { }); } }) { - log::warn!("nip29: marking the refused join failed: {error}"); + log::warn!("marking the refused join failed: {error}"); } } })); @@ -337,11 +330,11 @@ impl GroupsRegistry { .close_on(opts) .await { - log::warn!("nip29: browsing {relay} failed: {error}"); + log::warn!("browsing {relay} failed: {error}"); } if let Err(error) = tx.send_async(Signal::BrowseSettled(id)).await { - log::warn!("nip29: the browse settle signal was dropped: {error}"); + log::warn!("the browse settle signal was dropped: {error}"); } })); } @@ -405,7 +398,7 @@ impl GroupsRegistry { let (authors, lists) = match task.await { Ok(ok) => ok, Err(error) => { - log::warn!("nip29: loading the cached groups failed: {error}"); + log::warn!("loading the cached groups failed: {error}"); (HashSet::new(), Vec::new()) } }; @@ -413,21 +406,20 @@ impl GroupsRegistry { if let Err(error) = this.update(cx, |this, cx| { this.absorb_lists(lists, cx); }) { - log::warn!("nip29: applying the cached group lists failed: {error}"); + log::warn!("applying the cached group lists failed: {error}"); return; } - // Show the cached metadata before the relays answer. let previews = match this.update(cx, |this, cx| this.load_cached_previews(cx)) { Ok(task) => match task.await { Ok(previews) => previews, Err(error) => { - log::warn!("nip29: loading the cached group metadata failed: {error}"); + log::warn!("loading the cached group metadata failed: {error}"); Vec::new() } }, Err(error) => { - log::warn!("nip29: discovering the groups failed: {error}"); + log::warn!("discovering the groups failed: {error}"); return; } }; @@ -437,12 +429,12 @@ impl GroupsRegistry { Ok(task) => match task.await { Ok(members) => members, Err(error) => { - log::warn!("nip29: loading the cached group members failed: {error}"); + log::warn!("loading the cached group members failed: {error}"); Vec::new() } }, Err(error) => { - log::warn!("nip29: discovering the groups failed: {error}"); + log::warn!("discovering the groups failed: {error}"); return; } }; @@ -451,16 +443,14 @@ impl GroupsRegistry { this.absorb_cached_previews(previews, cx); this.absorb_cached_members(members, cx); }) { - log::warn!("nip29: discovering the groups failed: {error}"); + log::warn!("discovering the groups failed: {error}"); return; } - // Reconcile the contacts' group lists with the pool relays: only - // the events missing locally are downloaded and stored. let lists = match this.update(cx, |this, cx| this.sync_lists(authors, cx)) { Ok(task) => task, Err(error) => { - log::warn!("nip29: discovering the groups failed: {error}"); + log::warn!("discovering the groups failed: {error}"); return; } }; @@ -469,7 +459,7 @@ impl GroupsRegistry { let sweep = match this.update(cx, |this, cx| this.sync_sweep(cx)) { Ok(task) => task, Err(error) => { - log::warn!("nip29: discovering the groups failed: {error}"); + log::warn!("discovering the groups failed: {error}"); return; } }; @@ -477,7 +467,7 @@ impl GroupsRegistry { let lists = match lists.await { Ok(lists) => lists, Err(error) => { - log::warn!("nip29: syncing the contacts group lists failed: {error}"); + log::warn!("syncing the contacts group lists failed: {error}"); Vec::new() } }; @@ -485,13 +475,13 @@ impl GroupsRegistry { if let Err(error) = this.update(cx, |this, cx| { this.absorb_lists(lists, cx); }) { - log::warn!("nip29: applying the contacts group lists failed: {error}"); + log::warn!("applying the contacts group lists failed: {error}"); } let sweep = match sweep.await { Ok(events) => events, Err(error) => { - log::warn!("nip29: syncing the discovered groups failed: {error}"); + log::warn!("syncing the discovered groups failed: {error}"); Vec::new() } }; @@ -501,7 +491,7 @@ impl GroupsRegistry { this.absorb_discovered(relay, &event, cx); } }) { - log::warn!("nip29: applying the discovered groups failed: {error}"); + log::warn!("applying the discovered groups failed: {error}"); } // The previews cover the relays the lists just referenced. @@ -509,12 +499,12 @@ impl GroupsRegistry { Ok(task) => match task.await { Ok(previews) => previews, Err(error) => { - log::warn!("nip29: syncing the group previews failed: {error}"); + log::warn!("syncing the group previews failed: {error}"); Vec::new() } }, Err(error) => { - log::warn!("nip29: discovering the groups failed: {error}"); + log::warn!("discovering the groups failed: {error}"); return; } }; @@ -527,7 +517,7 @@ impl GroupsRegistry { this.discovery .update(cx, |discovery, cx| discovery.settle(cx)); }) { - log::warn!("nip29: discovering the groups failed: {error}"); + log::warn!("discovering the groups failed: {error}"); } })); } @@ -560,7 +550,7 @@ impl GroupsRegistry { let metadata = match GroupMetadata::parse(&event) { Ok(metadata) => metadata, Err(error) => { - log::warn!("nip29: ignoring malformed cached group metadata: {error}"); + log::warn!("ignoring malformed cached group metadata: {error}"); return newest; } }; @@ -717,7 +707,7 @@ impl GroupsRegistry { let metadata = match GroupMetadata::parse(event) { Ok(metadata) => metadata, Err(error) => { - log::warn!("nip29: ignoring malformed group metadata from {relay}: {error}"); + log::warn!("ignoring malformed group metadata from {relay}: {error}"); return; } }; @@ -762,7 +752,7 @@ impl GroupsRegistry { async move { if let Err(error) = client.add_relay(relay.clone()).and_connect().await { - log::warn!("nip29: connecting to {relay} failed: {error}"); + log::warn!("connecting to {relay} failed: {error}"); } let filter = Filter::new() @@ -811,10 +801,7 @@ impl GroupsRegistry { fn absorb_group_list(&mut self, event: &Event, cx: &mut Context) { let Ok(list) = GroupList::parse(event) else { - log::warn!( - "nip29: ignoring a malformed group list from {}", - event.pubkey - ); + log::warn!("ignoring a malformed group list from {}", event.pubkey); return; }; @@ -861,7 +848,7 @@ impl GroupsRegistry { let metadata = match GroupMetadata::parse(event) { Ok(metadata) => metadata, Err(error) => { - log::warn!("nip29: ignoring malformed group metadata from {relay}: {error}"); + log::warn!("ignoring malformed group metadata from {relay}: {error}"); return; } }; @@ -925,7 +912,7 @@ impl GroupsRegistry { self.tasks.push(cx.spawn(async move |this, cx| { if let Err(error) = sent.await { - log::warn!("nip29: leaving {} failed: {error}", key.id()); + log::warn!("leaving {} failed: {error}", key.id()); return; } this.update(cx, |this, cx| { @@ -963,7 +950,7 @@ impl GroupsRegistry { self.tasks.push(cx.background_spawn(async move { for id in ids { if let Err(error) = client.unsubscribe(&id).await { - log::warn!("nip29: unsubscribing {id} failed: {error}"); + log::warn!("unsubscribing {id} failed: {error}"); } } })); @@ -995,11 +982,14 @@ impl GroupsRegistry { self.tasks .push(cx.spawn(async move |this, cx| match task.await { Ok(list) => { - if let Err(error) = this.update(cx, |this, cx| this.merge(list, cx)) { - log::warn!("nip29: applying the cached group list failed: {error}"); - } + this.update(cx, |this, cx| { + this.merge(list, cx); + }) + .ok(); + } + Err(error) => { + log::warn!("loading the cached group list failed: {error}"); } - Err(error) => log::warn!("nip29: loading the cached group list failed: {error}"), })); } @@ -1008,7 +998,7 @@ impl GroupsRegistry { let client = nostr.read(cx).client(); let signer = nostr.read(cx).signer(); - let updates: Task> = cx.background_spawn(async move { + cx.background_spawn(async move { let me = signer.get_public_key_async().await?; let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE); let filter = Filter::new().author(me).kind(Kind::SimpleGroups).limit(1); @@ -1019,14 +1009,9 @@ impl GroupsRegistry { .close_on(opts) .await?; - Ok(()) - }); - - self.tasks.push(cx.spawn(async move |_this, _cx| { - if let Err(error) = updates.await { - log::warn!("nip29: subscribing to the group list failed: {error}"); - } - })); + Ok::<(), anyhow::Error>(()) + }) + .detach(); self.watch_forks(cx); } @@ -1050,7 +1035,6 @@ impl GroupsRegistry { self.tasks.push(cx.background_spawn(async move { loop { executor.timer(FORK_INTERVAL).await; - if tx.send_async(Signal::Forks).await.is_err() { break; } @@ -1091,14 +1075,14 @@ impl GroupsRegistry { .with_id(fork_subscription_id()) .await { - log::warn!("nip29: subscribing to the admins' group lists failed: {error}"); + log::warn!("subscribing to the admins' group lists failed: {error}"); } })); } fn absorb_fork_list(&mut self, event: &Event, cx: &mut Context) { let Ok(list) = GroupList::parse(event) else { - log::warn!("nip29: ignoring a malformed admin group list"); + log::warn!("ignoring a malformed admin group list"); return; }; @@ -1201,7 +1185,7 @@ impl GroupsRegistry { self.tasks.push(cx.background_spawn(async move { for id in [id, page] { if let Err(error) = client.unsubscribe(&id).await { - log::warn!("nip29: unsubscribing {id} failed: {error}"); + log::warn!("unsubscribing {id} failed: {error}"); } } @@ -1210,7 +1194,7 @@ impl GroupsRegistry { .custom_tag(SingleLetterTag::LOWERCASE_R, cache_tag); if let Err(error) = client.database().delete(filter).await { - log::warn!("nip29: clearing the group cache failed: {error}"); + log::warn!("clearing the group cache failed: {error}"); } })); @@ -1271,18 +1255,17 @@ impl GroupsRegistry { let event = match builder.finalize_async(&signer).await { Ok(event) => event, Err(error) => { - log::warn!("nip29: signing the group list failed: {error}"); + log::warn!("signing the group list failed: {error}"); return; } }; match client.send_event(&event).to_nip65().await { Ok(output) if output.failed.is_empty() => {} - Ok(output) => log::warn!( - "nip29: {} relay(s) rejected the group list", - output.failed.len() - ), - Err(error) => log::warn!("nip29: publishing the group list failed: {error}"), + Ok(output) => { + log::warn!("{} relay(s) rejected the group list", output.failed.len()) + } + Err(error) => log::warn!("publishing the group list failed: {error}"), } })); } @@ -1307,7 +1290,7 @@ impl GroupsRegistry { .read_with(cx, |group, cx| group.save_envelope(event, cx)) .await { - log::warn!("nip29: caching failed: {error}"); + log::warn!("caching failed: {error}"); return; } @@ -1318,7 +1301,7 @@ impl GroupsRegistry { }); } Err(error) => { - log::warn!("nip29: reloading {} failed: {error}", logged.id()); + log::warn!("reloading {} failed: {error}", logged.id()); } } })); @@ -1345,7 +1328,7 @@ impl GroupsRegistry { }); } Err(error) => { - log::warn!("nip29: loading {} state failed: {error}", logged.id()); + log::warn!("loading {} state failed: {error}", logged.id()); } })); } @@ -1365,7 +1348,7 @@ impl GroupsRegistry { .read_with(cx, |group, cx| group.save_envelope(event.clone(), cx)) .await { - log::warn!("nip29: caching failed: {error}"); + log::warn!("caching failed: {error}"); return; } @@ -1411,7 +1394,7 @@ impl GroupsRegistry { self.tasks.push(cx.spawn(async move |this, cx| { if let Err(error) = purge.await { - log::warn!("nip29: dropping the deleted envelope failed: {error}"); + log::warn!("dropping the deleted envelope failed: {error}"); } this.update(cx, |this, cx| { group.update(cx, |group, cx| { @@ -1427,7 +1410,7 @@ impl GroupsRegistry { if id == list_subscription_id() { match GroupList::parse(&event) { Ok(list) => self.merge(list, cx), - Err(error) => log::warn!("nip29: ignoring a malformed group list: {error}"), + Err(error) => log::warn!("ignoring a malformed group list: {error}"), } return; } @@ -1470,7 +1453,7 @@ impl GroupsRegistry { } let Ok(metadata) = GroupMetadata::parse(event) else { - log::warn!("nip29: ignoring malformed group metadata from {relay}"); + log::warn!("ignoring malformed group metadata from {relay}"); return; }; @@ -1515,12 +1498,12 @@ impl GroupsRegistry { fn handle_closed(&mut self, id: SubscriptionId, reason: String, cx: &mut Context) { if let Some(relay) = self.browse.get(&id) { - log::warn!("nip29: browsing {relay} was refused: {reason}"); + log::warn!("browsing {relay} was refused: {reason}"); return; } if id == fork_subscription_id() { - log::debug!("nip29: the admins' group lists were refused: {reason}"); + log::debug!("the admins' group lists were refused: {reason}"); return; } @@ -1531,7 +1514,7 @@ impl GroupsRegistry { if is_page(&id) { self.routes.remove(&id); log::warn!( - "nip29: loading older messages in {} was refused: {reason}", + "loading older messages in {} was refused: {reason}", key.id() ); diff --git a/crates/workspace/src/panels/browse.rs b/crates/workspace/src/panels/browse.rs index 772889e7..9fd0b4f3 100644 --- a/crates/workspace/src/panels/browse.rs +++ b/crates/workspace/src/panels/browse.rs @@ -16,7 +16,6 @@ use theme::ActiveTheme; use ui::avatar::Avatar; use ui::button::{Button, ButtonVariants}; use ui::dock::{DockArea, DockPlacement, Panel, PanelEvent, PanelHandle}; -use ui::indicator::Indicator; use ui::input::{Input, InputEvent, InputState}; use ui::notification::Notification; use ui::scroll::Scrollbar; @@ -214,10 +213,6 @@ impl BrowsePanel { self.render_card_rows(cards, cx) } - /// A row holds two group cards side by side. - /// - /// The cards are the relative slice of the visible rows, so they pair up - /// in order, two per row. fn render_card_rows( &self, cards: &[GroupCandidate], @@ -235,8 +230,6 @@ impl BrowsePanel { .p_2() .gap_2() .children(first.map(|card| self.render_card(card, cx))) - // Keep an odd last card in one column: an empty spacer - // takes the second slot so the card cannot stretch. .child(match second { Some(card) => self.render_card(card, cx), None => div().flex_1().into_any_element(), @@ -339,7 +332,7 @@ impl BrowsePanel { .text_sm() .text_color(cx.theme().text_muted) .text_ellipsis() - .line_clamp(3) + .line_clamp(2) .map(|this| { if let Some(about) = about { this.child(about) @@ -458,28 +451,31 @@ impl Render for BrowsePanel { v_flex() .size_full() .child( - h_flex() - .gap_2() - .px_2() + v_flex() + .h_32() .flex_shrink_0() - .child(Input::new(&self.input).cleanable(true).flex_1()) + .items_center() + .justify_center() + .bg(cx.theme().surface_background) .child( - Button::new("browse-go") - .label("Go") - .primary() - .on_click(cx.listener(|this, _event, window, cx| { - this.browse(window, cx); - })), - ) - .when(self.loading || (discover && discovering), |this| { - this.child(Indicator::new().small().color(cx.theme().icon_accent)) - }), + h_flex() + .w_full() + .max_w_56() + .gap_1() + .child(Input::new(&self.input).cleanable(true).flex_1()) + .child(Button::new("browse-go").label("Go").primary().on_click( + cx.listener(|this, _event, window, cx| { + this.browse(window, cx); + }), + )), + ), ) .child( div() .flex_1() .min_h_0() .relative() + .px_2() .child(body) .child(Scrollbar::vertical(&self.scroll_handle)), )