diff --git a/src/ui/feed.rs b/src/ui/feed.rs index 42fca73d..ffcf29bd 100644 --- a/src/ui/feed.rs +++ b/src/ui/feed.rs @@ -9,11 +9,16 @@ use egui::{ }; use nostr_types::{EventKind, Id, PublicKeyHex}; +struct FeedPostParams { + id: Id, + indent: usize, + as_reply_to: bool, + threaded: bool, +} + pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Frame, ui: &mut Ui) { let feed = GLOBALS.feed.blocking_lock().get(); - //let screen_rect = ctx.input().screen_rect; // Rect - Globals::trim_desired_events_sync(); let desired_count: isize = match GLOBALS.desired_events.try_read() { Ok(v) => v.len() as isize, @@ -79,7 +84,18 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram }); } else { if let Some(id) = app.replying_to { - render_post(app, ctx, frame, ui, id, 0, true); + render_post_actual( + app, + ctx, + frame, + ui, + FeedPostParams { + id, + indent: 0, + as_reply_to: true, + threaded: false, + }, + ); } ui.with_layout(Layout::right_to_left(Align::TOP), |ui| { @@ -125,6 +141,8 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram ui.separator(); + let threaded = GLOBALS.settings.blocking_read().view_threaded; + ScrollArea::vertical().show(ui, |ui| { let bgcolor = if ctx.style().visuals.dark_mode { Color32::BLACK @@ -133,28 +151,110 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram }; Frame::none().fill(bgcolor).show(ui, |ui| { for id in feed.iter() { - // Stop rendering at the bottom of the window: - // (This confuses the scrollbar a bit, so I'm taking it out for now) - //let pos2 = ui.next_widget_position(); - //if pos2.y > screen_rect.max.y { - // break; - //} - - render_post(app, ctx, frame, ui, *id, 0, false); + render_post_maybe_fake( + app, + ctx, + frame, + ui, + FeedPostParams { + id: *id, + indent: 0, + as_reply_to: false, + threaded, + }, + ); } }); }); } -fn render_post( +fn render_post_maybe_fake( app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui, - id: Id, - indent: usize, - as_reply_to: bool, + feed_post_params: FeedPostParams, ) { + let FeedPostParams { + id, + indent, + as_reply_to, + threaded, + } = feed_post_params; + + // We always get the event even offscreen so we can estimate its height + let maybe_event = GLOBALS.events.blocking_read().get(&id).cloned(); + if maybe_event.is_none() { + return; + } + let event = maybe_event.unwrap(); + + let screen_rect = ctx.input().screen_rect; // Rect + let pos2 = ui.next_widget_position(); + + // If too far off of the screen, don't actually render the post, just make some space + // so the scrollbar isn't messed up + if pos2.y < -2000.0 || pos2.y > screen_rect.max.y + 2000.0 { + // ESTIMATE HEIGHT + // This doesn't have to be perfect, but the closer we are, the less wobbly the scroll bar is. + // This is affected by font size, so adjust if we add that as a setting. + // A single-line post currently is 110 pixels high. Every additional line adds 18 pixels. + let mut height = 92.0; + let mut lines = event.content.lines().count(); + // presume wrapping at 80 chars, although window width makes a big diff. + lines += event.content.lines().filter(|l| l.len() > 80).count(); + height += 18.0 * (lines as f32); + + ui.add_space(height); + + // Yes, and we need to fake render threads to get their approx height too. + if threaded && !as_reply_to && !app.hides.contains(&id) { + let replies = Globals::get_replies_sync(event.id); + for reply_id in replies { + render_post_maybe_fake( + app, + ctx, + _frame, + ui, + FeedPostParams { + id: reply_id, + indent: indent + 1, + as_reply_to, + threaded, + }, + ); + } + } + } else { + render_post_actual( + app, + ctx, + _frame, + ui, + FeedPostParams { + id, + indent, + as_reply_to, + threaded, + }, + ); + } +} + +fn render_post_actual( + app: &mut GossipUi, + ctx: &Context, + _frame: &mut eframe::Frame, + ui: &mut Ui, + feed_post_params: FeedPostParams, +) { + let FeedPostParams { + id, + indent, + as_reply_to, + threaded, + } = feed_post_params; + let maybe_event = GLOBALS.events.blocking_read().get(&id).cloned(); if maybe_event.is_none() { return; @@ -169,7 +269,6 @@ fn render_post( let maybe_person = GLOBALS.people.blocking_write().get(&event.pubkey.into()); let reactions = Globals::get_reactions_sync(event.id); - let replies = Globals::get_replies_sync(event.id); // Person Things we can render: // pubkey @@ -204,8 +303,6 @@ fn render_post( // Try LayoutJob - let threaded = GLOBALS.settings.blocking_read().view_threaded; - #[allow(clippy::collapsible_else_if)] let bgcolor = if GLOBALS.event_is_new.blocking_read().contains(&event.id) { if ctx.style().visuals.dark_mode { @@ -339,8 +436,20 @@ fn render_post( ui.separator(); if threaded && !as_reply_to && !app.hides.contains(&id) { + let replies = Globals::get_replies_sync(event.id); for reply_id in replies { - render_post(app, ctx, _frame, ui, reply_id, indent + 1, as_reply_to); + render_post_maybe_fake( + app, + ctx, + _frame, + ui, + FeedPostParams { + id: reply_id, + indent: indent + 1, + as_reply_to, + threaded, + }, + ); } } }