add browse panel for discovering relay groups
This commit is contained in:
+33
-57
@@ -35,17 +35,12 @@ struct GlobalGroupsRegistry(Entity<GroupsRegistry>);
|
||||
|
||||
impl Global for GlobalGroupsRegistry {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GroupsEvent {
|
||||
Updated,
|
||||
Error(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Browse {
|
||||
pub relay: RelayUrl,
|
||||
pub loading: bool,
|
||||
pub groups: Vec<GroupCandidate>,
|
||||
Browsed(Box<GroupCandidate>),
|
||||
BrowseSettled(RelayUrl),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -53,6 +48,7 @@ enum Signal {
|
||||
Event(SubscriptionId, Event),
|
||||
Eose(SubscriptionId),
|
||||
Closed(SubscriptionId, String),
|
||||
BrowseSettled(SubscriptionId),
|
||||
Forks,
|
||||
}
|
||||
|
||||
@@ -97,7 +93,7 @@ impl EventEmitter<GroupsEvent> for GroupsRegistry {}
|
||||
pub struct GroupsRegistry {
|
||||
groups: Vec<Entity<Group>>,
|
||||
list: GroupList,
|
||||
browse: HashMap<SubscriptionId, Browse>,
|
||||
browse: HashMap<SubscriptionId, RelayUrl>,
|
||||
routes: HashMap<SubscriptionId, GroupKey>,
|
||||
observers: HashMap<GroupKey, Subscription>,
|
||||
forks: HashMap<PublicKey, (Timestamp, GroupList)>,
|
||||
@@ -246,19 +242,11 @@ impl GroupsRegistry {
|
||||
pub fn browse(&mut self, relay: RelayUrl, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let id = browse_subscription(&relay);
|
||||
let tx = self.signal_tx.clone();
|
||||
|
||||
self.browse.insert(
|
||||
id.clone(),
|
||||
Browse {
|
||||
relay: relay.clone(),
|
||||
loading: true,
|
||||
groups: Vec::new(),
|
||||
},
|
||||
);
|
||||
|
||||
cx.emit(GroupsEvent::Updated);
|
||||
cx.notify();
|
||||
self.browse.insert(id.clone(), relay.clone());
|
||||
|
||||
self.tasks.push(cx.background_spawn(async move {
|
||||
client.add_relay(relay.clone()).and_connect().await.ok();
|
||||
@@ -269,18 +257,25 @@ impl GroupsRegistry {
|
||||
.timeout(Some(BROWSE_TIMEOUT));
|
||||
|
||||
if let Err(error) = client
|
||||
.subscribe(ReqTarget::single(relay, [filter]))
|
||||
.with_id(id)
|
||||
.subscribe(ReqTarget::single(relay.clone(), [filter]))
|
||||
.with_id(id.clone())
|
||||
.close_on(opts)
|
||||
.await
|
||||
{
|
||||
log::warn!("nip29: browsing failed: {error}");
|
||||
log::warn!("nip29: 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}");
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn browsed(&self, relay: &RelayUrl) -> Option<&Browse> {
|
||||
self.browse.get(&browse_subscription(relay))
|
||||
fn settle_browse(&mut self, id: SubscriptionId, cx: &mut Context<Self>) {
|
||||
let Some(relay) = self.browse.get(&id).cloned() else {
|
||||
return;
|
||||
};
|
||||
cx.emit(GroupsEvent::BrowseSettled(relay));
|
||||
}
|
||||
|
||||
pub fn load_more(&mut self, key: &GroupKey, cx: &mut Context<Self>) {
|
||||
@@ -516,6 +511,9 @@ impl GroupsRegistry {
|
||||
GroupEvent::Updated => {
|
||||
this.refresh_entry(group, cx);
|
||||
this.sync_forks(false, cx);
|
||||
|
||||
cx.emit(GroupsEvent::Updated);
|
||||
cx.notify();
|
||||
}
|
||||
_ => {}
|
||||
}),
|
||||
@@ -611,9 +609,6 @@ impl GroupsRegistry {
|
||||
|
||||
self.upsert_entry(&key, Some(name));
|
||||
self.publish_list(cx);
|
||||
|
||||
cx.emit(GroupsEvent::Updated);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn publish_list(&mut self, cx: &mut Context<Self>) {
|
||||
@@ -741,15 +736,12 @@ impl GroupsRegistry {
|
||||
}
|
||||
|
||||
fn absorb_candidate(&mut self, id: &SubscriptionId, event: &Event, cx: &mut Context<Self>) {
|
||||
let Some(browse) = self.browse.get_mut(id) else {
|
||||
let Some(relay) = self.browse.get(id).cloned() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(metadata) = GroupMetadata::parse(event) else {
|
||||
log::warn!(
|
||||
"nip29: ignoring malformed group metadata from {}",
|
||||
browse.relay
|
||||
);
|
||||
log::warn!("nip29: ignoring malformed group metadata from {relay}");
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -757,28 +749,13 @@ impl GroupsRegistry {
|
||||
return;
|
||||
};
|
||||
|
||||
let candidate = GroupCandidate {
|
||||
key: GroupKey::new(browse.relay.clone(), group),
|
||||
cx.emit(GroupsEvent::Browsed(Box::new(GroupCandidate {
|
||||
key: GroupKey::new(relay, group),
|
||||
metadata,
|
||||
};
|
||||
|
||||
match browse.groups.iter_mut().find(|e| e.key == candidate.key) {
|
||||
Some(existing) => *existing = candidate,
|
||||
None => browse.groups.push(candidate),
|
||||
}
|
||||
|
||||
cx.emit(GroupsEvent::Updated);
|
||||
cx.notify();
|
||||
})));
|
||||
}
|
||||
|
||||
fn handle_eose(&mut self, id: SubscriptionId, cx: &mut Context<Self>) {
|
||||
if let Some(browse) = self.browse.get_mut(&id) {
|
||||
browse.loading = false;
|
||||
cx.emit(GroupsEvent::Updated);
|
||||
cx.notify();
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(key) = self.routes.get(&id).cloned() else {
|
||||
return;
|
||||
};
|
||||
@@ -807,12 +784,8 @@ impl GroupsRegistry {
|
||||
}
|
||||
|
||||
fn handle_closed(&mut self, id: SubscriptionId, reason: String, cx: &mut Context<Self>) {
|
||||
if let Some(browse) = self.browse.get_mut(&id) {
|
||||
log::warn!("nip29: browsing {} was refused: {reason}", browse.relay);
|
||||
browse.loading = false;
|
||||
|
||||
cx.emit(GroupsEvent::Updated);
|
||||
cx.notify();
|
||||
if let Some(relay) = self.browse.get(&id) {
|
||||
log::warn!("nip29: browsing {relay} was refused: {reason}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -922,6 +895,9 @@ impl GroupsRegistry {
|
||||
Signal::Closed(id, reason) => {
|
||||
this.handle_closed(id, reason, cx);
|
||||
}
|
||||
Signal::BrowseSettled(id) => {
|
||||
this.settle_browse(id, cx);
|
||||
}
|
||||
Signal::Forks => {
|
||||
this.sync_forks(true, cx);
|
||||
}
|
||||
|
||||
@@ -469,6 +469,18 @@ pub struct GroupCandidate {
|
||||
pub metadata: GroupMetadata,
|
||||
}
|
||||
|
||||
impl GroupCandidate {
|
||||
pub fn display_name(&self) -> &str {
|
||||
self.metadata
|
||||
.name()
|
||||
.unwrap_or_else(|| self.key.id().as_str())
|
||||
}
|
||||
|
||||
pub fn display_image(&self) -> Option<&str> {
|
||||
self.metadata.picture.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct GroupAdmins(Vec<(PublicKey, Vec<String>)>);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user