Skip to content

Commit

Permalink
Merge branch 'master' of github.com:binary-com/deriv-app into sprint-…
Browse files Browse the repository at this point in the history
…10/account-package-refactor
  • Loading branch information
“yauheni-kryzhyk-deriv” committed Sep 26, 2023
2 parents b6e3955 + ed4a87f commit baaca7a
Show file tree
Hide file tree
Showing 255 changed files with 3,598 additions and 1,998 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const RadioButton = ({
label,
second_line_label,
onClick,
className,
...props
}: TRadioButton) => {
return (
Expand All @@ -76,7 +77,7 @@ const RadioButton = ({
onChange={onChange}
onBlur={onBlur}
disabled={props.selected}
className={classNames('currency-list__radio-button')}
className={classNames(className, 'currency-list__radio-button')}
{...props}
/>
<label
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ const PageOverlayWrapper = observer(({ routes, subroutes }: PageOverlayWrapperPr
const { client, common, ui } = useStore();
const { is_mobile } = ui;
const { logout } = client;
const { is_from_derivgo, routeBackInApp } = common;
const { is_from_derivgo } = common;

const list_groups = routes.map(route_group => ({
icon: route_group.icon,
label: route_group?.getTitle(),
subitems: route_group?.subroutes?.length ? route_group.subroutes.map(sub => subroutes.indexOf(sub)) : [],
}));

const onClickClose = React.useCallback(() => routeBackInApp(history), [routeBackInApp, history]);
const onClickClose = React.useCallback(() => history.push(shared_routes.traders_hub), [history]);

const selected_route = getSelectedRoute({ routes: subroutes as Array<TRoute>, pathname: location.pathname });

Expand Down
59 changes: 58 additions & 1 deletion packages/analytics/src/rudderstack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,52 @@ type VirtualSignupEmailConfirmationAction = {
error_message?: string;
};

type IndicatorsTypesFormAction = {
action:
| 'open'
| 'close'
| 'add_active'
| 'clean_all_active'
| 'delete_active'
| 'edit_active'
| 'search'
| 'info_open'
| 'info_close';
form_name?: string;
indicator_type_name?: string;
indicators_category_name?: string;
search_string?: string;
subform_name?: string;
account_type: string;
device_type: string;
};

type MarketTypesFormAction = {
action:
| 'open'
| 'close'
| 'choose_market_type'
| 'search'
| 'info_redirect'
| 'add_to_favorites'
| 'delete_from_favorites';
form_name: string;
market_type_name: string;
search_string?: string;
tab_market_name?: string;
account_type: string;
device_type: string;
};

type ChartTypesFormAction = {
action: 'open' | 'close' | 'choose_chart_type' | 'choose_time_interval';
form_name: string;
chart_type_name: string;
time_interval_name: string;
account_type: string;
device_type: string;
};

type TradeTypesFormAction =
| {
action: 'open' | 'close' | 'info_close';
Expand Down Expand Up @@ -88,6 +134,9 @@ type IdentifyAction = {
};

type TEvents = {
ce_chart_types_form: ChartTypesFormAction;
ce_indicators_types_form: IndicatorsTypesFormAction;
ce_market_types_form: MarketTypesFormAction;
ce_virtual_signup_form: VirtualSignupFormAction;
ce_real_account_signup_form: RealAccountSignupFormAction;
ce_virtual_signup_email_confirmation: VirtualSignupEmailConfirmationAction;
Expand All @@ -99,6 +148,7 @@ export class RudderStack {
has_identified = false;
has_initialized = false;
current_page = '';
account_type = '';

constructor() {
this.init();
Expand All @@ -123,6 +173,10 @@ export class RudderStack {
}
}

setAccountType(account_type: string) {
this.account_type = account_type;
}

identifyEvent = (user_id: string, payload: TEvents['identify']) => {
if (this.has_initialized) {
RudderAnalytics.identify(user_id, payload);
Expand Down Expand Up @@ -155,7 +209,10 @@ export class RudderStack {
*/
track<T extends keyof TEvents>(event: T, payload: TEvents[T]) {
if (this.has_initialized && this.has_identified) {
RudderAnalytics.track(event, payload);
RudderAnalytics.track(event, {
...payload,
account_type: this.account_type,
});
}
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"version": "1.0.0",
"main": "src/index.ts",
"dependencies": {
"@deriv/deriv-api": "^1.0.11",
"@deriv/utils": "^1.0.0",
"@deriv/shared": "^1.0.0",
"react": "^17.0.2",
Expand Down
8 changes: 8 additions & 0 deletions packages/api/src/APIContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createContext } from 'react';

// Don't need to type `deriv_api` here, We will be using these methods inside
// the `useQuery`, `useMutation` and `useSubscription` hook to make it type-safe.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const APIContext = createContext<Record<string, any> | null>(null);

export default APIContext;
53 changes: 46 additions & 7 deletions packages/api/src/APIProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import React, { PropsWithChildren } from 'react';
// @ts-expect-error `@deriv/deriv-api` is not in TypeScript, Hence we ignore the TS error.
import DerivAPIBasic from '@deriv/deriv-api/dist/DerivAPIBasic';
import { getAppId, getSocketURL, useWS } from '@deriv/shared';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import APIContext from './APIContext';

declare global {
interface Window {
ReactQueryClient: QueryClient;
ReactQueryClient?: QueryClient;
DerivAPI?: Record<string, DerivAPIBasic>;
}
}

Expand All @@ -18,13 +23,47 @@ const getSharedQueryClientContext = (): QueryClient => {
return window.ReactQueryClient;
};

// This is a temporary workaround to share a single `DerivAPIBasic` instance for every unique URL.
// Later once we have each package separated we won't need this anymore and can remove this.
const getDerivAPIInstance = (): DerivAPIBasic => {
const endpoint = getSocketURL();
const app_id = getAppId();
const language = 'EN'; // Need to use the language from the app context.
const brand = 'deriv';
const wss = `wss://${endpoint}/websockets/v3?app_id=${app_id}&l=${language}&brand=${brand}`;

if (!window.DerivAPI) {
window.DerivAPI = {};
}

if (!window.DerivAPI?.[wss]) {
window.DerivAPI[wss] = new DerivAPIBasic({ connection: new WebSocket(wss) });
}

return window.DerivAPI?.[wss];
};

const queryClient = getSharedQueryClientContext();

const APIProvider = ({ children }: PropsWithChildren<unknown>) => (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools />
</QueryClientProvider>
);
type TProps = {
/** If set to true, the APIProvider will instantiate it's own socket connection. */
standalone?: boolean;
};

const APIProvider = ({ children, standalone = false }: PropsWithChildren<TProps>) => {
const WS = useWS();
// Use the new API instance if the `standalone` prop is set to true,
// else use the legacy socket connection.
const active_connection = standalone ? getDerivAPIInstance() : WS;

return (
<APIContext.Provider value={active_connection}>
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools />
</QueryClientProvider>
</APIContext.Provider>
);
};

export default APIProvider;
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,26 @@ import React from 'react';
import { renderHook } from '@testing-library/react-hooks';
import { TSocketResponse } from '../../types';
import APIProvider from '../APIProvider';
import useRequest from '../useRequest';
import useMutation from '../useMutation';

jest.mock('@deriv/shared', () => ({
WS: {
...jest.requireActual('@deriv/shared'),
useWS: () => ({
send: jest.fn(() =>
Promise.resolve<TSocketResponse<'verify_email'>>({
msg_type: 'verify_email',
verify_email: 1,
echo_req: {},
})
),
},
}),
}));

describe('useRequest', () => {
describe('useMutation', () => {
test('should call verify_email and get 1 in response', async () => {
const wrapper = ({ children }: { children: JSX.Element }) => <APIProvider>{children}</APIProvider>;

const { result, waitFor } = renderHook(() => useRequest('verify_email'), { wrapper });
const { result, waitFor } = renderHook(() => useMutation('verify_email'), { wrapper });

result.current.mutate({ payload: { verify_email: 'john@example.com', type: 'request_email' } });

Expand Down
5 changes: 3 additions & 2 deletions packages/api/src/__tests__/usePaginatedFetch.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import APIProvider from '../APIProvider';
import usePaginatedFetch from '../usePaginatedFetch';

jest.mock('@deriv/shared', () => ({
WS: {
...jest.requireActual('@deriv/shared'),
useWS: () => ({
send: jest.fn(() =>
Promise.resolve<TSocketResponse<'p2p_advert_list'>>({
p2p_advert_list: {
Expand All @@ -23,7 +24,7 @@ jest.mock('@deriv/shared', () => ({
req_id: 1,
})
),
},
}),
}));

describe('usePaginatedFetch', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,27 @@ import React from 'react';
import { renderHook } from '@testing-library/react-hooks';
import { TSocketResponse } from '../../types';
import APIProvider from '../APIProvider';
import useFetch from '../useFetch';
import useQuery from '../useQuery';

jest.mock('@deriv/shared', () => ({
WS: {
...jest.requireActual('@deriv/shared'),
useWS: () => ({
send: jest.fn(() =>
Promise.resolve<TSocketResponse<'ping'>>({
msg_type: 'ping',
ping: 'pong',
echo_req: {},
})
),
},
subscribe: jest.fn(),
}),
}));

describe('useFetch', () => {
describe('useQuery', () => {
test('should call ping and get pong in response', async () => {
const wrapper = ({ children }: { children: JSX.Element }) => <APIProvider>{children}</APIProvider>;

const { result, waitFor } = renderHook(() => useFetch('ping'), { wrapper });
const { result, waitFor } = renderHook(() => useQuery('ping'), { wrapper });

await waitFor(() => result.current.isSuccess, { timeout: 10000 });

Expand Down
8 changes: 6 additions & 2 deletions packages/api/src/__tests__/useSubscription.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import { useWS } from '@deriv/shared';
import { act, renderHook } from '@testing-library/react-hooks';
import APIProvider from '../APIProvider';
import useSubscription from '../useSubscription';

jest.mock('@deriv/shared');
Expand Down Expand Up @@ -29,9 +31,11 @@ describe('useSubscription', () => {
}),
});

const { result, waitForNextUpdate } = renderHook(() => useSubscription('p2p_order_info'));
const wrapper = ({ children }: { children: JSX.Element }) => <APIProvider>{children}</APIProvider>;

expect(result.current.is_loading).toBe(false);
const { result, waitForNextUpdate } = renderHook(() => useSubscription('p2p_order_info'), { wrapper });

expect(result.current.isLoading).toBe(false);
expect(result.current.error).toBe(undefined);
expect(result.current.data?.p2p_order_info).toBe(undefined);

Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ export { default as useMT5AccountsList } from './useMT5AccountsList';
export { default as useSettings } from './useSettings';
export { default as useSortedMT5Accounts } from './useSortedMT5Accounts';
export { default as useTradingAccountsList } from './useTradingAccountsList';
export { default as useTradingPlatformAccounts } from './useTradingPlatformAccounts';
export { default as useTradingPlatformInvestorPasswordChange } from './useTradingPlatformInvestorPasswordChange';
export { default as useTradingPlatformInvestorPasswordReset } from './useTradingPlatformInvestorPasswordReset';
export { default as useTradingPlatformPasswordChange } from './useTradingPlatformPasswordChange';
export { default as useTransferBetweenAccounts } from './useTransferBetweenAccounts';
export { default as useWalletAccountsList } from './useWalletAccountsList';
export { default as useDerivezServiceToken } from './useDerivezServiceToken';
8 changes: 3 additions & 5 deletions packages/api/src/hooks/useAccountTypes.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { useMemo } from 'react';
import useFetch from '../useFetch';
import useQuery from '../useQuery';

/**
* A custom hook to get available account types for a specific landing company.
*/
/** A custom hook to get available account types for a specific landing company. */
const useAccountTypes = (landing_company?: string) => {
const { data, ...rest } = useFetch('get_account_types', {
const { data, ...rest } = useQuery('get_account_types', {
payload: { company: landing_company },
options: { enabled: Boolean(landing_company) },
});
Expand Down
4 changes: 2 additions & 2 deletions packages/api/src/hooks/useAuthorize.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useCallback, useMemo } from 'react';
import { getActiveAuthTokenIDFromLocalStorage, getActiveLoginIDFromLocalStorage } from '@deriv/utils';
import useFetch from '../useFetch';
import useInvalidateQuery from '../useInvalidateQuery';
import useQuery from '../useQuery';

/** A custom hook that authorize the user with the given token. If no token is given,
* it will use the current token from localStorage.
Expand All @@ -10,7 +10,7 @@ const useAuthorize = () => {
const current_token = getActiveAuthTokenIDFromLocalStorage();
const invalidate = useInvalidateQuery();

const { data, ...rest } = useFetch('authorize', {
const { data, ...rest } = useQuery('authorize', {
payload: { authorize: current_token || '' },
options: { enabled: Boolean(current_token) },
});
Expand Down
9 changes: 6 additions & 3 deletions packages/api/src/hooks/useAvailableMT5Accounts.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { useMemo } from 'react';
import useFetch from '../useFetch';
import useQuery from '../useQuery';

const market_type_to_leverage_mapper: Record<string, number> = {
gaming: 500,
financial: 1000,
all: 100,
};

/** @description This hook is used to get all the available MT5 accounts. */
/** A custom hook to get the list of available MT5 accounts. */
const useAvailableMT5Accounts = () => {
const { data: mt5_available_accounts, ...rest } = useFetch('trading_platform_available_accounts', {
const { data: mt5_available_accounts, ...rest } = useQuery('trading_platform_available_accounts', {
payload: { platform: 'mt5' },
});

Expand All @@ -18,8 +18,11 @@ const useAvailableMT5Accounts = () => {
mt5_available_accounts?.trading_platform_available_accounts?.map(account => {
return {
...account,
/** The market type for the account */
market_type: account.market_type === 'gaming' ? 'synthetic' : account.market_type,
/** The platform for the account */
platform: 'mt5',
/** Leverage for the account */
leverage:
market_type_to_leverage_mapper[
account.market_type as keyof typeof market_type_to_leverage_mapper
Expand Down
Loading

0 comments on commit baaca7a

Please sign in to comment.