mirror of
https://github.com/Alvin-Zilverstand/narrow_casting_system.git
synced 2026-04-17 14:48:06 +02:00
240 lines
7.2 KiB
JavaScript
240 lines
7.2 KiB
JavaScript
|
|
// WebSocket Management for SnowWorld Admin Dashboard
|
||
|
|
class WebSocketManager {
|
||
|
|
constructor() {
|
||
|
|
this.socket = null;
|
||
|
|
this.isConnected = false;
|
||
|
|
this.reconnectAttempts = 0;
|
||
|
|
this.maxReconnectAttempts = 5;
|
||
|
|
this.reconnectDelay = 1000;
|
||
|
|
this.init();
|
||
|
|
}
|
||
|
|
|
||
|
|
init() {
|
||
|
|
this.connect();
|
||
|
|
}
|
||
|
|
|
||
|
|
connect() {
|
||
|
|
try {
|
||
|
|
this.socket = io('http://localhost:3000', {
|
||
|
|
transports: ['websocket', 'polling'],
|
||
|
|
timeout: 5000,
|
||
|
|
forceNew: true
|
||
|
|
});
|
||
|
|
|
||
|
|
this.setupEventListeners();
|
||
|
|
} catch (error) {
|
||
|
|
console.error('WebSocket connection error:', error);
|
||
|
|
this.handleConnectionError();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
setupEventListeners() {
|
||
|
|
this.socket.on('connect', () => {
|
||
|
|
console.log('WebSocket connected');
|
||
|
|
this.isConnected = true;
|
||
|
|
this.reconnectAttempts = 0;
|
||
|
|
this.updateConnectionStatus(true);
|
||
|
|
|
||
|
|
// Join admin room for global updates
|
||
|
|
this.socket.emit('joinZone', 'admin');
|
||
|
|
|
||
|
|
this.showToast('Verbonden met server', 'success');
|
||
|
|
});
|
||
|
|
|
||
|
|
this.socket.on('disconnect', () => {
|
||
|
|
console.log('WebSocket disconnected');
|
||
|
|
this.isConnected = false;
|
||
|
|
this.updateConnectionStatus(false);
|
||
|
|
|
||
|
|
// Attempt reconnection
|
||
|
|
this.attemptReconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
this.socket.on('connect_error', (error) => {
|
||
|
|
console.error('WebSocket connection error:', error);
|
||
|
|
this.handleConnectionError();
|
||
|
|
});
|
||
|
|
|
||
|
|
// Content updates
|
||
|
|
this.socket.on('contentUpdated', (data) => {
|
||
|
|
console.log('Content update received:', data);
|
||
|
|
this.handleContentUpdate(data);
|
||
|
|
});
|
||
|
|
|
||
|
|
// Schedule updates
|
||
|
|
this.socket.on('scheduleUpdated', (data) => {
|
||
|
|
console.log('Schedule update received:', data);
|
||
|
|
this.handleScheduleUpdate(data);
|
||
|
|
});
|
||
|
|
|
||
|
|
// Zone-specific updates
|
||
|
|
this.socket.on('zoneUpdate', (data) => {
|
||
|
|
console.log('Zone update received:', data);
|
||
|
|
this.handleZoneUpdate(data);
|
||
|
|
});
|
||
|
|
|
||
|
|
// System notifications
|
||
|
|
this.socket.on('systemNotification', (data) => {
|
||
|
|
console.log('System notification:', data);
|
||
|
|
this.handleSystemNotification(data);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
handleContentUpdate(data) {
|
||
|
|
// Clear content cache to force refresh
|
||
|
|
if (window.ui) {
|
||
|
|
window.ui.clearContentCache();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Show notification based on update type
|
||
|
|
switch (data.type) {
|
||
|
|
case 'content_added':
|
||
|
|
this.showToast(`Nieuwe content toegevoegd: ${data.content.title}`, 'info');
|
||
|
|
break;
|
||
|
|
case 'content_deleted':
|
||
|
|
this.showToast('Content verwijderd', 'warning');
|
||
|
|
break;
|
||
|
|
case 'content_updated':
|
||
|
|
this.showToast('Content bijgewerkt', 'info');
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Refresh current view if on content tab
|
||
|
|
if (window.ui && window.ui.currentTab === 'content') {
|
||
|
|
window.ui.loadContent();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
handleScheduleUpdate(data) {
|
||
|
|
// Show notification
|
||
|
|
this.showToast(`Planning bijgewerkt voor zone: ${data.zone}`, 'info');
|
||
|
|
|
||
|
|
// Refresh schedule view if currently viewing this zone
|
||
|
|
const currentZone = document.getElementById('scheduleZoneSelect')?.value;
|
||
|
|
if (window.ui && window.ui.currentTab === 'schedule' && currentZone === data.zone) {
|
||
|
|
window.ui.loadSchedule();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
handleZoneUpdate(data) {
|
||
|
|
// Handle zone-specific updates
|
||
|
|
this.showToast(`Zone ${data.zone} bijgewerkt`, 'info');
|
||
|
|
|
||
|
|
// Refresh relevant views
|
||
|
|
if (window.ui) {
|
||
|
|
if (window.ui.currentTab === 'zones') {
|
||
|
|
window.ui.loadZonesOverview();
|
||
|
|
} else if (window.ui.currentTab === 'content') {
|
||
|
|
window.ui.loadContent();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
handleSystemNotification(data) {
|
||
|
|
// Handle system-level notifications
|
||
|
|
const { message, type, duration } = data;
|
||
|
|
this.showToast(message, type || 'info', duration);
|
||
|
|
}
|
||
|
|
|
||
|
|
updateConnectionStatus(connected) {
|
||
|
|
const statusDot = document.getElementById('connectionStatus');
|
||
|
|
const statusText = document.getElementById('connectionText');
|
||
|
|
|
||
|
|
if (statusDot) {
|
||
|
|
statusDot.className = connected ? 'status-dot' : 'status-dot disconnected';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (statusText) {
|
||
|
|
statusText.textContent = connected ? 'Verbonden' : 'Verbinding verbroken';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
attemptReconnect() {
|
||
|
|
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
||
|
|
console.error('Max reconnection attempts reached');
|
||
|
|
this.showToast('Kan geen verbinding maken met de server', 'error');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
this.reconnectAttempts++;
|
||
|
|
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
|
||
|
|
|
||
|
|
console.log(`Attempting reconnection ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`);
|
||
|
|
|
||
|
|
setTimeout(() => {
|
||
|
|
if (!this.isConnected) {
|
||
|
|
this.connect();
|
||
|
|
}
|
||
|
|
}, delay);
|
||
|
|
}
|
||
|
|
|
||
|
|
handleConnectionError() {
|
||
|
|
this.isConnected = false;
|
||
|
|
this.updateConnectionStatus(false);
|
||
|
|
|
||
|
|
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
||
|
|
this.showToast('Verbinding verbroken. Probeert opnieuw...', 'warning');
|
||
|
|
} else {
|
||
|
|
this.showToast('Kan geen verbinding maken met de server', 'error');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Public methods
|
||
|
|
joinZone(zone) {
|
||
|
|
if (this.isConnected && this.socket) {
|
||
|
|
this.socket.emit('joinZone', zone);
|
||
|
|
console.log(`Joined zone: ${zone}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
leaveZone(zone) {
|
||
|
|
if (this.isConnected && this.socket) {
|
||
|
|
this.socket.emit('leaveZone', zone);
|
||
|
|
console.log(`Left zone: ${zone}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
sendMessage(event, data) {
|
||
|
|
if (this.isConnected && this.socket) {
|
||
|
|
this.socket.emit(event, data);
|
||
|
|
} else {
|
||
|
|
console.warn('Cannot send message: not connected');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
disconnect() {
|
||
|
|
if (this.socket) {
|
||
|
|
this.socket.disconnect();
|
||
|
|
this.isConnected = false;
|
||
|
|
this.updateConnectionStatus(false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
reconnect() {
|
||
|
|
this.disconnect();
|
||
|
|
this.reconnectAttempts = 0;
|
||
|
|
this.connect();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Utility methods
|
||
|
|
showToast(message, type = 'info', duration = 5000) {
|
||
|
|
if (window.ui) {
|
||
|
|
window.ui.showToast(message, type);
|
||
|
|
} else {
|
||
|
|
// Fallback to browser notification
|
||
|
|
console.log(`Toast [${type}]: ${message}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get connection status
|
||
|
|
getConnectionStatus() {
|
||
|
|
return {
|
||
|
|
connected: this.isConnected,
|
||
|
|
reconnectAttempts: this.reconnectAttempts,
|
||
|
|
socketId: this.socket?.id || null
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Create global WebSocket instance
|
||
|
|
window.wsManager = new WebSocketManager();
|