import { HubConnection, HubConnectionBuilder, LogLevel, HttpTransportType } from '@microsoft/signalr'; import { AppState, AppStateStatus } from 'react-native'; import { SOCKET_URL } from './config'; export interface SocketClient { on(event: string, callback: (...args: any[]) => void): void; off(event: string, callback?: (...args: any[]) => void): void; emit(event: string, ...args: any[]): void; disconnect(): void; status: string; } let connection: HubConnection | null = null; let socketClient: SocketClient | null = null; let appStateListener: any = null; export function connectSocket(token: string): SocketClient { if (connection && (connection.state === 'Connected' || connection.state === 'Connecting')) { return socketClient!; } connection = new HubConnectionBuilder() .withUrl(SOCKET_URL, { accessTokenFactory: () => token, transport: HttpTransportType.WebSockets | HttpTransportType.LongPolling, skipNegotiation: false, }) .withAutomaticReconnect({ nextRetryDelayInMilliseconds: retryContext => { if (retryContext.elapsedMilliseconds < 10000) return 2000; if (retryContext.elapsedMilliseconds < 30000) return 10000; return 30000; } }) .configureLogging(LogLevel.Information) .build(); socketClient = { on: (event: string, callback: (...args: any[]) => void) => { connection?.on(event, callback); }, off: (event: string, callback?: (...args: any[]) => void) => { if (callback) { connection?.off(event, callback); } else { connection?.off(event); } }, emit: (event: string, ...args: any[]) => { if (connection?.state === 'Connected') { connection.invoke(event, ...args).catch(err => { console.error(`SignalR emit error (${event}):`, err); }); } else { console.warn(`SignalR emit skipped (${event}): not connected`); } }, disconnect: () => { if (appStateListener) { appStateListener.remove(); appStateListener = null; } connection?.stop(); }, get status() { return connection?.state || 'Disconnected'; } }; // Lifecycle handling (AppState) appStateListener = AppState.addEventListener('change', (nextAppState: AppStateStatus) => { if (nextAppState === 'active') { if (connection?.state === 'Disconnected') { console.log('SignalR: Re-connecting on App Foreground...'); connection.start().catch(e => console.error('SignalR start error (background to foreground):', e)); } } }); connection.start() .then(() => console.log('SignalR: Connected!')) .catch(err => console.error('SignalR: Initial connection error:', err)); return socketClient; } export function getSocket(): SocketClient | null { return socketClient; } export function disconnectSocket() { if (socketClient) { socketClient.disconnect(); socketClient = null; connection = null; } }