commit 2929bfaf0c82c37143d57d9029f95d7c4e3adb52
parent adba4ece22c252ca47696191f8cbaf4108861480
Author: William Casarin <jb55@jb55.com>
Date: Mon, 20 Oct 2025 11:20:54 -0700
Merge seen note perf improvements by kernel #1174
kernelkind (2):
fix(notif-indicator): more performant impl
refactor(NoteFreshness): remove now unnecessary `NoteFreshness`
Diffstat:
4 files changed, 20 insertions(+), 146 deletions(-)
diff --git a/crates/notedeck_columns/src/app.rs b/crates/notedeck_columns/src/app.rs
@@ -698,11 +698,7 @@ fn render_damus_mobile(
break 'brk;
}
- let unseen_notif = unseen_notification(
- app,
- app_ctx.ndb,
- app_ctx.accounts.get_selected_account().key.pubkey,
- );
+ let unseen_notif = unseen_notification(app, app_ctx.accounts, active_col);
if skb_rect.is_none() {
let resp = toolbar(ui, unseen_notif);
diff --git a/crates/notedeck_columns/src/timeline/cache.rs b/crates/notedeck_columns/src/timeline/cache.rs
@@ -258,7 +258,7 @@ impl TimelineCache {
return;
};
- tl.current_view_mut().freshness.set_fresh();
+ tl.seen_latest_notes = true;
}
}
diff --git a/crates/notedeck_columns/src/timeline/mod.rs b/crates/notedeck_columns/src/timeline/mod.rs
@@ -8,7 +8,6 @@ use crate::{
use notedeck::{
contacts::hybrid_contacts_filter,
- debouncer::Debouncer,
filter::{self, HybridFilter},
tr, Accounts, CachedNote, ContactState, FilterError, FilterState, FilterStates, Localization,
NoteCache, NoteRef, UnknownIds,
@@ -17,12 +16,8 @@ use notedeck::{
use egui_virtual_list::VirtualList;
use enostr::{PoolRelay, Pubkey, RelayPool};
use nostrdb::{Filter, Ndb, Note, NoteKey, Transaction};
-use std::{
- cell::RefCell,
- collections::HashSet,
- time::{Duration, UNIX_EPOCH},
-};
-use std::{rc::Rc, time::SystemTime};
+use std::rc::Rc;
+use std::{cell::RefCell, collections::HashSet};
use tracing::{debug, error, info, warn};
@@ -111,7 +106,6 @@ pub struct TimelineTab {
pub selection: i32,
pub filter: ViewFilter,
pub list: Rc<RefCell<VirtualList>>,
- pub freshness: NotesFreshness,
}
impl TimelineTab {
@@ -153,7 +147,6 @@ impl TimelineTab {
selection,
filter,
list,
- freshness: NotesFreshness::default(),
}
}
@@ -243,6 +236,7 @@ pub struct Timeline {
pub filter: FilterStates,
pub views: Vec<TimelineTab>,
pub selected_view: usize,
+ pub seen_latest_notes: bool,
pub subscription: TimelineSub,
pub enable_front_insert: bool,
@@ -317,6 +311,7 @@ impl Timeline {
subscription,
selected_view,
enable_front_insert,
+ seen_latest_notes: false,
}
}
@@ -489,7 +484,7 @@ impl Timeline {
if new_note_ids.is_empty() {
return Ok(());
} else {
- debug!("{} new notes! {:?}", new_note_ids.len(), new_note_ids);
+ self.seen_latest_notes = false;
}
self.insert(&new_note_ids, ndb, txn, unknown_ids, note_cache, reversed)
@@ -880,101 +875,3 @@ pub fn is_timeline_ready(
}
}
}
-
-#[derive(Debug)]
-pub struct NotesFreshness {
- debouncer: Debouncer,
- state: NotesFreshnessState,
-}
-
-#[derive(Debug)]
-enum NotesFreshnessState {
- Fresh {
- timestamp_viewed: u64,
- },
- Stale {
- have_unseen: bool,
- timestamp_last_viewed: u64,
- },
-}
-
-impl Default for NotesFreshness {
- fn default() -> Self {
- Self {
- debouncer: Debouncer::new(Duration::from_secs(2)),
- state: NotesFreshnessState::Stale {
- have_unseen: true,
- timestamp_last_viewed: 0,
- },
- }
- }
-}
-
-impl NotesFreshness {
- pub fn set_fresh(&mut self) {
- if !self.debouncer.should_act() {
- return;
- }
- self.state = NotesFreshnessState::Fresh {
- timestamp_viewed: timestamp_now(),
- };
- self.debouncer.bounce();
- }
-
- pub fn update(&mut self, check_have_unseen: impl FnOnce(u64) -> bool) {
- if !self.debouncer.should_act() {
- return;
- }
-
- match &self.state {
- NotesFreshnessState::Fresh { timestamp_viewed } => {
- let Ok(dur) = SystemTime::now()
- .duration_since(UNIX_EPOCH + Duration::from_secs(*timestamp_viewed))
- else {
- return;
- };
-
- if dur > Duration::from_secs(2) {
- self.state = NotesFreshnessState::Stale {
- have_unseen: check_have_unseen(*timestamp_viewed),
- timestamp_last_viewed: *timestamp_viewed,
- };
- }
- }
- NotesFreshnessState::Stale {
- have_unseen,
- timestamp_last_viewed,
- } => {
- if *have_unseen {
- return;
- }
-
- self.state = NotesFreshnessState::Stale {
- have_unseen: check_have_unseen(*timestamp_last_viewed),
- timestamp_last_viewed: *timestamp_last_viewed,
- };
- }
- }
-
- self.debouncer.bounce();
- }
-
- pub fn has_unseen(&self) -> bool {
- match &self.state {
- NotesFreshnessState::Fresh {
- timestamp_viewed: _,
- } => false,
- NotesFreshnessState::Stale {
- have_unseen,
- timestamp_last_viewed: _,
- } => *have_unseen,
- }
- }
-}
-
-fn timestamp_now() -> u64 {
- std::time::SystemTime::now()
- .duration_since(std::time::UNIX_EPOCH)
- .unwrap_or(Duration::ZERO)
- .as_secs()
-}
diff --git a/crates/notedeck_columns/src/toolbar.rs b/crates/notedeck_columns/src/toolbar.rs
@@ -1,4 +1,3 @@
-use nostrdb::Transaction;
use notedeck::AppContext;
use crate::{
@@ -10,42 +9,24 @@ use crate::{
#[profiling::function]
pub fn unseen_notification(
columns: &mut Damus,
- ndb: &nostrdb::Ndb,
- current_pk: notedeck::enostr::Pubkey,
+ accounts: ¬edeck::Accounts,
+ active_col: usize,
) -> bool {
- let Some(tl) = columns
- .timeline_cache
- .get_mut(&TimelineKind::Notifications(current_pk))
- else {
- return false;
- };
+ let top = columns.columns(accounts).column(active_col).router().top();
+ let current_pk = accounts.get_selected_account().keypair().pubkey;
- let freshness = &mut tl.current_view_mut().freshness;
- freshness.update(|timestamp_last_viewed| {
- profiling::scope!("NotesFreshness::update closure");
- let filter = {
- profiling::scope!("NotesFreshness::update filter instantiation");
- enostr::Filter::new_with_capacity(1)
- .pubkeys([current_pk.bytes()])
- .kinds(crate::timeline::kind::notification_kinds())
- .limit(1)
- .since(timestamp_last_viewed)
- .build()
- };
- let txn = Transaction::new(ndb).expect("txn");
-
- let Some(res) = {
- profiling::scope!("NoteFreshness::update Ndb::query");
- ndb.query(&txn, &[filter], 1)
- }
- .ok() else {
+ if let Route::Timeline(TimelineKind::Notifications(notif_pk)) = top {
+ if notif_pk == current_pk {
return false;
- };
+ }
+ }
- !res.is_empty()
- });
+ let notif_kind = TimelineKind::Notifications(*current_pk);
+ let Some(tl) = columns.timeline_cache.get_mut(¬if_kind) else {
+ return false;
+ };
- freshness.has_unseen()
+ !tl.seen_latest_notes
}
/// When you click the toolbar button, these actions