From debad91e29c5094205d40895c353dbf3e9c06348 Mon Sep 17 00:00:00 2001 From: 2ro <17595647+2ro@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:05:24 -0400 Subject: [PATCH] nostr: raise news cap to 18 so English variant survives multi-language batches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A single news post now ships as nine per-`d` language variants and the publisher emits English first, giving it the oldest `created_at` in the batch. With the store capped at 8 events across all d-tags, the English event was always the 9th-newest and got evicted — English readers saw no news. The catch-up/subscription filter also capped fetches at 4, so fresh installs only ever received the newest four events. Raise NEWS_CAP from 8 to 18 (two full nine-language posts) and bump the news filter limit from 4 to 18 to match. Selection logic is unchanged. Extend the reconcile_news test with a nine-variant batch that proves the untagged English event survives under the real cap. --- src/nostr/client.rs | 2 +- src/nostr/store.rs | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/nostr/client.rs b/src/nostr/client.rs index 1256b62b..99da2b10 100644 --- a/src/nostr/client.rs +++ b/src/nostr/client.rs @@ -1387,7 +1387,7 @@ async fn run_service(svc: Arc, wallet: Wallet) { Filter::new() .kind(Kind::LongFormTextNote) .author(pk) - .limit(4) + .limit(18) }); if let Ok(events) = client diff --git a/src/nostr/store.rs b/src/nostr/store.rs index 5bbb85fd..313eaf47 100644 --- a/src/nostr/store.rs +++ b/src/nostr/store.rs @@ -30,8 +30,10 @@ use crate::nostr::types::*; const PROCESSED_TTL_SECS: i64 = 30 * 86_400; /// Cap on stored news posts (newest kept, older pruned) — the panel only ever -/// shows the latest, so this is just a small archive bound. -const NEWS_CAP: usize = 8; +/// shows the latest, so this is just a small archive bound. Sized for two full +/// nine-language posts so the oldest-`created_at` variant (English is published +/// first) is not evicted before its readers see it. +const NEWS_CAP: usize = 18; /// Nostr metadata archive for a wallet. pub struct NostrStore { @@ -385,12 +387,16 @@ mod tests { use super::*; fn item(d: &str, created_at: i64) -> NewsItem { + item_lang(d, created_at, None) + } + + fn item_lang(d: &str, created_at: i64, lang: Option<&str>) -> NewsItem { NewsItem { d: d.to_string(), created_at, title: format!("t{created_at}"), summary: String::new(), - lang: None, + lang: lang.map(str::to_string), published_at: None, } } @@ -418,4 +424,28 @@ mod tests { assert_eq!(all[0].created_at, 9); assert_eq!(all[2].created_at, 7); } + + #[test] + fn nine_language_batch_retains_english_under_news_cap() { + // A single post now ships as nine per-`d` language variants. The + // publisher emits English FIRST, so the untagged (lang == None) English + // event carries the OLDEST `created_at` in the batch. Under the real + // `NEWS_CAP` the whole batch must survive so English readers see it. + let langs = ["es", "fr", "de", "it", "pt", "ja", "zh", "ko"]; + let mut all = vec![]; + // English published first → oldest created_at, no lang tag. + all = reconcile_news(all, item_lang("post-en", 100, None), NEWS_CAP); + for (i, code) in langs.iter().enumerate() { + all = reconcile_news( + all, + item_lang(&format!("post-{code}"), 101 + i as i64, Some(code)), + NEWS_CAP, + ); + } + assert_eq!(all.len(), 9); + assert!( + all.iter().any(|n| n.d == "post-en" && n.lang.is_none()), + "untagged English variant must survive the cap" + ); + } }