From a159ac3fd614b93c1ffaf3d505af845c9a16afbe Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Tue, 10 Mar 2026 20:34:57 -0700 Subject: [PATCH] Fix activity feed blinking and show live relative timestamps --- web_server.py | 3 ++- webui/static/script.js | 56 ++++++++++++++++++++++++++---------------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/web_server.py b/web_server.py index 6d293f4c..c7df3a2a 100644 --- a/web_server.py +++ b/web_server.py @@ -3846,11 +3846,12 @@ def add_activity_item(icon: str, title: str, subtitle: str, time_ago: str = "Now """Add activity item to the feed (replicates dashboard.py functionality)""" try: import time + from datetime import datetime, timezone activity_item = { 'icon': icon, 'title': title, 'subtitle': subtitle, - 'time': time_ago, + 'time': datetime.now(timezone.utc).isoformat(), 'timestamp': time.time(), 'show_toast': show_toast } diff --git a/webui/static/script.js b/webui/static/script.js index fece11f5..19f98da4 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -29835,21 +29835,16 @@ async function fetchAndUpdateActivityFeed() { } } +// Cache last feed signature to avoid unnecessary DOM rebuilds (prevents blink) +let _lastActivityFeedSig = ''; + function updateActivityFeed(activities) { const feedContainer = document.getElementById('dashboard-activity-feed'); - if (!feedContainer) { - console.warn('Activity feed container not found!'); - return; - } - - console.log('Updating activity feed with', activities.length, 'activities:', activities); - - // Clear existing content - feedContainer.innerHTML = ''; + if (!feedContainer) return; if (activities.length === 0) { - console.log('No activities found, showing placeholder'); - // Show placeholder if no activities + if (_lastActivityFeedSig === 'empty') return; + _lastActivityFeedSig = 'empty'; feedContainer.innerHTML = `
📊 @@ -29857,14 +29852,30 @@ function updateActivityFeed(activities) {

System Started

Dashboard initialized successfully

-

Now

+

Just now

`; return; } - // Add activities (limit to 5 most recent) - activities.slice(0, 5).forEach((activity, index) => { + const items = activities.slice(0, 5); + // Build signature from titles+subtitles to detect actual changes + const sig = items.map(a => a.title + a.subtitle).join('|'); + const feedChanged = sig !== _lastActivityFeedSig; + _lastActivityFeedSig = sig; + + if (!feedChanged) { + // Just update timestamps without rebuilding DOM + const timeEls = feedContainer.querySelectorAll('.activity-time'); + items.forEach((activity, i) => { + if (timeEls[i]) timeEls[i].textContent = timeAgo(activity.time); + }); + return; + } + + // Full rebuild only when feed content actually changed + feedContainer.innerHTML = ''; + items.forEach((activity, index) => { const activityElement = document.createElement('div'); activityElement.className = 'activity-item'; activityElement.innerHTML = ` @@ -29873,13 +29884,11 @@ function updateActivityFeed(activities) {

${escapeHtml(activity.title)}

${escapeHtml(activity.subtitle)}

-

${escapeHtml(activity.time)}

+

${timeAgo(activity.time)}

`; - feedContainer.appendChild(activityElement); - // Add separator between items (except after last item) - if (index < activities.slice(0, 5).length - 1) { + if (index < items.length - 1) { const separator = document.createElement('div'); separator.className = 'activity-separator'; feedContainer.appendChild(separator); @@ -47365,9 +47374,14 @@ async function hydrateMirroredDiscoveryStates() { function timeAgo(dateStr) { if (!dateStr) return ''; - const diff = Date.now() - new Date(dateStr + (dateStr.includes('Z') ? '' : 'Z')).getTime(); - const mins = Math.floor(diff / 60000); - if (mins < 1) return 'just now'; + // Handle ISO formats: "Z" suffix, "+00:00" offset, or bare (assume UTC) + let ts = dateStr; + if (!ts.includes('Z') && !ts.includes('+') && !ts.includes('-', 10)) ts += 'Z'; + const diff = Date.now() - new Date(ts).getTime(); + const secs = Math.floor(diff / 1000); + if (secs < 5) return 'just now'; + if (secs < 60) return `${secs}s ago`; + const mins = Math.floor(secs / 60); if (mins < 60) return `${mins}m ago`; const hrs = Math.floor(mins / 60); if (hrs < 24) return `${hrs}h ago`;