commit ba67d65435394a7a12d70fab6ccd0ed9af814cf1
parent eac5d41e3cb1587c65d1fe29cbc23df0ec302ed5
Author: alltheseas <alltheseas@users.noreply.github.com>
Date: Mon, 10 Nov 2025 20:37:47 -0600
Filter out future-dated notes
Diffstat:
4 files changed, 51 insertions(+), 7 deletions(-)
diff --git a/crates/notedeck/src/lib.rs b/crates/notedeck/src/lib.rs
@@ -85,8 +85,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 (24h in seconds).
+pub const MAX_FUTURE_NOTE_SKEW_SECS: u64 = ONE_DAY_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,15 @@ 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
@@ -16,7 +16,9 @@ use enostr::{FilledKeypair, NoteId, Pubkey, RelayPool};
use nostrdb::{IngestMetadata, Ndb, NoteBuilder, NoteKey, Transaction};
use notedeck::{
get_wallet_for,
+ is_future_timestamp,
note::{reaction_sent_id, ReactAction, ZapTargetAmount},
+ unix_time_secs,
Accounts, GlobalWallet, Images, NoteAction, NoteCache, NoteZapTargetOwned, UnknownIds,
ZapAction, ZapTarget, ZappingError, Zaps,
};
@@ -506,6 +508,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 {
@@ -519,6 +522,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, ¬e);
diff --git a/crates/notedeck_columns/src/timeline/mod.rs b/crates/notedeck_columns/src/timeline/mod.rs
@@ -9,7 +9,9 @@ use crate::{
use notedeck::{
contacts::hybrid_contacts_filter,
filter::{self, HybridFilter},
- tr, Accounts, CachedNote, ContactState, FilterError, FilterState, FilterStates, Localization,
+ is_future_timestamp, tr,
+ unix_time_secs,
+ Accounts, CachedNote, ContactState, FilterError, FilterState, FilterStates, Localization,
NoteCache, NoteRef, UnknownIds,
};
@@ -368,8 +370,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 +424,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 +437,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, ¬e);