From 6c3b65c47f9c294115cee2702278ed324533cba2 Mon Sep 17 00:00:00 2001 From: Aaron Acev Date: Fri, 31 Jul 2026 11:29:40 -0700 Subject: [PATCH] fix(desktop): show workflows across joined channels Co-authored-by: Aaron Acev Signed-off-by: Aaron Acev --- desktop/src-tauri/src/commands/workflows.rs | 38 ++++++++++++------- .../src-tauri/src/commands/workflows_tests.rs | 11 ++++++ 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/desktop/src-tauri/src/commands/workflows.rs b/desktop/src-tauri/src/commands/workflows.rs index 1d5f309fb5..4cc11b9aef 100644 --- a/desktop/src-tauri/src/commands/workflows.rs +++ b/desktop/src-tauri/src/commands/workflows.rs @@ -70,11 +70,12 @@ pub async fn get_channel_workflows( /// /// The Workflows overview screen previously issued one `get_channel_workflows` /// query per member channel (`Promise.all` fanout in `WorkflowsView`), i.e. N -/// relay POSTs. A nostr `#h` filter matches ANY of its listed values, so one -/// query with all channel ids returns the same set. Each `WorkflowWire` carries -/// its own `channel_id` (from the event's `h` tag), so the frontend can still -/// group results by channel. Neither this nor the per-channel command sets a -/// `limit`, so batching does not change result completeness. +/// relay POSTs. Keep the single relay round-trip, but send one filter per +/// channel: deployed relays can treat multiple values inside one `#h` filter as +/// a single value, which makes workflows outside the first channel disappear +/// from the overview. Multiple Nostr filters are ORed and preserve the intended +/// cross-channel result set. Each `WorkflowWire` carries its own `channel_id` +/// (from the event's `h` tag), so the frontend can still group results. #[tauri::command] pub async fn get_channels_workflows( channel_ids: Vec, @@ -84,14 +85,8 @@ pub async fn get_channels_workflows( return Ok(Vec::new()); } - let events = query_relay( - &state, - &[serde_json::json!({ - "kinds": [30620], - "#h": channel_ids, - })], - ) - .await?; + let filters = workflow_channel_filters(channel_ids); + let events = query_relay(&state, &filters).await?; Ok(events.iter().map(workflow_from_event).collect()) } @@ -301,6 +296,23 @@ fn now_secs() -> i64 { .unwrap_or_default() } +/// Build one workflow-definition filter per channel. +/// +/// Multiple filters are ORed by Nostr relays. Keeping each `#h` array to one +/// value also works with deployed relays that do not implement multi-value tag +/// matching correctly. +fn workflow_channel_filters(channel_ids: Vec) -> Vec { + channel_ids + .into_iter() + .map(|channel_id| { + serde_json::json!({ + "kinds": [30620], + "#h": [channel_id], + }) + }) + .collect() +} + /// First value of the tag whose name matches `name` (e.g. `d`, `h`). fn tag_value(ev: &nostr::Event, name: &str) -> Option { ev.tags.iter().find_map(|t| { diff --git a/desktop/src-tauri/src/commands/workflows_tests.rs b/desktop/src-tauri/src/commands/workflows_tests.rs index f07f4b0f42..773047a69f 100644 --- a/desktop/src-tauri/src/commands/workflows_tests.rs +++ b/desktop/src-tauri/src/commands/workflows_tests.rs @@ -207,3 +207,14 @@ fn runs_and_approvals_serialize_to_bare_empty_array() { "[]" ); } + +#[test] +fn workflow_channel_filters_use_one_h_value_per_filter() { + let second_channel = "33333333-3333-3333-3333-333333333333"; + let filters = workflow_channel_filters(vec![CHAN.to_string(), second_channel.to_string()]); + + assert_eq!(filters.len(), 2); + assert_eq!(filters[0]["kinds"], serde_json::json!([30620])); + assert_eq!(filters[0]["#h"], serde_json::json!([CHAN])); + assert_eq!(filters[1]["#h"], serde_json::json!([second_channel])); +}