Änderung: Verbesserung der Verbindungsverwaltung und Benutzeroberfläche in mehreren Komponenten

Änderungen:
- Hinzufügung eines Verbindungsstatus-Indicators in der AppHeader.vue, der den aktuellen Verbindungsstatus anzeigt.
- Erweiterung der MultiChatDialog.vue um verbesserte Netzwerkereignisbehandlungen und eine Herzschlag-Logik zur Aufrechterhaltung der WebSocket-Verbindung.
- Anpassungen im Store zur Verwaltung des Verbindungsstatus und zur Implementierung von Wiederverbindungslogik mit exponentiellem Backoff.
- Diese Anpassungen verbessern die Benutzererfahrung durch klare Statusanzeigen und erhöhen die Stabilität der WebSocket-Verbindungen.
This commit is contained in:
Torsten Schulz (local)
2025-09-15 08:45:11 +02:00
parent 8f4327efb5
commit d475e8b2f7
3 changed files with 188 additions and 7 deletions

View File

@@ -180,7 +180,11 @@ export default {
isPicking: false,
maxLightness: 92, // exclude extremely light colors (>% L)
// Reconnect control
reconnectIntervalMs: 5000,
reconnectIntervalMs: 3000,
reconnectAttempts: 0,
maxReconnectAttempts: 20,
heartbeatInterval: null,
heartbeatIntervalMs: 30000, // 30 seconds
connectAttemptTimeout: null,
joinFallbackTimer: null,
// Faster handshake watchdog separate from reconnect interval
@@ -258,6 +262,9 @@ export default {
this.opened = false;
console.log('[Chat WS] dialog close — closing websocket');
this.disconnectChatSocket();
// Remove network event listeners
window.removeEventListener('online', this.onOnline);
window.removeEventListener('offline', this.onOffline);
},
onOptionsToggle(e) {
e?.stopPropagation?.();
@@ -270,11 +277,18 @@ export default {
this.showOptions = false;
},
onOnline() {
console.log('[Chat WS] Network online detected');
if (this.opened && !this.chatConnected && !this.connectRacing && (!this.chatWs || this.chatWs.readyState !== WebSocket.OPEN)) {
console.log('[Chat WS] online — attempting reconnect');
this.reconnectAttempts = 0; // Reset attempts on network recovery
this.reconnectIntervalMs = 3000; // Reset to base interval
this.connectChatSocket();
}
},
onOffline() {
console.log('[Chat WS] Network offline detected');
this.setStatus('disconnected');
},
async loadRooms() {
try {
const data = await fetchPublicRooms();
@@ -315,6 +329,9 @@ export default {
// Stelle die WS-Verbindung her, wenn der Dialog geöffnet wird
this.opened = true;
this.connectChatSocket();
// Add network event listeners
window.addEventListener('online', this.onOnline);
window.addEventListener('offline', this.onOffline);
},
connectChatSocket() {
if (this.connectRacing) return; // avoid overlapping races
@@ -413,6 +430,9 @@ export default {
this.wsUrl = url;
this.chatWs = ws;
this.transportConnected = true;
this.reconnectAttempts = 0; // Reset reconnect counter on successful connection
this.reconnectIntervalMs = 3000; // Reset to base interval
this.startHeartbeat(); // Start heartbeat to keep connection alive
console.log('[Chat WS] open in', dt, 'ms', '| protocol:', ws.protocol || '(none)', '| url:', url);
// Close all other candidates
this.pendingWs.forEach(r => {
@@ -569,7 +589,21 @@ export default {
},
scheduleReconnect() {
if (this.reconnectTimer) return;
console.log('[Chat WS] scheduleReconnect in', this.reconnectIntervalMs, 'ms', '| opened:', this.opened);
// Check if we've exceeded max attempts
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error('[Chat WS] Max reconnect attempts reached, waiting longer before retry');
this.reconnectAttempts = 0; // Reset counter
this.reconnectIntervalMs = 30000; // Wait 30 seconds
} else {
this.reconnectAttempts++;
// Use exponential backoff with jitter
const baseDelay = Math.min(3000 * Math.pow(1.5, this.reconnectAttempts - 1), 15000);
const jitter = Math.random() * 1000; // Add up to 1 second jitter
this.reconnectIntervalMs = baseDelay + jitter;
}
console.log('[Chat WS] scheduleReconnect in', Math.round(this.reconnectIntervalMs), 'ms', '| opened:', this.opened, '| attempt:', this.reconnectAttempts);
this.reconnectTimer = setTimeout(() => {
this.reconnectTimer = null;
if (this.opened && !this.chatConnected) {
@@ -594,6 +628,7 @@ export default {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
this.stopHeartbeat(); // Stop heartbeat when disconnecting
this.cleanupPendingSockets();
if (this.connectOpenTimer) { clearTimeout(this.connectOpenTimer); this.connectOpenTimer = null; }
if (this.connectAttemptTimeout) { clearTimeout(this.connectAttemptTimeout); this.connectAttemptTimeout = null; }
@@ -613,6 +648,35 @@ export default {
this.usersInRoom = [];
this.selectedTargetUser = null;
},
startHeartbeat() {
this.stopHeartbeat(); // Clear any existing heartbeat
if (!this.opened || !this.chatConnected) return;
this.heartbeatInterval = setInterval(() => {
if (!this.chatWs || this.chatWs.readyState !== WebSocket.OPEN) {
console.warn('[Chat WS] Heartbeat failed - connection not open, attempting reconnect');
this.stopHeartbeat();
this.scheduleReconnect();
return;
}
try {
// Send a ping message to keep connection alive
this.wsSend({ type: 'ping' });
console.log('[Chat WS] Heartbeat sent');
} catch (error) {
console.warn('[Chat WS] Heartbeat failed:', error);
this.stopHeartbeat();
this.scheduleReconnect();
}
}, this.heartbeatIntervalMs);
},
stopHeartbeat() {
if (this.heartbeatInterval) {
clearInterval(this.heartbeatInterval);
this.heartbeatInterval = null;
}
},
getSelectedRoomName() {
const r = this.rooms.find(x => x.id === this.selectedRoom);
return r?.title || r?.name || '';