commit 1e6faee64497ecffb76c6bc7935908ede8e65759
parent dd6ab8320d1b918b7348cbb315bf3993a5e1eaec
Author: triesap <triesap@radroots.dev>
Date: Sun, 4 Jan 2026 00:25:56 +0000
jsonrpc: dedupe list_set fetch across multiple d tags
- Add helper to fetch events per d tag and merge results
- Deduplicate fetched events by id using HashSet
- Import RadrootsNostrClient for helper signature
- Truncate response rows to requested limit
Diffstat:
1 file changed, 59 insertions(+), 13 deletions(-)
diff --git a/src/api/jsonrpc/methods/events/list_set/list.rs b/src/api/jsonrpc/methods/events/list_set/list.rs
@@ -3,6 +3,7 @@
use anyhow::Result;
use jsonrpsee::server::RpcModule;
use serde::{Deserialize, Serialize};
+use std::collections::HashSet;
use std::time::Duration;
use crate::api::jsonrpc::nostr::{event_tags, event_view_with_tags, NostrEventView};
@@ -12,6 +13,7 @@ use radroots_events::kinds::{is_nip51_list_set_kind, KIND_LIST_SET_GENERIC};
use radroots_events::list_set::RadrootsListSet;
use radroots_events_codec::list_set::decode::list_set_from_tags;
use radroots_nostr::prelude::{
+ RadrootsNostrClient,
RadrootsNostrEvent,
RadrootsNostrFilter,
RadrootsNostrKind,
@@ -89,6 +91,52 @@ where
items
}
+async fn fetch_list_set_events(
+ client: &RadrootsNostrClient,
+ base_filter: RadrootsNostrFilter,
+ d_tags: Option<Vec<String>>,
+ timeout: Duration,
+) -> Result<Vec<RadrootsNostrEvent>, RpcError> {
+ match d_tags {
+ Some(d_tags) if d_tags.len() > 1 => {
+ let mut events = Vec::new();
+ let mut seen = HashSet::new();
+ for d_tag in d_tags.into_iter().filter(|tag| !tag.trim().is_empty()) {
+ let filter = base_filter.clone().identifiers([d_tag]);
+ let items = client
+ .fetch_events(filter, timeout)
+ .await
+ .map_err(|e| RpcError::Other(format!("fetch failed: {e}")))?;
+ for item in items {
+ let id = item.id.to_string();
+ if seen.insert(id) {
+ events.push(item);
+ }
+ }
+ }
+ Ok(events)
+ }
+ Some(d_tags) => {
+ let mut filter = base_filter;
+ if let Some(d_tag) = d_tags.into_iter().find(|tag| !tag.trim().is_empty()) {
+ filter = filter.identifiers([d_tag]);
+ }
+ let events = client
+ .fetch_events(filter, timeout)
+ .await
+ .map_err(|e| RpcError::Other(format!("fetch failed: {e}")))?;
+ Ok(events)
+ }
+ None => {
+ let events = client
+ .fetch_events(base_filter, timeout)
+ .await
+ .map_err(|e| RpcError::Other(format!("fetch failed: {e}")))?;
+ Ok(events)
+ }
+ }
+}
+
pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Result<()> {
registry.track("events.list_set.list");
m.register_async_method("events.list_set.list", |params, ctx, _| async move {
@@ -120,22 +168,20 @@ pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Res
filter = filter.author(ctx.state.pubkey);
}
- if let Some(d_tags) = d_tags {
- if !d_tags.is_empty() {
- filter = filter.identifiers(d_tags);
- }
- }
-
filter = apply_time_bounds(filter, since, until);
- let events = ctx
- .state
- .client
- .fetch_events(filter, Duration::from_secs(timeout_or(timeout_secs)))
- .await
- .map_err(|e| RpcError::Other(format!("fetch failed: {e}")))?;
+ let events = fetch_list_set_events(
+ &ctx.state.client,
+ filter,
+ d_tags,
+ Duration::from_secs(timeout_or(timeout_secs)),
+ )
+ .await?;
- let items = build_list_set_rows(events);
+ let mut items = build_list_set_rows(events);
+ if items.len() > limit {
+ items.truncate(limit);
+ }
Ok::<ListSetListResponse, RpcError>(ListSetListResponse { list_sets: items })
})?;