Skip to content

Commit e6b369e

Browse files
authored
Merge pull request #893 from multiplex55/codex/optimize-recent-notes-widget-caching
Cache `recent_notes` rows and refresh only on notes version changes
2 parents cc0d29b + 0c089b4 commit e6b369e

1 file changed

Lines changed: 71 additions & 24 deletions

File tree

src/dashboard/widgets/recent_notes.rs

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,25 @@ impl Default for RecentNotesConfig {
5555

5656
pub struct RecentNotesWidget {
5757
cfg: RecentNotesConfig,
58+
cached: Vec<CachedRecentNote>,
59+
last_notes_version: u64,
60+
}
61+
62+
#[derive(Clone)]
63+
struct CachedRecentNote {
64+
title: String,
65+
slug: String,
66+
tags: Vec<String>,
67+
snippet: String,
5868
}
5969

6070
impl RecentNotesWidget {
6171
pub fn new(cfg: RecentNotesConfig) -> Self {
62-
Self { cfg }
72+
Self {
73+
cfg,
74+
cached: Vec::new(),
75+
last_notes_version: u64::MAX,
76+
}
6377
}
6478

6579
pub fn settings_ui(
@@ -132,11 +146,11 @@ impl RecentNotesWidget {
132146
.unwrap_or(0)
133147
}
134148

135-
fn build_action(&self, note: &Note) -> (Action, Option<String>) {
149+
fn build_cached_action(&self, note: &CachedRecentNote) -> (Action, Option<String>) {
136150
match self.cfg.open_mode {
137151
NoteOpenMode::Panel => (
138152
Action {
139-
label: note.alias.as_ref().unwrap_or(&note.title).clone(),
153+
label: note.title.clone(),
140154
desc: "Note".into(),
141155
action: format!("note:open:{}", note.slug),
142156
args: None,
@@ -159,10 +173,7 @@ impl RecentNotesWidget {
159173
action: "query:note open ".into(),
160174
args: None,
161175
},
162-
Some(format!(
163-
"note open {}",
164-
note.alias.as_ref().unwrap_or(&note.title)
165-
)),
176+
Some(format!("note open {}", note.title)),
166177
),
167178
}
168179
}
@@ -181,13 +192,49 @@ impl RecentNotesWidget {
181192
clean.to_string()
182193
}
183194
}
195+
196+
fn refresh_cache(&mut self, ctx: &DashboardContext<'_>) {
197+
if self.last_notes_version == ctx.notes_version {
198+
return;
199+
}
200+
201+
let snapshot = ctx.data_cache.snapshot();
202+
let mut notes_with_ts: Vec<(u64, Note)> = snapshot
203+
.notes
204+
.as_ref()
205+
.iter()
206+
.filter(|note| {
207+
self.cfg
208+
.filter_tag
209+
.as_ref()
210+
.is_none_or(|tag| note.tags.iter().any(|t| t.eq_ignore_ascii_case(tag)))
211+
})
212+
.map(|note| (Self::modified_ts(note), note.clone()))
213+
.collect();
214+
215+
notes_with_ts.sort_by(|a, b| b.0.cmp(&a.0));
216+
notes_with_ts.truncate(self.cfg.count);
217+
218+
self.cached = notes_with_ts
219+
.into_iter()
220+
.map(|(_, note)| {
221+
let snippet = Self::snippet(&note);
222+
CachedRecentNote {
223+
title: note.alias.as_ref().unwrap_or(&note.title).clone(),
224+
slug: note.slug,
225+
tags: note.tags,
226+
snippet,
227+
}
228+
})
229+
.collect();
230+
231+
self.last_notes_version = ctx.notes_version;
232+
}
184233
}
185234

186235
impl Default for RecentNotesWidget {
187236
fn default() -> Self {
188-
Self {
189-
cfg: RecentNotesConfig::default(),
190-
}
237+
Self::new(RecentNotesConfig::default())
191238
}
192239
}
193240

@@ -198,15 +245,9 @@ impl Widget for RecentNotesWidget {
198245
ctx: &DashboardContext<'_>,
199246
_activation: WidgetActivation,
200247
) -> Option<WidgetAction> {
201-
let snapshot = ctx.data_cache.snapshot();
202-
let mut notes: Vec<Note> = snapshot.notes.as_ref().clone();
203-
if let Some(tag) = &self.cfg.filter_tag {
204-
notes.retain(|n| n.tags.iter().any(|t| t.eq_ignore_ascii_case(tag)));
205-
}
206-
notes.sort_by(|a, b| Self::modified_ts(b).cmp(&Self::modified_ts(a)));
207-
notes.truncate(self.cfg.count);
248+
self.refresh_cache(ctx);
208249

209-
if notes.is_empty() {
250+
if self.cached.is_empty() {
210251
ui.label("No notes found");
211252
return None;
212253
}
@@ -224,16 +265,15 @@ impl Widget for RecentNotesWidget {
224265
egui::ScrollArea::both()
225266
.id_source(scroll_id)
226267
.auto_shrink([false; 2])
227-
.show_rows(ui, row_height, notes.len(), |ui, range| {
228-
for note in &notes[range] {
229-
let display = note.alias.as_ref().unwrap_or(&note.title);
230-
let (action, query_override) = self.build_action(note);
268+
.show_rows(ui, row_height, self.cached.len(), |ui, range| {
269+
for note in &self.cached[range] {
270+
let (action, query_override) = self.build_cached_action(note);
231271
let mut clicked_row = false;
232272
ui.vertical(|ui| {
233-
clicked_row |= ui.add(egui::Button::new(display).wrap(false)).clicked();
273+
clicked_row |= ui.add(egui::Button::new(&note.title).wrap(false)).clicked();
234274
if self.cfg.show_snippet {
235275
ui.add(
236-
egui::Label::new(egui::RichText::new(Self::snippet(note)).small())
276+
egui::Label::new(egui::RichText::new(&note.snippet).small())
237277
.wrap(false),
238278
);
239279
}
@@ -259,4 +299,11 @@ impl Widget for RecentNotesWidget {
259299

260300
clicked
261301
}
302+
303+
fn on_config_updated(&mut self, settings: &serde_json::Value) {
304+
if let Ok(cfg) = serde_json::from_value::<RecentNotesConfig>(settings.clone()) {
305+
self.cfg = cfg;
306+
self.last_notes_version = u64::MAX;
307+
}
308+
}
262309
}

0 commit comments

Comments
 (0)