notedeck

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

commit d576082297c8be5daf6c52b92568fb840ac2a957
parent 0eec8c8c2b219d42b69bf5477f62f7d223029089
Author: William Casarin <jb55@jb55.com>
Date:   Sat,  8 Jun 2024 12:07:29 -0500

reset virtual list if notes are spliced into timeline

Calling egui_virtual_list's `items_inserted_at_start` is incorrect if we
insert notes inbetween other notes in the timeline. To prevent our list
getting confused, let's handle this case explicitly by calling 'reset'
when we splice notes in.

Ideally we would update egui_virtual_list to handle spliced-in items,
but we will leave that to a future update.

Diffstat:
Msrc/app.rs | 62++++++++++++++++++++++++++++----------------------------------
Msrc/timeline.rs | 16++++++++++++++--
2 files changed, 42 insertions(+), 36 deletions(-)

diff --git a/src/app.rs b/src/app.rs @@ -7,7 +7,7 @@ use crate::imgcache::ImageCache; use crate::notecache::{CachedNote, NoteCache}; use crate::route::Route; use crate::timeline; -use crate::timeline::{NoteRef, Timeline, ViewFilter}; +use crate::timeline::{MergeKind, NoteRef, Timeline, ViewFilter}; use crate::ui; use crate::ui::profile::SimpleProfilePreviewController; use crate::ui::DesktopSidePanel; @@ -381,24 +381,9 @@ fn poll_notes_for_timeline<'a>( // ViewFilter::NotesAndReplies { - let timeline = &mut damus.timelines[timeline_ind]; - - let prev_items = timeline.notes(ViewFilter::NotesAndReplies).len(); - let refs: Vec<NoteRef> = new_refs.iter().map(|(_note, nr)| *nr).collect(); - timeline.view_mut(ViewFilter::NotesAndReplies).notes = - timeline::merge_sorted_vecs(timeline.notes(ViewFilter::NotesAndReplies), &refs); - - let new_items = timeline.notes(ViewFilter::NotesAndReplies).len() - prev_items; - - // TODO: technically items could have been added inbetween - if new_items > 0 { - damus.timelines[timeline_ind] - .view(ViewFilter::NotesAndReplies) - .list - .borrow_mut() - .items_inserted_at_start(new_items); - } + + insert_notes_into_timeline(damus, timeline_ind, ViewFilter::NotesAndReplies, &refs) } // @@ -416,26 +401,35 @@ fn poll_notes_for_timeline<'a>( } } - let timeline = &mut damus.timelines[timeline_ind]; - - let prev_items = timeline.notes(ViewFilter::Notes).len(); - - timeline.view_mut(ViewFilter::Notes).notes = - timeline::merge_sorted_vecs(timeline.notes(ViewFilter::Notes), &filtered_refs); + insert_notes_into_timeline(damus, timeline_ind, ViewFilter::Notes, &filtered_refs); + } - let new_items = timeline.notes(ViewFilter::Notes).len() - prev_items; + Ok(()) +} - // TODO: technically items could have been added inbetween - if new_items > 0 { - damus.timelines[timeline_ind] - .view(ViewFilter::Notes) - .list - .borrow_mut() - .items_inserted_at_start(new_items); +fn insert_notes_into_timeline( + app: &mut Damus, + timeline_ind: usize, + filter: ViewFilter, + new_refs: &[NoteRef], +) { + let timeline = &mut app.timelines[timeline_ind]; + let num_prev_items = timeline.notes(filter).len(); + let (notes, merge_kind) = timeline::merge_sorted_vecs(timeline.notes(filter), new_refs); + + timeline.view_mut(filter).notes = notes; + let new_items = timeline.notes(filter).len() - num_prev_items; + + // TODO: technically items could have been added inbetween + if new_items > 0 { + let mut list = app.timelines[timeline_ind].view(filter).list.borrow_mut(); + + match merge_kind { + // TODO: update egui_virtual_list to support spliced inserts + MergeKind::Spliced => list.reset(), + MergeKind::FrontInsert => list.items_inserted_at_start(new_items), } } - - Ok(()) } #[cfg(feature = "profiling")] diff --git a/src/timeline.rs b/src/timeline.rs @@ -35,6 +35,7 @@ impl PartialOrd for NoteRef { } } +#[derive(Copy, Clone, Eq, PartialEq)] pub enum ViewFilter { Notes, NotesAndReplies, @@ -306,13 +307,24 @@ pub fn timeline_view(ui: &mut egui::Ui, app: &mut Damus, timeline: usize) { }); } -pub fn merge_sorted_vecs<T: Ord + Copy>(vec1: &[T], vec2: &[T]) -> Vec<T> { +pub enum MergeKind { + FrontInsert, + Spliced, +} + +pub fn merge_sorted_vecs<T: Ord + Copy>(vec1: &[T], vec2: &[T]) -> (Vec<T>, MergeKind) { let mut merged = Vec::with_capacity(vec1.len() + vec2.len()); let mut i = 0; let mut j = 0; + let mut result: Option<MergeKind> = None; while i < vec1.len() && j < vec2.len() { if vec1[i] <= vec2[j] { + if result.is_none() && j < vec2.len() { + // if we're pushing from our large list and still have + // some left in vec2, then this is a splice + result = Some(MergeKind::Spliced); + } merged.push(vec1[i]); i += 1; } else { @@ -329,5 +341,5 @@ pub fn merge_sorted_vecs<T: Ord + Copy>(vec1: &[T], vec2: &[T]) -> Vec<T> { merged.extend_from_slice(&vec2[j..]); } - merged + (merged, result.unwrap_or(MergeKind::FrontInsert)) }