Add pagination to message screen.
This commit is contained in:
parent
0b9ed3a68a
commit
cedbd2b3c0
5 changed files with 94 additions and 9 deletions
34
js/db.js
34
js/db.js
|
|
@ -30,6 +30,7 @@ function openDb() {
|
||||||
msgStore = db.createObjectStore("messages", { keyPath: "id" });
|
msgStore = db.createObjectStore("messages", { keyPath: "id" });
|
||||||
msgStore.createIndex("from", "from", { unique: false });
|
msgStore.createIndex("from", "from", { unique: false });
|
||||||
msgStore.createIndex("fingerprint", ["from", "timestamp"], { unique: false });
|
msgStore.createIndex("fingerprint", ["from", "timestamp"], { unique: false });
|
||||||
|
msgStore.createIndex("cidTimestampId", ["chatId", "timestamp", "id"], { unique: false });
|
||||||
} else {
|
} else {
|
||||||
msgStore = tx.objectStore("messages");
|
msgStore = tx.objectStore("messages");
|
||||||
}
|
}
|
||||||
|
|
@ -172,6 +173,39 @@ export async function loadLatestMessages(chatId, limit = 50) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function loadOlderMessages(chatId, oldestTimestamp, oldestId, limit = 50) {
|
||||||
|
const db = await openDb();
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const tx = db.transaction("messages", "readonly");
|
||||||
|
const store = tx.objectStore("messages");
|
||||||
|
const idx = store.index("cidTimestampId");
|
||||||
|
|
||||||
|
const out = [];
|
||||||
|
|
||||||
|
const parsedTimestamp = isNaN(oldestTimestamp) ? oldestTimestamp : Number(oldestTimestamp);
|
||||||
|
|
||||||
|
const range = IDBKeyRange.bound(
|
||||||
|
[chatId, -Infinity, ""],
|
||||||
|
[chatId, parsedTimestamp, oldestId],
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
idx.openCursor(range, "prev").onsuccess = (e) => {
|
||||||
|
const cursor = e.target.result;
|
||||||
|
if (!cursor) return resolve(out);
|
||||||
|
|
||||||
|
out.push(cursor.value);
|
||||||
|
|
||||||
|
if (out.length >= limit) resolve(out);
|
||||||
|
else cursor.continue();
|
||||||
|
};
|
||||||
|
|
||||||
|
tx.onerror = () => reject(tx.error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function upsertMedia(reqId, blob, filename) {
|
export async function upsertMedia(reqId, blob, filename) {
|
||||||
const db = await openDb();
|
const db = await openDb();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { loadChatsSorted, loadLatestMessages, loadMedia, upsertChats, upsertMedia, upsertMessages } from "./db.js";
|
import { loadChatsSorted, loadLatestMessages, loadMedia, loadOlderMessages, upsertChats, upsertMedia, upsertMessages } from "./db.js";
|
||||||
import { waha } from "./waha.js";
|
import { waha } from "./waha.js";
|
||||||
|
|
||||||
let online = false;
|
let online = false;
|
||||||
|
|
@ -107,6 +107,14 @@ export async function getChatMessages(chatId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getMoreChatMessages(chatId, oldestTimestamp, oldestId, limit = 50) {
|
||||||
|
if (online) {
|
||||||
|
return waha.getChatMessages(chatId, oldestTimestamp);
|
||||||
|
} else {
|
||||||
|
return await loadOlderMessages(chatId, oldestTimestamp, oldestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function getChatPicture(chatId) {
|
export async function getChatPicture(chatId) {
|
||||||
if (online) {
|
if (online) {
|
||||||
return await waha.getChatPicture(chatId);
|
return await waha.getChatPicture(chatId);
|
||||||
|
|
|
||||||
40
js/ui.js
40
js/ui.js
|
|
@ -1,7 +1,7 @@
|
||||||
import { formatTime } from "./utils.js";
|
import { formatTime } from "./utils.js";
|
||||||
import { waha } from "./waha.js";
|
import { waha } from "./waha.js";
|
||||||
import { config } from "./config.js";
|
import { config } from "./config.js";
|
||||||
import { getChatPicture, getMessage, getMedia } from "./storage.js";
|
import { getChatPicture, getMessage, getMedia, getMoreChatMessages } from "./storage.js";
|
||||||
|
|
||||||
export const elements = {
|
export const elements = {
|
||||||
chatList: document.getElementById('chat-list'),
|
chatList: document.getElementById('chat-list'),
|
||||||
|
|
@ -136,19 +136,48 @@ export const ui = {
|
||||||
elements.messagesContainer.innerHTML = '<div class="loading-chats">No messages. Say hello!</div>';
|
elements.messagesContainer.innerHTML = '<div class="loading-chats">No messages. Say hello!</div>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loadMore = document.createElement("button");
|
||||||
|
loadMore.classList.add("load-more-btn");
|
||||||
|
loadMore.innerText = "Load more";
|
||||||
|
loadMore.addEventListener('click', () => {
|
||||||
|
this.loadMoreMessages(chatId, userID);
|
||||||
|
});
|
||||||
|
elements.messagesContainer.appendChild(loadMore);
|
||||||
|
|
||||||
for (const msg of messages) {
|
for (const msg of messages) {
|
||||||
this.appendSingleMessage(msg, activeChatName, userID, chatId);
|
this.appendSingleMessage(msg, userID, chatId);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async loadMoreMessages(chatId, userId) {
|
||||||
|
const oldest = document.querySelector('.message-group:first-of-type');
|
||||||
|
const oldestTimestamp = oldest.dataset.timestamp;
|
||||||
|
const oldestId = oldest.id;
|
||||||
|
console.log(oldest)
|
||||||
|
console.log(oldestId)
|
||||||
|
console.log(oldestTimestamp)
|
||||||
|
|
||||||
|
const loadMoreButton = document.querySelector('.load-more-btn');
|
||||||
|
loadMoreButton.removeEventListener('click', this.loadMoreMessages);
|
||||||
|
|
||||||
|
const msgs = await getMoreChatMessages(chatId, oldestTimestamp, oldestId);
|
||||||
|
msgs.shift();
|
||||||
|
|
||||||
|
msgs.forEach(async msg => {
|
||||||
|
loadMoreButton.after(this.generateMessage(msg, userId, chatId));
|
||||||
|
});
|
||||||
|
|
||||||
|
loadMoreButton.addEventListener('click', this.loadMoreMessages);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append a single message (used for optimistic updates immediately upon sending)
|
* Append a single message (used for optimistic updates immediately upon sending)
|
||||||
*/
|
*/
|
||||||
appendSingleMessage(msg, activeChatName, userID, chatId, isLocal = false) {
|
appendSingleMessage(msg, userID, chatId, isLocal = false) {
|
||||||
elements.messagesContainer.appendChild(this.generateMessage(msg, activeChatName, userID, chatId, isLocal))
|
elements.messagesContainer.appendChild(this.generateMessage(msg, userID, chatId, isLocal))
|
||||||
},
|
},
|
||||||
|
|
||||||
generateTempMessageLink(msg) {
|
generateTempMessageLink(msg) {
|
||||||
|
|
@ -169,7 +198,7 @@ export const ui = {
|
||||||
return a;
|
return a;
|
||||||
},
|
},
|
||||||
|
|
||||||
generateMessage(msg, activeChatName, userID, chatId, isLocal = false) {
|
generateMessage(msg, userID, chatId, isLocal = false) {
|
||||||
const isOutgoing = msg.fromMe || msg.sender === 'me';
|
const isOutgoing = msg.fromMe || msg.sender === 'me';
|
||||||
|
|
||||||
function getPrevMessageElem() {
|
function getPrevMessageElem() {
|
||||||
|
|
@ -181,6 +210,7 @@ export const ui = {
|
||||||
const groupDiv = document.createElement('div');
|
const groupDiv = document.createElement('div');
|
||||||
groupDiv.className = `message-group ${isOutgoing ? 'outgoing' : 'incoming'}`;
|
groupDiv.className = `message-group ${isOutgoing ? 'outgoing' : 'incoming'}`;
|
||||||
groupDiv.id = msg.id;
|
groupDiv.id = msg.id;
|
||||||
|
groupDiv.dataset.timestamp = msg.timestamp;
|
||||||
groupDiv.dataset.from = msg.participant || msg.from;
|
groupDiv.dataset.from = msg.participant || msg.from;
|
||||||
|
|
||||||
const senderName = isOutgoing ? userID : (msg._data.notifyName || msg.from);
|
const senderName = isOutgoing ? userID : (msg._data.notifyName || msg.from);
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ async function downloadFile(path, options = {}) {
|
||||||
|
|
||||||
export const waha = {
|
export const waha = {
|
||||||
async getVersion() {
|
async getVersion() {
|
||||||
return request('/api/version');
|
return await request('/api/version');
|
||||||
},
|
},
|
||||||
|
|
||||||
async getChats() {
|
async getChats() {
|
||||||
|
|
@ -89,8 +89,9 @@ export const waha = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async getChatMessages(chatId) {
|
async getChatMessages(chatId, beforeTimestamp) {
|
||||||
return request(`/api/${config.session}/chats/${chatId}/messages?downloadMedia=false&limit=40`);
|
console.log(`/api/${config.session}/chats/${chatId}/messages?downloadMedia=false&limit=40&sortBy=timestamp${beforeTimestamp ? `&filter.timestamp.gte=${beforeTimestamp}` : "" }`);
|
||||||
|
return request(`/api/${config.session}/chats/${chatId}/messages?downloadMedia=false&limit=40${beforeTimestamp ? `&filter.timestamp.lte=${beforeTimestamp}` : "" }`);
|
||||||
},
|
},
|
||||||
|
|
||||||
async getSingleChatMessage(chatId, messageId, downladMedia) {
|
async getSingleChatMessage(chatId, messageId, downladMedia) {
|
||||||
|
|
|
||||||
12
style.css
12
style.css
|
|
@ -506,6 +506,18 @@ input:focus {
|
||||||
radial-gradient(circle at 90% 80%, rgba(168, 85, 247, 0.03) 0%, transparent 40%);
|
radial-gradient(circle at 90% 80%, rgba(168, 85, 247, 0.03) 0%, transparent 40%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.load-more-btn {
|
||||||
|
margin: auto;
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
padding: .4em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-more-btn:hover {
|
||||||
|
background-color: var(--text-primary);
|
||||||
|
color: var(--bg-main);
|
||||||
|
}
|
||||||
|
|
||||||
.message-group {
|
.message-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue