extract group discovery into a discovery entity

This commit is contained in:
2026-10-07 09:10:25 +07:00
parent c058270eff
commit 96ceacb5fb
3 changed files with 429 additions and 323 deletions
+44 -78
View File
@@ -6,7 +6,7 @@ use gpui::{
IntoElement, ParentElement, Render, SharedString, Styled, Subscription, WeakEntity, Window,
div, uniform_list,
};
use nip29::{GroupCandidate, GroupKey, GroupsEvent, GroupsRegistry};
use nip29::{Discovery, GroupCandidate, GroupKey, GroupsEvent, GroupsRegistry};
use nostr_sdk::prelude::RelayUrl;
use settings::AppSettings;
use smallvec::{SmallVec, smallvec};
@@ -35,19 +35,18 @@ pub struct BrowsePanel {
relay: Option<RelayUrl>,
/// The groups the relay has announced so far
candidates: Vec<GroupCandidate>,
/// The communities of the user's contacts
contacts: Vec<GroupCandidate>,
/// Whether the contacts' communities are still resolving
contacts_loading: bool,
/// The groups discovered from the cache and the relays
discovery: Entity<Discovery>,
/// Whether the relay is still answering
loading: bool,
_subscriptions: SmallVec<[Subscription; 2]>,
_subscriptions: SmallVec<[Subscription; 3]>,
}
impl BrowsePanel {
fn new(dock: WeakEntity<DockArea>, window: &mut Window, cx: &mut Context<Self>) -> Self {
let input = cx.new(|cx| InputState::new(window, cx).placeholder(INPUT_PLACEHOLDER));
let registry = GroupsRegistry::global(cx);
let discovery = registry.read(cx).discovery();
let mut subscriptions = smallvec![];
@@ -59,6 +58,10 @@ impl BrowsePanel {
}),
);
subscriptions.push(cx.observe(&discovery, |_, _, cx| {
cx.notify();
}));
subscriptions.push(
cx.subscribe(&registry, |this, _registry, event, cx| match event {
GroupsEvent::Browsed(candidate) => {
@@ -67,19 +70,15 @@ impl BrowsePanel {
GroupsEvent::BrowseSettled(relay) => {
this.settle(relay.clone(), cx);
}
GroupsEvent::Suggested(candidate) => {
this.push_contact(candidate.as_ref().clone(), cx);
}
GroupsEvent::SuggestSettled => {
this.settle_contacts(cx);
}
_ => {}
}),
);
// Wait for the panel to leave the stack before kicking off the sync.
cx.defer_in(window, |this, _window, cx| {
this.load_contacts(cx);
// Wait for the panel to leave the stack before kicking off the sweep.
cx.defer_in(window, |_this, _window, cx| {
GroupsRegistry::global(cx).update(cx, |registry, cx| {
registry.discover(cx);
});
});
Self {
@@ -88,50 +87,12 @@ impl BrowsePanel {
input,
relay: None,
candidates: Vec::new(),
contacts: Vec::new(),
contacts_loading: true,
discovery,
loading: false,
_subscriptions: subscriptions,
}
}
/// Pull the resolved contacts' communities and start the contacts sync.
fn load_contacts(&mut self, cx: &mut Context<Self>) {
let registry = GroupsRegistry::global(cx);
let (candidates, loading) = registry.update(cx, |registry, cx| {
registry.load_contacts_groups(cx);
(
registry.contacts_candidates().to_vec(),
registry.contacts_loading(),
)
});
self.contacts = candidates;
self.contacts_loading = loading;
cx.notify();
}
fn push_contact(&mut self, candidate: GroupCandidate, cx: &mut Context<Self>) {
match self.contacts.iter_mut().find(|c| c.key == candidate.key) {
Some(existing) => *existing = candidate,
None => self.contacts.push(candidate),
}
self.contacts.sort_by(|a, b| {
a.display_name()
.to_lowercase()
.cmp(&b.display_name().to_lowercase())
});
cx.notify();
}
fn settle_contacts(&mut self, cx: &mut Context<Self>) {
self.contacts_loading = false;
cx.notify();
}
/// Browse the relay in the input.
fn browse(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let registry = GroupsRegistry::global(cx);
@@ -211,10 +172,10 @@ impl BrowsePanel {
range: Range<usize>,
cx: &mut Context<Self>,
) -> Vec<AnyElement> {
let registry = GroupsRegistry::global(cx);
let hide_avatar = AppSettings::get_hide_avatar(cx);
let panel = cx.entity().downgrade();
let registry = GroupsRegistry::global(cx);
let joined: Vec<GroupKey> = registry
.read(cx)
.groups()
@@ -229,16 +190,19 @@ impl BrowsePanel {
.map(|candidate| {
let key = candidate.key.clone();
let name = SharedString::from(candidate.display_name().to_owned());
let seed = key.cache_tag();
let is_joined = joined.contains(&key);
let picture = candidate
.display_image()
.map(|picture| SharedString::from(picture.to_owned()));
let seed = key.cache_tag();
let detail = if relay_detail {
key.relay().domain().unwrap_or_else(|| key.relay().as_str())
} else {
key.id().as_str()
};
let is_joined = joined.contains(&key);
let action = if is_joined {
Button::new(format!("browse-joined-{seed}"))
@@ -335,9 +299,9 @@ impl Focusable for BrowsePanel {
impl Render for BrowsePanel {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let count = self.candidates.len();
let from_contacts = self.relay.is_none();
let discover = self.relay.is_none();
let (contacts, contact_count) = if from_contacts {
let (discovered, discovered_count, discovering) = if discover {
let registry = GroupsRegistry::global(cx);
let joined: Vec<GroupKey> = registry
.read(cx)
@@ -346,34 +310,37 @@ impl Render for BrowsePanel {
.map(|group| group.read(cx).key().clone())
.collect();
let contacts: Vec<GroupCandidate> = self
.contacts
let discovery = self.discovery.read(cx);
let discovering = discovery.loading();
let discovered: Vec<GroupCandidate> = discovery
.discovered()
.iter()
.filter(|candidate| !joined.contains(&candidate.key))
.cloned()
.collect();
let count = contacts.len();
(contacts, count)
let count = discovered.len();
(discovered, count, discovering)
} else {
(Vec::new(), 0)
(Vec::new(), 0, false)
};
let body = if from_contacts {
if contact_count > 0 {
let body = if discover {
if discovered_count > 0 {
uniform_list(
"browse-contacts",
contact_count,
"browse-discovered",
discovered_count,
cx.processor(move |this, range, _window, cx| {
this.render_rows(&contacts, true, range, cx)
this.render_rows(&discovered, true, range, cx)
}),
)
.h_full()
.into_any_element()
} else if self.contacts_loading {
notice("Loading communities from your contacts…", cx)
} else if discovering {
notice("Discovering groups…", cx)
} else {
notice("No communities from your contacts.", cx)
notice("No groups found.", cx)
}
} else if count > 0 {
uniform_list(
@@ -407,19 +374,18 @@ impl Render for BrowsePanel {
this.browse(window, cx);
})),
)
.when(
self.loading || (from_contacts && self.contacts_loading),
|this| this.child(Indicator::new().small().color(cx.theme().icon_accent)),
),
.when(self.loading || (discover && discovering), |this| {
this.child(Indicator::new().small().color(cx.theme().icon_accent))
}),
)
.when(from_contacts && contact_count > 0, |this| {
.when(discover && discovered_count > 0, |this| {
this.child(
div()
.px_3()
.pb_1()
.text_xs()
.text_color(cx.theme().text_muted)
.child("From your contacts"),
.child("Discovered groups"),
)
})
.child(div().flex_1().min_h_0().child(body))