Fix critical frontend issues: race conditions, memory leaks, silent failures

- Add loading guard to loadPictureSources to prevent concurrent fetches
- Pause perf chart polling and uptime timer when browser tab is hidden
- Disconnect KC and LED preview WebSockets when leaving targets tab
- Add error toasts to loadCaptureTemplates and saveKCBrightness
- Skip auto-refresh polling when document is hidden
- Widen auto-start dashboard cards for better text display

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 17:29:47 +03:00
parent b51839ef3c
commit 82e12ffaac
9 changed files with 86 additions and 17 deletions

View File

@@ -44,6 +44,11 @@ export function switchTab(name, { updateHash = true, skipLoad = false } = {}) {
} else {
if (typeof window.stopPerfPolling === 'function') window.stopPerfPolling();
if (typeof window.stopUptimeTimer === 'function') window.stopUptimeTimer();
// Clean up WebSockets when leaving targets tab
if (name !== 'targets') {
if (typeof window.disconnectAllKCWebSockets === 'function') window.disconnectAllKCWebSockets();
if (typeof window.disconnectAllLedPreviewWS === 'function') window.disconnectAllLedPreviewWS();
}
if (!apiKey || skipLoad) return;
if (name === 'streams') {
if (typeof window.loadPictureSources === 'function') window.loadPictureSources();
@@ -100,16 +105,15 @@ export function startAutoRefresh() {
}
setRefreshInterval(setInterval(() => {
if (apiKey) {
const activeTab = localStorage.getItem('activeTab') || 'dashboard';
if (activeTab === 'targets') {
// Skip refresh while user interacts with a picker or slider
const panel = document.getElementById('targets-panel-content');
if (panel && panel.contains(document.activeElement) && document.activeElement.matches('input')) return;
if (typeof window.loadTargetsTab === 'function') window.loadTargetsTab();
} else if (activeTab === 'dashboard') {
if (typeof window.loadDashboard === 'function') window.loadDashboard();
}
if (!apiKey || document.hidden) return;
const activeTab = localStorage.getItem('activeTab') || 'dashboard';
if (activeTab === 'targets') {
// Skip refresh while user interacts with a picker or slider
const panel = document.getElementById('targets-panel-content');
if (panel && panel.contains(document.activeElement) && document.activeElement.matches('input')) return;
if (typeof window.loadTargetsTab === 'function') window.loadTargetsTab();
} else if (activeTab === 'dashboard') {
if (typeof window.loadDashboard === 'function') window.loadDashboard();
}
}, dashboardPollInterval));
}