This commit is contained in:
Timothy Jaeryang Baek
2026-07-15 00:14:01 -04:00
parent 254888cf15
commit 63ada24706
31 changed files with 2560 additions and 2754 deletions
+78 -21
View File
@@ -118,9 +118,48 @@
let heartbeatInterval = null;
let disconnectToastTimer = null;
let disconnectWarningShown = false;
let pageIsVisible = true;
let pageWasHidden = false;
let lastVisibleAt = Date.now();
let disconnectReason = null;
const BREAKPOINT = 768;
const DISCONNECT_TOAST_DELAY_MS = 2000;
const RECENT_RESUME_GRACE_MS = 8000;
const RESUME_DISCONNECT_REASONS = new Set(['ping timeout', 'transport close', 'transport error']);
const clearDisconnectToastTimer = () => {
if (disconnectToastTimer) {
clearTimeout(disconnectToastTimer);
disconnectToastTimer = null;
}
};
const recentlyResumed = () =>
pageWasHidden && Date.now() - lastVisibleAt < RECENT_RESUME_GRACE_MS;
const isLikelyResumeDisconnect = (reason) => {
return (!pageIsVisible || recentlyResumed()) && RESUME_DISCONNECT_REASONS.has(reason);
};
const scheduleDisconnectToast = () => {
clearDisconnectToastTimer();
const resumeDelay = isLikelyResumeDisconnect(disconnectReason)
? Math.max(RECENT_RESUME_GRACE_MS - (Date.now() - lastVisibleAt), 0)
: 0;
disconnectToastTimer = setTimeout(() => {
disconnectToastTimer = null;
if ($socket?.connected || !pageIsVisible || isLikelyResumeDisconnect(disconnectReason)) {
return;
}
disconnectWarningShown = true;
toast.warning($i18n.t('Connection lost. Reconnecting...'));
}, resumeDelay + DISCONNECT_TOAST_DELAY_MS);
};
const setupSocket = async (enableWebsocket) => {
const _socket = io(`${WEBUI_BASE_URL}` || undefined, {
@@ -144,19 +183,17 @@
console.log('connected', _socket.id);
// Cancel any pending disconnect toast if we reconnected quickly
if (disconnectToastTimer) {
clearTimeout(disconnectToastTimer);
disconnectToastTimer = null;
}
clearDisconnectToastTimer();
if (hasConnectedOnce) {
socketConnected.set(true);
// Only show "Reconnected" if the user actually saw the disconnect warning
if (disconnectWarningShown) {
toast.success($i18n.t('Reconnected'));
disconnectWarningShown = false;
}
}
disconnectWarningShown = false;
disconnectReason = null;
hasConnectedOnce = true;
const res = await getVersion(localStorage.token);
@@ -213,18 +250,15 @@
_socket.on('disconnect', (reason, details) => {
console.log(`Socket ${_socket.id} disconnected due to ${reason}`);
socketConnected.set(false);
// Delay showing the disconnect toast so brief interruptions
// (e.g. mobile tab backgrounding) don't flash a nuisance warning
if (disconnectToastTimer) {
clearTimeout(disconnectToastTimer);
}
disconnectReason = reason;
disconnectWarningShown = false;
disconnectToastTimer = setTimeout(() => {
disconnectToastTimer = null;
disconnectWarningShown = true;
toast.warning($i18n.t('Connection lost. Reconnecting...'));
}, DISCONNECT_TOAST_DELAY_MS);
// Delay visible warnings while mobile browsers resume suspended tabs.
if (isLikelyResumeDisconnect(reason)) {
clearDisconnectToastTimer();
} else {
scheduleDisconnectToast();
}
if (heartbeatInterval) {
clearInterval(heartbeatInterval);
@@ -1044,18 +1078,39 @@
};
// Set yourself as the last active tab when this tab is focused
const handlePageHidden = () => {
pageIsVisible = false;
pageWasHidden = true;
clearDisconnectToastTimer();
};
const handlePageVisible = () => {
pageIsVisible = true;
lastVisibleAt = Date.now();
isLastActiveTab.set(true); // This tab is now the active tab
bc.postMessage('active'); // Notify other tabs that this tab is active
// Check token expiry when the tab becomes active
checkTokenExpiry();
if ($socket && !$socket.connected) {
scheduleDisconnectToast();
}
};
const handleVisibilityChange = () => {
if (document.visibilityState === 'visible') {
isLastActiveTab.set(true); // This tab is now the active tab
bc.postMessage('active'); // Notify other tabs that this tab is active
// Check token expiry when the tab becomes active
checkTokenExpiry();
handlePageVisible();
} else {
handlePageHidden();
}
};
// Add event listener for visibility state changes
document.addEventListener('visibilitychange', handleVisibilityChange);
window.addEventListener('pagehide', handlePageHidden);
window.addEventListener('pageshow', handlePageVisible);
// Call visibility change handler initially to set state on load
handleVisibilityChange();
@@ -1228,6 +1283,8 @@
document.removeEventListener('touchmove', touchmoveHandler);
document.removeEventListener('touchend', touchendHandler);
document.removeEventListener('visibilitychange', handleVisibilityChange);
window.removeEventListener('pagehide', handlePageHidden);
window.removeEventListener('pageshow', handlePageVisible);
};
});