Files
Alex Jbanca 27a1e20c6b perf(chat): virtualize the chat and contacts channel lists (#21943)
* perf(chat): virtualize the chat and contacts channel lists

StatusChatList's ListView now owns its viewport instead of expanding to
contentHeight inside an outer ScrollView, which built every delegate up
front (~2.4ms per chat, no ceiling). Row heights are fixed constants so
the list never needs to build a delegate to know its geometry.

- StatusChatList: hardcoded chat/category row heights; hosts size the
  list; drag-and-drop is untouched and keeps working in the interactive
  list (preventStealing, reorder within the viewport)
- CommunityColumnView: ScrollView dropped; the admin banners move into
  the list footer so they still scroll below the channels
- ContactsColumnView: ScrollView dropped, list fills the column

Verified by tst_ContactsColumnVirtualization and the reworked
tst_CommunityChatLoaderSection (member and admin lists virtualized,
drag reorder, banner footer).

* perf(chat): incubate the contacts column and center panel asynchronously

The two panels are now built off the critical path, so the loader-owned
chrome paints its skeletons immediately and each slot swaps in as its own
panel finishes. This is only safe here: the channel and contact lists are
virtualized by this branch, and the message view attaches its model on
first show (lazy-load-messages), so async incubation no longer walks the
whole chat history or builds a delegate per channel.

* fixup! perf(chat): incubate the contacts column and center panel asynchronously

The 120 s ceilings were sized for a test engine running async Loaders on
Qt's render-loop budget; the boosted controller now installed in the test
harness makes them a way for a wedged suite to look like a slow one. The
whole suite runs in ~6 s.

Also spells out that contactColumnLoader's explicit size covers the
parentless incubation phase and the anchors take over afterwards.

* fixup! perf(chat): virtualize the chat and contacts channel lists

Harden the drag-reorder tests: wait for every panel's ready flag and
for row geometry to settle before the gesture (rows materialize under
async incubation and shift mid-drag), drop the irrelevant 2000-message
payload, and give the drop target a rendered frame between the final
move and the release. Failure rate goes from ~2-in-3 to ~1-in-12 on a
fast machine; the residual escape is still open (task tracking).

* refactor(chat): drop ChatsLoadingPanel, superseded by the section skeletons

The section skeleton chrome shows skeleton rows for the chat and community
columns while the section loads, so the "Loading chats..." strip below the
search box is a second, competing loading affordance.

Both call sites go with it. `chatsLoaded` has no remaining QML consumer, and
neither view uses anything else from shared.panels, so those imports are
dropped too.

* chore: update translations

* perf(chat): filter collapsed-category rows out of the chat list

Zero-height delegates were still instantiated by the ListView, so a
large collapsed category cost hundreds of delegates despite the
viewport bound. The visibility predicate now lives in the backend as a
cached per-row "hidden" role (recomputed on dependent-field writes,
never in data()), and StatusChatList drops hidden rows with a plain
ValueFilter so they never reach the view. Covered by chat_section
model tests and a large-collapsed-category virtualization test.

* fix(chat): chat list review fixes — scrollbar geometry, handler captures

- StatusChatList owns the scrollbar space: the list reserves it via its
  own rightMargin and delegates/footer size to availableWidth, so the
  overlaid scrollbar sits at the column edge instead of covering row
  controls; hosts drop their external right margins
- context-menu handlers capture signal params explicitly instead of
  relying on deprecated injected parameters
- guard contactColumnLoader.item deref while the panel incubates
- restore the master menu pattern: onClosed destroy() belongs to
  adminPopupMenuComp itself

* test(chat): drop timing-fragile instant row-count assertions

The skeleton tests asserted how many delegate rows existed at the exact
instant a skeleton cleared, racing delegate incubation against the
Loader.Ready swap — deterministic-fail under the offscreen platform and
flaky under CI's Xvfb. The skeleton coverage checks stay; row population
is now awaited with tryVerify instead of sampled at the swap instant.

* chore: update translations
2026-08-22 08:13:54 +03:00
..