notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 8fd3e39af3fdd8343710650c83de61b2d54cc812
parent 647d15be216eda1a4808f2d54095451238cf310c
Author: William Casarin <jb55@jb55.com>
Date:   Thu,  4 Dec 2025 01:25:39 -0800

Merge hide futures notes by elsat #1202

alltheseas (4):
      Filter out future-dated notes
      Run cargo fmt
      Update time.rs

Diffstat:
Mcrates/notedeck/src/lib.rs | 5+++--
Mcrates/notedeck/src/time.rs | 35+++++++++++++++++++++++++++++++----
Mcrates/notedeck_columns/src/actionbar.rs | 11++++++++---
Mcrates/notedeck_columns/src/timeline/mod.rs | 14++++++++++++--
4 files changed, 54 insertions(+), 11 deletions(-)

diff --git a/crates/notedeck/src/lib.rs b/crates/notedeck/src/lib.rs @@ -83,8 +83,9 @@ pub use route::DrawerRouter; pub use storage::{AccountStorage, DataPath, DataPathType, Directory}; pub use style::NotedeckTextStyle; pub use theme::ColorTheme; -pub use time::time_ago_since; -pub use time::time_format; +pub use time::{ + is_future_timestamp, time_ago_since, time_format, unix_time_secs, MAX_FUTURE_NOTE_SKEW_SECS, +}; pub use timecache::TimeCached; pub use unknowns::{get_unknown_note_ids, NoteRefsUnkIdAction, SingleUnkIdAction, UnknownIds}; pub use urls::{supported_mime_hosted_at_url, SupportedMimeType, UrlMimes}; diff --git a/crates/notedeck/src/time.rs b/crates/notedeck/src/time.rs @@ -10,6 +10,22 @@ const ONE_WEEK_IN_SECONDS: u64 = 604_800; const ONE_MONTH_IN_SECONDS: u64 = 2_592_000; // 30 days const ONE_YEAR_IN_SECONDS: u64 = 31_536_000; // 365 days +/// Maximum tolerated skew for note timestamps in the future (2 minutes / 120 seconds). +pub const MAX_FUTURE_NOTE_SKEW_SECS: u64 = 2 * ONE_MINUTE_IN_SECONDS; + +/// Returns the current UNIX timestamp in seconds. +pub fn unix_time_secs() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Time went backwards") + .as_secs() +} + +/// Whether `timestamp` is further in the future than the allowed skew. +pub fn is_future_timestamp(timestamp: u64, now: u64) -> bool { + timestamp > now + MAX_FUTURE_NOTE_SKEW_SECS +} + /// Calculate relative time between two timestamps, with two units only /// when the scale is large enough (e.g., "1y 6m", "5d 4h"), /// but not for hours/minutes/seconds. @@ -95,10 +111,7 @@ pub fn time_format(_i18n: &mut Localization, timestamp: u64) -> String { } pub fn time_ago_since(i18n: &mut Localization, timestamp: u64) -> String { - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - .as_secs(); + let now = unix_time_secs(); time_ago_between(i18n, timestamp, now) } @@ -354,4 +367,18 @@ mod tests { result ); } + + #[test] + fn test_future_skew_helper() { + let now = 1_000_000u64; + assert!(!is_future_timestamp(now, now)); + assert!(!is_future_timestamp( + now + MAX_FUTURE_NOTE_SKEW_SECS - 1, + now + )); + assert!(is_future_timestamp( + now + MAX_FUTURE_NOTE_SKEW_SECS + 1, + now + )); + } } diff --git a/crates/notedeck_columns/src/actionbar.rs b/crates/notedeck_columns/src/actionbar.rs @@ -15,10 +15,10 @@ use egui_nav::Percent; use enostr::{FilledKeypair, NoteId, Pubkey, RelayPool}; use nostrdb::{IngestMetadata, Ndb, NoteBuilder, NoteKey, Transaction}; use notedeck::{ - get_wallet_for, + get_wallet_for, is_future_timestamp, note::{reaction_sent_id, ReactAction, ZapTargetAmount}, - Accounts, GlobalWallet, Images, MediaJobSender, NoteAction, NoteCache, NoteZapTargetOwned, - UnknownIds, ZapAction, ZapTarget, ZappingError, Zaps, + unix_time_secs, Accounts, GlobalWallet, Images, MediaJobSender, NoteAction, NoteCache, + NoteZapTargetOwned, UnknownIds, ZapAction, ZapTarget, ZappingError, Zaps, }; use notedeck_ui::media::MediaViewerFlags; use tracing::error; @@ -504,6 +504,7 @@ pub fn process_thread_notes( return; } + let now = unix_time_secs(); let mut has_spliced_resp = false; let mut num_new_notes = 0; for key in notes { @@ -517,6 +518,10 @@ pub fn process_thread_notes( continue; }; + if is_future_timestamp(note.created_at(), now) { + continue; + } + // Ensure that unknown ids are captured when inserting notes UnknownIds::update_from_note(txn, ndb, unknown_ids, note_cache, &note); diff --git a/crates/notedeck_columns/src/timeline/mod.rs b/crates/notedeck_columns/src/timeline/mod.rs @@ -9,8 +9,8 @@ use crate::{ use notedeck::{ contacts::hybrid_contacts_filter, filter::{self, HybridFilter}, - tr, Accounts, CachedNote, ContactState, FilterError, FilterState, FilterStates, Localization, - NoteCache, NoteRef, UnknownIds, + is_future_timestamp, tr, unix_time_secs, Accounts, CachedNote, ContactState, FilterError, + FilterState, FilterStates, Localization, NoteCache, NoteRef, UnknownIds, }; use egui_virtual_list::VirtualList; @@ -368,8 +368,13 @@ impl Timeline { filters }; + let now = unix_time_secs(); let mut unknown_pks = HashSet::new(); for note_ref in notes { + if is_future_timestamp(note_ref.created_at, now) { + continue; + } + for (view, filter) in filters.iter().enumerate() { if let Ok(note) = ndb.get_note_by_key(txn, note_ref.key) { if filter( @@ -417,6 +422,7 @@ impl Timeline { reversed: bool, ) -> Result<()> { let mut payloads: Vec<NotePayload> = Vec::with_capacity(new_note_ids.len()); + let now = unix_time_secs(); for key in new_note_ids { let note = if let Ok(note) = ndb.get_note_by_key(txn, *key) { @@ -429,6 +435,10 @@ impl Timeline { continue; }; + if is_future_timestamp(note.created_at(), now) { + continue; + } + // Ensure that unknown ids are captured when inserting notes // into the timeline UnknownIds::update_from_note(txn, ndb, unknown_ids, note_cache, &note);