From cf155bd322fc3c3c1e293aeac8cb6df50cd59d91 Mon Sep 17 00:00:00 2001 From: Peng Ding Date: Wed, 8 Apr 2026 20:14:06 +0800 Subject: [PATCH] feat: hide empty sessions and auto-refresh message drawer - Filter out sessions that have never been used (no bot_id, not connected, no users) from the admin panel session list (#22) - Auto-poll for new messages every 5s while the message drawer is open, appending them at the bottom with auto-scroll (#24) Closes #22 Closes #24 --- src/weilink/admin/admin.html | 57 ++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/src/weilink/admin/admin.html b/src/weilink/admin/admin.html index e2a26d9..e0a0326 100644 --- a/src/weilink/admin/admin.html +++ b/src/weilink/admin/admin.html @@ -1006,7 +1006,9 @@ // Render // ============================================================ function renderSessions(data) { - const sessions = data.sessions || []; + const allSessions = data.sessions || []; + // Hide sessions that have never been used (no bot_id, not connected, no users) + const sessions = allSessions.filter(s => s.connected || s.bot_id || s.users.length > 0); const tbody = document.getElementById('sessionsBody'); const empty = document.getElementById('emptyState'); @@ -1310,6 +1312,7 @@ // ============================================================ const _msgState = { userId: null, botId: null, offset: 0, total: 0, loading: false }; let _msgSearchTimer = null; +let _msgPollTimer = null; const MSG_TYPE_ICONS = { IMAGE: '\ud83d\uddbc\ufe0f', VOICE: '\ud83c\udfa4', FILE: '\ud83d\udcce', VIDEO: '\ud83c\udfac' }; const PAGE_SIZE = 30; @@ -1336,6 +1339,9 @@ document.getElementById('msgDrawer').classList.add('active'); document.body.style.overflow = 'hidden'; reloadMessages(); + // Auto-refresh every 5 seconds while drawer is open + if (_msgPollTimer) clearInterval(_msgPollTimer); + _msgPollTimer = setInterval(pollNewMessages, 5000); } function closeMessageDrawer() { @@ -1343,6 +1349,7 @@ document.body.style.overflow = ''; _msgState.userId = null; _msgState.botId = null; + if (_msgPollTimer) { clearInterval(_msgPollTimer); _msgPollTimer = null; } } async function reloadMessages() { @@ -1390,7 +1397,7 @@ _msgState.total = resp.total; const messages = resp.messages || []; - renderBubbles(messages, prepend); + renderBubbles(messages, prepend ? 'prepend' : 'replace'); _msgState.offset += messages.length; // Footer @@ -1415,7 +1422,7 @@ _msgState.loading = false; } -function renderBubbles(messages, prepend) { +function renderBubbles(messages, mode = 'replace') { const container = document.getElementById('msgBubbles'); // API returns newest-first; reverse for chronological order (oldest at top) const ordered = [...messages].reverse(); @@ -1424,7 +1431,7 @@ let prevTs = 0; // If prepending, we need to check the timestamp of the first existing bubble - if (prepend && container.children.length > 0) { + if (mode === 'prepend' && container.children.length > 0) { // Get the timestamp of the first existing bubble for time-sep logic const firstExisting = container.querySelector('.bubble-wrap'); if (firstExisting) { @@ -1493,8 +1500,13 @@ `; }); - if (prepend) { + if (mode === 'prepend') { container.insertAdjacentHTML('afterbegin', html); + } else if (mode === 'append') { + const body = document.getElementById('msgDrawerBody'); + const isAtBottom = body.scrollHeight - body.scrollTop - body.clientHeight < 50; + container.insertAdjacentHTML('beforeend', html); + if (isAtBottom) body.scrollTop = body.scrollHeight; } else { container.innerHTML = html; // Scroll to bottom for initial load @@ -1524,6 +1536,41 @@ return String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0'); } +async function pollNewMessages() { + if (_msgState.loading || !_msgState.userId) return; + try { + const params = { + user_id: _msgState.userId, + bot_id: _msgState.botId, + limit: 1, + offset: 0, + }; + const typeVal = document.getElementById('msgTypeFilter').value; + if (typeVal) params.msg_type = typeVal; + const searchVal = document.getElementById('msgSearchInput').value.trim(); + if (searchVal) params.text_contains = searchVal; + + const resp = await api.getMessages(params); + if (resp.error || resp.total <= _msgState.total) return; + + // Fetch only the new messages + const diff = resp.total - _msgState.total; + params.limit = diff; + const fullResp = await api.getMessages(params); + if (fullResp.error || !fullResp.messages || fullResp.messages.length === 0) return; + + renderBubbles(fullResp.messages, 'append'); + _msgState.total = resp.total; + _msgState.offset += fullResp.messages.length; + + // Update footer + const loaded = document.querySelectorAll('#msgBubbles .bubble-wrap').length; + document.getElementById('msgDrawerFooter').textContent = + i18n.t('messages.showing', { n: loaded, total: _msgState.total }); + document.getElementById('msgEmpty').style.display = loaded === 0 ? '' : 'none'; + } catch (e) { /* ignore polling errors */ } +} + function debouncedReload() { if (_msgSearchTimer) clearTimeout(_msgSearchTimer); _msgSearchTimer = setTimeout(() => reloadMessages(), 300);