Skip to content
This repository has been archived by the owner on Sep 26, 2024. It is now read-only.

mayuran/DPROD-2759/ws_improvements #7070

Merged
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 38 additions & 22 deletions src/common/websocket/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const PING_INTERVAL = 30000
export class ApiManager {
private socket: WebSocket
public derivApi: TDerivApi
private pingInterval: NodeJS.Timer
private ready: boolean

public static instance: ApiManager
Expand All @@ -34,22 +33,59 @@ export class ApiManager {
return ApiManager.instance
}

public static readonly READY_STATE_KEY = 'websocket_ready_state'

private setReadyStateInSessionStorage(state: number) {
mayuran-deriv marked this conversation as resolved.
Show resolved Hide resolved
if (isBrowser()) {
sessionStorage.setItem(ApiManager.READY_STATE_KEY, state.toString())
}
}

public init(lang?: string) {
if (!this.ready) {
if (!this.socket) {
console.log('WS connecting...')
const language = lang === 'ach' ? getCrowdin() : lang?.replace('-', '_')
const socket_url = getSocketURL()
const app_id = getAppId()
const websocket_connection_url = `${socket_url}?app_id=${app_id}&l=${language}&brand=${brand_name.toLowerCase()}`

this.socket = new WebSocket(websocket_connection_url)
this.setReadyStateInSessionStorage(this.socket?.readyState)
}
this.derivApi = new DerivAPIBasic({ connection: this.socket })
this.registerKeepAlive()
this.socket.addEventListener('open', () => {
console.log('WS connected.')
Copy link
Contributor Author

@mayuran-deriv mayuran-deriv Feb 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kept console logs for QA purpose, later will remove console logs.

this.setReadyStateInSessionStorage(this?.socket?.readyState)
})

this.socket.addEventListener('close', () => {
console.log('WS closed')
this.derivApi.disconnect()
this.ready = null
sessionStorage.removeItem(ApiManager.READY_STATE_KEY)
mayuran-deriv marked this conversation as resolved.
Show resolved Hide resolved
})
this.ready = true
}
}

public reconnectIfNotConnected(lang?: string): Promise<void> {
console.log('WS reconnecting....')
return new Promise((resolve, reject) => {
if (this?.socket?.readyState !== 1) {
this.socket = null
this.ready = null
this.init(lang)
this?.socket?.addEventListener?.('open', () => {
console.log('WS connected using reconnect method.')
resolve()
})
} else {
resolve()
}
})
}

public augmentedSend<T extends TSocketEndpointNames>(
name: T,
request?: TSocketRequestProps<T> extends never ? undefined : TSocketRequestProps<T>,
Expand All @@ -70,25 +106,6 @@ export class ApiManager {
return this.derivApi.authorize({ authorize: token })
}

private registerKeepAlive() {
if (this.pingInterval) {
clearInterval(this.pingInterval)
}
this.socket.addEventListener('open', () => {
this.pingInterval = setInterval(() => {
this.socket.send(JSON.stringify({ ping: 1 }))
}, PING_INTERVAL)
})

this.socket.addEventListener('close', () => {
clearInterval(this.pingInterval)
})

this.socket.addEventListener('error', () => {
clearInterval(this.pingInterval)
})
}

public reset(language: string) {
const socket_url = getSocketURL()
const app_id = getAppId()
Expand All @@ -99,7 +116,6 @@ export class ApiManager {

this.socket = new WebSocket(websocket_connection_url)
this.derivApi = new DerivAPIBasic({ connection: this.socket })
this.registerKeepAlive()
}
}
let apiManager: ApiManager
Expand Down
31 changes: 24 additions & 7 deletions src/components/hooks/useWS.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useCallback, useState } from 'react'
import { TSocketEndpointNames, TSocketResponseData } from 'common/websocket/types'
import apiManager from 'common/websocket'
import { getLanguage, isBrowser } from 'common/utility'

const useWS = <T extends TSocketEndpointNames>(name: T) => {
const [is_loading, setIsLoading] = useState(false)
Expand All @@ -15,13 +16,29 @@ const useWS = <T extends TSocketEndpointNames>(name: T) => {
const send = useCallback(
async (data?: Parameters<typeof apiManager.augmentedSend<T>>[1]) => {
setIsLoading(true)
try {
const response = await apiManager.augmentedSend(name, data)
setData(response[name] as TSocketResponseData<T>)
} catch (e) {
setError(e)
} finally {
setIsLoading(false)
const readyState = parseInt(sessionStorage.getItem('websocket_ready_state'))
mayuran-deriv marked this conversation as resolved.
Show resolved Hide resolved
if (readyState === 1 || readyState === 0) {
mayuran-deriv marked this conversation as resolved.
Show resolved Hide resolved
try {
const response = await apiManager.augmentedSend(name, data)
setData(response[name] as TSocketResponseData<T>)
} catch (e) {
setError(e)
} finally {
setIsLoading(false)
}
} else {
if (isBrowser()) {
const currentLanguage = getLanguage() ?? 'en'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't getLanguage() return a default lang? Why?
I think that instead of such checks you should be able to return a default.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its old method which we are using to get language so i didn't change the method

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can improve it. Why not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thats where the high test coverage helps :) to increase confidence and thus enable refactors like that ;)
so developers are not worried of changing stuff as they know its gonna be caught by tests

apiManager
.reconnectIfNotConnected(currentLanguage)
mayuran-deriv marked this conversation as resolved.
Show resolved Hide resolved
.then(() => {
send()
})
.catch((e) => {
setError(e)
setIsLoading(false)
})
}
}
},
[name],
Expand Down
21 changes: 18 additions & 3 deletions src/store/website-status-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ type WebsiteStatusContextType = {
}

export const WebsiteStatusContext = createContext<WebsiteStatusContextType>(null)
let statusDataPromise = null
let statusDataPromiseResolve = null

export const WebsiteStatusProvider = ({ children }: WebsiteStatusProviderProps) => {
const WEBSITE_STATUS_COUNTRY_KEY = 'website_status'
const { data, send } = useWS(WEBSITE_STATUS_COUNTRY_KEY)
const { data: wsData, send } = useWS(WEBSITE_STATUS_COUNTRY_KEY)
const [data, setData] = useState(null)
const [days_from_today, setDaysFromToday] = useState(null)

useEffect(() => {
Expand All @@ -30,8 +33,20 @@ export const WebsiteStatusProvider = ({ children }: WebsiteStatusProviderProps)
})

useEffect(() => {
send()
}, [send])
if (wsData) {
setData(wsData)
statusDataPromiseResolve(wsData)
} else if (!statusDataPromise) {
statusDataPromise = new Promise((resolve) => {
statusDataPromiseResolve = resolve
})
send()
} else if (statusDataPromise) {
statusDataPromise.then((data) => {
setData(data)
})
}
}, [send, wsData])
mayuran-deriv marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
if (data) {
Expand Down
Loading