- Refactoring: Error Boundaries, apiFetch Wrapper, Socket Status-Tracking - Refactoring: UI Kontrast (theme-aware colors), unused imports bereinigt - Symbol-Verwaltung: Neues Split-Panel (Meine Symbole + Bibliothek) - Symbol-Verwaltung: Umbenennen (TLF rot/blau), Duplikate erlaubt - Symbol-Verwaltung: Karten-Sidebar zeigt eigene Symbole bevorzugt - Schlauch-Labels: Groessere Schrift (13px/10px), verschiebbar (Drag) - Schema: TenantSymbol customName, sortOrder, unique constraint entfernt - Open Source Referenz entfernt (kostenloses Projekt)
82 lines
2.4 KiB
TypeScript
82 lines
2.4 KiB
TypeScript
'use client'
|
|
|
|
import { io, Socket } from 'socket.io-client'
|
|
|
|
let socket: Socket | null = null
|
|
let currentRoom: string | null = null
|
|
|
|
export type SocketStatus = 'connected' | 'disconnected' | 'reconnecting'
|
|
type StatusListener = (status: SocketStatus) => void
|
|
|
|
let currentStatus: SocketStatus = 'disconnected'
|
|
const statusListeners = new Set<StatusListener>()
|
|
|
|
function setStatus(status: SocketStatus) {
|
|
if (status === currentStatus) return
|
|
currentStatus = status
|
|
statusListeners.forEach(fn => fn(status))
|
|
}
|
|
|
|
/** Subscribe to socket connection status changes. Returns an unsubscribe function. */
|
|
export function onSocketStatus(listener: StatusListener): () => void {
|
|
statusListeners.add(listener)
|
|
// Immediately notify current status
|
|
listener(currentStatus)
|
|
return () => { statusListeners.delete(listener) }
|
|
}
|
|
|
|
/** Get current socket connection status */
|
|
export function getSocketStatus(): SocketStatus {
|
|
return currentStatus
|
|
}
|
|
|
|
export function getSocket(): Socket {
|
|
if (!socket) {
|
|
socket = io({
|
|
path: '/socket.io',
|
|
transports: ['websocket', 'polling'],
|
|
upgrade: true,
|
|
reconnection: true,
|
|
reconnectionAttempts: Infinity,
|
|
reconnectionDelay: 1000,
|
|
reconnectionDelayMax: 5000,
|
|
timeout: 10000,
|
|
forceNew: false,
|
|
})
|
|
socket.on('connect', () => {
|
|
console.log('[Socket.io] Connected:', socket?.id)
|
|
setStatus('connected')
|
|
// Re-join project room after reconnect
|
|
if (currentRoom) {
|
|
console.log('[Socket.io] Re-joining room:', currentRoom)
|
|
socket?.emit('join-project', currentRoom)
|
|
}
|
|
})
|
|
socket.on('disconnect', (reason) => {
|
|
console.warn('[Socket.io] Disconnected:', reason)
|
|
setStatus('disconnected')
|
|
if (reason === 'io server disconnect') {
|
|
// Server disconnected us, need to manually reconnect
|
|
socket?.connect()
|
|
}
|
|
})
|
|
socket.on('connect_error', (err) => {
|
|
console.warn('[Socket.io] Connection error:', err.message)
|
|
})
|
|
socket.io.on('reconnect', (attempt) => {
|
|
console.log('[Socket.io] Reconnected after', attempt, 'attempts')
|
|
setStatus('connected')
|
|
})
|
|
socket.io.on('reconnect_attempt', (attempt) => {
|
|
console.log('[Socket.io] Reconnect attempt', attempt)
|
|
setStatus('reconnecting')
|
|
})
|
|
}
|
|
return socket
|
|
}
|
|
|
|
/** Track which room the socket should be in (for auto-rejoin on reconnect) */
|
|
export function setSocketRoom(projectId: string | null): void {
|
|
currentRoom = projectId
|
|
}
|