Replace relay subscriptions with cache-backed discovery sync

This commit is contained in:
2026-10-07 17:20:40 +07:00
parent 586fea796d
commit 64b49443ff
4 changed files with 462 additions and 754 deletions
+91 -23
View File
@@ -4,12 +4,12 @@ use std::rc::Rc;
use gpui::prelude::FluentBuilder;
use gpui::{
AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable,
IntoElement, ParentElement, Render, ScrollStrategy, SharedString, Styled, Subscription,
IntoElement, ParentElement, Render, ScrollStrategy, SharedString, Styled, Subscription, Task,
WeakEntity, Window, div, px, relative, size,
};
use gpui_base::{VirtualListScrollHandle, v_virtual_list};
use nip29::{Discovery, GroupCandidate, GroupKey, GroupsEvent, GroupsRegistry};
use nostr_sdk::prelude::RelayUrl;
use nostr_sdk::prelude::{RelayUrl, Timestamp};
use settings::AppSettings;
use smallvec::{SmallVec, smallvec};
use theme::ActiveTheme;
@@ -40,19 +40,21 @@ pub struct BrowsePanel {
/// The groups the relay has announced so far
candidates: Vec<GroupCandidate>,
/// The groups discovered from the cache and the relays
discovery: Entity<Discovery>,
discovery: Discovery,
/// Whether the discovery sync is still running
discovering: bool,
/// The scroll position of the group list
scroll_handle: VirtualListScrollHandle,
/// Whether the relay is still answering
loading: bool,
_subscriptions: SmallVec<[Subscription; 3]>,
tasks: SmallVec<[Task<()>; 2]>,
_subscriptions: SmallVec<[Subscription; 2]>,
}
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![];
@@ -64,10 +66,6 @@ impl BrowsePanel {
}),
);
subscriptions.push(cx.observe(&discovery, |_, _, cx| {
cx.notify();
}));
subscriptions.push(
cx.subscribe(&registry, |this, _registry, event, cx| match event {
GroupsEvent::Browsed(candidate) => {
@@ -76,15 +74,23 @@ impl BrowsePanel {
GroupsEvent::BrowseSettled(relay) => {
this.settle(relay.clone(), cx);
}
GroupsEvent::Synced => {
this.refresh_discovery(cx);
}
GroupsEvent::Members {
key,
created_at,
members,
} => {
this.push_members(key.clone(), *created_at, *members, 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);
});
cx.defer_in(window, |this, _window, cx| {
this.load_cached(cx);
this.sync_discovery(cx);
});
Self {
@@ -93,13 +99,77 @@ impl BrowsePanel {
input,
relay: None,
candidates: Vec::new(),
discovery,
discovery: Discovery::default(),
discovering: true,
scroll_handle: VirtualListScrollHandle::new(),
loading: false,
tasks: smallvec![],
_subscriptions: subscriptions,
}
}
/// Load the groups from the local database.
fn load_cached(&mut self, cx: &mut Context<Self>) {
let registry = GroupsRegistry::global(cx);
let discovery = registry.read(cx).cached_discovery(cx);
self.tasks.push(cx.spawn(async move |this, cx| {
let discovery = discovery.await;
this.update(cx, |this, cx| {
this.discovery = discovery;
cx.notify();
})
.ok();
}));
}
/// Sync the discoverable groups with the relays.
fn sync_discovery(&mut self, cx: &mut Context<Self>) {
self.discovering = true;
let registry = GroupsRegistry::global(cx);
let sync = registry.read(cx).discover(cx);
self.tasks.push(cx.spawn(async move |this, cx| {
sync.await;
this.update(cx, |this, cx| {
this.discovering = false;
cx.notify();
})
.ok();
}));
}
/// A browsed group announced its member list.
fn push_members(
&mut self,
key: GroupKey,
created_at: Timestamp,
members: usize,
cx: &mut Context<Self>,
) {
self.discovery.set_members(key, created_at, members);
cx.notify();
}
/// The discovery synced new groups: re-query the local database.
fn refresh_discovery(&mut self, cx: &mut Context<Self>) {
let registry = GroupsRegistry::global(cx);
let discovery = registry.read(cx).cached_discovery(cx);
self.tasks.push(cx.spawn(async move |this, cx| {
let discovery = discovery.await;
this.update(cx, |this, cx| {
this.discovery = discovery;
cx.notify();
})
.ok();
}));
}
/// Browse the relay in the input.
fn browse(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let registry = GroupsRegistry::global(cx);
@@ -189,7 +259,6 @@ impl BrowsePanel {
let cards: Vec<GroupCandidate> = self
.discovery
.read(cx)
.discovered()
.iter()
.filter(|candidate| !joined.contains(&candidate.key))
@@ -242,7 +311,7 @@ impl BrowsePanel {
fn render_card(&self, candidate: &GroupCandidate, cx: &mut Context<Self>) -> AnyElement {
let key = candidate.key.clone();
let seed = key.cache_tag();
let members = self.discovery.read(cx).members(&key).unwrap_or(0);
let members = self.discovery.members(&key).unwrap_or(0);
let name = SharedString::from(candidate.display_name().to_owned());
let relay = SharedString::from(key.relay().as_str().to_owned());
@@ -346,11 +415,11 @@ impl BrowsePanel {
h_flex()
.w_full()
.gap_1p5()
.child(Icon::new(IconName::Group))
.text_color(cx.theme().text_muted)
.child(Icon::new(IconName::Book))
.child(
div()
.text_sm()
.text_color(cx.theme().text_muted)
.child(SharedString::from(format!("{members} members"))),
),
),
@@ -392,8 +461,6 @@ impl Render for BrowsePanel {
let (discovered_count, discovering) = if discover {
let registry = GroupsRegistry::global(cx);
let discovery = self.discovery.read(cx);
let discovering = discovery.loading();
let joined: Vec<GroupKey> = registry
.read(cx)
@@ -402,13 +469,14 @@ impl Render for BrowsePanel {
.map(|group| group.read(cx).key().clone())
.collect();
let discovered_count = discovery
let discovered_count = self
.discovery
.discovered()
.iter()
.filter(|candidate| !joined.contains(&candidate.key))
.count();
(discovered_count, discovering)
(discovered_count, self.discovering)
} else {
(0, false)
};