notedeck

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

commit 559e9577fc952b5b6493d015618c48c1685e4ae2
parent 563fbb9c4b0c7a90f163e2423aa90091450e2bb3
Author: kernelkind <kernelkind@gmail.com>
Date:   Sat, 13 Sep 2025 14:36:59 -0400

fix: no longer make the scroll position jump oddly

only allow front insert in profile when body is fully obstructed

Closes: https://github.com/damus-io/notedeck/issues/1072

Signed-off-by: kernelkind <kernelkind@gmail.com>

Diffstat:
Mcrates/notedeck_columns/src/timeline/mod.rs | 24+++++++++++++++++++++---
Mcrates/notedeck_columns/src/ui/profile/mod.rs | 30+++++++++++++++++++-----------
2 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/crates/notedeck_columns/src/timeline/mod.rs b/crates/notedeck_columns/src/timeline/mod.rs @@ -133,6 +133,7 @@ impl TimelineTab { ndb: &Ndb, txn: &Transaction, reversed: bool, + use_front_insert: bool, ) -> Option<UnknownPks<'a>> { if payloads.is_empty() { return None; @@ -158,7 +159,11 @@ impl TimelineTab { debug!("spliced when inserting {num_refs} new notes, resetting virtual list",); list.reset(); } - MergeKind::FrontInsert => { + MergeKind::FrontInsert => 's: { + if !use_front_insert { + break 's; + } + // only run this logic if we're reverse-chronological // reversed in this case means chronological, since the // default is reverse-chronological. yeah it's confusing. @@ -210,6 +215,7 @@ pub struct Timeline { pub selected_view: usize, pub subscription: TimelineSub, + pub enable_front_insert: bool, } impl Timeline { @@ -271,12 +277,16 @@ impl Timeline { let subscription = TimelineSub::default(); let selected_view = 0; + // by default, disabled for profiles since they contain widgets above the list items + let enable_front_insert = !matches!(kind, TimelineKind::Profile(_)); + Timeline { kind, filter, views, subscription, selected_view, + enable_front_insert, } } @@ -402,7 +412,9 @@ impl Timeline { match view.filter { ViewFilter::NotesAndReplies => { let res: Vec<&NotePayload<'_>> = payloads.iter().collect(); - if let Some(res) = view.insert(res, ndb, txn, reversed) { + if let Some(res) = + view.insert(res, ndb, txn, reversed, self.enable_front_insert) + { res.process(unknown_ids, ndb, txn); } } @@ -418,7 +430,13 @@ impl Timeline { } } - if let Some(res) = view.insert(filtered_payloads, ndb, txn, reversed) { + if let Some(res) = view.insert( + filtered_payloads, + ndb, + txn, + reversed, + self.enable_front_insert, + ) { res.process(unknown_ids, ndb, txn); } } diff --git a/crates/notedeck_columns/src/ui/profile/mod.rs b/crates/notedeck_columns/src/ui/profile/mod.rs @@ -39,6 +39,11 @@ pub enum ProfileViewAction { Follow(Pubkey), } +struct ProfileScrollResponse { + body_end_pos: f32, + action: Option<ProfileViewAction>, +} + impl<'a, 'd> ProfileView<'a, 'd> { #[allow(clippy::too_many_arguments)] pub fn new( @@ -65,9 +70,13 @@ impl<'a, 'd> ProfileView<'a, 'd> { pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<ProfileViewAction> { let scroll_id = ProfileView::scroll_id(self.col_id, self.pubkey); - let scroll_area = ScrollArea::vertical().id_salt(scroll_id); + let scroll_area = ScrollArea::vertical().id_salt(scroll_id).animated(false); + + let profile_timeline = self + .timeline_cache + .get_mut(&TimelineKind::Profile(*self.pubkey))?; - let output = scroll_area.show(ui, |ui| 's: { + let output = scroll_area.show(ui, |ui| { let mut action = None; let txn = Transaction::new(self.note_context.ndb).expect("txn"); let profile = self @@ -82,13 +91,6 @@ impl<'a, 'd> ProfileView<'a, 'd> { action = Some(profile_view_action); } - let Some(profile_timeline) = self - .timeline_cache - .get_mut(&TimelineKind::Profile(*self.pubkey)) - else { - break 's action; - }; - let tabs_resp = tabs_ui( ui, self.note_context.i18n, @@ -121,10 +123,16 @@ impl<'a, 'd> ProfileView<'a, 'd> { action = Some(ProfileViewAction::Note(note_action)); } - action + ProfileScrollResponse { + body_end_pos: tabs_resp.response.rect.bottom(), + action, + } }); - output.inner + // only allow front insert when the profile body is fully obstructed + profile_timeline.enable_front_insert = output.inner.body_end_pos < ui.clip_rect().top(); + + output.inner.action } }