From 13b3d7d73aed0ff38b4b6227eb2e570cf2e07895 Mon Sep 17 00:00:00 2001 From: "torlando-agent[bot]" <281092095+torlando-agent[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2026 01:07:51 -0400 Subject: [PATCH] fix(chat): stream older messages on scroll-up too (no synchronous batch) on_scroll() loaded a full MESSAGES_PER_PAGE batch synchronously under the LVGL lock, which froze scrolling. Make it trigger the same incremental tick_background_fill() streaming as the open path instead. _bg_fill_active is now std::atomic since on_scroll() (LVGL task) sets it while tick_background_fill() (main loop) reads it; the target is written before the flag for visibility. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01UWZuYkHBRqNb6BZHV8sTG5 --- lib/tdeck_ui/UI/LXMF/ChatScreen.cpp | 21 ++++++++++++++------- lib/tdeck_ui/UI/LXMF/ChatScreen.h | 7 ++++++- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/tdeck_ui/UI/LXMF/ChatScreen.cpp b/lib/tdeck_ui/UI/LXMF/ChatScreen.cpp index f2d36d36..72423305 100644 --- a/lib/tdeck_ui/UI/LXMF/ChatScreen.cpp +++ b/lib/tdeck_ui/UI/LXMF/ChatScreen.cpp @@ -260,11 +260,12 @@ void ChatScreen::refresh() { create_message_bubble(item); } - // Queue the rest of the first page to stream in on the main loop. + // Queue the rest of the first page to stream in on the main loop. Set the + // target before activating so tick sees a consistent target. _bg_fill_target = (_all_message_hashes.size() > MESSAGES_PER_PAGE) ? _all_message_hashes.size() - MESSAGES_PER_PAGE : 0; - _bg_fill_active = (_display_start_idx > _bg_fill_target); + _bg_fill_active.store(_display_start_idx > _bg_fill_target); // Scroll to bottom lv_obj_scroll_to_y(_message_list, LV_COORD_MAX, LV_ANIM_OFF); @@ -274,17 +275,17 @@ void ChatScreen::refresh() { // the main loop. Each tick prepends a small batch under a brief LVGL lock, so a // large conversation fills in without freezing the UI or holding the lock long. void ChatScreen::tick_background_fill() { - if (!_bg_fill_active) { + if (!_bg_fill_active.load()) { return; } if (_display_start_idx <= _bg_fill_target) { - _bg_fill_active = false; + _bg_fill_active.store(false); return; } size_t remaining = _display_start_idx - _bg_fill_target; load_more_messages(remaining < BG_FILL_BATCH ? remaining : BG_FILL_BATCH); if (_display_start_idx <= _bg_fill_target) { - _bg_fill_active = false; + _bg_fill_active.store(false); } } @@ -356,8 +357,14 @@ void ChatScreen::on_scroll(lv_event_t* event) { // Check if scrolled to top lv_coord_t scroll_y = lv_obj_get_scroll_y(screen->_message_list); - if (scroll_y <= 5) { // Near top (with small threshold) - screen->load_more_messages(); + if (scroll_y <= 5 && screen->_display_start_idx > 0 && !screen->_bg_fill_active.load()) { + // Near the top: stream the next page in incrementally on the main loop + // (tick_background_fill) rather than loading a full batch synchronously + // under the LVGL lock here, which froze scrolling. Target before active. + screen->_bg_fill_target = (screen->_display_start_idx > MESSAGES_PER_PAGE) + ? screen->_display_start_idx - MESSAGES_PER_PAGE + : 0; + screen->_bg_fill_active.store(true); } } diff --git a/lib/tdeck_ui/UI/LXMF/ChatScreen.h b/lib/tdeck_ui/UI/LXMF/ChatScreen.h index f5597070..6638e045 100644 --- a/lib/tdeck_ui/UI/LXMF/ChatScreen.h +++ b/lib/tdeck_ui/UI/LXMF/ChatScreen.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "LXMF/LXMessage.h" #include "LXMF/MessageStore.h" @@ -192,7 +193,11 @@ private: static constexpr size_t BG_FILL_BATCH = 2; // older messages streamed per tick static constexpr size_t MAX_DISPLAYED_MESSAGES = 50; // Cap to prevent memory exhaustion bool _loading_more; // Prevent concurrent loads - bool _bg_fill_active = false; // streaming the rest of the page in + // Streaming state. _bg_fill_active is atomic because on_scroll() (LVGL task) + // and refresh() may set it while tick_background_fill() (main loop) reads it; + // writers always set _bg_fill_target BEFORE _bg_fill_active so the target is + // visible once active is observed true. + std::atomic _bg_fill_active{false}; // streaming the rest of the page in size_t _bg_fill_target = 0; // _display_start_idx to fill down to // Load more messages (infinite scroll + background fill)