openvibers/openvibe.live Commits

Channel chat history: the newest 50 rows first, then 10-row pieces per idle moment (A/B against production on /@goosely, phone CPU: TBT 2.3 s → 1.3 s, longest task 751 → 383 ms) (#210)

OP OpenVibers committed 27 days ago · d3f0fe0949 · 2 files, +7 −5

Changes

ddiff --git a/public/js/chat.js b/public/js/chat.js
iindex 9dbf56ac..d935306e 100644
---- a/public/js/chat.js
++++ b/public/js/chat.js
@@@ -2408,7 +2408,7 @@ async function hydrateActiveChatHistory(streamId, { clear = false } = {}) {
const cached = _chatCacheRead(room);
if (cached) {
_chatPinned = true;
-- _swapChatHistory(messages, _renderHistoryOffscreen(chatEl, () => _renderHistoryPayload(_historyPieces(cached, 120)[0])));
++ _swapChatHistory(messages, _renderHistoryOffscreen(chatEl, () => _renderHistoryPayload(_historyPieces(cached, 50)[0])));
messages.dataset.room = room;
} else if (clear || roomChanged) {
messages.innerHTML = '<div class="chat-loading-history"><i class="fa-solid fa-spinner fa-spin"></i> Loading history…</div>';
@@@ -2435,9 +2435,10 @@ async function hydrateActiveChatHistory(streamId, { clear = false } = {}) {
if (now.messages !== messages) return; // the page changed under us
if (payload) {
-- // The newest 120 rows first; older scrollback goes above them 25 rows at a time, as many as fit in each
-- // idle moment (all 1000 rows at once was a 2.7 s task on a phone), keeping the reader where they are.
-- const pieces = _historyPieces(payload, 25, 120);
++ // The newest 50 rows first (more than a chat pane shows); older scrollback goes above them 10 rows at a
++ // time, as many as fit in each idle moment, so no task blocks a phone (all 1000 rows at once took 2.7 s;
++ // 25 rows were still ~75 ms), keeping the reader where they are.
++ const pieces = _historyPieces(payload, 10, 50);
_swapChatHistory(messages, _renderHistoryOffscreen(now, () => _renderHistoryPayload(pieces[0])));
messages.dataset.room = room;
_chatCacheWrite(room, payload);
@@@ -2449,7 +2450,7 @@ async function hydrateActiveChatHistory(streamId, { clear = false } = {}) {
if (gen !== _chatHydrateGen || getChatEl().messages !== messages) return;
const top = messages.scrollTop, h = messages.scrollHeight;
do messages.prepend(..._renderHistoryOffscreen(getChatEl(), () => _renderHistoryPayload(pieces[i++]), false).childNodes);
-- while (i < pieces.length && dl && dl.timeRemaining() > 15);
++ while (i < pieces.length && dl && dl.timeRemaining() > 20);
messages.scrollTop = _chatPinned ? messages.scrollHeight : top + messages.scrollHeight - h;
older(i);
});
ddiff --git a/test/chat-history-pieces.test.js b/test/chat-history-pieces.test.js
iindex 682052fb..a472ecbb 100644
---- a/test/chat-history-pieces.test.js
++++ b/test/chat-history-pieces.test.js
@@@ -47,6 +47,7 @@ assert.ok(pieces[0].data.messages.some((m) => m.id === 'c299') && pieces[0].glob
assert.deepStrictEqual([pieces[0].kind, pieces[0].data.channel, pieces[0].data.liveSlots], ['channel', 'goosely', [1]]);
// A bigger first piece (what fits on screen), then small ones.
++assert.deepStrictEqual(_historyPieces(payload, 10, 50).map(rows), [50, ...Array(50).fill(10)], '550 rows: 50, then 50 × 10');
assert.deepStrictEqual(_historyPieces(payload, 25, 120).map(rows), [120, ...Array(17).fill(25), 5], '550 rows: 120, then 17 × 25, then 5');
// The global room has no second feed; a small payload is one piece; an empty one is itself.