From 81683f80839fee21c5fccbd2a9f5191894837179 Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Wed, 1 Nov 2023 15:59:05 +0800 Subject: [PATCH 1/6] feat: useTicksHistory and useActiveSymbols hooks --- src/App.tsx | 2 +- src/api/hooks/useAPI.ts | 3 +- src/api/hooks/useActiveSymbols.ts | 23 +++++++++++ src/api/hooks/useLogin.ts | 4 +- src/api/hooks/useSubscription.ts | 66 +++++++++++++++++++++++++++++++ src/api/hooks/useTicksHistory.ts | 37 +++++++++++++++++ src/api/types.ts | 26 +++++++++++- src/components/layout/Layout.tsx | 51 ++++++++++++++++++++++++ src/components/layout/index.tsx | 13 ------ 9 files changed, 207 insertions(+), 18 deletions(-) create mode 100644 src/api/hooks/useActiveSymbols.ts create mode 100644 src/api/hooks/useSubscription.ts create mode 100644 src/api/hooks/useTicksHistory.ts create mode 100644 src/components/layout/Layout.tsx delete mode 100644 src/components/layout/index.tsx diff --git a/src/App.tsx b/src/App.tsx index 269b97b328..e93f8ddf80 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import ErrorBoundary from 'Components/common/error-boundary'; -import Layout from 'Components/layout'; +import Layout from 'Components/layout/Layout'; const App = () => ( diff --git a/src/api/hooks/useAPI.ts b/src/api/hooks/useAPI.ts index f4406fc731..e5a23cceff 100644 --- a/src/api/hooks/useAPI.ts +++ b/src/api/hooks/useAPI.ts @@ -2,6 +2,7 @@ import { useCallback, useContext } from 'react'; import type { TSocketEndpointNames, + TSocketError, TSocketPaginateableEndpointNames, TSocketRequestPayload, TSocketResponseData, @@ -36,7 +37,7 @@ const useAPI = () => { ): { subscribe: ( onData: (response: Promise>) => void, - onError: (response: Promise>) => void + onError: (response: Promise>) => void ) => { unsubscribe?: VoidFunction }; } => api?.subscribe({ [name]: 1, subscribe: 1, ...(payload || {}) }), [api] diff --git a/src/api/hooks/useActiveSymbols.ts b/src/api/hooks/useActiveSymbols.ts new file mode 100644 index 0000000000..fede4b396f --- /dev/null +++ b/src/api/hooks/useActiveSymbols.ts @@ -0,0 +1,23 @@ +import { useMemo } from 'react'; +import useQuery from './useQuery'; +import useLogin from './useLogin'; + +const useActiveSymbols = () => { + const { is_logged_in, isAuthorized } = useLogin(); + const { data: active_symbols, ...rest } = useQuery('active_symbols', { + payload: { active_symbols: 'brief' }, + options: { enabled: is_logged_in ? isAuthorized : true }, + }); + + const modified_active_symbols = useMemo( + () => ({ ...active_symbols?.active_symbols }), + [active_symbols?.active_symbols] + ); + + return { + data: modified_active_symbols, + ...rest, + }; +}; + +export default useActiveSymbols; diff --git a/src/api/hooks/useLogin.ts b/src/api/hooks/useLogin.ts index 10e4d17ad9..3bf3c88077 100644 --- a/src/api/hooks/useLogin.ts +++ b/src/api/hooks/useLogin.ts @@ -6,7 +6,7 @@ import { useEffect, useMemo } from 'react'; const useLogin = () => { const search = window.location.search; const client_account_params = readLoginQueryParams(); - const { data: client_info } = useAuthorize(); + const { data: client_info, isSuccess } = useAuthorize(); let is_logged_in = false; if (localStorage.getItem('active_loginId')) { @@ -25,7 +25,7 @@ const useLogin = () => { if (search) deleteQueryParams(); } }, [client_info, client_account_params, client_object, search]); - return { is_logged_in }; + return { is_logged_in, isAuthorized: isSuccess }; }; export default useLogin; diff --git a/src/api/hooks/useSubscription.ts b/src/api/hooks/useSubscription.ts new file mode 100644 index 0000000000..a4ea9d76de --- /dev/null +++ b/src/api/hooks/useSubscription.ts @@ -0,0 +1,66 @@ +import { useCallback, useEffect, useRef, useState } from 'react'; +import useAPI from './useAPI'; +import type { + TSocketAcceptableProps, + TSocketError, + TSocketRequestPayload, + TSocketResponseData, + TSocketSubscribableEndpointNames, +} from '../types'; + +const useSubscription = (name: T) => { + const [isLoading, setIsLoading] = useState(false); + const [isSubscribed, setSubscribed] = useState(false); + const [error, setError] = useState>(); + const [data, setData] = useState>(); + const subscriber = useRef<{ unsubscribe?: VoidFunction }>(); + const { subscribe: _subscribe } = useAPI(); + + const subscribe = useCallback( + (...props: TSocketAcceptableProps) => { + const prop = props?.[0]; + const payload = prop && 'payload' in prop ? (prop.payload as TSocketRequestPayload) : undefined; + + setIsLoading(true); + setSubscribed(true); + + try { + subscriber.current = _subscribe(name, payload).subscribe( + async response => { + setData(await response); + setIsLoading(false); + }, + async response => { + setError((await response).error as unknown as TSocketError); + setIsLoading(false); + } + ); + } catch (e) { + setError(e as TSocketError); + } + }, + [_subscribe, name] + ); + + const unsubscribe = useCallback(() => { + subscriber.current?.unsubscribe?.(); + setSubscribed(false); + }, []); + + useEffect(() => { + return () => { + unsubscribe(); + }; + }, [unsubscribe]); + + return { + subscribe, + unsubscribe, + isLoading, + isSubscribed, + error, + data, + }; +}; + +export default useSubscription; diff --git a/src/api/hooks/useTicksHistory.ts b/src/api/hooks/useTicksHistory.ts new file mode 100644 index 0000000000..5253bea4e2 --- /dev/null +++ b/src/api/hooks/useTicksHistory.ts @@ -0,0 +1,37 @@ +import { useCallback } from 'react'; +import useSubscription from './useSubscription'; + +import { TSocketRequestPayload } from 'Api/types'; + +type TTicksHistoryPayload = TSocketRequestPayload<'ticks_history'>['payload']; + +const useTicksHistory = () => { + const { subscribe: _subscribe, unsubscribe, data, ...rest } = useSubscription('ticks_history'); + + const subscribe = useCallback( + (symbol: TTicksHistoryPayload['ticks_history'], granularity: TTicksHistoryPayload['granularity']) => { + const style: TTicksHistoryPayload['style'] = granularity ? 'candles' : 'ticks'; + + return _subscribe({ + payload: { + adjust_start_time: 1, + ticks_history: symbol, + end: 'latest', + count: 1000, + granularity: !granularity ? undefined : granularity, + style, + }, + }); + }, + [_subscribe] + ); + + return { + subscribe, + unsubscribe, + data, + ...rest, + }; +}; + +export default useTicksHistory; diff --git a/src/api/types.ts b/src/api/types.ts index 063d70aeb0..85a4ba408e 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -589,7 +589,7 @@ type TSocketEndpoints = { response: StatesListResponse; }; ticks_history: { - request: TicksHistoryRequest; + request: Omit & { count?: number | string }; // count type from @deriv-api is wrong. Hence redefinition. response: TicksHistoryResponse; }; ticks: { @@ -731,3 +731,27 @@ export type TSocketPaginateableEndpointNames = KeysMatching< TSocketEndpoints, { request: { limit?: number; offset?: number } } >; + +export type TSocketError = { + /** + * Echo of the request made. + */ + echo_req: { + [k: string]: unknown; + }; + /** + * Error object. + */ + error: { + code: string; + message: string; + }; + /** + * Action name of the request made. + */ + msg_type: T; + /** + * [Optional] Used to map request to response. + */ + req_id?: number; +}; diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx new file mode 100644 index 0000000000..1b7fb55bee --- /dev/null +++ b/src/components/layout/Layout.tsx @@ -0,0 +1,51 @@ +import { Fragment, PropsWithChildren, useEffect, useState } from 'react'; +import Header from './header'; +import Footer from './footer'; +import useTicksHistory from 'Api/hooks/useTicksHistory'; + +type TParams = Parameters['subscribe']>[1]; +const Layout = ({ children }: PropsWithChildren) => { + const [symbol, setSymbol] = useState('R_50'); + const [granularity, setGranularity] = useState(undefined); + const { subscribe, unsubscribe } = useTicksHistory(); + const handleChange = (e: React.ChangeEvent) => { + if ('target' in e) { + if (e.target.name === 'symbol') setSymbol(e.target.value); + if (e.target.name === 'granularity') setGranularity(Number(e.target.value) as TParams); + } + }; + useEffect(() => { + subscribe(symbol, granularity); + return () => { + unsubscribe(); + }; + }, [subscribe, unsubscribe, symbol, granularity]); + + return ( + +
+ {children} +
+ + +
+
+ + +
+