import { MutableRefObject } from 'react'; import { sharedWebSockets } from './globals'; import { Options, SendMessage, Subscriber, WebSocketLike } from './types'; import { isEventSourceSupported, ReadyState, isReactNative } from './constants'; import { attachListeners } from './attach-listener'; import { attachSharedListeners } from './attach-shared-listeners'; import { addSubscriber, removeSubscriber, hasSubscribers } from './manage-subscribers'; //TODO ensure that all onClose callbacks are called const cleanSubscribers = ( url: string, subscriber: Subscriber, optionsRef: MutableRefObject, setReadyState: (readyState: ReadyState) => void, clearSocketIoPingInterval: (() => void) | null, ) => { return () => { removeSubscriber(url, subscriber); if (!hasSubscribers(url)) { try { const socketLike = sharedWebSockets[url]; if (socketLike instanceof WebSocket) { socketLike.onclose = (event: WebSocketEventMap['close']) => { if (optionsRef.current.onClose) { optionsRef.current.onClose(event); } setReadyState(ReadyState.CLOSED); }; } socketLike.close(); } catch (e) { } if (clearSocketIoPingInterval) clearSocketIoPingInterval(); delete sharedWebSockets[url]; } } }; export const createOrJoinSocket = ( webSocketRef: MutableRefObject, url: string, setReadyState: (readyState: ReadyState) => void, optionsRef: MutableRefObject, setLastMessage: (message: WebSocketEventMap['message']) => void, startRef: MutableRefObject<() => void>, reconnectCount: MutableRefObject, sendMessage: SendMessage, ): (() => void) => { if (!isEventSourceSupported && optionsRef.current.eventSourceOptions) { if (isReactNative) { throw new Error('EventSource is not supported in ReactNative'); } else { throw new Error('EventSource is not supported'); } } if (optionsRef.current.share) { let clearSocketIoPingInterval: ((() => void) | null) = null; if (sharedWebSockets[url] === undefined) { sharedWebSockets[url] = optionsRef.current.eventSourceOptions ? new EventSource(url, optionsRef.current.eventSourceOptions) : new WebSocket(url, optionsRef.current.protocols); webSocketRef.current = sharedWebSockets[url]; setReadyState(ReadyState.CONNECTING); clearSocketIoPingInterval = attachSharedListeners( sharedWebSockets[url], url, optionsRef, sendMessage, ); } else { webSocketRef.current = sharedWebSockets[url]; setReadyState(sharedWebSockets[url].readyState); } const subscriber: Subscriber = { setLastMessage, setReadyState, optionsRef, reconnectCount, reconnect: startRef, }; addSubscriber(url, subscriber); return cleanSubscribers( url, subscriber, optionsRef, setReadyState, clearSocketIoPingInterval, ); } else { webSocketRef.current = optionsRef.current.eventSourceOptions ? new EventSource(url, optionsRef.current.eventSourceOptions) : new WebSocket(url, optionsRef.current.protocols); setReadyState(ReadyState.CONNECTING); if (!webSocketRef.current) { throw new Error('WebSocket failed to be created'); } return attachListeners( webSocketRef.current, { setLastMessage, setReadyState }, optionsRef, startRef.current, reconnectCount, sendMessage, ); } };