Fix account tab persistence and viewer fallback

This commit is contained in:
2026-06-27 19:09:07 +02:00
parent 3eb6bdf534
commit 59cdad220a
3 changed files with 85 additions and 17 deletions
+1 -1
View File
@@ -142,7 +142,7 @@ kubectl apply -f k8s/telegram-scraper.yaml
Then open: Then open:
```text ```text
http://<server-ip>:8080 http://<server-ip>:7887
``` ```
### Volumes (Docker) ### Volumes (Docker)
+37 -6
View File
@@ -10,6 +10,8 @@ const state = {
authStates: {}, authStates: {},
}; };
const ACTIVE_ACCOUNT_STORAGE_KEY = "telegramScraper.activeAccount";
/* ── API helper ─────────────────────────────────────── */ /* ── API helper ─────────────────────────────────────── */
async function api(path, options = {}) { async function api(path, options = {}) {
@@ -78,6 +80,33 @@ function isAccountAuthorized(auth) {
); );
} }
function saveActiveAccount(accountId) {
if (accountId) {
localStorage.setItem(ACTIVE_ACCOUNT_STORAGE_KEY, accountId);
}
}
function loadSavedActiveAccount(accounts) {
let savedId = localStorage.getItem(ACTIVE_ACCOUNT_STORAGE_KEY);
if (!savedId) {
savedId = localStorage.getItem("activeAccount");
if (savedId) {
localStorage.setItem(ACTIVE_ACCOUNT_STORAGE_KEY, savedId);
localStorage.removeItem("activeAccount");
}
}
if (savedId && accounts.some((account) => account.id === savedId)) {
return savedId;
}
if (savedId) {
localStorage.removeItem(ACTIVE_ACCOUNT_STORAGE_KEY);
}
if (state.activeAccount && accounts.some((account) => account.id === state.activeAccount)) {
return state.activeAccount;
}
return accounts[0]?.id || null;
}
/* ── Account tab management ─────────────────────────── */ /* ── Account tab management ─────────────────────────── */
function renderAccountTabs(accounts) { function renderAccountTabs(accounts) {
@@ -175,9 +204,8 @@ function renderAccountPanel(accountId) {
} }
function switchAccount(accountId) { function switchAccount(accountId) {
if (state.activeAccount === accountId) return;
state.activeAccount = accountId; state.activeAccount = accountId;
localStorage.setItem("activeAccount", accountId); saveActiveAccount(accountId);
// Update tabs // Update tabs
document.querySelectorAll(".account-tab").forEach((tab) => { document.querySelectorAll(".account-tab").forEach((tab) => {
@@ -490,9 +518,10 @@ async function loadAccounts() {
accounts.forEach((acc) => renderAccountPanel(acc.id)); accounts.forEach((acc) => renderAccountPanel(acc.id));
// Determine active account (persisted across reloads) // Determine active account (persisted across reloads)
const savedId = localStorage.getItem("activeAccount"); const activeId = loadSavedActiveAccount(accounts);
const activeId = (savedId && accounts.some(a => a.id === savedId)) ? savedId : (state.activeAccount || accounts[0].id); if (activeId) {
switchAccount(activeId); switchAccount(activeId);
}
// Update settings // Update settings
renderSettingsAccounts(); renderSettingsAccounts();
@@ -524,11 +553,13 @@ function renderSettingsAccounts() {
node.querySelector(".account-remove-btn").addEventListener("click", async () => { node.querySelector(".account-remove-btn").addEventListener("click", async () => {
if (!confirmAction(`Remove account "${acc.label || acc.id}"? All its data will be deleted.`)) return; if (!confirmAction(`Remove account "${acc.label || acc.id}"? All its data will be deleted.`)) return;
try { try {
const removingActive = state.activeAccount === acc.id;
await api(`/api/accounts/${acc.id}`, { method: "DELETE" }); await api(`/api/accounts/${acc.id}`, { method: "DELETE" });
await loadAccounts(); if (removingActive) {
if (state.activeAccount === acc.id) {
state.activeAccount = null; state.activeAccount = null;
localStorage.removeItem(ACTIVE_ACCOUNT_STORAGE_KEY);
} }
await loadAccounts();
} catch (err) { } catch (err) {
alert(`Failed to remove account: ${err.message}`); alert(`Failed to remove account: ${err.message}`);
} }
+46 -9
View File
@@ -87,6 +87,25 @@ function scrollToBottom() {
setTimeout(go, 150); setTimeout(go, 150);
} }
function makeScrollSentinel() {
const sentinel = document.createElement("div");
sentinel.id = "scroll-sentinel";
sentinel.className = "scroll-sentinel";
return sentinel;
}
function resetMessageView(title = "Select a channel", subtitle = "Reading messages from the local SQLite database.") {
const root = document.getElementById("messages-list");
if (root) {
root.innerHTML = "";
root.appendChild(makeScrollSentinel());
}
document.getElementById("viewer-title").textContent = title;
document.getElementById("viewer-subtitle").textContent = subtitle;
viewerState.oldestMessageId = null;
viewerState.newestMessageId = null;
}
function renderChannelList() { function renderChannelList() {
const root = document.getElementById("viewer-channel-list"); const root = document.getElementById("viewer-channel-list");
if (!root) return; if (!root) return;
@@ -179,6 +198,13 @@ async function switchViewerAccount(accountId) {
renderChannelList(); renderChannelList();
if (viewerState.channelId) { if (viewerState.channelId) {
await loadChannel(viewerState.channelId); await loadChannel(viewerState.channelId);
} else {
resetMessageView(
"No channels",
viewerState.accountId
? "This account has no tracked channels yet."
: "No local legacy channels found."
);
} }
} }
@@ -190,10 +216,10 @@ async function loadViewerAccount(params) {
} catch { } catch {
viewerState.accounts = []; viewerState.accounts = [];
} }
viewerState.accountId = const requestedExists = requested && viewerState.accounts.some((acc) => acc.id === requested);
requested || viewerState.accountId = requestedExists
viewerState.accounts[0]?.id || ? requested
null; : viewerState.accounts[0]?.id || null;
renderAccountSelector(); renderAccountSelector();
@@ -289,8 +315,9 @@ function makeDateSep(text) {
function renderMessages(payload, append = false) { function renderMessages(payload, append = false) {
const root = document.getElementById("messages-list"); const root = document.getElementById("messages-list");
const sentinel = document.getElementById("scroll-sentinel");
if (!root) return; if (!root) return;
let sentinel = document.getElementById("scroll-sentinel");
if (!sentinel) sentinel = makeScrollSentinel();
const channel = payload.channel; const channel = payload.channel;
document.getElementById("viewer-title").textContent = document.getElementById("viewer-title").textContent =
@@ -408,14 +435,24 @@ async function main() {
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
await loadViewerAccount(params); await loadViewerAccount(params);
viewerState.channels = await api(channelsEndpoint()); viewerState.channels = await api(channelsEndpoint());
viewerState.channelId = const requestedChannel = params.get("channel");
params.get("channel") || const requestedChannelExists =
viewerState.channels[0]?.channel_id || requestedChannel &&
null; viewerState.channels.some((channel) => channel.channel_id === requestedChannel);
viewerState.channelId = requestedChannelExists
? requestedChannel
: viewerState.channels[0]?.channel_id || null;
renderChannelList(); renderChannelList();
if (viewerState.channelId) { if (viewerState.channelId) {
await loadChannel(viewerState.channelId); await loadChannel(viewerState.channelId);
} else {
resetMessageView(
"No channels",
viewerState.accountId
? "This account has no tracked channels yet."
: "No local legacy channels found."
);
} }
setupInfiniteScroll(); setupInfiniteScroll();