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;