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:
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
}
}