Harden account import/continuous behavior and finish multi-account UI polish
This commit is contained in:
1 parent
59cdad220a
commit
881e7ddfca
8 files changed
+572
-35
No files matched your search
+74
-4
@@ -8,7 +8,9 @@ const viewerState = {
|
||||
oldestMessageId: null,
|
||||
newestMessageId: null,
|
||||
userId: null,
|
||||
search: "",
|
||||
loading: false,
|
||||
autoRefreshTimer: null,
|
||||
sentinelObserver: null,
|
||||
};
|
||||
|
||||
@@ -21,6 +23,24 @@ async function api(path) {
|
||||
return data;
|
||||
}
|
||||
|
||||
function showToast(message, type = "info") {
|
||||
let root = document.getElementById("toast-root");
|
||||
if (!root) {
|
||||
root = document.createElement("div");
|
||||
root.id = "toast-root";
|
||||
root.className = "toast-root";
|
||||
document.body.appendChild(root);
|
||||
}
|
||||
const toast = document.createElement("div");
|
||||
toast.className = `toast toast-${type}`;
|
||||
toast.textContent = message;
|
||||
root.appendChild(toast);
|
||||
window.setTimeout(() => {
|
||||
toast.classList.add("toast-hide");
|
||||
window.setTimeout(() => toast.remove(), 250);
|
||||
}, 3600);
|
||||
}
|
||||
|
||||
function formatDateHeader(dateStr) {
|
||||
if (!dateStr) return "";
|
||||
const d = new Date(dateStr.replace(" ", "T"));
|
||||
@@ -136,10 +156,13 @@ function renderChannelList() {
|
||||
}
|
||||
|
||||
function messageEndpoint(channelId, before = "") {
|
||||
const search = viewerState.search
|
||||
? `&search=${encodeURIComponent(viewerState.search)}`
|
||||
: "";
|
||||
if (viewerState.accountId) {
|
||||
return `/api/accounts/${encodeURIComponent(viewerState.accountId)}/channels/${encodeURIComponent(channelId)}/messages?limit=80${before}`;
|
||||
return `/api/accounts/${encodeURIComponent(viewerState.accountId)}/channels/${encodeURIComponent(channelId)}/messages?limit=80${before}${search}`;
|
||||
}
|
||||
return `/api/channels/${encodeURIComponent(channelId)}/messages?limit=80${before}`;
|
||||
return `/api/channels/${encodeURIComponent(channelId)}/messages?limit=80${before}${search}`;
|
||||
}
|
||||
|
||||
function channelsEndpoint() {
|
||||
@@ -172,6 +195,9 @@ function renderAccountSelector() {
|
||||
|
||||
async function switchViewerAccount(accountId) {
|
||||
viewerState.accountId = accountId || null;
|
||||
viewerState.search = "";
|
||||
const searchInput = document.getElementById("viewer-search");
|
||||
if (searchInput) searchInput.value = "";
|
||||
viewerState.accounts.forEach((acc) => {
|
||||
if (acc.id === accountId) {
|
||||
const opts = document.getElementById("viewer-account-select")?.options;
|
||||
@@ -323,7 +349,7 @@ function renderMessages(payload, append = false) {
|
||||
document.getElementById("viewer-title").textContent =
|
||||
channel?.name || payload.channel_id;
|
||||
document.getElementById("viewer-subtitle").textContent = channel
|
||||
? `${channel.message_count} messages, latest: ${channel.last_date || "-"}`
|
||||
? `${payload.messages.length} shown${viewerState.search ? ` for "${viewerState.search}"` : ""}, ${channel.message_count} total, latest: ${channel.last_date || "-"}`
|
||||
: "No local database found for this channel yet.";
|
||||
|
||||
if (!append) {
|
||||
@@ -371,6 +397,15 @@ function renderMessages(payload, append = false) {
|
||||
viewerState.oldestMessageId = payload.messages[0].message_id;
|
||||
viewerState.newestMessageId =
|
||||
payload.messages[payload.messages.length - 1].message_id;
|
||||
} else if (!append) {
|
||||
viewerState.oldestMessageId = null;
|
||||
viewerState.newestMessageId = null;
|
||||
const empty = document.createElement("div");
|
||||
empty.className = "viewer-empty-state";
|
||||
empty.textContent = viewerState.search
|
||||
? `No messages found for "${viewerState.search}".`
|
||||
: "No saved messages in this channel yet.";
|
||||
root.appendChild(empty);
|
||||
}
|
||||
|
||||
if (!append) {
|
||||
@@ -407,6 +442,40 @@ async function loadChannel(channelId, append = false) {
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshCurrentChannel() {
|
||||
if (!viewerState.channelId || viewerState.loading) return;
|
||||
viewerState.oldestMessageId = null;
|
||||
viewerState.newestMessageId = null;
|
||||
await loadChannel(viewerState.channelId);
|
||||
}
|
||||
|
||||
function setupViewerTools() {
|
||||
const searchInput = document.getElementById("viewer-search");
|
||||
const refreshBtn = document.getElementById("viewer-refresh-btn");
|
||||
const autoRefresh = document.getElementById("viewer-auto-refresh");
|
||||
let searchTimer = null;
|
||||
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener("input", () => {
|
||||
clearTimeout(searchTimer);
|
||||
searchTimer = setTimeout(() => {
|
||||
viewerState.search = searchInput.value.trim();
|
||||
refreshCurrentChannel();
|
||||
}, 250);
|
||||
});
|
||||
}
|
||||
|
||||
if (refreshBtn) {
|
||||
refreshBtn.addEventListener("click", () => refreshCurrentChannel());
|
||||
}
|
||||
|
||||
viewerState.autoRefreshTimer = window.setInterval(() => {
|
||||
if (!autoRefresh || autoRefresh.checked) {
|
||||
refreshCurrentChannel();
|
||||
}
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
function setupInfiniteScroll() {
|
||||
const sentinel = document.getElementById("scroll-sentinel");
|
||||
if (!sentinel) return;
|
||||
@@ -456,6 +525,7 @@ async function main() {
|
||||
}
|
||||
|
||||
setupInfiniteScroll();
|
||||
setupViewerTools();
|
||||
|
||||
const toggle = document.getElementById("sidebar-toggle");
|
||||
const sidebar = document.querySelector(".viewer-sidebar");
|
||||
@@ -473,5 +543,5 @@ async function main() {
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
alert(error.message);
|
||||
showToast(error.message, "error");
|
||||
});
|
||||
Reference in new issue
Block a user