diff --git a/packages/account/package.json b/packages/account/package.json index 6feecfbc8024..cfd2445d9f8d 100644 --- a/packages/account/package.json +++ b/packages/account/package.json @@ -31,7 +31,6 @@ "@deriv/api-types": "^1.0.94", "@deriv/components": "^1.0.0", "@deriv/shared": "^1.0.0", - "@deriv/stores":"^1.0.0", "@deriv/translations": "^1.0.0", "bowser": "^2.9.0", "classnames": "^2.2.6", diff --git a/packages/account/src/App.tsx b/packages/account/src/App.tsx index f658afe8facd..0a666cb80bf1 100644 --- a/packages/account/src/App.tsx +++ b/packages/account/src/App.tsx @@ -1,27 +1,27 @@ import React from 'react'; import Routes from './Containers/routes'; import ResetTradingPassword from './Containers/reset-trading-password'; -import { setWebsocket } from '@deriv/shared'; -import { StoreProvider } from '@deriv/stores'; -import { TCoreStores } from '@deriv/stores/types'; +import { MobxContentProvider } from './Stores/connect'; +import initStore from './Stores/init-store'; +import TCoreStore from './Stores/index'; -// TODO: add correct types for WS after implementing them +// TODO: add correct types for stores and WS after implementing them type TAppProps = { passthrough: { - root_store: TCoreStores; + root_store: TCoreStore; WS: Record; }; }; const App = ({ passthrough }: TAppProps) => { const { root_store, WS } = passthrough; - setWebsocket(WS); + initStore(root_store, WS); return ( - + - + ); }; diff --git a/packages/account/src/Components/Routes/__tests__/binary-link.spec.tsx b/packages/account/src/Components/Routes/__tests__/binary-link.spec.tsx index 7c47af226cd7..450cbb9b6aa3 100644 --- a/packages/account/src/Components/Routes/__tests__/binary-link.spec.tsx +++ b/packages/account/src/Components/Routes/__tests__/binary-link.spec.tsx @@ -6,6 +6,12 @@ import { PlatformContext } from '@deriv/shared'; import { findRouteByPath } from '../helpers'; import BinaryLink from '../binary-link'; +jest.mock('Stores/connect', () => ({ + __esModule: true, + default: 'mockedDefaultExport', + connect: () => Component => Component, +})); + jest.mock('../helpers', () => ({ findRouteByPath: jest.fn(() => '/test/path'), normalizePath: jest.fn(() => '/test/path'), diff --git a/packages/account/src/Components/Routes/__tests__/binary-routes.spec.tsx b/packages/account/src/Components/Routes/__tests__/binary-routes.spec.tsx index 3938f7960aea..c57920aaecb8 100644 --- a/packages/account/src/Components/Routes/__tests__/binary-routes.spec.tsx +++ b/packages/account/src/Components/Routes/__tests__/binary-routes.spec.tsx @@ -5,6 +5,12 @@ import { render, screen } from '@testing-library/react'; import { PlatformContext } from '@deriv/shared'; import BinaryRoutes from '../binary-routes'; +jest.mock('Stores/connect', () => ({ + __esModule: true, + default: 'mockedDefaultExport', + connect: () => Component => Component, +})); + jest.mock('../route-with-sub-routes', () => jest.fn(() =>
RouteWithSubRoutes
)); jest.mock('Constants/routes-config', () => () => [{}]); diff --git a/packages/account/src/Components/account-limits/__tests__/account-limits.spec.tsx b/packages/account/src/Components/account-limits/__tests__/account-limits.spec.tsx index 07c98028a783..bc0f8b7f825c 100644 --- a/packages/account/src/Components/account-limits/__tests__/account-limits.spec.tsx +++ b/packages/account/src/Components/account-limits/__tests__/account-limits.spec.tsx @@ -3,7 +3,12 @@ import { screen, render } from '@testing-library/react'; import { formatMoney, isDesktop, isMobile, PlatformContext } from '@deriv/shared'; import AccountLimits from '../account-limits'; import { BrowserRouter } from 'react-router-dom'; -import { StoreProvider, mockStore } from '@deriv/stores'; + +jest.mock('Stores/connect.js', () => ({ + __esModule: true, + default: 'mockedDefaultExport', + connect: () => (Component: React.ReactElement) => Component, +})); jest.mock('@deriv/components', () => { const original_module = jest.requireActual('@deriv/components'); @@ -13,11 +18,6 @@ jest.mock('@deriv/components', () => { Loading: jest.fn(() => 'mockedLoading'), }; }); -jest.mock('@deriv/shared/src/services/ws-methods', () => ({ - __esModule: true, // this property makes it work, - default: 'mockedDefaultExport', - useWS: () => undefined, -})); jest.mock('@deriv/shared', () => ({ ...jest.requireActual('@deriv/shared'), @@ -31,133 +31,110 @@ jest.mock('Components/load-error-message', () => jest.fn(() => 'mockedLoadErrorM jest.mock('../account-limits-footer', () => jest.fn(() => 'mockedAccountLimitsFooter')); describe('', () => { - let store = mockStore({}); - const props = { + const props: React.ComponentProps = { + currency: 'AUD', + is_fully_authenticated: true, + is_switching: false, + is_virtual: false, overlay_ref: document.createElement('div'), - }; - const mock = { - client: { - currency: 'AUD', - is_fully_authenticated: true, - is_switching: false, - is_virtual: false, - getLimits: jest.fn(() => Promise.resolve({ data: {} })), - account_limits: { - account_balance: 300000, - daily_transfers: { - dxtrade: { - allowed: 12, - available: 12, + getLimits: jest.fn(() => Promise.resolve({ data: {} })), + account_limits: { + account_balance: 300000, + daily_transfers: { + dxtrade: { + allowed: 12, + available: 12, + }, + internal: { + allowed: 10, + available: 10, + }, + mt5: { + allowed: 10, + available: 10, + }, + }, + lifetime_limit: 13907.43, + market_specific: { + commodities: [ + { + name: 'Commodities', + payout_limit: 5000, + profile_name: 'moderate_risk', + turnover_limit: 50000, }, - internal: { - allowed: 10, - available: 10, + ], + cryptocurrency: [ + { + name: 'Cryptocurrencies', + payout_limit: 100.0, + profile_name: 'extreme_risk', + turnover_limit: 1000.0, }, - mt5: { - allowed: 10, - available: 10, + ], + forex: [ + { + name: 'Smart FX', + payout_limit: 5000, + profile_name: 'moderate_risk', + turnover_limit: 50000, }, - }, - lifetime_limit: 13907.43, - market_specific: { - commodities: [ - { - name: 'Commodities', - payout_limit: 5000, - profile_name: 'moderate_risk', - turnover_limit: 50000, - }, - ], - cryptocurrency: [ - { - name: 'Cryptocurrencies', - payout_limit: 100.0, - profile_name: 'extreme_risk', - turnover_limit: 1000.0, - }, - ], - forex: [ - { - name: 'Smart FX', - payout_limit: 5000, - profile_name: 'moderate_risk', - turnover_limit: 50000, - }, - { - name: 'Major Pairs', - payout_limit: 20000, - profile_name: 'medium_risk', - turnover_limit: 100000, - }, - { - name: 'Minor Pairs', - payout_limit: 5000, - profile_name: 'moderate_risk', - turnover_limit: 50000, - }, - ], - indices: [ - { - name: 'Stock Indices', - payout_limit: 20000, - profile_name: 'medium_risk', - turnover_limit: 100000, - }, - ], - synthetic_index: [ - { - name: 'Synthetic Indices', - payout_limit: 50000, - profile_name: 'low_risk', - turnover_limit: 500000, - }, - ], - }, - num_of_days: 30, - num_of_days_limit: 13907.43, - open_positions: 100, - payout: 50000, - remainder: 13907.43, - withdrawal_for_x_days_monetary: 0, - withdrawal_since_inception_monetary: 0, + { + name: 'Major Pairs', + payout_limit: 20000, + profile_name: 'medium_risk', + turnover_limit: 100000, + }, + { + name: 'Minor Pairs', + payout_limit: 5000, + profile_name: 'moderate_risk', + turnover_limit: 50000, + }, + ], + indices: [ + { + name: 'Stock Indices', + payout_limit: 20000, + profile_name: 'medium_risk', + turnover_limit: 100000, + }, + ], + synthetic_index: [ + { + name: 'Synthetic Indices', + payout_limit: 50000, + profile_name: 'low_risk', + turnover_limit: 500000, + }, + ], }, + num_of_days: 30, + num_of_days_limit: 13907.43, + open_positions: 100, + payout: 50000, + remainder: 13907.43, + withdrawal_for_x_days_monetary: 0, + withdrawal_since_inception_monetary: 0, }, }; - store = mockStore(mock); + it('should render the Loading component if is_switching is true', () => { - store = mockStore({ - client: { - is_switching: true, - }, - }); - render( - - - - ); + render(); expect(screen.getByText('mockedLoading')).toBeInTheDocument(); }); it('should render DemoMessage component if is_virtual is true', () => { - store = mockStore({ - client: { - is_switching: false, - is_virtual: true, - }, - }); - render( - - - - ); + render(); expect(screen.queryByTestId('dt_account_demo_message_wrapper')).toHaveClass('account__demo-message-wrapper'); expect(screen.getByText('mockedDemoMessage')).toBeInTheDocument(); }); it('should render LoadErrorMessage component if there is api_initial_load_error', () => { - store = mockStore({ - client: { - account_limits: { + render( + ', () => { num_of_days_limit: '', remainder: '', withdrawal_since_inception_monetary: '', - }, - is_switching: false, - is_virtual: false, - }, - }); - render( - - - + }} + /> ); expect(screen.getByText('mockedLoadErrorMessage')).toBeInTheDocument(); }); it('should render AccountLimits component', () => { - store = mockStore(mock); - render( - - - - ); + render(); expect(screen.queryByTestId('account_limits_data')).toBeInTheDocument(); }); it('should call setIsPopupOverlayShown fn ', () => { - store = mockStore(mock); const setIsPopupOverlayShown = jest.fn(); - render( - - - - ); + render(); expect(setIsPopupOverlayShown).toHaveBeenCalledTimes(1); }); it('should render Loading component if is_loading is true', () => { - render( - - - - ); + render(); expect(screen.queryByTestId('account_limits_data')).toBeInTheDocument(); }); it('should render AccountLimitsArticle component if should_show_article is true and is_from_derivgo is false in mobile mode', () => { (isMobile as jest.Mock).mockReturnValue(true); (isDesktop as jest.Mock).mockReturnValue(false); - render( - - - - ); + render(); expect(screen.getByRole('heading', { name: /account limits/i })).toBeInTheDocument(); expect( screen.queryByText(/to learn more about trading limits and how they apply, please go to the/i) @@ -228,18 +180,9 @@ describe('', () => { }); it('should render AccountLimitsArticle component if should_show_article is true and is_from_derivgo is true in mobile mode', () => { - store = mockStore({ - common: { - is_from_derivgo: true, - }, - }); (isMobile as jest.Mock).mockReturnValue(true); (isDesktop as jest.Mock).mockReturnValue(false); - render( - - - - ); + render(); expect(screen.getByRole('heading', { name: /account limits/i })).toBeInTheDocument(); expect( screen.queryByText(/to learn more about trading limits and how they apply, please go to the/i) @@ -247,23 +190,14 @@ describe('', () => { }); it('should not render AccountLimitsArticle component if should_show_article is false', () => { - store = mockStore(mock); (isMobile as jest.Mock).mockReturnValue(true); (isDesktop as jest.Mock).mockReturnValue(false); - render( - - - - ); + render(); expect(screen.queryByText('/account limits/i')).not.toBeInTheDocument(); }); it('should render Trading limits table and its trading limits contents properly', () => { - render( - - - - ); + render(); expect(screen.queryByTestId('account_limits_data')).toBeInTheDocument(); expect( @@ -284,17 +218,13 @@ describe('', () => { }); it('should render Maximum number of open positions- table cell and its contents properly', () => { - render( - - - - ); + render(); expect( screen.getByRole('cell', { name: /\*maximum number of open positions/i, }) ).toBeInTheDocument(); - const { open_positions } = store.client.account_limits; + const { open_positions } = props.account_limits; expect( screen.getByRole('cell', { name: open_positions?.toString(), @@ -303,21 +233,13 @@ describe('', () => { }); it('should call formatMoney', () => { - render( - - - - ); - const { account_balance } = store.client.account_limits; - expect(formatMoney).toHaveBeenCalledWith(store.client.currency, account_balance, true); + render(); + const { account_balance } = props.account_limits; + expect(formatMoney).toHaveBeenCalledWith(props.currency, account_balance, true); }); it('should render Trading limits table and its maximum daily turnover contents properly', () => { - render( - - - - ); + render(); expect(screen.queryByTestId('trading_daily_turnover_table')).toBeInTheDocument(); expect( screen.getByRole('columnheader', { @@ -337,20 +259,12 @@ describe('', () => { }); it('should not render withdrawal_limits_table is_app_settings is true', () => { - render( - - - - ); + render(); expect(screen.queryByTestId('withdrawal_limits_table')).not.toBeInTheDocument(); }); it('should render withdrawal_limits_table is_app_settings is false', () => { - render( - - - - ); + render(); expect(screen.queryByTestId('withdrawal_limits_table')).toBeInTheDocument(); expect( screen.getByRole('columnheader', { @@ -360,20 +274,12 @@ describe('', () => { }); it('withdrawal_limits_table should have a Limits header if is_fully_authenticated is true', () => { - render( - - - - ); + render(); expect(screen.getByTestId('withdrawal_limits_table')).toHaveTextContent('Limit'); }); it('show show withdrawal limit lifted message if is_fully_authenticated is true', () => { - render( - - - - ); + render(); expect( screen.getByRole('cell', { @@ -383,17 +289,10 @@ describe('', () => { }); it('withdrawal_limits_table should show `Total withdrawal limit` if is_fully_authenticated is false and is_appstore is true', () => { - store = mockStore({ - client: { - is_fully_authenticated: false, - }, - }); render( - - - + ); @@ -401,33 +300,19 @@ describe('', () => { }); it('withdrawal_limits_table should show `Total withdrawal allowed` when is_fully_authenticated is false and is_appstore is true', () => { - store = mockStore({ - client: { - is_fully_authenticated: false, - }, - }); render( - - - + ); expect(screen.getByText(/total withdrawal allowed/i)).toBeInTheDocument(); }); it('withdrawal_limits_table should show the verfiy button when is_fully_authenticated is false and is_appstore is true', () => { - store = mockStore({ - client: { - is_fully_authenticated: false, - }, - }); render( - - - + ); @@ -437,48 +322,34 @@ describe('', () => { name: /verify/i, }) ).toHaveAttribute('href', '/account/proof-of-identity'); - const { num_of_days_limit } = store.client.account_limits; - expect(formatMoney).toHaveBeenCalledWith(store.client.currency, num_of_days_limit, true); + const { num_of_days_limit } = props.account_limits; + expect(formatMoney).toHaveBeenCalledWith(props.currency, num_of_days_limit, true); }); it('withdrawal_limits_table should show total withdrawn and withdrawn remaining details', () => { - store = mockStore({ - client: { - is_fully_authenticated: false, - }, - }); render( - - - + ); - const { withdrawal_since_inception_monetary, remainder } = store.client.account_limits; + const { withdrawal_since_inception_monetary, remainder } = props.account_limits; expect(screen.getByText(/total withdrawn/i)).toBeInTheDocument(); - expect(formatMoney).toHaveBeenCalledWith(store.client.currency, withdrawal_since_inception_monetary, true); + expect(formatMoney).toHaveBeenCalledWith(props.currency, withdrawal_since_inception_monetary, true); expect(screen.getByText(/maximum withdrawal remaining/i)).toBeInTheDocument(); - expect(formatMoney).toHaveBeenCalledWith(store.client.currency, remainder, true); + expect(formatMoney).toHaveBeenCalledWith(props.currency, remainder, true); }); it('should show limit_notice message when is_appstore is true and is_fully_authenticated is false in mobile mode', () => { - store = mockStore({ - client: { - is_fully_authenticated: false, - }, - }); (isMobile as jest.Mock).mockReturnValue(true); (isDesktop as jest.Mock).mockReturnValue(false); render( - - - + ); @@ -486,19 +357,12 @@ describe('', () => { }); it('should not show limit_notice message when is_appstore is false and is_fully_authenticated is false', () => { - store = mockStore({ - client: { - is_fully_authenticated: false, - }, - }); (isMobile as jest.Mock).mockReturnValue(false); (isDesktop as jest.Mock).mockReturnValue(true); render( - - - + ); @@ -508,14 +372,9 @@ describe('', () => { }); it('should show AccountLimitsArticle when should_show_article and isDesktop is true', () => { - store = mockStore(mock); (isMobile as jest.Mock).mockReturnValue(false); (isDesktop as jest.Mock).mockReturnValue(true); - render( - - - - ); + render(); expect(screen.getByRole('heading', { name: /account limits/i })).toBeInTheDocument(); expect(screen.getByText(/these are default limits that we apply to your accounts\./i)).toBeInTheDocument(); expect( @@ -531,11 +390,7 @@ describe('', () => { it('should show AccountLimitsFooter if footer_ref is passed', () => { const footer = React.createRef(); - render( - - - - ); + render(); expect(screen.getByText(/mockedaccountlimitsfooter/i)).toBeInTheDocument(); }); }); diff --git a/packages/account/src/Components/account-limits/account-limits.tsx b/packages/account/src/Components/account-limits/account-limits.tsx index 0bb41406c1ab..78ad00173a5a 100644 --- a/packages/account/src/Components/account-limits/account-limits.tsx +++ b/packages/account/src/Components/account-limits/account-limits.tsx @@ -12,13 +12,38 @@ import AccountLimitsFooter from './account-limits-footer'; import AccountLimitsOverlay from './account-limits-overlay'; import AccountLimitsTableCell from './account-limits-table-cell'; import AccountLimitsTableHeader from './account-limits-table-header'; -import AccountLimitsTurnoverLimitRow from './account-limits-turnover-limit-row'; -import { observer, useStore } from '@deriv/stores'; +import AccountLimitsTurnoverLimitRow, { TAccountLimitsCollection } from './account-limits-turnover-limit-row'; import { FormikValues } from 'formik'; type TAccountLimits = { + account_limits: { + api_initial_load_error?: string; + open_positions?: React.ReactNode; + account_balance: string | number; + daily_transfers?: object; + payout: string | number; + lifetime_limit?: number; + market_specific: { + commodities: TAccountLimitsCollection[]; + cryptocurrency: TAccountLimitsCollection[]; + forex: TAccountLimitsCollection[]; + indices: TAccountLimitsCollection[]; + synthetic_index: TAccountLimitsCollection[]; + }; + num_of_days?: number; + num_of_days_limit: string | number; + remainder: string | number; + withdrawal_for_x_days_monetary?: number; + withdrawal_since_inception_monetary: string | number; + }; + currency: string; footer_ref?: React.RefObject; is_app_settings?: boolean; + getLimits: () => Promise<{ data: object }>; + is_fully_authenticated: boolean; + is_from_derivgo?: boolean; + is_switching: boolean; + is_virtual: boolean; overlay_ref: HTMLDivElement; setIsOverlayShown?: (is_overlay_shown?: boolean) => void; setIsPopupOverlayShown?: (is_popup_overlay_shown: boolean) => void; @@ -26,332 +51,327 @@ type TAccountLimits = { should_show_article?: boolean; }; -const AccountLimits = observer( - ({ - footer_ref, - is_app_settings, - overlay_ref, - setIsOverlayShown: setIsPopupOverlayShown, - should_bypass_scrollbars, - should_show_article = true, - }: TAccountLimits) => { - const { client, common } = useStore(); - const { account_limits, currency, getLimits, is_fully_authenticated, is_virtual, is_switching } = client; - const { is_from_derivgo } = common; - const isMounted = useIsMounted(); - const [is_loading, setLoading] = React.useState(false); - const [is_overlay_shown, setIsOverlayShown] = React.useState(false); - const { is_appstore } = React.useContext(PlatformContext); +const AccountLimits = ({ + account_limits, + currency, + footer_ref, + getLimits, + is_app_settings, + is_fully_authenticated, + is_switching, + is_virtual, + overlay_ref, + is_from_derivgo, + setIsOverlayShown: setIsPopupOverlayShown, + should_bypass_scrollbars, + should_show_article, +}: TAccountLimits) => { + const isMounted = useIsMounted(); + const [is_loading, setLoading] = React.useState(false); + const [is_overlay_shown, setIsOverlayShown] = React.useState(false); + const { is_appstore } = React.useContext(PlatformContext); - React.useEffect(() => { - if (is_virtual) { - setLoading(false); - } else { - getLimits().then(() => { - if (isMounted()) setLoading(false); - }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + React.useEffect(() => { + if (is_virtual) { + setLoading(false); + } else { + getLimits().then(() => { + if (isMounted()) setLoading(false); + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - React.useEffect(() => { - if (!is_virtual && account_limits && is_loading) { - setLoading(false); - } - }, [account_limits, is_virtual, is_loading]); + React.useEffect(() => { + if (!is_virtual && account_limits && is_loading) { + setLoading(false); + } + }, [account_limits, is_virtual, is_loading]); - React.useEffect(() => { - if (typeof setIsPopupOverlayShown === 'function') { - setIsPopupOverlayShown(is_overlay_shown); - } - }, [is_overlay_shown, setIsPopupOverlayShown]); + React.useEffect(() => { + if (typeof setIsPopupOverlayShown === 'function') { + setIsPopupOverlayShown(is_overlay_shown); + } + }, [is_overlay_shown, setIsPopupOverlayShown]); - const toggleOverlay = () => setIsOverlayShown(!is_overlay_shown); + const toggleOverlay = () => setIsOverlayShown(!is_overlay_shown); - if (is_switching) { - return ; - } + if (is_switching) { + return ; + } - if (is_virtual) { - return ( -
- -
- ); - } + if (is_virtual) { + return ( +
+ +
+ ); + } - const { - api_initial_load_error, - open_positions, - account_balance, - payout, - market_specific, - num_of_days_limit, - remainder, - withdrawal_since_inception_monetary, - } = account_limits; + const { + api_initial_load_error, + open_positions, + account_balance, + payout, + market_specific, + num_of_days_limit, + remainder, + withdrawal_since_inception_monetary, + }: TAccountLimits['account_limits'] = account_limits; - if (api_initial_load_error) { - return ; - } + if (api_initial_load_error) { + return ; + } - if (is_switching || is_loading) { - return ; - } + if (is_switching || is_loading) { + return ; + } - const { commodities, forex, indices, synthetic_index } = { ...market_specific }; - const forex_ordered = forex - ?.slice() - .sort((a: FormikValues, b: FormikValues) => (a.name > b.name ? 1 : b.name > a.name ? -1 : 0)); - const derived_ordered = synthetic_index - ?.slice() - .sort((a: FormikValues, b: FormikValues) => (a.level > b.level ? 1 : -1)); + const { commodities, forex, indices, synthetic_index } = { ...market_specific }; + const forex_ordered = forex + ?.slice() + .sort((a: FormikValues, b: FormikValues) => (a.name > b.name ? 1 : b.name > a.name ? -1 : 0)); + const derived_ordered = synthetic_index + ?.slice() + .sort((a: FormikValues, b: FormikValues) => (a.level > b.level ? 1 : -1)); - const context_value: TAccountLimitsContext = { - currency, - footer_ref, - overlay_ref, - toggleOverlay, - }; + const context_value: TAccountLimitsContext = { + currency, + footer_ref, + overlay_ref, + toggleOverlay, + }; - return ( - -
-
- {should_show_article && isMobile() && ( - - )} -
- - - - - - - - - - - - - - - ( - - )} - > - - - - {open_positions} - - - - ( - - )} - > - - - - {/* null or 0 are expected form BE when max balance limit is not set */} - {account_balance ? ( - formatMoney(currency, account_balance, true) - ) : ( - - )} - - - - ( - - )} - > - - - - {formatMoney(currency, payout, true)} - - - - - - - - - -
- - - - ( - - )} - > - - - - - - - - - - - - - -
- {/* We only show "Withdrawal Limits" on account-wide settings pages. */} - {!is_app_settings && ( - - +
+
+ {should_show_article && isMobile() && } +
+ +
+ + + + + + + + + + + + + ( + + )} > - - - - - - {is_fully_authenticated && ( - - - + + + {open_positions} + + + ( + + )} + > + + + + {/* null or 0 are expected form BE when max balance limit is not set */} + {account_balance ? ( + formatMoney(currency, account_balance, true) + ) : ( + + )} + + + + ( + + )} + > + + + + {formatMoney(currency, payout, true)} + + + + + + + + + +
+ + + + ( + + )} + > + + + + + + + + + + + + + +
+ {/* We only show "Withdrawal Limits" on account-wide settings pages. */} + {!is_app_settings && ( + + + + + + + + {is_fully_authenticated && ( + + + + )} + + + + {is_fully_authenticated ? ( + + + + + {localize( + 'Your account is fully authenticated and your withdrawal limits have been lifted.' + )} + + + + - - - {is_fully_authenticated ? ( + ) : ( + - - - {localize( - 'Your account is fully authenticated and your withdrawal limits have been lifted.' - )} - - - - - - ) : ( - - - - {is_appstore ? ( - - ) : ( - - )} - {is_appstore && !is_fully_authenticated && ( - + {is_appstore ? ( + + ) : ( + + )} + {is_appstore && !is_fully_authenticated && ( + + + {localize( + 'To increase limit please verify your identity' + )} + + - {localize( - 'To increase limit please verify your identity' - )} + {localize('Verify')} - - - {localize('Verify')} - - - - )} - - - {formatMoney(currency, num_of_days_limit, true)} - - - - - - - - {formatMoney( - currency, - withdrawal_since_inception_monetary, - true - )} - - - - - - - - {formatMoney(currency, remainder, true)} - - - + + + )} + + + {formatMoney(currency, num_of_days_limit, true)} + + + + + + + + {formatMoney( + currency, + withdrawal_since_inception_monetary, + true + )} + + + + + + + + {formatMoney(currency, remainder, true)} + + + + )} + +
+ {(!is_appstore || isMobile()) && ( +
+ + {is_fully_authenticated ? ( + + ) : ( + )} - - - {(!is_appstore || isMobile()) && ( -
- - {is_fully_authenticated ? ( - - ) : ( - - )} - -
- )} - - )} - -
- {should_show_article && isDesktop() && } - {footer_ref && } - {is_overlay_shown && overlay_ref && } + +
+ )} + + )} +
-
-
- ); - } -); + {should_show_article && isDesktop() && } + {footer_ref && } + {is_overlay_shown && overlay_ref && } + + + + ); +}; export default AccountLimits; diff --git a/packages/account/src/Components/api-token/_tests_/api-token.spec.js b/packages/account/src/Components/api-token/__tests__/api-token.spec.tsx similarity index 84% rename from packages/account/src/Components/api-token/_tests_/api-token.spec.js rename to packages/account/src/Components/api-token/__tests__/api-token.spec.tsx index 1243918e1153..0c6ff192bcd4 100644 --- a/packages/account/src/Components/api-token/_tests_/api-token.spec.js +++ b/packages/account/src/Components/api-token/__tests__/api-token.spec.tsx @@ -1,8 +1,7 @@ import React from 'react'; import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; -import { getPropertyValue, isDesktop, isMobile, useIsMounted, WS } from '@deriv/shared'; -import ApiToken from '../api-token'; -import { StoreProvider, mockStore } from '@deriv/stores'; +import { getPropertyValue, isDesktop, isMobile, useIsMounted } from '@deriv/shared'; +import ApiToken, { TApiToken } from '../api-token'; jest.mock('@deriv/shared', () => ({ ...jest.requireActual('@deriv/shared'), @@ -11,29 +10,6 @@ jest.mock('@deriv/shared', () => ({ isMobile: jest.fn(() => false), useIsMounted: jest.fn().mockImplementation(() => () => true), })); -jest.mock('@deriv/shared/src/services/ws-methods', () => ({ - __esModule: true, // this property makes it work, - default: 'mockedDefaultExport', - WS: { - apiToken: jest.fn(() => - Promise.resolve({ - api_token: { - tokens: [], - }, - }) - ), - authorized: { - apiToken: jest.fn(() => - Promise.resolve({ - api_token: { - tokens: [], - }, - }) - ), - }, - }, - useWS: () => undefined, -})); jest.mock('@deriv/components', () => ({ ...jest.requireActual('@deriv/components'), @@ -72,18 +48,13 @@ describe('', () => { const your_access_description = "To access your mobile apps and other third-party apps, you'll first need to generate an API token."; - let store = mockStore(); - store = mockStore({ - client: { - is_switching: false, - }, - }); - const mock_props = { + const mock_props: TApiToken = { footer_ref: undefined, is_app_settings: false, + is_switching: false, overlay_ref: undefined, setIsOverlayShown: jest.fn(), - WS: { + ws: { apiToken: jest.fn(() => Promise.resolve({ api_token: { @@ -104,13 +75,9 @@ describe('', () => { }; it('should render ApiToken component without app_settings and footer', async () => { - render( - - - - ); + render(); - expect(WS.authorized.apiToken).toHaveBeenCalled(); + expect(mock_props.ws.authorized.apiToken).toHaveBeenCalled(); expect(await screen.findByText(admin_scope_description)).toBeInTheDocument(); expect(await screen.findByText(admin_scope_note)).toBeInTheDocument(); @@ -128,13 +95,9 @@ describe('', () => { it('should not render ApiToken component if is not mounted', () => { useIsMounted.mockImplementationOnce(() => () => false); - render( - - - - ); + render(); - expect(WS.authorized.apiToken).toHaveBeenCalled(); + expect(mock_props.ws.authorized.apiToken).toHaveBeenCalled(); expect(screen.getByText('Loading')).toBeInTheDocument(); expect(screen.queryByText(admin_scope_description)).not.toBeInTheDocument(); @@ -154,11 +117,7 @@ describe('', () => { isMobile.mockReturnValueOnce(true); isDesktop.mockReturnValueOnce(false); - render( - - - - ); + render(); expect(await screen.findByText(admin_scope_description)).toBeInTheDocument(); expect(await screen.findByText(admin_scope_note)).toBeInTheDocument(); @@ -175,11 +134,7 @@ describe('', () => { it('should render ApiToken component with app_settings', async () => { mock_props.is_app_settings = true; - render( - - - - ); + render(); await waitFor(() => { expect(screen.queryByText(our_access_description)).not.toBeInTheDocument(); @@ -195,11 +150,7 @@ describe('', () => { mock_props.footer_ref = footer_portal_root_el; mock_props.overlay_ref = overlay_portal_root_el; - render( - - - - ); + render(); expect(await screen.findByText(learn_more_title)).toBeInTheDocument(); expect(screen.queryByText(our_access_description)).not.toBeInTheDocument(); @@ -215,18 +166,14 @@ describe('', () => { }); it('should choose checkbox, enter a valid value and create token', async () => { - render( - - - - ); + render(); expect(screen.queryByText('New token name')).not.toBeInTheDocument(); - const checkboxes = await screen.findAllByRole('checkbox'); + const checkboxes: HTMLInputElement[] = await screen.findAllByRole('checkbox'); const create_btn = await screen.findByRole('button'); - const read_checkbox = checkboxes.find(card => card.name === 'read'); // Typecasting it since find can return undefined as well - const token_name_input = await screen.findByLabelText('Token name'); + const read_checkbox = checkboxes.find(card => card.name === 'read') as HTMLInputElement; // Typecasting it since find can return undefined as well + const token_name_input: HTMLInputElement = await screen.findByLabelText('Token name'); expect(checkboxes.length).toBe(5); expect(create_btn).toBeDisabled(); @@ -255,7 +202,7 @@ describe('', () => { const updated_token_name_input = await screen.findByLabelText('Token name'); expect(updated_token_name_input.value).toBe(''); - const createToken = WS.apiToken; + const createToken = mock_props.ws.apiToken; expect(createToken).toHaveBeenCalledTimes(1); }); @@ -279,11 +226,7 @@ describe('', () => { }, ]); - render( - - - - ); + render(); expect(await screen.findByText('First test token')).toBeInTheDocument(); expect(await screen.findByText('Last used')).toBeInTheDocument(); @@ -312,7 +255,7 @@ describe('', () => { expect(yes_btn_1).toBeInTheDocument(); fireEvent.click(yes_btn_1); - const deleteToken = WS.authorized.apiToken; + const deleteToken = mock_props.ws.authorized.apiToken; expect(deleteToken).toHaveBeenCalled(); await waitFor(() => { expect(yes_btn_1).not.toBeInTheDocument(); @@ -344,11 +287,7 @@ describe('', () => { }, ]); - render( - - - - ); + render(); expect(await screen.findByText('First test token')).toBeInTheDocument(); expect(screen.queryByText('FirstTokenID')).not.toBeInTheDocument(); @@ -410,11 +349,7 @@ describe('', () => { }, ]); - render( - - - - ); + render(); expect((await screen.findAllByText('Name')).length).toBe(3); expect((await screen.findAllByText('Last Used')).length).toBe(3); @@ -429,7 +364,7 @@ describe('', () => { }); it('should show token error if exists', async () => { - WS.authorized.apiToken = jest.fn(() => + mock_props.ws.authorized.apiToken = jest.fn(() => Promise.resolve({ api_token: { tokens: [] }, error: { message: 'New test error' }, @@ -438,11 +373,7 @@ describe('', () => { getPropertyValue.mockReturnValue('New test error'); - render( - - - - ); + render(); expect(await screen.findByText('New test error')).toBeInTheDocument(); }); diff --git a/packages/account/src/Components/api-token/api-token.tsx b/packages/account/src/Components/api-token/api-token.tsx index 558e16a5725c..0c6dbb2e9170 100644 --- a/packages/account/src/Components/api-token/api-token.tsx +++ b/packages/account/src/Components/api-token/api-token.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import { Formik, Form, Field, FormikValues, FormikErrors, FieldProps } from 'formik'; import { Timeline, Input, Button, ThemedScrollbars, Loading } from '@deriv/components'; import InlineNoteWithIcon from '../inline-note-with-icon'; -import { isDesktop, isMobile, getPropertyValue, useIsMounted, WS } from '@deriv/shared'; +import { isDesktop, isMobile, getPropertyValue, useIsMounted } from '@deriv/shared'; import { localize } from '@deriv/translations'; import LoadErrorMessage from 'Components/load-error-message'; import ApiTokenArticle from './api-token-article'; @@ -13,7 +13,6 @@ import ApiTokenOverlay from './api-token-overlay'; import ApiTokenTable from './api-token-table'; import ApiTokenContext from './api-token-context'; import { TToken } from 'Types'; -import { observer, useStore } from '@deriv/stores'; const MIN_TOKEN = 2; const MAX_TOKEN = 32; @@ -33,6 +32,7 @@ type AptTokenState = { export type TApiToken = { footer_ref: Element | DocumentFragment | undefined; is_app_settings: boolean; + is_switching: boolean; overlay_ref: | undefined | ((...args: unknown[]) => unknown) @@ -41,11 +41,10 @@ export type TApiToken = { }>; setIsOverlayShown: (is_overlay_shown: boolean | undefined) => void; // eslint-disable-next-line @typescript-eslint/no-explicit-any + ws: any; }; -const ApiToken = ({ footer_ref, is_app_settings, overlay_ref, setIsOverlayShown }: TApiToken) => { - const { client } = useStore(); - const { is_switching } = client; +const ApiToken = ({ footer_ref, is_app_settings, is_switching, overlay_ref, setIsOverlayShown, ws }: TApiToken) => { const isMounted = useIsMounted(); const prev_is_switching = React.useRef(is_switching); const [state, setState] = React.useReducer( @@ -123,8 +122,9 @@ const ApiToken = ({ footer_ref, is_app_settings, overlay_ref, setIsOverlayShown const selectedTokenScope = (values: FormikValues) => Object.keys(values).filter(item => item !== 'token_name' && values[item]); + const handleSubmit = async (values: FormikValues, { setSubmitting, setFieldError, resetForm }: any) => { - const token_response = await WS.apiToken({ + const token_response = await ws.apiToken({ api_token: 1, new_token: values.token_name, new_token_scopes: selectedTokenScope(values), @@ -163,14 +163,14 @@ const ApiToken = ({ footer_ref, is_app_settings, overlay_ref, setIsOverlayShown const getApiTokens = async () => { setState({ is_loading: true }); - const token_response = await WS.authorized.apiToken({ api_token: 1 }); + const token_response = await ws.authorized.apiToken({ api_token: 1 }); populateTokenResponse(token_response); }; const deleteToken = async (token: string) => { setState({ is_delete_loading: true }); - const token_response = await WS.authorized.apiToken({ api_token: 1, delete_token: token }); + const token_response = await ws.authorized.apiToken({ api_token: 1, delete_token: token }); populateTokenResponse(token_response); @@ -356,4 +356,4 @@ const ApiToken = ({ footer_ref, is_app_settings, overlay_ref, setIsOverlayShown ); }; -export default observer(ApiToken); +export default ApiToken; diff --git a/packages/account/src/Components/icon-with-message/__tests__/icon-with-message.spec.tsx b/packages/account/src/Components/icon-with-message/__tests__/icon-with-message.spec.tsx index 6c032996c671..a7654d2bcf37 100644 --- a/packages/account/src/Components/icon-with-message/__tests__/icon-with-message.spec.tsx +++ b/packages/account/src/Components/icon-with-message/__tests__/icon-with-message.spec.tsx @@ -1,7 +1,12 @@ import React from 'react'; import { fireEvent, screen, render } from '@testing-library/react'; import IconWithMessage from '../icon-with-message'; -import { mockStore, StoreProvider } from '@deriv/stores'; + +jest.mock('Stores/connect', () => ({ + __esModule: true, + default: 'mockedDefaultExport', + connect: () => Component => Component, +})); jest.mock('@deriv/components', () => { const original_module = jest.requireActual('@deriv/components'); @@ -15,46 +20,27 @@ describe('', () => { icon: 'string', message: 'title', }; - const store = mockStore(); it('should render the IconWithMessage component', () => { - render( - - - - ); + render(); expect(screen.getByText('mockedIcon')).toBeInTheDocument(); expect(screen.getByText('title')).toBeInTheDocument(); }); it('should not render the button component if has_button is false', () => { - render( - - - - ); + render(); const btn = screen.queryByTestId('icon-with-message-button'); expect(btn).not.toBeInTheDocument(); }); it('should show "Switch to real account" button label if user have real account', () => { - store.client.has_any_real_account = true; - render( - - - - ); + render(); const btn = screen.getByTestId('icon-with-message-button'); expect(btn).toBeInTheDocument(); expect(screen.getByText('Switch to real account')).toBeInTheDocument(); }); it('should show "Add a real account" button label if user doesnt have real account', () => { - store.client.has_any_real_account = false; - render( - - - - ); + render(); expect(screen.getByText('Add a real account')).toBeInTheDocument(); }); @@ -63,16 +49,16 @@ describe('', () => { icon: 'string', message: 'title', has_button: true, + has_real_account: true, }; - store.client.has_any_real_account = true; const toggleShouldShowRealAccountsList = jest.fn(); const toggleAccountsDialog = jest.fn(); - store.ui.toggleShouldShowRealAccountsList = toggleShouldShowRealAccountsList; - store.ui.toggleAccountsDialog = toggleAccountsDialog; render( - - - + ); const btn = screen.getByTestId('icon-with-message-button'); expect(btn).toBeInTheDocument(); diff --git a/packages/account/src/Components/icon-with-message/icon-with-message.tsx b/packages/account/src/Components/icon-with-message/icon-with-message.tsx index ce3ca078034d..bdd939eaf56f 100644 --- a/packages/account/src/Components/icon-with-message/icon-with-message.tsx +++ b/packages/account/src/Components/icon-with-message/icon-with-message.tsx @@ -3,18 +3,26 @@ import classNames from 'classnames'; import { Icon, Text, Button } from '@deriv/components'; import { isMobile, PlatformContext } from '@deriv/shared'; import { localize } from '@deriv/translations'; -import { observer, useStore } from '@deriv/stores'; +import { connect } from 'Stores/connect'; +import RootStore from 'Stores/index'; type TIconWithMessage = { icon: string; has_button?: boolean; + has_real_account?: boolean; message: string; + toggleAccountsDialog: (status?: boolean) => void; + toggleShouldShowRealAccountsList: (value: boolean) => void; }; -const IconWithMessage = observer(({ has_button, icon, message }: TIconWithMessage) => { - const { client, ui } = useStore(); - const { has_any_real_account: has_real_account } = client; - const { toggleAccountsDialog, toggleShouldShowRealAccountsList } = ui; +const IconWithMessage = ({ + has_button, + has_real_account, + icon, + message, + toggleAccountsDialog, + toggleShouldShowRealAccountsList, +}: TIconWithMessage) => { const { is_appstore } = React.useContext(PlatformContext); return ( @@ -45,6 +53,10 @@ const IconWithMessage = observer(({ has_button, icon, message }: TIconWithMessag )} ); -}); +}; -export default IconWithMessage; +export default connect(({ client, ui }: RootStore) => ({ + has_real_account: client.has_any_real_account, + toggleAccountsDialog: ui.toggleAccountsDialog, + toggleShouldShowRealAccountsList: ui.toggleShouldShowRealAccountsList, +}))(IconWithMessage); diff --git a/packages/account/src/Components/self-exclusion/__tests__/self-exclusion.spec.tsx b/packages/account/src/Components/self-exclusion/__tests__/self-exclusion.spec.tsx index 909dbbdfdb9a..2453cecb7313 100644 --- a/packages/account/src/Components/self-exclusion/__tests__/self-exclusion.spec.tsx +++ b/packages/account/src/Components/self-exclusion/__tests__/self-exclusion.spec.tsx @@ -3,71 +3,46 @@ import { act } from 'react-dom/test-utils'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import SelfExclusion from '../self-exclusion'; import { FormikValues } from 'formik'; -import { StoreProvider, mockStore } from '@deriv/stores'; -import { WS } from '@deriv/shared'; const portal_root = document.createElement('div'); document.body.appendChild(portal_root); +jest.mock('Stores/connect.js', () => ({ + __esModule: true, + default: 'mockedDefaultExport', + connect: () => (Component: React.Component) => Component, +})); + jest.mock('@deriv/shared', () => ({ ...jest.requireActual('@deriv/shared'), useIsMounted: jest.fn().mockImplementation(() => () => true), })); -jest.mock('@deriv/shared/src/services/ws-methods', () => ({ - __esModule: true, // this property makes it work, - default: 'mockedDefaultExport', - WS: { - authorized: { - getLimits: jest.fn(() => - Promise.resolve({ - get_limits: {}, - }) - ), - getSelfExclusion: jest.fn(() => - Promise.resolve({ - error: { message: '' }, - }) - ), - setSelfExclusion: jest.fn(() => - Promise.resolve({ - error: {}, - }) - ), - }, - }, - useWS: () => undefined, -})); jest.mock('../self-exclusion-modal', () => { const MockSelfExclusionModal = () =>
SelfExclusionModal
; return MockSelfExclusionModal; }); -const mock = { - client: { - currency: '', - standpoint: { - svg: false, - }, - is_uk: false, - is_virtual: false, - is_switching: false, - landing_company_shortcode: '', - logout: jest.fn(), - }, - ui: { - is_tablet: false, - }, -}; -let store = mockStore(mock); + describe('', () => { let mock_props = { + currency: '', footer_ref: undefined, is_app_settings: false, is_appstore: false, + is_cr: false, + is_eu: false, + is_mf: false, + is_mlt: false, + is_mx: false, + is_switching: false, + is_tablet: false, + is_uk: false, + is_virtual: false, is_wrapper_bypassed: false, + logout: jest.fn(), overlay_ref: document.createElement('div'), setIsOverlayShown: jest.fn(), - WS: { + ws: { authorized: { getLimits: () => Promise.resolve({ @@ -79,52 +54,61 @@ describe('', () => { }), setSelfExclusion: () => Promise.resolve({ - error: false, + error: {}, }), }, }, }; beforeEach(() => { - mock.client.currency = 'Test currency'; - store = mockStore(mock); mock_props = { + currency: 'Test currency', footer_ref: undefined, is_app_settings: false, is_appstore: false, + is_cr: false, + is_eu: false, + is_mf: false, + is_mlt: false, + is_mx: false, + is_switching: false, + is_tablet: false, + is_uk: false, + is_virtual: false, is_wrapper_bypassed: false, + logout: jest.fn(), overlay_ref: document.createElement('div'), setIsOverlayShown: jest.fn(), + ws: { + authorized: { + getLimits: () => + Promise.resolve({ + get_limits: {}, + }), + getSelfExclusion: () => + Promise.resolve({ + error: { message: '' }, + }), + setSelfExclusion: () => + Promise.resolve({ + error: {}, + }), + }, + }, }; }); - afterEach(() => { - jest.clearAllMocks(); - }); it('should render SelfExclusion component for virtual account', () => { - store = mockStore({ - client: { - is_virtual: true, - }, - }); + mock_props.is_virtual = true; - render( - - - - ); + render(); expect(screen.getByText('This feature is not available for demo accounts.')).toBeInTheDocument(); }); it('should render SelfExclusion component with SelfExclusionModal', async () => { - store = mockStore(mock); await act(async () => { - render( - - - - ); + render(); }); expect(screen.getByText('SelfExclusionModal')).toBeInTheDocument(); @@ -136,40 +120,22 @@ describe('', () => { }); it('should render SelfExclusion component with error', async () => { - WS.authorized.getSelfExclusion = jest.fn(() => + mock_props.ws.authorized.getSelfExclusion = () => Promise.resolve({ error: { message: 'Test getSelfExclusion response error' }, - }) - ); + }); await act(async () => { - render( - - - - ); + render(); }); expect(screen.queryByText('Test getSelfExclusion response error')).toBeInTheDocument(); }); it('Should trigger session_duration_limit input and show error if the value is greater than 60480 or does not show if less than 60480', async () => { - store = mockStore({ - client: { - is_eu: true, - }, - }); - WS.authorized.getSelfExclusion = jest.fn(() => - Promise.resolve({ - error: { message: '' }, - }) - ); + mock_props.is_eu = true; - render( - - - - ); + render(); const inputs = await screen.findAllByRole('textbox'); const session_duration_limit_input = inputs.find( @@ -197,17 +163,8 @@ describe('', () => { it('Should trigger exclude_until input and show error depends on input value', async () => { (Date.now as jest.Mock) = jest.fn(() => new Date('2022-02-03')); - WS.authorized.getSelfExclusion = jest.fn(() => - Promise.resolve({ - error: { message: '' }, - }) - ); - store = mockStore(mock); - render( - - - - ); + + render(); const inputs = await screen.findAllByRole('textbox'); const exclude_until_input = inputs.find((input: FormikValues) => input.name === 'exclude_until'); @@ -230,24 +187,13 @@ describe('', () => { }); it('should trigger inputs with data, add new data, and show error wih invalid input data', async () => { - store = mockStore(mock); - WS.authorized.getSelfExclusion = jest.fn(() => - Promise.resolve({ - error: { message: '' }, - }) - ); - WS.authorized.setSelfExclusion = jest.fn(() => + mock_props.ws.authorized.setSelfExclusion = () => Promise.resolve({ error: { message: 'Test setSelfExclusion response error' }, - }) - ); + }); await act(async () => { - render( - - - - ); + render(); }); expect(screen.getByText('Your stake and loss limits')).toBeInTheDocument(); @@ -316,19 +262,15 @@ describe('', () => { }); it('should trigger inputs with correct data set timeout limit and logout', async () => { - WS.authorized.setSelfExclusion = jest.fn(() => + mock_props.ws.authorized.setSelfExclusion = () => Promise.resolve({ error: false, - }) - ); - const logout = store.client.logout; + }); + + const logout = mock_props.logout; await act(async () => { - render( - - - - ); + render(); }); expect(screen.getByText('Your stake and loss limits')).toBeInTheDocument(); diff --git a/packages/account/src/Components/self-exclusion/self-exclusion.tsx b/packages/account/src/Components/self-exclusion/self-exclusion.tsx index 2df2503ac0dd..0fbfc605ef18 100644 --- a/packages/account/src/Components/self-exclusion/self-exclusion.tsx +++ b/packages/account/src/Components/self-exclusion/self-exclusion.tsx @@ -8,7 +8,6 @@ import { getCurrencyDisplayCode, validNumber, useIsMounted, - WS, } from '@deriv/shared'; import { localize } from '@deriv/translations'; import DemoMessage from 'Components/demo-message'; @@ -19,15 +18,26 @@ import SelfExclusionModal from './self-exclusion-modal'; import SelfExclusionWrapper from './self-exclusion-wrapper'; import SelfExclusionForm from './self-exclusion-form'; import { FormikHelpers, FormikValues } from 'formik'; -import { observer, useStore } from '@deriv/stores'; type TSelfExclusion = { + currency: string; footer_ref?: React.RefObject; - is_appstore: boolean; is_app_settings: boolean; + is_cr: boolean; + is_appstore: boolean; + is_eu: boolean; + is_mf: boolean; + is_mlt: boolean; + is_mx: boolean; + is_switching: boolean; + is_tablet: boolean; + is_uk: boolean; + is_virtual: boolean; is_wrapper_bypassed: boolean; + logout: () => void; overlay_ref: HTMLDivElement; setIsOverlayShown?: React.Dispatch>; + ws: FormikValues; }; type TExclusionData = { @@ -73,20 +83,25 @@ type TResponse = { }; const SelfExclusion = ({ + currency, footer_ref, is_app_settings, + is_cr, is_appstore, + is_eu, + is_mf, + is_mlt, + is_mx, + is_switching, + is_tablet, + is_uk, + is_virtual, + is_wrapper_bypassed, + logout, overlay_ref, setIsOverlayShown, + ws, }: TSelfExclusion) => { - const { client, ui } = useStore(); - const { currency, is_virtual, is_switching, standpoint, is_eu, is_uk, logout, landing_company_shortcode } = client; - const { is_tablet } = ui; - const is_wrapper_bypassed = false; - const is_mlt = landing_company_shortcode === 'malta'; - const is_mf = landing_company_shortcode === 'maltainvest'; - const is_mx = landing_company_shortcode === 'iom'; - const is_cr = standpoint.svg; const exclusion_fields_settings = Object.freeze({ max_number: 9999999999999, max_open_positions: 999999999, @@ -181,6 +196,7 @@ const SelfExclusion = ({ }, [state.show_article, setIsOverlayShown]); const resetState = () => setState(initial_state); + const validateFields = (values: FormikValues) => { const errors: Record = {}; @@ -293,6 +309,7 @@ const SelfExclusion = ({ }); return errors; }; + const handleSubmit = async (values: FormikValues, { setSubmitting }: FormikHelpers) => { const need_logout_exclusions = ['exclude_until', 'timeout_until']; const string_exclusions = ['exclude_until']; @@ -308,7 +325,7 @@ const SelfExclusion = ({ request[attr] = string_exclusions.includes(attr) ? values[attr] : +values[attr]; }); - WS.authorized.setSelfExclusion(request).then((response: TResponse) => resolve(response)); + ws.authorized.setSelfExclusion(request).then((response: TResponse) => resolve(response)); }); if (has_need_logout) { @@ -387,7 +404,7 @@ const SelfExclusion = ({ const getSelfExclusion = () => { setState({ is_loading: true }); - WS.authorized.getSelfExclusion({ get_self_exclusion: 1 }).then((self_exclusion_response: FormikValues) => { + ws.authorized.getSelfExclusion({ get_self_exclusion: 1 }).then((self_exclusion_response: FormikValues) => { populateExclusionResponse(self_exclusion_response); }); }; @@ -395,7 +412,7 @@ const SelfExclusion = ({ const getLimits = () => { setState({ is_loading: true }); - WS.authorized.getLimits({ get_limits: 1 }).then((limits: FormikValues) => { + ws.authorized.getLimits({ get_limits: 1 }).then((limits: FormikValues) => { exclusion_limits.current = limits; }); }; @@ -467,7 +484,7 @@ const SelfExclusion = ({ return ( - {/* Only show the modal in non-"" views, others will + {/* Only show the modal in non-"" views, others will use the overlay provided by */} {!is_app_settings && } @@ -477,4 +494,4 @@ const SelfExclusion = ({ ); }; -export default observer(SelfExclusion); +export default SelfExclusion; diff --git a/packages/account/src/Containers/account.jsx b/packages/account/src/Containers/account.jsx index 5c035d7a13ae..f1fb09a3fcaf 100644 --- a/packages/account/src/Containers/account.jsx +++ b/packages/account/src/Containers/account.jsx @@ -1,13 +1,14 @@ import 'Styles/account.scss'; + +import { FadeWrapper, Icon, Loading, PageOverlay, Text, VerticalTab } from '@deriv/components'; import { PlatformContext, getSelectedRoute, isMobile, matchRoute, routes as shared_routes } from '@deriv/shared'; import PropTypes from 'prop-types'; import React from 'react'; -import { withRouter } from 'react-router-dom'; -import { VerticalTab, FadeWrapper, PageOverlay, Loading, Text, Icon } from '@deriv/components'; -import { observer, useStore } from '@deriv/stores'; +import { connect } from 'Stores/connect'; import { flatten } from '../Helpers/flatten'; import { localize } from '@deriv/translations'; import { useHistory } from 'react-router'; +import { withRouter } from 'react-router-dom'; const AccountLogout = ({ logout, history }) => { return ( @@ -103,20 +104,23 @@ const PageOverlayWrapper = ({ ); }; -const Account = observer(({ history, location, routes }) => { - const { client, common, ui } = useStore(); - const { - is_virtual, - is_logged_in, - is_logging_in, - is_risky_client, - is_pending_proof_of_ownership, - landing_company_shortcode, - should_allow_authentication, - logout, - } = client; - const { is_from_derivgo, routeBackInApp, platform } = common; - const { toggleAccountSettings, is_account_settings_visible } = ui; +const Account = ({ + active_account_landing_company, + history, + is_from_derivgo, + is_logged_in, + is_logging_in, + is_pending_proof_of_ownership, + is_virtual, + is_visible, + location, + logout, + platform, + routeBackInApp, + routes, + should_allow_authentication, + toggleAccount, +}) => { const { is_appstore } = React.useContext(PlatformContext); const subroutes = flatten(routes.map(i => i.subroutes)); let list_groups = [...routes]; @@ -129,17 +133,17 @@ const Account = observer(({ history, location, routes }) => { const onClickClose = React.useCallback(() => routeBackInApp(history), [routeBackInApp, history]); React.useEffect(() => { - toggleAccountSettings(true); - }, [toggleAccountSettings]); + toggleAccount(true); + }, [toggleAccount]); routes.forEach(menu_item => { menu_item.subroutes.forEach(route => { if (route.path === shared_routes.financial_assessment) { - route.is_disabled = is_virtual || (landing_company_shortcode === 'maltainvest' && !is_risky_client); + route.is_disabled = is_virtual; } if (route.path === shared_routes.trading_assessment) { - route.is_disabled = is_virtual || landing_company_shortcode !== 'maltainvest'; + route.is_disabled = is_virtual || active_account_landing_company !== 'maltainvest'; } if (route.path === shared_routes.proof_of_identity || route.path === shared_routes.proof_of_address) { @@ -165,11 +169,7 @@ const Account = observer(({ history, location, routes }) => { const selected_route = getSelectedRoute({ routes: subroutes, pathname: location.pathname }); return ( - +
{
); -}); +}; Account.propTypes = { active_account_landing_company: PropTypes.string, history: PropTypes.object, + is_from_derivgo: PropTypes.bool, + is_logged_in: PropTypes.bool, + is_logging_in: PropTypes.bool, + is_pending_proof_of_ownership: PropTypes.bool, + is_virtual: PropTypes.bool, + is_visible: PropTypes.bool, location: PropTypes.object, + logout: PropTypes.func, + platform: PropTypes.string, + routeBackInApp: PropTypes.func, routes: PropTypes.arrayOf(PropTypes.object), + should_allow_authentication: PropTypes.bool, + toggleAccount: PropTypes.func, }; -export default withRouter(Account); +export default connect(({ client, common, ui }) => ({ + active_account_landing_company: client.landing_company_shortcode, + is_from_derivgo: common.is_from_derivgo, + is_logged_in: client.is_logged_in, + is_logging_in: client.is_logging_in, + is_pending_proof_of_ownership: client.is_pending_proof_of_ownership, + is_virtual: client.is_virtual, + is_visible: ui.is_account_settings_visible, + logout: client.logout, + platform: common.platform, + routeBackInApp: common.routeBackInApp, + should_allow_authentication: client.should_allow_authentication, + toggleAccount: ui.toggleAccountSettings, +}))(withRouter(Account)); diff --git a/packages/account/src/Containers/reset-trading-password.jsx b/packages/account/src/Containers/reset-trading-password.jsx index 76669b31cb1f..94709bbcd8f2 100644 --- a/packages/account/src/Containers/reset-trading-password.jsx +++ b/packages/account/src/Containers/reset-trading-password.jsx @@ -1,32 +1,54 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { useLocation } from 'react-router-dom'; import { CFD_PLATFORMS } from '@deriv/shared'; -import { observer, useStore } from '@deriv/stores'; +import { connect } from 'Stores/connect'; import ResetTradingPasswordModal from '../Components/reset-trading-password-modal'; -const ResetTradingPassword = observer(() => { - const { ui, client } = useStore(); - const { enableApp, disableApp, is_visible, is_loading, setResetTradingPasswordModalOpen } = ui; +const ResetTradingPassword = ({ + enableApp, + disableApp, + toggleResetTradingPasswordModal, + mt5_verification_code, + dxtrade_verification_code, + is_visible, + is_loading, +}) => { const location = useLocation(); const query_params = new URLSearchParams(location.search); const cfd_platform = /^trading_platform_(.*)_password_reset$/.exec(query_params.get('action') || '')?.[1]; const [platform] = React.useState(cfd_platform); - const verification_code = - platform === CFD_PLATFORMS.MT5 - ? client.verification_code.trading_platform_mt5_password_reset - : client.verification_code.trading_platform_dxtrade_password_reset; + const verification_code = platform === CFD_PLATFORMS.MT5 ? mt5_verification_code : dxtrade_verification_code; return ( ); -}); +}; -export default ResetTradingPassword; +ResetTradingPassword.propTypes = { + disableApp: PropTypes.func, + enableApp: PropTypes.func, + is_loading: PropTypes.bool, + is_visible: PropTypes.bool, + toggleResetTradingPasswordModal: PropTypes.func, + mt5_verification_code: PropTypes.string, + dxtrade_verification_code: PropTypes.string, +}; + +export default connect(({ ui, client }) => ({ + disableApp: ui.disableApp, + enableApp: ui.enableApp, + is_loading: ui.is_loading, + is_visible: ui.is_reset_trading_password_modal_visible, + toggleResetTradingPasswordModal: ui.setResetTradingPasswordModalOpen, + mt5_verification_code: client.verification_code.trading_platform_mt5_password_reset, + dxtrade_verification_code: client.verification_code.trading_platform_dxtrade_password_reset, +}))(ResetTradingPassword); diff --git a/packages/account/src/Containers/routes.jsx b/packages/account/src/Containers/routes.jsx index 49b81f854432..ac85dcb0e31d 100644 --- a/packages/account/src/Containers/routes.jsx +++ b/packages/account/src/Containers/routes.jsx @@ -1,26 +1,41 @@ +import { PropTypes as MobxPropTypes } from 'mobx-react'; import PropTypes from 'prop-types'; import React from 'react'; import { withRouter } from 'react-router'; import { BinaryRoutes } from 'Components/Routes'; -import { observer, useStore } from '@deriv/stores'; +import { connect } from 'Stores/connect'; import ErrorComponent from 'Components/error-component'; -const Routes = observer(props => { - const { client, common } = useStore(); - const { is_logged_in, is_logging_in } = client; - const { error } = common; +const Routes = props => { if (props.has_error) { - return ; + return ; } - return ; -}); + return ( + + ); +}; Routes.propTypes = { + error: MobxPropTypes.objectOrObservableObject, + has_error: PropTypes.bool, + is_logged_in: PropTypes.bool, + is_logging_in: PropTypes.bool, is_virtual: PropTypes.bool, passthrough: PropTypes.object, }; // need to wrap withRouter around connect // to prevent updates on from being blocked -export default withRouter(Routes); +export default withRouter( + connect(({ client, common }) => ({ + is_logged_in: client.is_logged_in, + is_logging_in: client.is_logging_in, + error: common.error, + has_error: common.has_error, + }))(Routes) +); diff --git a/packages/account/src/Sections/Assessment/FinancialAssessment/financial-assessment.jsx b/packages/account/src/Sections/Assessment/FinancialAssessment/financial-assessment.jsx index 1ca1e3ca9c35..88684b5f67ba 100644 --- a/packages/account/src/Sections/Assessment/FinancialAssessment/financial-assessment.jsx +++ b/packages/account/src/Sections/Assessment/FinancialAssessment/financial-assessment.jsx @@ -1,5 +1,6 @@ import classNames from 'classnames'; import React from 'react'; +import { PropTypes } from 'prop-types'; import { Formik } from 'formik'; import { useHistory, withRouter } from 'react-router'; import { @@ -16,7 +17,7 @@ import { } from '@deriv/components'; import { routes, isMobile, isDesktop, platforms, PlatformContext, WS } from '@deriv/shared'; import { localize, Localize } from '@deriv/translations'; -import { observer, useStore } from '@deriv/stores'; +import { connect } from 'Stores/connect'; import LeaveConfirm from 'Components/leave-confirm'; import IconMessageContent from 'Components/icon-message-content'; import DemoMessage from 'Components/demo-message'; @@ -174,22 +175,20 @@ const SubmittedPage = ({ platform, routeBackInApp }) => { ); }; -const FinancialAssessment = observer(() => { - const { client, common, notifications } = useStore(); - const { - landing_company_shortcode, - is_virtual, - is_financial_account, - is_trading_experience_incomplete, - is_svg, - setFinancialAndTradingAssessment, - updateAccountStatus, - is_authentication_needed, - is_financial_information_incomplete, - } = client; - const { platform, routeBackInApp } = common; - const { refreshNotifications } = notifications; - const is_mf = landing_company_shortcode === 'maltainvest'; +const FinancialAssessment = ({ + is_authentication_needed, + is_financial_account, + is_mf, + is_svg, + is_trading_experience_incomplete, + is_financial_information_incomplete, + is_virtual, + platform, + refreshNotifications, + routeBackInApp, + setFinancialAndTradingAssessment, + updateAccountStatus, +}) => { const history = useHistory(); const { is_appstore } = React.useContext(PlatformContext); const [is_loading, setIsLoading] = React.useState(true); @@ -1006,6 +1005,34 @@ const FinancialAssessment = observer(() => { ); -}); +}; + +FinancialAssessment.propTypes = { + is_authentication_needed: PropTypes.bool, + is_financial_account: PropTypes.bool, + is_mf: PropTypes.bool, + is_svg: PropTypes.bool, + is_trading_experience_incomplete: PropTypes.bool, + is_financial_information_incomplete: PropTypes.bool, + is_virtual: PropTypes.bool, + platform: PropTypes.string, + refreshNotifications: PropTypes.func, + routeBackInApp: PropTypes.func, + setFinancialAndTradingAssessment: PropTypes.func, + updateAccountStatus: PropTypes.func, +}; -export default withRouter(FinancialAssessment); +export default connect(({ client, common, notifications }) => ({ + is_authentication_needed: client.is_authentication_needed, + is_financial_account: client.is_financial_account, + is_mf: client.landing_company_shortcode === 'maltainvest', + is_svg: client.is_svg, + is_financial_information_incomplete: client.is_financial_information_incomplete, + is_trading_experience_incomplete: client.is_trading_experience_incomplete, + is_virtual: client.is_virtual, + platform: common.platform, + refreshNotifications: notifications.refreshNotifications, + routeBackInApp: common.routeBackInApp, + setFinancialAndTradingAssessment: client.setFinancialAndTradingAssessment, + updateAccountStatus: client.updateAccountStatus, +}))(withRouter(FinancialAssessment)); diff --git a/packages/account/src/Sections/Assessment/TradingAssessment/trading-assessment.jsx b/packages/account/src/Sections/Assessment/TradingAssessment/trading-assessment.jsx index 242ac860951b..f3f91274c509 100644 --- a/packages/account/src/Sections/Assessment/TradingAssessment/trading-assessment.jsx +++ b/packages/account/src/Sections/Assessment/TradingAssessment/trading-assessment.jsx @@ -16,7 +16,7 @@ import { } from '@deriv/components'; import FormFooter from 'Components/form-footer'; import { isMobile, routes, WS } from '@deriv/shared'; -import { observer, useStore } from '@deriv/stores'; +import { connect } from 'Stores/connect'; import { useHistory, withRouter } from 'react-router'; import { Formik, Form } from 'formik'; @@ -35,9 +35,7 @@ const populateData = form_data => { }; }; -const TradingAssessment = observer(() => { - const { client } = useStore(); - const { is_virtual, setFinancialAndTradingAssessment } = client; +const TradingAssessment = ({ is_virtual, setFinancialAndTradingAssessment }) => { const history = useHistory(); const [is_loading, setIsLoading] = React.useState(true); const [is_btn_loading, setIsBtnLoading] = React.useState(false); @@ -284,6 +282,9 @@ const TradingAssessment = observer(() => { }} ); -}); +}; -export default withRouter(TradingAssessment); +export default connect(({ client }) => ({ + is_virtual: client.is_virtual, + setFinancialAndTradingAssessment: client.setFinancialAndTradingAssessment, +}))(withRouter(TradingAssessment)); diff --git a/packages/account/src/Sections/Profile/LanguageSettings/language-settings.tsx b/packages/account/src/Sections/Profile/LanguageSettings/language-settings.tsx index cb0b7eea8b27..8a97f4f2f697 100644 --- a/packages/account/src/Sections/Profile/LanguageSettings/language-settings.tsx +++ b/packages/account/src/Sections/Profile/LanguageSettings/language-settings.tsx @@ -2,15 +2,20 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { Button, DesktopWrapper } from '@deriv/components'; import { localize, getAllowedLanguages } from '@deriv/translations'; +import { connect } from 'Stores/connect'; import FormSubHeader from 'Components/form-sub-header'; +import TCoreStore from '../../../Stores'; import { Formik, FormikHandlers, FormikHelpers, FormikValues } from 'formik'; import FormFooter from 'Components/form-footer'; import LanguageRadioButton from 'Components/language-settings'; -import { observer, useStore } from '@deriv/stores'; -const LanguageSettings = observer(() => { - const { common } = useStore(); - const { changeSelectedLanguage, current_language, isCurrentLanguage } = common; +type TLanguageSettings = { + current_language: string; + changeSelectedLanguage: (lang: string) => void; + isCurrentLanguage: (lang: string) => boolean; +}; + +const LanguageSettings = ({ changeSelectedLanguage, current_language, isCurrentLanguage }: TLanguageSettings) => { const { i18n } = useTranslation(); const allowed_language_keys: string[] = Object.keys(getAllowedLanguages()); const initial_values = { language_code: current_language }; @@ -64,6 +69,10 @@ const LanguageSettings = observer(() => { }} ); -}); +}; -export default LanguageSettings; +export default connect(({ common }: TCoreStore) => ({ + changeSelectedLanguage: common.changeSelectedLanguage, + current_language: common.current_language, + isCurrentLanguage: common.isCurrentLanguage, +}))(LanguageSettings); diff --git a/packages/account/src/Sections/Profile/PersonalDetails/__tests__/personal-details.spec.js b/packages/account/src/Sections/Profile/PersonalDetails/__tests__/personal-details.spec.js index 1f54dcae50be..5908e032cb17 100644 --- a/packages/account/src/Sections/Profile/PersonalDetails/__tests__/personal-details.spec.js +++ b/packages/account/src/Sections/Profile/PersonalDetails/__tests__/personal-details.spec.js @@ -4,7 +4,6 @@ import { cleanup, render, waitForElementToBeRemoved, waitFor } from '@testing-li import { createBrowserHistory } from 'history'; import { Router } from 'react-router'; import { PersonalDetailsForm } from '../personal-details.jsx'; -import { StoreProvider, mockStore } from '@deriv/stores'; afterAll(cleanup); @@ -16,11 +15,9 @@ jest.mock('@deriv/shared/src/services/ws-methods', () => ({ return Promise.resolve([...payload]); }, }, - useWS: () => undefined, })); describe('', () => { - let store = mockStore(); const history = createBrowserHistory(); it('should_render_successfully', async () => { @@ -35,36 +32,20 @@ describe('', () => { value: 'value', }, ]; - store = mockStore({ - client: { - account_settings: { - email_consent: 1, - }, - is_virtual: false, - states_list: residence_list, - residence_list: residence_list, - has_residence: true, - getChangeableFields: () => [], - fetchResidenceList: fetchResidenceList, - fetchStatesList: fetchStatesList, - }, - }); - - // store.client.fetchResidenceList = fetchResidenceList; - // store.client.fetchStatesList = fetchStatesList; - // store.client.residence_list = residence_list; - // store.client.account_settings = { - // email_consent: 1, - // }; - // store.client.is_virtual = false; - // store.client.states_list = residence_list; - // store.client.getChangeableFields = () => []; - // store.client.has_residence = true; const screen = render( - - - + []} + is_virtual={false} + states_list={residence_list} + /> ); await waitForElementToBeRemoved(() => screen.container.querySelector('.account__initial-loader')); diff --git a/packages/account/src/Sections/Profile/PersonalDetails/personal-details.jsx b/packages/account/src/Sections/Profile/PersonalDetails/personal-details.jsx index 3978f839086f..b7b352190d0a 100644 --- a/packages/account/src/Sections/Profile/PersonalDetails/personal-details.jsx +++ b/packages/account/src/Sections/Profile/PersonalDetails/personal-details.jsx @@ -39,14 +39,14 @@ import { removeEmptyPropertiesFromObject, } from '@deriv/shared'; import { Localize, localize } from '@deriv/translations'; -import { observer, useStore } from '@deriv/stores'; -import LeaveConfirm from 'Components/leave-confirm'; -import FormFooter from 'Components/form-footer'; import FormBody from 'Components/form-body'; import FormBodySection from 'Components/form-body-section'; +import FormFooter from 'Components/form-footer'; import FormSubHeader from 'Components/form-sub-header'; +import LeaveConfirm from 'Components/leave-confirm'; import LoadErrorMessage from 'Components/load-error-message'; import POAAddressMismatchHintBox from 'Components/poa-address-mismatch-hint-box'; +import { connect } from 'Stores/connect'; import { getEmploymentStatusList } from 'Sections/Assessment/FinancialAssessment/financial-information-list'; import { validateName, validate } from 'Helpers/utils'; @@ -96,7 +96,31 @@ const TaxResidenceSelect = ({ field, errors, setFieldValue, values, is_changeabl ); -export const PersonalDetailsForm = observer(({ history }) => { +export const PersonalDetailsForm = ({ + authentication_status, + is_eu, + is_mf, + is_uk, + is_svg, + is_virtual, + residence_list, + states_list, + current_landing_company, + refreshNotifications, + showPOAAddressMismatchSuccessNotification, + showPOAAddressMismatchFailureNotification, + Notifications, + fetchResidenceList, + fetchStatesList, + has_residence, + account_settings, + getChangeableFields, + history, + is_social_signup, + updateAccountStatus, + has_poa_address_mismatch, + is_language_changing, +}) => { const [is_loading, setIsLoading] = React.useState(true); const [is_state_loading, setIsStateLoading] = useStateCallback(false); @@ -104,38 +128,7 @@ export const PersonalDetailsForm = observer(({ history }) => { const [is_btn_loading, setIsBtnLoading] = React.useState(false); const [is_submit_success, setIsSubmitSuccess] = useStateCallback(false); - const { client, notifications, ui, common } = useStore(); - - const { - authentication_status, - is_eu, - landing_company_shortcode, - is_uk, - is_svg, - is_virtual, - residence_list, - states_list, - current_landing_company, - fetchResidenceList, - fetchStatesList, - has_residence, - account_settings, - getChangeableFields, - updateAccountStatus, - is_social_signup, - account_status, - } = client; - - const { - refreshNotifications, - showPOAAddressMismatchSuccessNotification, - showPOAAddressMismatchFailureNotification, - } = notifications; - const { Notifications } = ui; - const { is_language_changing } = common; - const is_mf = landing_company_shortcode === 'maltainvest'; - const has_poa_address_mismatch = account_status.status?.includes('poa_address_mismatch'); const [rest_state, setRestState] = React.useState({ show_form: true, errors: false, @@ -148,6 +141,7 @@ export const PersonalDetailsForm = observer(({ history }) => { }); const { is_appstore } = React.useContext(PlatformContext); + const isMounted = useIsMounted(); React.useEffect(() => { @@ -1278,10 +1272,55 @@ export const PersonalDetailsForm = observer(({ history }) => { )} ); -}); +}; PersonalDetailsForm.propTypes = { + authentication_status: PropTypes.object, + is_eu: PropTypes.bool, + is_mf: PropTypes.bool, + is_uk: PropTypes.bool, + is_svg: PropTypes.bool, + is_virtual: PropTypes.bool, + residence_list: PropTypes.arrayOf(PropTypes.object), + states_list: PropTypes.array, + refreshNotifications: PropTypes.func, + showPOAAddressMismatchSuccessNotification: PropTypes.func, + showPOAAddressMismatchFailureNotification: PropTypes.func, + Notifications: PropTypes.node, + fetchResidenceList: PropTypes.func, + fetchStatesList: PropTypes.func, + has_residence: PropTypes.bool, + account_settings: PropTypes.object, + getChangeableFields: PropTypes.func, + current_landing_company: PropTypes.object, history: PropTypes.object, + is_social_signup: PropTypes.bool, + updateAccountStatus: PropTypes.func, + has_poa_address_mismatch: PropTypes.bool, + is_language_changing: PropTypes.bool, }; -export default withRouter(PersonalDetailsForm); +export default connect(({ client, notifications, ui, common }) => ({ + account_settings: client.account_settings, + authentication_status: client.authentication_status, + has_residence: client.has_residence, + getChangeableFields: client.getChangeableFields, + current_landing_company: client.current_landing_company, + is_eu: client.is_eu, + is_mf: client.landing_company_shortcode === 'maltainvest', + is_svg: client.is_svg, + is_uk: client.is_uk, + is_virtual: client.is_virtual, + residence_list: client.residence_list, + states_list: client.states_list, + fetchResidenceList: client.fetchResidenceList, + fetchStatesList: client.fetchStatesList, + is_social_signup: client.is_social_signup, + refreshNotifications: notifications.refreshNotifications, + showPOAAddressMismatchSuccessNotification: notifications.showPOAAddressMismatchSuccessNotification, + showPOAAddressMismatchFailureNotification: notifications.showPOAAddressMismatchFailureNotification, + Notifications: ui.notification_messages_ui, + updateAccountStatus: client.updateAccountStatus, + has_poa_address_mismatch: client.account_status.status?.includes('poa_address_mismatch'), + is_language_changing: common.is_language_changing, +}))(withRouter(PersonalDetailsForm)); diff --git a/packages/account/src/Sections/Security/AccountClosed/account-closed.jsx b/packages/account/src/Sections/Security/AccountClosed/account-closed.jsx index e3379dfa2279..d3e553109ee5 100644 --- a/packages/account/src/Sections/Security/AccountClosed/account-closed.jsx +++ b/packages/account/src/Sections/Security/AccountClosed/account-closed.jsx @@ -2,11 +2,9 @@ import React from 'react'; import { Modal, Text } from '@deriv/components'; import { Localize } from '@deriv/translations'; import { getStaticUrl, PlatformContext } from '@deriv/shared'; -import { observer, useStore } from '@deriv/stores'; +import { connect } from 'Stores/connect'; -const AccountClosed = observer(() => { - const { client } = useStore(); - const { logout } = client; +const AccountClosed = ({ logout }) => { const [is_modal_open, setModalState] = React.useState(true); const [timer, setTimer] = React.useState(10); const { is_appstore } = React.useContext(PlatformContext); @@ -40,6 +38,8 @@ const AccountClosed = observer(() => { ); -}); +}; -export default AccountClosed; +export default connect(({ client }) => ({ + logout: client.logout, +}))(AccountClosed); diff --git a/packages/account/src/Sections/Security/AccountLimits/account-limits.jsx b/packages/account/src/Sections/Security/AccountLimits/account-limits.jsx index 5a0771faf09b..8428842a49f2 100644 --- a/packages/account/src/Sections/Security/AccountLimits/account-limits.jsx +++ b/packages/account/src/Sections/Security/AccountLimits/account-limits.jsx @@ -1,4 +1,15 @@ import AccountLimits from 'Components/account-limits/account-limits'; import 'Components/account-limits/account-limits.scss'; +import { connect } from 'Stores/connect'; -export default AccountLimits; +export default connect(({ client, common, ui }) => ({ + account_limits: client.account_limits, + currency: client.currency, + getLimits: client.getLimits, + is_fully_authenticated: client.is_fully_authenticated, + is_from_derivgo: common.is_from_derivgo, + is_virtual: client.is_virtual, + is_switching: client.is_switching, + should_show_article: true, + toggleAccountsDialog: ui.toggleAccountsDialog, +}))(AccountLimits); diff --git a/packages/account/src/Sections/Security/ApiToken/api-token.jsx b/packages/account/src/Sections/Security/ApiToken/api-token.jsx index cb02ee645088..05363454eef6 100644 --- a/packages/account/src/Sections/Security/ApiToken/api-token.jsx +++ b/packages/account/src/Sections/Security/ApiToken/api-token.jsx @@ -1,4 +1,6 @@ +import { WS } from '@deriv/shared'; +import { connect } from 'Stores/connect'; import ApiToken from 'Components/api-token/api-token'; import 'Components/api-token/api-token.scss'; -export default ApiToken; +export default connect(({ client }) => ({ is_switching: client.is_switching, ws: WS }))(ApiToken); diff --git a/packages/account/src/Sections/Security/ClosingAccount/__tests__/closing-account-reason.spec.js b/packages/account/src/Sections/Security/ClosingAccount/__tests__/closing-account-reason.spec.js index ca7527b3c459..49ff2da25f9f 100644 --- a/packages/account/src/Sections/Security/ClosingAccount/__tests__/closing-account-reason.spec.js +++ b/packages/account/src/Sections/Security/ClosingAccount/__tests__/closing-account-reason.spec.js @@ -1,10 +1,14 @@ import React from 'react'; import { act, render, screen, waitFor, fireEvent, userEvent } from '@testing-library/react'; import ClosingAccountReason from '../closing-account-reason'; -import { mockStore, StoreProvider } from '@deriv/stores'; + +jest.mock('Stores/connect', () => ({ + __esModule: true, + default: 'mockedDefaultExport', + connect: () => Component => Component, +})); describe('', () => { - let store = mockStore(); beforeAll(() => { const modal_root_el = document.createElement('div'); modal_root_el.setAttribute('id', 'modal_root'); @@ -17,22 +21,14 @@ describe('', () => { }); test('Should render properly', async () => { - render( - - - - ); + render(); await waitFor(() => { screen.getAllByText(/Please tell us why you’re leaving/i); }); }); test('Should be disabled when no reason has been selected', async () => { - render( - - - - ); + render(); // clicking the checkbox twice to select and unselect fireEvent.click(screen.getByRole('checkbox', { name: /I have other financial priorities./i })); @@ -47,11 +43,7 @@ describe('', () => { }); test('should reduce remaining chars', async () => { - render( - - - - ); + render(); expect(screen.getByText(/Remaining characters: 110/i)).toBeInTheDocument(); diff --git a/packages/account/src/Sections/Security/ClosingAccount/closing-account-reason.jsx b/packages/account/src/Sections/Security/ClosingAccount/closing-account-reason.jsx index 7a2eeeba0d55..d78f78ff2f73 100644 --- a/packages/account/src/Sections/Security/ClosingAccount/closing-account-reason.jsx +++ b/packages/account/src/Sections/Security/ClosingAccount/closing-account-reason.jsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import { routes, PlatformContext, WS } from '@deriv/shared'; import { localize } from '@deriv/translations'; import { FormSubmitButton, Modal, Icon, Loading, Text, Button } from '@deriv/components'; -import { observer, useStore } from '@deriv/stores'; +import { connect } from 'Stores/connect'; import AccountHasPendingConditions from './account-has-balance.jsx'; import ClosingAccountReasonFrom from './closing-account-reason-form.jsx'; @@ -73,9 +73,7 @@ const GeneralErrorContent = ({ message, onClick }) => ( const character_limit_no = 110; const max_allowed_reasons = 3; -const ClosingAccountReason = observer(({ onBackClick }) => { - const { client } = useStore(); - const { dxtrade_accounts_list, mt5_login_list, account_list } = client; +const ClosingAccountReason = ({ onBackClick, mt5_login_list, client_accounts, dxtrade_accounts_list }) => { const { is_appstore } = React.useContext(PlatformContext); const [is_account_closed, setIsAccountClosed] = React.useState(false); const [is_loading, setIsLoading] = React.useState(false); @@ -230,7 +228,7 @@ const ClosingAccountReason = observer(({ onBackClick }) => { @@ -241,6 +239,10 @@ const ClosingAccountReason = observer(({ onBackClick }) => { ); -}); +}; -export default ClosingAccountReason; +export default connect(({ client }) => ({ + client_accounts: client.account_list, + mt5_login_list: client.mt5_login_list, + dxtrade_accounts_list: client.dxtrade_accounts_list, +}))(ClosingAccountReason); diff --git a/packages/account/src/Sections/Security/ClosingAccount/closing-account-steps.jsx b/packages/account/src/Sections/Security/ClosingAccount/closing-account-steps.jsx index 51a7215a66bc..fb138cfd3f5b 100644 --- a/packages/account/src/Sections/Security/ClosingAccount/closing-account-steps.jsx +++ b/packages/account/src/Sections/Security/ClosingAccount/closing-account-steps.jsx @@ -1,14 +1,12 @@ import React from 'react'; import classNames from 'classnames'; -import { observer, useStore } from '@deriv/stores'; +import { connect } from 'Stores/connect'; import { localize, Localize } from '@deriv/translations'; import { Link } from 'react-router-dom'; import { Button, Text, StaticUrl } from '@deriv/components'; import { PlatformContext } from '@deriv/shared'; -const ClosingAccountSteps = observer(({ redirectToReasons }) => { - const { common } = useStore(); - const { is_from_derivgo } = common; +const ClosingAccountSteps = ({ redirectToReasons, is_from_derivgo }) => { const { is_appstore } = React.useContext(PlatformContext); return ( @@ -86,5 +84,5 @@ const ClosingAccountSteps = observer(({ redirectToReasons }) => { )} ); -}); -export default ClosingAccountSteps; +}; +export default connect(({ common }) => ({ is_from_derivgo: common.is_from_derivgo }))(ClosingAccountSteps); diff --git a/packages/account/src/Sections/Security/LoginHistory/login-history.jsx b/packages/account/src/Sections/Security/LoginHistory/login-history.jsx index 099b5c5d9139..449c4e253e09 100644 --- a/packages/account/src/Sections/Security/LoginHistory/login-history.jsx +++ b/packages/account/src/Sections/Security/LoginHistory/login-history.jsx @@ -1,10 +1,11 @@ +import PropTypes from 'prop-types'; import React from 'react'; import classNames from 'classnames'; import { Loading, Table, Text, ThemedScrollbars } from '@deriv/components'; import Bowser from 'bowser'; import { convertDateFormat, isMobile, isDesktop, PlatformContext, WS } from '@deriv/shared'; -import { observer, useStore } from '@deriv/stores'; import { localize } from '@deriv/translations'; +import { connect } from 'Stores/connect'; import LoadErrorMessage from 'Components/load-error-message'; const API_FETCH_LIMIT = 50; @@ -167,9 +168,7 @@ const ListCell = ({ title, text, className, right }) => ( ); -const LoginHistory = observer(() => { - const { client } = useStore(); - const { is_switching } = client; +const LoginHistory = ({ is_switching }) => { const [is_loading, setLoading] = React.useState(true); const [error, setError] = React.useState(''); const [data, setData] = React.useState([]); @@ -198,6 +197,12 @@ const LoginHistory = observer(() => { {data.length ? : null} ); -}); +}; + +LoginHistory.propTypes = { + is_switching: PropTypes.bool, +}; -export default LoginHistory; +export default connect(({ client }) => ({ + is_switching: client.is_switching, +}))(LoginHistory); diff --git a/packages/account/src/Sections/Security/Passwords/deriv-email.jsx b/packages/account/src/Sections/Security/Passwords/deriv-email.jsx index a962f2526c33..c2c85c1492a9 100644 --- a/packages/account/src/Sections/Security/Passwords/deriv-email.jsx +++ b/packages/account/src/Sections/Security/Passwords/deriv-email.jsx @@ -8,11 +8,9 @@ import { Button, Text, Input } from '@deriv/components'; import FormSubHeader from 'Components/form-sub-header'; import SentEmailModal from 'Components/sent-email-modal'; import UnlinkAccountModal from 'Components/unlink-account-modal'; -import { observer, useStore } from '@deriv/stores'; +import { connect } from 'Stores/connect'; -const DerivEmail = observer(({ email, social_identity_provider, is_social_signup }) => { - const { common } = useStore(); - const { is_from_derivgo } = common; +const DerivEmail = ({ email, social_identity_provider, is_social_signup, is_from_derivgo }) => { const [is_unlink_account_modal_open, setIsUnlinkAccountModalOpen] = React.useState(false); const [is_send_email_modal_open, setIsSendEmailModalOpen] = React.useState(false); @@ -90,13 +88,18 @@ const DerivEmail = observer(({ email, social_identity_provider, is_social_signup ); -}); +}; DerivEmail.propTypes = { email: PropTypes.string, is_dark_mode_on: PropTypes.bool, is_social_signup: PropTypes.bool, + is_from_derivgo: PropTypes.bool, social_identity_provider: PropTypes.string, }; -export default withRouter(DerivEmail); +export default withRouter( + connect(({ common }) => ({ + is_from_derivgo: common.is_from_derivgo, + }))(DerivEmail) +); diff --git a/packages/account/src/Sections/Security/Passwords/passwords.jsx b/packages/account/src/Sections/Security/Passwords/passwords.jsx index 719d784a160c..86f6dbcb1f58 100644 --- a/packages/account/src/Sections/Security/Passwords/passwords.jsx +++ b/packages/account/src/Sections/Security/Passwords/passwords.jsx @@ -1,36 +1,32 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { Loading } from '@deriv/components'; -import { observer, useStore } from '@deriv/stores'; +import { connect } from 'Stores/connect'; import DerivPassword from './deriv-password.jsx'; import DerivEmail from './deriv-email.jsx'; import PasswordsPlatform from './passwords-platform.jsx'; -const Passwords = observer(() => { +const Passwords = ({ + email, + is_dark_mode_on, + mt5_login_list, + is_social_signup, + dxtrade_accounts_list, + social_identity_provider, + is_eu_user, + financial_restricted_countries, + is_loading_dxtrade, + is_loading_mt5, + is_mt5_password_not_set, + is_dxtrade_password_not_set, + is_from_derivgo, +}) => { const [is_loading, setIsLoading] = React.useState(true); - const { client, ui, common, traders_hub } = useStore(); - const { - is_populating_mt5_account_list, - is_populating_dxtrade_account_list, - is_social_signup, - email, - social_identity_provider, - mt5_login_list, - is_mt5_password_not_set, - dxtrade_accounts_list, - is_dxtrade_password_not_set, - } = client; - const { is_from_derivgo } = common; - const { is_eu_user, financial_restricted_countries } = traders_hub; - const { is_dark_mode_on } = ui; React.useEffect(() => { - if ( - is_populating_mt5_account_list === false && - is_populating_dxtrade_account_list === false && - is_social_signup !== undefined - ) { + if (is_loading_mt5 === false && is_loading_dxtrade === false && is_social_signup !== undefined) { setIsLoading(false); } - }, [is_populating_mt5_account_list, is_populating_dxtrade_account_list, is_social_signup]); + }, [is_loading_mt5, is_loading_dxtrade, is_social_signup]); if (is_loading) { return ; @@ -65,6 +61,36 @@ const Passwords = observer(() => { )} ); -}); +}; -export default Passwords; +Passwords.propTypes = { + email: PropTypes.string, + is_dark_mode_on: PropTypes.bool, + dxtrade_accounts_list: PropTypes.array, + is_social_signup: PropTypes.bool, + mt5_login_list: PropTypes.array, + social_identity_provider: PropTypes.string, + is_loading_mt5: PropTypes.bool, + is_loading_dxtrade: PropTypes.bool, + is_eu_user: PropTypes.bool, + financial_restricted_countries: PropTypes.bool, + is_mt5_password_not_set: PropTypes.bool, + is_dxtrade_password_not_set: PropTypes.bool, + is_from_derivgo: PropTypes.bool, +}; + +export default connect(({ client, ui, common, traders_hub }) => ({ + email: client.email, + is_dark_mode_on: ui.is_dark_mode_on, + is_social_signup: client.is_social_signup, + mt5_login_list: client.mt5_login_list, + dxtrade_accounts_list: client.dxtrade_accounts_list, + social_identity_provider: client.social_identity_provider, + is_eu_user: traders_hub.is_eu_user, + financial_restricted_countries: traders_hub.financial_restricted_countries, + is_loading_mt5: client.is_populating_mt5_account_list, + is_loading_dxtrade: client.is_populating_dxtrade_account_list, + is_mt5_password_not_set: client.is_mt5_password_not_set, + is_dxtrade_password_not_set: client.is_dxtrade_password_not_set, + is_from_derivgo: common.is_from_derivgo, +}))(Passwords); diff --git a/packages/account/src/Sections/Security/SelfExclusion/self-exclusion.jsx b/packages/account/src/Sections/Security/SelfExclusion/self-exclusion.jsx index e0f4928efa53..76609ebb4e7b 100644 --- a/packages/account/src/Sections/Security/SelfExclusion/self-exclusion.jsx +++ b/packages/account/src/Sections/Security/SelfExclusion/self-exclusion.jsx @@ -1,5 +1,6 @@ import React from 'react'; -import { PlatformContext } from '@deriv/shared'; +import { PlatformContext, WS } from '@deriv/shared'; +import { connect } from 'Stores/connect'; import SelfExclusionComponent from 'Components/self-exclusion/self-exclusion'; import 'Components/self-exclusion/self-exclusion.scss'; @@ -8,4 +9,18 @@ const SelfExclusion = props => { return ; }; -export default SelfExclusion; +export default connect(({ client, ui }) => ({ + is_tablet: ui.is_tablet, + currency: client.currency, + is_virtual: client.is_virtual, + is_switching: client.is_switching, + is_cr: client.standpoint.svg, + is_eu: client.is_eu, + is_mlt: client.landing_company_shortcode === 'malta', + is_mf: client.landing_company_shortcode === 'maltainvest', + is_mx: client.landing_company_shortcode === 'iom', + is_uk: client.is_uk, + is_wrapper_bypassed: false, + logout: client.logout, + ws: WS, +}))(SelfExclusion); diff --git a/packages/account/src/Sections/Security/TwoFactorAuthentication/two-factor-authentication.jsx b/packages/account/src/Sections/Security/TwoFactorAuthentication/two-factor-authentication.jsx index 4b805f37eec9..c18230f8aec6 100644 --- a/packages/account/src/Sections/Security/TwoFactorAuthentication/two-factor-authentication.jsx +++ b/packages/account/src/Sections/Security/TwoFactorAuthentication/two-factor-authentication.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import classNames from 'classnames'; import React from 'react'; import QRCode from 'qrcode.react'; @@ -13,16 +14,20 @@ import { } from '@deriv/components'; import { getPropertyValue, isMobile, PlatformContext, WS } from '@deriv/shared'; import { localize, Localize } from '@deriv/translations'; +import { connect } from 'Stores/connect'; import LoadErrorMessage from 'Components/load-error-message'; import DigitForm from './digit-form.jsx'; import TwoFactorAuthenticationArticle from './two-factor-authentication-article.jsx'; -import { observer, useStore } from '@deriv/stores'; -const TwoFactorAuthentication = observer(() => { - const { client, ui } = useStore(); - const { email_address, getTwoFAStatus, has_enabled_two_fa, is_switching, setTwoFAStatus, setTwoFAChangedStatus } = - client; - const { notification_messages_ui: Notifications } = ui; +const TwoFactorAuthentication = ({ + email_address, + is_switching, + setTwoFAStatus, + getTwoFAStatus, + has_enabled_two_fa, + Notifications, + setTwoFAChangedStatus, +}) => { const [is_loading, setLoading] = React.useState(true); const [is_qr_loading, setQrLoading] = React.useState(false); const [error_message, setErrorMessage] = React.useState(''); @@ -200,6 +205,24 @@ const TwoFactorAuthentication = observer(() => { ); -}); +}; -export default TwoFactorAuthentication; +TwoFactorAuthentication.propTypes = { + email_address: PropTypes.string, + is_switching: PropTypes.bool, + setTwoFAStatus: PropTypes.func, + getTwoFAStatus: PropTypes.func, + has_enabled_two_fa: PropTypes.bool, + Notifications: PropTypes.node, + setTwoFAChangedStatus: PropTypes.func, +}; + +export default connect(({ client, ui }) => ({ + email_address: client.email_address, + is_switching: client.is_switching, + setTwoFAStatus: client.setTwoFAStatus, + getTwoFAStatus: client.getTwoFAStatus, + has_enabled_two_fa: client.has_enabled_two_fa, + Notifications: ui.notification_messages_ui, + setTwoFAChangedStatus: client.setTwoFAChangedStatus, +}))(TwoFactorAuthentication); diff --git a/packages/account/src/Sections/Verification/ProofOfAddress/proof-of-address-form.jsx b/packages/account/src/Sections/Verification/ProofOfAddress/proof-of-address-form.jsx index b47e1d6190e6..aee2265a2ca7 100644 --- a/packages/account/src/Sections/Verification/ProofOfAddress/proof-of-address-form.jsx +++ b/packages/account/src/Sections/Verification/ProofOfAddress/proof-of-address-form.jsx @@ -24,6 +24,7 @@ import { getLocation, WS, } from '@deriv/shared'; +import { connect } from 'Stores/connect'; import FormFooter from 'Components/form-footer'; import FormBody from 'Components/form-body'; import FormBodySection from 'Components/form-body-section'; @@ -31,7 +32,6 @@ import FormSubHeader from 'Components/form-sub-header'; import LoadErrorMessage from 'Components/load-error-message'; import LeaveConfirm from 'Components/leave-confirm'; import FileUploaderContainer from 'Components/file-uploader-container'; -import { observer, useStore } from '@deriv/stores'; const validate = (errors, values) => (fn, arr, err_msg) => { arr.forEach(field => { @@ -53,14 +53,18 @@ const UploaderSideNote = () => ( ); -const ProofOfAddressForm = observer(({ is_resubmit, onSubmit }) => { - const { client, notifications } = useStore(); - const { account_settings, fetchResidenceList, fetchStatesList, is_eu, states_list } = client; - const { - addNotificationMessageByKey: addNotificationByKey, - removeNotificationMessage, - removeNotificationByKey, - } = notifications; +const ProofOfAddressForm = ({ + account_settings, + addNotificationByKey, + is_eu, + is_resubmit, + fetchResidenceList, + fetchStatesList, + onSubmit, + removeNotificationByKey, + removeNotificationMessage, + states_list, +}) => { const [document_file, setDocumentFile] = React.useState({ files: [], error_message: null }); const [is_loading, setIsLoading] = React.useState(true); const [form_values, setFormValues] = useStateCallback({}); @@ -454,11 +458,28 @@ const ProofOfAddressForm = observer(({ is_resubmit, onSubmit }) => { )} ); -}); +}; ProofOfAddressForm.propTypes = { + account_settings: PropTypes.object, + addNotificationByKey: PropTypes.func, + is_eu: PropTypes.bool, is_resubmit: PropTypes.bool, + fetchResidenceList: PropTypes.func, + fetchStatesList: PropTypes.func, onSubmit: PropTypes.func, + removeNotificationByKey: PropTypes.func, + removeNotificationMessage: PropTypes.func, + states_list: PropTypes.array, }; -export default ProofOfAddressForm; +export default connect(({ client, notifications }) => ({ + account_settings: client.account_settings, + is_eu: client.is_eu, + addNotificationByKey: notifications.addNotificationMessageByKey, + removeNotificationMessage: notifications.removeNotificationMessage, + removeNotificationByKey: notifications.removeNotificationByKey, + states_list: client.states_list, + fetchResidenceList: client.fetchResidenceList, + fetchStatesList: client.fetchStatesList, +}))(ProofOfAddressForm); diff --git a/packages/account/src/Sections/Verification/ProofOfAddress/proof-of-address.jsx b/packages/account/src/Sections/Verification/ProofOfAddress/proof-of-address.jsx index 1b9e16ba3e4e..7d558e882487 100644 --- a/packages/account/src/Sections/Verification/ProofOfAddress/proof-of-address.jsx +++ b/packages/account/src/Sections/Verification/ProofOfAddress/proof-of-address.jsx @@ -1,26 +1,45 @@ import DemoMessage from 'Components/demo-message'; import { PlatformContext } from '@deriv/shared'; import ProofOfAddressContainer from './proof-of-address-container.jsx'; +import { PropTypes } from 'prop-types'; import React from 'react'; -import { observer, useStore } from '@deriv/stores'; +import { connect } from 'Stores/connect'; -const ProofOfAddress = observer(() => { - const { client, notifications, common } = useStore(); - const { app_routing_history } = common; - const { is_virtual, landing_company_shortcode, has_restricted_mt5_account, is_switching } = client; - const { refreshNotifications } = notifications; +const ProofOfAddress = ({ + is_virtual, + is_mx_mlt, + is_switching, + has_restricted_mt5_account, + refreshNotifications, + app_routing_history, +}) => { const { is_appstore } = React.useContext(PlatformContext); if (is_virtual) return ; return ( ); -}); +}; -export default ProofOfAddress; +ProofOfAddress.propTypes = { + is_mx_mlt: PropTypes.bool, + is_switching: PropTypes.bool, + is_virtual: PropTypes.bool, + refreshNotifications: PropTypes.func, + has_restricted_mt5_account: PropTypes.bool, +}; + +export default connect(({ client, notifications, common }) => ({ + is_mx_mlt: client.landing_company_shortcode === 'iom' || client.landing_company_shortcode === 'malta', + is_switching: client.is_switching, + is_virtual: client.is_virtual, + refreshNotifications: notifications.refreshNotifications, + has_restricted_mt5_account: client.has_restricted_mt5_account, + app_routing_history: common.app_routing_history, +}))(ProofOfAddress); diff --git a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity.jsx b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity.jsx index ebcbd6f37dcf..ed975b1a027b 100644 --- a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity.jsx +++ b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity.jsx @@ -2,25 +2,26 @@ import { AutoHeightWrapper } from '@deriv/components'; import ProofOfIdentityContainer from './proof-of-identity-container.jsx'; import React from 'react'; import { changeMetaTagWithOG } from '@deriv/shared'; -import { observer, useStore } from '@deriv/stores'; +import { connect } from 'Stores/connect'; import { withRouter } from 'react-router-dom'; -const ProofOfIdentity = observer(({ is_from_external, onStateChange }) => { - const { client, common, notifications } = useStore(); - const { - account_status, - account_settings, - fetchResidenceList, - getChangeableFields, - is_switching, - is_high_risk, - is_withdrawal_lock, - should_allow_authentication, - is_virtual, - updateAccountStatus, - } = client; - const { refreshNotifications } = notifications; - const { app_routing_history, routeBackInApp } = common; +const ProofOfIdentity = ({ + account_settings, + account_status, + app_routing_history, + fetchResidenceList, + getChangeableFields, + is_from_external, + is_switching, + is_virtual, + is_high_risk, + is_withdrawal_lock, + onStateChange, + refreshNotifications, + routeBackInApp, + should_allow_authentication, + updateAccountStatus, +}) => { // next useEffect implements seo requirements React.useEffect(() => { const description_content = 'Submit your proof of identity documents to verify your account and start trading'; @@ -64,6 +65,20 @@ const ProofOfIdentity = observer(({ is_from_external, onStateChange }) => { )} ); -}); +}; -export default withRouter(ProofOfIdentity); +export default connect(({ client, common, notifications }) => ({ + account_settings: client.account_settings, + account_status: client.account_status, + app_routing_history: common.app_routing_history, + fetchResidenceList: client.fetchResidenceList, + getChangeableFields: client.getChangeableFields, + is_switching: client.is_switching, + is_virtual: client.is_virtual, + is_high_risk: client.is_high_risk, + is_withdrawal_lock: client.is_withdrawal_lock, + refreshNotifications: notifications.refreshNotifications, + routeBackInApp: common.routeBackInApp, + should_allow_authentication: client.should_allow_authentication, + updateAccountStatus: client.updateAccountStatus, +}))(withRouter(ProofOfIdentity)); diff --git a/packages/account/src/Sections/Verification/ProofOfOwnership/__test__/proof-of-ownership.spec.js b/packages/account/src/Sections/Verification/ProofOfOwnership/__test__/proof-of-ownership.spec.js index 6bf1b94608e2..f79ea6d2e251 100644 --- a/packages/account/src/Sections/Verification/ProofOfOwnership/__test__/proof-of-ownership.spec.js +++ b/packages/account/src/Sections/Verification/ProofOfOwnership/__test__/proof-of-ownership.spec.js @@ -2,93 +2,80 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import { ProofOfOwnership } from '../proof-of-ownership.jsx'; import test_data from './test-data'; -import { StoreProvider, mockStore } from '@deriv/stores'; describe('proof-of-ownership.jsx', () => { let ownership_temp; beforeAll(() => { ownership_temp = test_data; }); - const ProofOfOwnershipScreen = () => { - return ( - - - - ); - }; - let store = mockStore(); it('should render no poo required status page', () => { - store = mockStore({ - client: { - account_status: { + render( + ); - + }} + updateAccountStatus={jest.fn()} + /> + ); const element = screen.getByText("Your proof of ownership isn't required.", { exact: true }); expect(element).toBeInTheDocument(); }); it('should render poo verified status page', () => { - store = mockStore({ - client: { - account_status: { + render( + ); - + }} + updateAccountStatus={jest.fn()} + /> + ); const element = screen.getByText('Proof of ownership verification passed.', { exact: true }); expect(element).toBeInTheDocument(); }); it('should render poo submitted status page', () => { - store = mockStore({ - client: { - account_status: { + render( + ); - + }} + updateAccountStatus={jest.fn()} + /> + ); const element = screen.getByText('We’ve received your proof of ownership.', { exact: true }); expect(element).toBeInTheDocument(); }); it('should render poo rejected status page', () => { - store = mockStore({ - client: { - account_status: { + render( + ); - + }} + updateAccountStatus={jest.fn()} + /> + ); const element = screen.getByTestId('dt_try-again-button', { exact: true }); expect(element).toBeInTheDocument(); }); it('should render ProofOfOwnershipForm', () => { - store = mockStore({ - client: { - account_status: { + render( + ); + }} + updateAccountStatus={jest.fn()} + /> + ); expect(screen.getByTestId('dt_poo_form', { exact: true })).toBeInTheDocument(); }); }); diff --git a/packages/account/src/Sections/Verification/ProofOfOwnership/proof-of-ownership.jsx b/packages/account/src/Sections/Verification/ProofOfOwnership/proof-of-ownership.jsx index b0ce9a0cde8d..e8c45c71728e 100644 --- a/packages/account/src/Sections/Verification/ProofOfOwnership/proof-of-ownership.jsx +++ b/packages/account/src/Sections/Verification/ProofOfOwnership/proof-of-ownership.jsx @@ -1,17 +1,19 @@ import React, { useEffect, useState } from 'react'; import { withRouter } from 'react-router-dom'; +import { connect } from 'Stores/connect'; import ProofOfOwnershipForm from './proof-of-ownership-form.jsx'; import { POONotRequired, POOVerified, POORejetced, POOSubmitted } from 'Components/poo/statuses'; import { Loading } from '@deriv/components'; import { POO_STATUSES } from './constants/constants'; import getPaymentMethodsConfig from './payment-method-config.js'; -import { observer, useStore } from '@deriv/stores'; -export const ProofOfOwnership = observer(() => { - const { client, notifications, ui } = useStore(); - const { account_status, email: client_email, updateAccountStatus } = client; - const { refreshNotifications } = notifications; - const { is_dark_mode_on: is_dark_mode } = ui; +export const ProofOfOwnership = ({ + account_status, + client_email, + is_dark_mode, + refreshNotifications, + updateAccountStatus, +}) => { const cards = account_status?.authentication?.ownership?.requests; const [status, setStatus] = useState(POO_STATUSES.none); const grouped_payment_method_data = React.useMemo(() => { @@ -66,6 +68,12 @@ export const ProofOfOwnership = observer(() => { return ; // Proof of ownership rejected } return ; -}); +}; -export default withRouter(ProofOfOwnership); +export default connect(({ client, notifications, ui }) => ({ + account_status: client.account_status, + client_email: client.email, + is_dark_mode: ui.is_dark_mode_on, + refreshNotifications: notifications.refreshNotifications, + updateAccountStatus: client.updateAccountStatus, +}))(withRouter(ProofOfOwnership)); diff --git a/packages/account/src/Stores/connect.js b/packages/account/src/Stores/connect.js new file mode 100644 index 000000000000..4ef42c8d18b6 --- /dev/null +++ b/packages/account/src/Stores/connect.js @@ -0,0 +1,31 @@ +import { useObserver } from 'mobx-react'; +import React from 'react'; + +const isClassComponent = Component => + !!(typeof Component === 'function' && Component.prototype && Component.prototype.isReactComponent); + +export const MobxContent = React.createContext(null); + +function injectStorePropsToComponent(propsToSelectFn, BaseComponent) { + const Component = own_props => { + const store = React.useContext(MobxContent); + + let ObservedComponent = BaseComponent; + + if (isClassComponent(BaseComponent)) { + const FunctionalWrapperComponent = props => ; + ObservedComponent = FunctionalWrapperComponent; + } + + return useObserver(() => ObservedComponent({ ...own_props, ...propsToSelectFn(store, own_props) })); + }; + + Component.displayName = BaseComponent.name; + return Component; +} + +export const MobxContentProvider = ({ store, children }) => { + return {children}; +}; + +export const connect = propsToSelectFn => Component => injectStorePropsToComponent(propsToSelectFn, Component); diff --git a/packages/account/src/Stores/index.ts b/packages/account/src/Stores/index.ts new file mode 100644 index 000000000000..9797a63b3b03 --- /dev/null +++ b/packages/account/src/Stores/index.ts @@ -0,0 +1,26 @@ +export type TCoreStore = { + client: Record; + common: Record; + ui: Record; + gtm: Record; + rudderstack: Record; + pushwoosh: Record; +}; + +export default class RootStore { + public client: Record; + public common: Record; + public ui: Record; + public gtm: Record; + public rudderstack: Record; + public pushwoosh: Record; + + constructor(core_store: TCoreStore) { + this.client = core_store.client; + this.common = core_store.common; + this.ui = core_store.ui; + this.gtm = core_store.gtm; + this.rudderstack = core_store.rudderstack; + this.pushwoosh = core_store.pushwoosh; + } +} diff --git a/packages/account/src/Stores/init-store.js b/packages/account/src/Stores/init-store.js new file mode 100644 index 000000000000..78df16195a36 --- /dev/null +++ b/packages/account/src/Stores/init-store.js @@ -0,0 +1,12 @@ +import { configure } from 'mobx'; +import { setWebsocket } from '@deriv/shared'; +import RootStore from 'Stores'; + +configure({ enforceActions: 'observed' }); + +const initStore = (core_store, websocket) => { + setWebsocket(websocket); + return new RootStore(core_store); +}; + +export default initStore; diff --git a/packages/bot-skeleton/src/services/api/api-middleware.js b/packages/bot-skeleton/src/services/api/api-middleware.js index 595a2bf427ef..98deb21eed81 100644 --- a/packages/bot-skeleton/src/services/api/api-middleware.js +++ b/packages/bot-skeleton/src/services/api/api-middleware.js @@ -1,7 +1,7 @@ import { datadogLogs } from '@datadog/browser-logs'; import { formatDate, formatTime } from '@deriv/shared'; -const DATADOG_CLIENT_TOKEN = process.env.DATADOG_CLIENT_TOKEN ?? ''; +const DATADOG_CLIENT_TOKEN_LOGS = process.env.DATADOG_CLIENT_TOKEN_LOGS ?? ''; const isProduction = process.env.CIRCLE_JOB === 'release_production'; const isStaging = process.env.CIRCLE_JOB === 'release_staging'; @@ -20,8 +20,8 @@ if (isProduction) { } datadogLogs.init({ - clientToken: DATADOG_CLIENT_TOKEN, - site: 'datadoghq.eu', + clientToken: DATADOG_CLIENT_TOKEN_LOGS, + site: 'datadoghq.com', forwardErrorsToLogs: false, service: 'Dbot', sessionSampleRate: dataDogSessionSampleRate, diff --git a/packages/bot-web-ui/src/stores/app-store.js b/packages/bot-web-ui/src/stores/app-store.js index 442359df96e9..58a6abf6fa28 100644 --- a/packages/bot-web-ui/src/stores/app-store.js +++ b/packages/bot-web-ui/src/stores/app-store.js @@ -31,8 +31,10 @@ export default class AppStore { const { client, common } = this.core; this.timer = setInterval(() => { - window.sendRequestsStatistic(run_panel?.is_running); - performance.clearMeasures(); + if (window.sendRequestsStatistic) { + window.sendRequestsStatistic(run_panel?.is_running); + performance.clearMeasures(); + } }, 10000); this.showDigitalOptionsMaltainvestError(client, common); @@ -87,8 +89,10 @@ export default class AppStore { ui.setPromptHandler(false); if (this.timer) clearInterval(this.timer); - window.sendRequestsStatistic(false); - performance.clearMeasures(); + if (window.sendRequestsStatistic) { + window.sendRequestsStatistic(false); + performance.clearMeasures(); + } } onBeforeUnload = event => { diff --git a/packages/bot-web-ui/src/stores/run-panel-store.js b/packages/bot-web-ui/src/stores/run-panel-store.js index e4f53be5f6da..8db047bbeec6 100644 --- a/packages/bot-web-ui/src/stores/run-panel-store.js +++ b/packages/bot-web-ui/src/stores/run-panel-store.js @@ -147,10 +147,12 @@ export default class RunPanelStore { } async onRunButtonClick() { - performance.mark('bot-start'); + if (window.sendRequestsStatistic) { + performance.mark('bot-start'); - window.sendRequestsStatistic(false); - performance.clearMeasures(); + window.sendRequestsStatistic(false); + performance.clearMeasures(); + } const { summary_card, route_prompt_dialog, self_exclusion } = this.root_store; const { client, ui } = this.core; const is_ios = mobileOSDetect() === 'iOS'; @@ -234,8 +236,10 @@ export default class RunPanelStore { if (this.error_type) { this.error_type = undefined; } - window.sendRequestsStatistic(true); - performance.clearMeasures(); + if (window.sendRequestsStatistic) { + window.sendRequestsStatistic(true); + performance.clearMeasures(); + } } onClearStatClick() { diff --git a/packages/bot-web-ui/webpack.config.js b/packages/bot-web-ui/webpack.config.js index 473580b22df5..9dc1a21b863b 100644 --- a/packages/bot-web-ui/webpack.config.js +++ b/packages/bot-web-ui/webpack.config.js @@ -120,7 +120,7 @@ module.exports = function (env) { 'process.env.GD_API_KEY': JSON.stringify(process.env.GD_API_KEY), 'process.env.GD_APP_ID': JSON.stringify(process.env.GD_APP_ID), 'process.env.DATADOG_APPLICATION_ID': JSON.stringify(process.env.DATADOG_APPLICATION_ID), - 'process.env.DATADOG_CLIENT_TOKEN': JSON.stringify(process.env.DATADOG_CLIENT_TOKEN), + 'process.env.DATADOG_CLIENT_TOKEN_LOGS': JSON.stringify(process.env.DATADOG_CLIENT_TOKEN_LOGS), 'process.env.DATADOG_SESSION_REPLAY_SAMPLE_RATE': JSON.stringify( process.env.DATADOG_SESSION_REPLAY_SAMPLE_RATE ), diff --git a/packages/cfd/src/Containers/trade-modal.tsx b/packages/cfd/src/Containers/trade-modal.tsx index 459918c984c2..320b5f5189a4 100644 --- a/packages/cfd/src/Containers/trade-modal.tsx +++ b/packages/cfd/src/Containers/trade-modal.tsx @@ -121,13 +121,13 @@ const TradeModal = ({ }; const downloadCenterAppOption = (platform_type: TCFDsPlatformType) => { - let appTitle = ''; + let app_title = ''; if (platform_type === 'dxtrade') { - appTitle = 'Run Deriv X on your browser'; + app_title = localize('Run Deriv X on your browser'); } else if (platform_type === 'derivez') { - appTitle = 'Run Deriv EZ on your browser'; + app_title = localize('Run Deriv EZ on your browser'); } else if (platform_type === 'ctrader') { - appTitle = 'Run Deriv cTrader on your browser'; + app_title = localize('Run Deriv cTrader on your browser'); } else { return null; } @@ -136,7 +136,7 @@ const TradeModal = ({
- {localize(appTitle)} + {app_title} { return { is_mock: true, client: { - fetchResidenceList: jest.fn(), - fetchStatesList: jest.fn(), - getChangeableFields: jest.fn(), - residence_list: [ - { - text: 'Text', - value: 'value', - }, - ], - states_list: [ - { - text: 'Text', - value: 'value', - }, - ], account_settings: {}, accounts: {}, - is_social_signup: false, active_account_landing_company: '', trading_platform_available_accounts: [], account_limits: { - account_balance: 300000, daily_transfers: { dxtrade: { allowed: 0, @@ -41,68 +24,6 @@ const mock = (): TStores & { is_mock: boolean } => { available: 0, }, }, - lifetime_limit: 13907.43, - market_specific: { - commodities: [ - { - name: 'Commodities', - payout_limit: 5000, - profile_name: 'moderate_risk', - turnover_limit: 50000, - }, - ], - cryptocurrency: [ - { - name: 'Cryptocurrencies', - payout_limit: 100.0, - profile_name: 'extreme_risk', - turnover_limit: 1000.0, - }, - ], - forex: [ - { - name: 'Smart FX', - payout_limit: 5000, - profile_name: 'moderate_risk', - turnover_limit: 50000, - }, - { - name: 'Major Pairs', - payout_limit: 20000, - profile_name: 'medium_risk', - turnover_limit: 100000, - }, - { - name: 'Minor Pairs', - payout_limit: 5000, - profile_name: 'moderate_risk', - turnover_limit: 50000, - }, - ], - indices: [ - { - name: 'Stock Indices', - payout_limit: 20000, - profile_name: 'medium_risk', - turnover_limit: 100000, - }, - ], - synthetic_index: [ - { - name: 'Synthetic Indices', - payout_limit: 50000, - profile_name: 'low_risk', - turnover_limit: 500000, - }, - ], - }, - num_of_days: 30, - num_of_days_limit: 13907.43, - open_positions: 100, - payout: 50000, - remainder: 13907.43, - withdrawal_for_x_days_monetary: 0, - withdrawal_since_inception_monetary: 0, }, account_status: { authentication: { @@ -128,7 +49,6 @@ const mock = (): TStores & { is_mock: boolean } => { document: { status: 'verified', }, - identity: { services: { idv: { @@ -193,8 +113,7 @@ const mock = (): TStores & { is_mock: boolean } => { current_fiat_currency: '', cfd_score: 0, setCFDScore: jest.fn(), - getLimits: jest.fn(() => Promise.resolve({ get_limits: {} })), - has_any_real_account: false, + getLimits: jest.fn(), has_active_real_account: false, has_logged_out: false, has_maltainvest_account: false, @@ -204,10 +123,6 @@ const mock = (): TStores & { is_mock: boolean } => { is_deposit_lock: false, is_dxtrade_allowed: false, is_eu: false, - is_eu_country: false, - is_uk: false, - has_residence: false, - is_fully_authenticated: false, is_financial_account: false, is_financial_information_incomplete: false, is_low_risk: false, @@ -234,11 +149,7 @@ const mock = (): TStores & { is_mock: boolean } => { responseTradingPlatformAccountsList: jest.fn(), standpoint: { iom: '', - svg: '', malta: '', - maltainvest: '', - gaming_company: '', - financial_company: '', }, switchAccount: jest.fn(), verification_code: { @@ -284,6 +195,7 @@ const mock = (): TStores & { is_mock: boolean } => { setTwoFAStatus: jest.fn(), has_changed_two_fa: false, setTwoFAChangedStatus: jest.fn(), + has_any_real_account: false, real_account_creation_unlock_date: 0, setPrevAccountType: jest.fn(), }, @@ -300,8 +212,6 @@ const mock = (): TStores & { is_mock: boolean } => { redirectOnClick: jest.fn(), setError: jest.fn(), }, - current_language: 'EN', - isCurrentLanguage: jest.fn(), is_from_derivgo: false, has_error: false, platform: '', @@ -309,6 +219,7 @@ const mock = (): TStores & { is_mock: boolean } => { routeTo: jest.fn(), changeCurrentLanguage: jest.fn(), changeSelectedLanguage: jest.fn(), + current_language: 'EN', is_network_online: false, server_time: undefined, is_language_changing: false, @@ -348,9 +259,7 @@ const mock = (): TStores & { is_mock: boolean } => { toggleReports: jest.fn(), setSubSectionIndex: jest.fn(), sub_section_index: 0, - toggleShouldShowRealAccountsList: jest.fn(), toggleReadyToDepositModal: jest.fn(), - is_tablet: false, is_ready_to_deposit_modal_visible: false, is_real_acc_signup_on: false, is_need_real_account_for_cashier_modal_visible: false, @@ -401,7 +310,6 @@ const mock = (): TStores & { is_mock: boolean } => { setP2POrderProps: jest.fn(), showAccountSwitchToRealNotification: jest.fn(), setP2PRedirectTo: jest.fn(), - addNotificationMessageByKey: jest.fn(), }, portfolio: { active_positions: [], diff --git a/packages/stores/types.ts b/packages/stores/types.ts index b95da3267e84..0b33c95fd546 100644 --- a/packages/stores/types.ts +++ b/packages/stores/types.ts @@ -6,8 +6,6 @@ import type { GetLimits, GetSettings, LogOutResponse, - ResidenceList, - StatesList, ProposalOpenContract, } from '@deriv/api-types'; import type { Moment } from 'moment'; @@ -20,33 +18,6 @@ type TPopulateSettingsExtensionsMenuItem = { value: (props: T) => JSX.Element; }; -type TAccountLimitsCollection = { - level?: string; - name: string; - payout_limit: number; - profile_name: string; - turnover_limit: number; -}; -type TAccount_limits = { - api_initial_load_error?: string; - open_positions?: React.ReactNode; - account_balance: string | number; - daily_transfers?: object; - payout: string | number; - lifetime_limit?: number; - market_specific: { - commodities: TAccountLimitsCollection[]; - cryptocurrency: TAccountLimitsCollection[]; - forex: TAccountLimitsCollection[]; - indices: TAccountLimitsCollection[]; - synthetic_index: TAccountLimitsCollection[]; - }; - num_of_days?: number; - num_of_days_limit: string | number; - remainder: string | number; - withdrawal_for_x_days_monetary?: number; - withdrawal_since_inception_monetary: string | number; -}; type TAccount = NonNullable[0] & { balance?: number; }; @@ -157,9 +128,6 @@ type TNotification = | ((excluded_until: number) => TNotificationMessage); type TClientStore = { - fetchResidenceList: () => Promise; - fetchStatesList: () => Promise; - getChangeableFields: () => string[]; accounts: { [k: string]: TActiveAccount }; active_accounts: TActiveAccount[]; active_account_landing_company: string; @@ -177,11 +145,8 @@ type TClientStore = { cfd_score: number; setCFDScore: (score: number) => void; currency: string; - residence_list: ResidenceList; - states_list: StatesList; current_currency_type?: string; current_fiat_currency?: string; - has_any_real_account: boolean; getLimits: () => Promise<{ get_limits?: GetLimits }>; has_active_real_account: boolean; has_logged_out: boolean; @@ -191,10 +156,6 @@ type TClientStore = { is_deposit_lock: boolean; is_dxtrade_allowed: boolean; is_eu: boolean; - is_eu_country: boolean; - is_uk: boolean; - is_social_signup: boolean; - has_residence: boolean; is_authorize: boolean; is_financial_account: boolean; is_financial_information_incomplete: boolean; @@ -230,11 +191,7 @@ type TClientStore = { }) => DetailsOfEachMT5Loginid[]; standpoint: { iom: string; - svg: string; malta: string; - maltainvest: string; - gaming_company: string; - financial_company: string; }; setAccountStatus: (status?: GetAccountStatus) => void; setBalanceOtherAccounts: (balance: number) => void; @@ -274,7 +231,7 @@ type TClientStore = { setTwoFAStatus: (status: boolean) => void; has_changed_two_fa: boolean; setTwoFAChangedStatus: (status: boolean) => void; - is_fully_authenticated: boolean; + has_any_real_account: boolean; real_account_creation_unlock_date: number; setPrevAccountType: (account_type: string) => void; }; @@ -293,7 +250,6 @@ type TCommonStoreError = { }; type TCommonStore = { - isCurrentLanguage(language_code: string): boolean; error: TCommonStoreError; has_error: boolean; is_from_derivgo: boolean; @@ -321,8 +277,6 @@ type TUiStore = { is_reports_visible: boolean; is_language_settings_modal_on: boolean; is_mobile: boolean; - sub_section_index: number; - toggleShouldShowRealAccountsList: (value: boolean) => void; openRealAccountSignup: ( value: 'maltainvest' | 'svg' | 'add_crypto' | 'choose' | 'add_fiat' | 'set_currency' | 'manage' ) => void; @@ -332,6 +286,7 @@ type TUiStore = { setReportsTabIndex: (value: number) => void; setIsClosingCreateRealAccountModal: (value: boolean) => void; setRealAccountSignupEnd: (status: boolean) => void; + sub_section_index: number; setSubSectionIndex: (index: number) => void; shouldNavigateAfterChooseCrypto: (value: string) => void; toggleAccountsDialog: () => void; @@ -339,7 +294,6 @@ type TUiStore = { toggleLanguageSettingsModal: () => void; toggleReadyToDepositModal: () => void; toggleSetCurrencyModal: () => void; - is_tablet: boolean; removeToast: (key: string) => void; is_ready_to_deposit_modal_visible: boolean; reports_route_tab_index: number; @@ -378,13 +332,12 @@ type TMenuStore = { }; type TNotificationStore = { - addNotificationMessageByKey: (key: string) => void; addNotificationMessage: (message: TNotification) => void; client_notifications: object; filterNotificationMessages: () => void; refreshNotifications: () => void; - removeNotificationByKey: (key: string) => void; - removeNotificationMessage: (key: string, should_show_again?: boolean) => void; + removeNotificationByKey: (obj: { key: string }) => void; + removeNotificationMessage: (obj: { key: string; should_show_again?: boolean }) => void; setP2POrderProps: () => void; showAccountSwitchToRealNotification: (loginid: string, currency: string) => void; setP2PRedirectTo: () => void;