fix(desktop): populate last_message_at in channel browser (#951)

Signed-off-by: Will Pfleger <wpfleger@squareup.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
Will Pfleger
2026-06-10 12:23:44 -07:00
committed by GitHub
co-authored by npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7
parent e5f0c32648
commit b792aa4704
2 changed files with 46 additions and 1 deletions
@@ -156,6 +156,51 @@ pub async fn get_channels(state: State<'_, AppState>) -> Result<Vec<ChannelInfo>
}
}
// Populate last_message_at by fetching the most recent human message per
// channel. Uses per-channel filters (single #h value each) so the relay can
// push the query to its indexed channel_id column. Multi-value #h is NOT
// SQL-pushed and would silently drop quieter channels under the global limit.
let channel_ids: Vec<String> = channels.iter().map(|c| c.id.clone()).collect();
if !channel_ids.is_empty() {
let filters: Vec<serde_json::Value> = channel_ids
.iter()
.map(|id| {
serde_json::json!({
"kinds": [9, 40002],
"#h": [id],
"limit": 1
})
})
.collect();
let message_events = query_relay(&state, &filters).await.unwrap_or_default();
let mut last_message_by_channel: std::collections::HashMap<String, u64> =
std::collections::HashMap::new();
for ev in &message_events {
if let Some(ch_id) = ev.tags.iter().find_map(|t| {
let s = t.as_slice();
(s.len() >= 2 && s[0] == "h").then(|| s[1].clone())
}) {
let ts = ev.created_at.as_secs();
last_message_by_channel
.entry(ch_id)
.and_modify(|existing| {
if ts > *existing {
*existing = ts;
}
})
.or_insert(ts);
}
}
for channel in &mut channels {
if let Some(&ts) = last_message_by_channel.get(&channel.id) {
channel.last_message_at = Some(nostr_convert::timestamp_to_iso(ts));
}
}
}
// NIP-DV: drop DMs the viewer has hidden. The relay maintains a per-viewer
// parameterized-replaceable snapshot (kind:30622, d=my pubkey) whose `h`
// tags list currently-hidden DM channel ids. The snapshot also carries
+1 -1
View File
@@ -533,7 +533,7 @@ pub fn relay_members_from_event(event: &Event) -> Value {
// ── Time helpers ────────────────────────────────────────────────────────────
/// Convert a unix-seconds timestamp to a UTC RFC-3339 string.
fn timestamp_to_iso(secs: u64) -> String {
pub(crate) fn timestamp_to_iso(secs: u64) -> String {
use std::time::{Duration, SystemTime, UNIX_EPOCH};
let dt = UNIX_EPOCH + Duration::from_secs(secs);
// Format manually as RFC-3339 — the `time` crate is already a transitive