diff --git a/src/client-library/deriv-api-client.ts b/src/client-library/deriv-api-client.ts index 9282be5..f90f2c5 100644 --- a/src/client-library/deriv-api-client.ts +++ b/src/client-library/deriv-api-client.ts @@ -8,10 +8,10 @@ import { TSocketSubscribeResponseData, } from '../types/api.types'; -export type DerivAPIClientOptions = { +export interface DerivAPIClientOptions { onOpen?: (e: Event) => void; onClose?: (e: CloseEvent) => void; -}; +} type DataHandler = (data: TSocketResponseData) => void; @@ -57,6 +57,8 @@ export type UnsubscribeHandlerArgs = { }; export class DerivAPIClient { + options?: DerivAPIClientOptions; + endpoint: string; websocket: WebSocket; requestHandler: RequestMap; subscribeHandler: SubscriptionMap; @@ -70,20 +72,25 @@ export class DerivAPIClient { keepAliveIntervalId: NodeJS.Timeout | null = null; constructor(endpoint: string, options?: DerivAPIClientOptions) { - this.websocket = new WebSocket(endpoint); + this.options = options; + this.endpoint = endpoint; + this.websocket = new WebSocket(this.endpoint); this.req_id = 0; this.requestHandler = new Map(); this.subscribeHandler = new Map(); this.waitForWebSocketOpen = PromiseUtils.createPromise(); + this.init(); + } + async init() { this.websocket.addEventListener('open', e => { - if (typeof options?.onOpen === 'function') options.onOpen(e); + if (typeof this.options?.onOpen === 'function') this.options.onOpen(e); const { resolve } = this.waitForWebSocketOpen; resolve({}); }); this.websocket.addEventListener('close', e => { - if (typeof options?.onClose === 'function') options.onClose(e); + if (typeof this.options?.onClose === 'function') this.options.onClose(e); }); this.websocket.addEventListener('message', async response => { @@ -258,6 +265,13 @@ export class DerivAPIClient { } } + async reconnect() { + this.websocket = new WebSocket(this.endpoint); + this.waitForWebSocketOpen = PromiseUtils.createPromise(); + this.init(); + this.reinitializeSubscriptions(this.subscribeHandler, this.authorizePayload); + } + isSocketClosingOrClosed() { return ![2, 3].includes(this.websocket.readyState); } diff --git a/src/client-library/deriv-api-manager.ts b/src/client-library/deriv-api-manager.ts index a5c6554..00e0bab 100644 --- a/src/client-library/deriv-api-manager.ts +++ b/src/client-library/deriv-api-manager.ts @@ -1,12 +1,24 @@ import { DerivAPIClient, DerivAPIClientOptions } from './deriv-api-client'; +export interface DerivAPIManagerOptions extends DerivAPIClientOptions { + reconnect?: boolean; +} + export class DerivAPIManager { options?: DerivAPIClientOptions; activeClient: DerivAPIClient; clientList: Map = new Map(); - constructor(endpoint: string, options?: DerivAPIClientOptions) { - const client = new DerivAPIClient(endpoint, options); + constructor(endpoint: string, options?: DerivAPIClientOptions & DerivAPIManagerOptions) { + const { reconnect = true, ...api_options } = options || {}; + + const client = new DerivAPIClient(endpoint, { + onClose: () => { + api_options?.onClose; + if (reconnect) this.handleReconnect(); + }, + onOpen: api_options?.onOpen, + }); this.clientList.set(endpoint, client); this.activeClient = client; this.options = options; @@ -16,16 +28,8 @@ export class DerivAPIManager { return this.activeClient; } - async reconnect() { - const current_url = this.activeClient.websocket.url; - const current_handler = this.activeClient.subscribeHandler; - const current_authorize_payload = this.activeClient.authorizePayload; - - this.clientList.delete(current_url); - const reinitialized_instance = new DerivAPIClient(current_url, this.options); - await reinitialized_instance.reinitializeSubscriptions(current_handler, current_authorize_payload); - this.clientList.set(current_url, reinitialized_instance); - this.activeClient = reinitialized_instance; + async handleReconnect() { + await this.activeClient.reconnect(); } /** diff --git a/src/context/api-context.tsx b/src/context/api-context.tsx index 85f9b1f..047db65 100644 --- a/src/context/api-context.tsx +++ b/src/context/api-context.tsx @@ -1,4 +1,4 @@ -import { createContext, PropsWithChildren, useEffect, useRef, useState } from 'react'; +import { createContext, PropsWithChildren } from 'react'; import { URLUtils } from '@deriv-com/utils'; import { DerivAPIManager } from '../client-library/deriv-api-manager'; @@ -8,7 +8,7 @@ type APIData = { export const APIDataContext = createContext(null); -type ConnectionState = 'idle' | 'active' | 'reconnecting' | 'disconnected'; +const derivApiManager = new DerivAPIManager(URLUtils.getWebsocketURL()); /** * Provides a React Query client and API data context to its child components. @@ -20,28 +20,5 @@ type ConnectionState = 'idle' | 'active' | 'reconnecting' | 'disconnected'; * @returns {JSX.Element} The provider component wrapping its children with API data context and React Query client. */ export const APIProvider = ({ children }: PropsWithChildren) => { - const [connection_state, setConnectionState] = useState('idle'); - const derivApiManager = useRef( - new DerivAPIManager(URLUtils.getWebsocketURL(), { - onOpen: () => { - setConnectionState('active'); - }, - onClose: () => { - setConnectionState('disconnected'); - }, - }) - ); - - useEffect(() => { - if (connection_state === 'disconnected') { - setConnectionState('reconnecting'); - derivApiManager.current.reconnect(); - } - }, [connection_state]); - - return ( - - {children} - - ); + return {children}; };