diff --git a/android/app/src/main/assets/public/main.js b/android/app/src/main/assets/public/main.js index e9b48c31..27f760e6 100644 --- a/android/app/src/main/assets/public/main.js +++ b/android/app/src/main/assets/public/main.js @@ -1838,6 +1838,9 @@ var currentDmId = null; var currentDmKey = null; // AES key for encrypting/decrypting messages var dmUsernameCache = {}; var dmPfpCache = {}; +var loadedMessages = {}; +var oldestLoadedMsgId = null; +var isLoadingOlderMessages = false; async function openDm(dmId, username, targetId) { try { @@ -1848,6 +1851,9 @@ async function openDm(dmId, username, targetId) { throw new Error("Missing DM room key"); } currentDmId = dmId; + loadedMessages = {}; + oldestLoadedMsgId = null; + isLoadingOlderMessages = false; let pfp = await getAvatarUrl(targetId, username); let iconHtml = ``; @@ -1863,6 +1869,7 @@ async function openDm(dmId, username, targetId) { } await loadDmMessages(dmId); + setupChatScrollListener(); if (dmMessagePollInterval) clearInterval(dmMessagePollInterval); dmMessagePollInterval = setInterval(() => { @@ -1886,9 +1893,9 @@ async function openDm(dmId, username, targetId) { } } -async function loadDmMessages(dmId) { +async function loadDmMessages(dmId, startOffset = "", isPrepend = false) { try { - let payload = JSON.stringify({ string1: dmId, string2: "50", string3: "" }); + let payload = JSON.stringify({ string1: dmId, string2: "50", string3: startOffset }); let res = await fetchEncrypted("dm/messages/get", payload); if (res && res.startsWith("error:")) { @@ -1897,14 +1904,26 @@ async function loadDmMessages(dmId) { } let messages = JSON.parse(res); - await renderMessages(messages); + let ids = Object.keys(messages).map(id => parseInt(id)); + if (ids.length > 0) { + let minId = Math.min(...ids); + if (oldestLoadedMsgId === null || minId < oldestLoadedMsgId) { + oldestLoadedMsgId = minId; + } + } + if (ids.length < 50 && startOffset !== "") { + oldestLoadedMsgId = 0; + } + + Object.assign(loadedMessages, messages); + await renderMessages(loadedMessages, isPrepend); } catch (e) { console.error(e); showBlahNotification("error:dm.messages.fetch.failed"); } } -async function renderMessages(messages) { +async function renderMessages(messages, isPrepend = false) { let container = document.getElementById("chat-messages"); if (!container) return; @@ -1981,16 +2000,37 @@ async function renderMessages(messages) { window.forceScrollToBottom = false; } let oldScrollTop = container.scrollTop; + let oldScrollHeight = container.scrollHeight; container.innerHTML = html; if (wasAtBottom) { container.scrollTop = container.scrollHeight; + } else if (isPrepend) { + container.scrollTop = oldScrollTop + (container.scrollHeight - oldScrollHeight); } else { container.scrollTop = oldScrollTop; } } +function setupChatScrollListener() { + let container = document.getElementById("chat-messages"); + if (!container) return; + + container.onscroll = async () => { + if (container.scrollTop === 0 && !isLoadingOlderMessages && oldestLoadedMsgId !== null && oldestLoadedMsgId > 0) { + isLoadingOlderMessages = true; + showAction("info.messages.loading.older", "messages.loading.older"); + try { + await loadDmMessages(currentDmId, (oldestLoadedMsgId - 1).toString(), true); + isLoadingOlderMessages = false; + } + catch (e) {} + clearAction("messages.loading.older"); + } + }; +} + async function sendMessage() { if (!currentDmId || !currentDmKey) return; diff --git a/webroot/main.js b/webroot/main.js index e9b48c31..27f760e6 100644 --- a/webroot/main.js +++ b/webroot/main.js @@ -1838,6 +1838,9 @@ var currentDmId = null; var currentDmKey = null; // AES key for encrypting/decrypting messages var dmUsernameCache = {}; var dmPfpCache = {}; +var loadedMessages = {}; +var oldestLoadedMsgId = null; +var isLoadingOlderMessages = false; async function openDm(dmId, username, targetId) { try { @@ -1848,6 +1851,9 @@ async function openDm(dmId, username, targetId) { throw new Error("Missing DM room key"); } currentDmId = dmId; + loadedMessages = {}; + oldestLoadedMsgId = null; + isLoadingOlderMessages = false; let pfp = await getAvatarUrl(targetId, username); let iconHtml = ``; @@ -1863,6 +1869,7 @@ async function openDm(dmId, username, targetId) { } await loadDmMessages(dmId); + setupChatScrollListener(); if (dmMessagePollInterval) clearInterval(dmMessagePollInterval); dmMessagePollInterval = setInterval(() => { @@ -1886,9 +1893,9 @@ async function openDm(dmId, username, targetId) { } } -async function loadDmMessages(dmId) { +async function loadDmMessages(dmId, startOffset = "", isPrepend = false) { try { - let payload = JSON.stringify({ string1: dmId, string2: "50", string3: "" }); + let payload = JSON.stringify({ string1: dmId, string2: "50", string3: startOffset }); let res = await fetchEncrypted("dm/messages/get", payload); if (res && res.startsWith("error:")) { @@ -1897,14 +1904,26 @@ async function loadDmMessages(dmId) { } let messages = JSON.parse(res); - await renderMessages(messages); + let ids = Object.keys(messages).map(id => parseInt(id)); + if (ids.length > 0) { + let minId = Math.min(...ids); + if (oldestLoadedMsgId === null || minId < oldestLoadedMsgId) { + oldestLoadedMsgId = minId; + } + } + if (ids.length < 50 && startOffset !== "") { + oldestLoadedMsgId = 0; + } + + Object.assign(loadedMessages, messages); + await renderMessages(loadedMessages, isPrepend); } catch (e) { console.error(e); showBlahNotification("error:dm.messages.fetch.failed"); } } -async function renderMessages(messages) { +async function renderMessages(messages, isPrepend = false) { let container = document.getElementById("chat-messages"); if (!container) return; @@ -1981,16 +2000,37 @@ async function renderMessages(messages) { window.forceScrollToBottom = false; } let oldScrollTop = container.scrollTop; + let oldScrollHeight = container.scrollHeight; container.innerHTML = html; if (wasAtBottom) { container.scrollTop = container.scrollHeight; + } else if (isPrepend) { + container.scrollTop = oldScrollTop + (container.scrollHeight - oldScrollHeight); } else { container.scrollTop = oldScrollTop; } } +function setupChatScrollListener() { + let container = document.getElementById("chat-messages"); + if (!container) return; + + container.onscroll = async () => { + if (container.scrollTop === 0 && !isLoadingOlderMessages && oldestLoadedMsgId !== null && oldestLoadedMsgId > 0) { + isLoadingOlderMessages = true; + showAction("info.messages.loading.older", "messages.loading.older"); + try { + await loadDmMessages(currentDmId, (oldestLoadedMsgId - 1).toString(), true); + isLoadingOlderMessages = false; + } + catch (e) {} + clearAction("messages.loading.older"); + } + }; +} + async function sendMessage() { if (!currentDmId || !currentDmKey) return;