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>
This commit is contained in:
kernelkind
2025-09-13 14:36:59 -04:00
parent 563fbb9c4b
commit 559e9577fc
2 changed files with 40 additions and 14 deletions

View File

@@ -133,6 +133,7 @@ impl TimelineTab {
ndb: &Ndb, ndb: &Ndb,
txn: &Transaction, txn: &Transaction,
reversed: bool, reversed: bool,
use_front_insert: bool,
) -> Option<UnknownPks<'a>> { ) -> Option<UnknownPks<'a>> {
if payloads.is_empty() { if payloads.is_empty() {
return None; return None;
@@ -158,7 +159,11 @@ impl TimelineTab {
debug!("spliced when inserting {num_refs} new notes, resetting virtual list",); debug!("spliced when inserting {num_refs} new notes, resetting virtual list",);
list.reset(); list.reset();
} }
MergeKind::FrontInsert => { MergeKind::FrontInsert => 's: {
if !use_front_insert {
break 's;
}
// only run this logic if we're reverse-chronological // only run this logic if we're reverse-chronological
// reversed in this case means chronological, since the // reversed in this case means chronological, since the
// default is reverse-chronological. yeah it's confusing. // default is reverse-chronological. yeah it's confusing.
@@ -210,6 +215,7 @@ pub struct Timeline {
pub selected_view: usize, pub selected_view: usize,
pub subscription: TimelineSub, pub subscription: TimelineSub,
pub enable_front_insert: bool,
} }
impl Timeline { impl Timeline {
@@ -271,12 +277,16 @@ impl Timeline {
let subscription = TimelineSub::default(); let subscription = TimelineSub::default();
let selected_view = 0; 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 { Timeline {
kind, kind,
filter, filter,
views, views,
subscription, subscription,
selected_view, selected_view,
enable_front_insert,
} }
} }
@@ -402,7 +412,9 @@ impl Timeline {
match view.filter { match view.filter {
ViewFilter::NotesAndReplies => { ViewFilter::NotesAndReplies => {
let res: Vec<&NotePayload<'_>> = payloads.iter().collect(); 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); 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); res.process(unknown_ids, ndb, txn);
} }
} }

View File

@@ -39,6 +39,11 @@ pub enum ProfileViewAction {
Follow(Pubkey), Follow(Pubkey),
} }
struct ProfileScrollResponse {
body_end_pos: f32,
action: Option<ProfileViewAction>,
}
impl<'a, 'd> ProfileView<'a, 'd> { impl<'a, 'd> ProfileView<'a, 'd> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
@@ -65,9 +70,13 @@ impl<'a, 'd> ProfileView<'a, 'd> {
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<ProfileViewAction> { pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<ProfileViewAction> {
let scroll_id = ProfileView::scroll_id(self.col_id, self.pubkey); 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 output = scroll_area.show(ui, |ui| 's: { let profile_timeline = self
.timeline_cache
.get_mut(&TimelineKind::Profile(*self.pubkey))?;
let output = scroll_area.show(ui, |ui| {
let mut action = None; let mut action = None;
let txn = Transaction::new(self.note_context.ndb).expect("txn"); let txn = Transaction::new(self.note_context.ndb).expect("txn");
let profile = self let profile = self
@@ -82,13 +91,6 @@ impl<'a, 'd> ProfileView<'a, 'd> {
action = Some(profile_view_action); 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( let tabs_resp = tabs_ui(
ui, ui,
self.note_context.i18n, self.note_context.i18n,
@@ -121,10 +123,16 @@ impl<'a, 'd> ProfileView<'a, 'd> {
action = Some(ProfileViewAction::Note(note_action)); 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
} }
} }