webui v157: read further back in a room by scrolling up

A room opens on its last 20 messages and that was all there ever was: reaching
the top of the pane loaded nothing, so anything older was only reachable through
the separate history browser.

Scrolling near the top now asks for a larger slice. p3HistoryMgr::getMessages
takes a count and nothing else -- no cursor, no "before this message" -- and
always answers with the newest ones, so the only way to reach older text is to
ask for more and let addMessages() drop what is already held. It re-sends what
we have, which is the price of that API; the slice is small, and the core keeps
ten days at most anyway.

An answer shorter than the count asked for means there is nothing older left,
and the pane stops asking. The opening slice stays at 20: every room opening
pays for it, and on a phone each request is a fresh connection on a core that
answers one at a time.

Older messages are inserted above what is on screen, which would push the
conversation down by their height; that height goes back into scrollTop, so the
reader does not move while a slice lands.
This commit is contained in:
jolavillette 2026-08-27 13:44:39 +02:00
parent e67c4c3170
commit 597ef472d8
3 changed files with 80 additions and 5 deletions

View File

@ -128,6 +128,29 @@ function updateChatStickToBottom(element) {
element.scrollHeight - element.scrollTop - element.clientHeight <= CHAT_STICK_SLACK_PX;
}
// Far enough from the top to have the next slice ready before the reader gets
// there, close enough not to fire on the first flick of a long conversation.
const CHAT_LOAD_OLDER_AT_PX = 120;
function loadOlderWhenAtTop(element) {
if (!element || element.scrollTop > CHAT_LOAD_OLDER_AT_PX) return;
// Older messages are inserted above the ones on screen, which pushes
// everything down by exactly the height they add. Put that height back into
// scrollTop and the reader does not move at all -- without it the pane jumps
// to a different part of the conversation each time a slice lands.
const previousHeight = element.scrollHeight;
const previousTop = element.scrollTop;
ChatLobbyModel.loadOlderHistory(() => {
requestAnimationFrame(() => {
const pane = document.querySelector('.chat-hub-messages');
if (!pane) return;
pane.scrollTop = previousTop + (pane.scrollHeight - previousHeight);
});
});
}
function renderUserTooltip(gxsId, name) {
const details = ChatHubState.gxsDetails[gxsId];
if (!details) return null;
@ -390,6 +413,7 @@ const ChatConversationView = () => {
// while the reader drags the pane.
event.redraw = false;
updateChatStickToBottom(event.target);
loadOlderWhenAtTop(event.target);
},
},
ChatLobbyModel.messages

View File

@ -734,7 +734,12 @@ const ChatLobbyModel = {
}
},
loadHistory(id, type) {
// How much of a conversation is on screen when it opens. Small on purpose:
// every room opening pays for it, and on a phone each request is a fresh
// connection on a core that answers one at a time.
HISTORY_PAGE: 20,
historyChatPeerId(id, type) {
const chatPeerId = {
broadcast_status_peer_id: '00000000000000000000000000000000',
type,
@ -746,20 +751,66 @@ const ChatLobbyModel = {
if (type === 3) chatPeerId.lobby_id.xstr64 = id;
else if (type === 2) chatPeerId.distant_chat_id = id;
else if (type === 1) chatPeerId.peer_id = id;
return chatPeerId;
},
loadHistory(id, type) {
this.historyLoaded = this.HISTORY_PAGE;
this.historyExhausted = false;
this.historyLoading = false;
rs.rsJsonApiRequest(
'/rsHistory/getMessages',
{
chatPeerId,
loadCount: 20,
chatPeerId: this.historyChatPeerId(id, type),
loadCount: this.HISTORY_PAGE,
},
(data, success) => {
if (success && data.msgs) {
if (data.msgs.length < this.HISTORY_PAGE) this.historyExhausted = true;
this.addMessages(data.msgs);
}
}
);
},
// Reading further back. p3HistoryMgr::getMessages takes a count and nothing
// else -- no cursor, no "before this message" -- and always answers with the
// newest ones, so the only way to see older text is to ask for a bigger slice
// and let addMessages() drop what is already here. It re-sends what we hold,
// which is the price of that API; a page is small and the core keeps ten days
// at most anyway (mMaxStorageDurationSeconds).
loadOlderHistory(done) {
const detail = this.currentLobby;
if (!detail || this.historyLoading || this.historyExhausted) return false;
const id = this.lastLobbyId;
if (!id) return false;
this.historyLoading = true;
const wanted = (this.historyLoaded || this.HISTORY_PAGE) + this.HISTORY_PAGE * 2;
rs.rsJsonApiRequest(
'/rsHistory/getMessages',
{
chatPeerId: this.historyChatPeerId(id, detail.chatType),
loadCount: wanted,
},
(data, success) => {
this.historyLoading = false;
if (!success || !data.msgs) {
if (done) done();
return;
}
// Fewer than asked for means the core has nothing older left.
if (data.msgs.length < wanted) this.historyExhausted = true;
this.historyLoaded = wanted;
this.addMessages(data.msgs);
if (done) done();
}
);
return true;
},
loadAllHistoryForRoom(lobbyId, callback) {
ChatHubState.isHistoryLoading = true;
ChatHubState.fullHistoryMessages = [];

View File

@ -135,7 +135,7 @@ const navbar = () => {
? 'Connected to RetroShare Core'
: 'Connection Lost',
}),
m('span.webui-version', { style: { fontSize: '0.7em' } }, 'v156'),
m('span.webui-version', { style: { fontSize: '0.7em' } }, 'v157'),
m('i.fas.fa-sync-alt.refresh-icon', {
style: { cursor: 'pointer', fontSize: '0.8em' },
onclick: () => window.location.reload(true),
@ -258,7 +258,7 @@ const MobileStatus = () => {
m('small', statusbar.formatBytes(state.totalOut)),
]),
]),
m('.mobile-status-sheet__version', 'WebUI v156'),
m('.mobile-status-sheet__version', 'WebUI v157'),
])),
];
},