diff --git a/packages/components/src/hooks/use-onclickoutside.ts b/packages/components/src/hooks/use-onclickoutside.ts index 714925fe097e..26c461872fa6 100644 --- a/packages/components/src/hooks/use-onclickoutside.ts +++ b/packages/components/src/hooks/use-onclickoutside.ts @@ -5,7 +5,7 @@ type Handler = (event: MouseEvent) => void; export function useOnClickOutside( ref: RefObject, handler: Handler, - validationFn: (event: MouseEvent) => boolean, + validationFn?: (event: MouseEvent) => boolean, mouseEvent: 'mousedown' | 'mouseup' = 'mousedown' ): void { useEventListener(mouseEvent, event => { diff --git a/packages/core/src/App/Components/Elements/Notifications/__tests__/empty-notification.spec.tsx b/packages/core/src/App/Components/Elements/Notifications/__tests__/empty-notification.spec.tsx index f1c69303c64f..500632ecd4af 100644 --- a/packages/core/src/App/Components/Elements/Notifications/__tests__/empty-notification.spec.tsx +++ b/packages/core/src/App/Components/Elements/Notifications/__tests__/empty-notification.spec.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { EmptyNotification } from '../empty-notification'; import { render, screen } from '@testing-library/react'; +import EmptyNotification from '../empty-notification'; describe('EmptyNotification Component', () => { it('should render EmptyNotification component', () => { diff --git a/packages/core/src/App/Components/Elements/Notifications/empty-notification.jsx b/packages/core/src/App/Components/Elements/Notifications/empty-notification.jsx index 8bdda7a95352..d324a5191bfc 100644 --- a/packages/core/src/App/Components/Elements/Notifications/empty-notification.jsx +++ b/packages/core/src/App/Components/Elements/Notifications/empty-notification.jsx @@ -31,4 +31,4 @@ const EmptyNotification = () => ( ); -export { EmptyNotification }; +export default EmptyNotification; diff --git a/packages/core/src/App/Containers/NotificationsDialog/__tests__/notification-list-wrapper.spec.tsx b/packages/core/src/App/Containers/NotificationsDialog/__tests__/notification-list-wrapper.spec.tsx new file mode 100644 index 000000000000..c0933e9a95d7 --- /dev/null +++ b/packages/core/src/App/Containers/NotificationsDialog/__tests__/notification-list-wrapper.spec.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { StoreProvider, mockStore } from '@deriv/stores'; +import { render, screen } from '@testing-library/react'; +import NotificationListWrapper from '../notification-list-wrapper'; + +jest.mock('App/Components/Routes', () => ({ BinaryLink: 'MockedBinaryLink' })); + +describe('NotificationListWrapper', () => { + const mock_store_without_notifications = mockStore({ notifications: { notifications: [] } }); + const mock_store_with_notifications = mockStore({ + notifications: { + notifications: [ + { + key: 'mock_notification_key', + header: 'Mock Notification Header', + message: 'Mock Notification Message', + action: { + route: '/mock/route', + text: 'Mock Notification Action', + }, + type: 'mock_notification_type', + }, + ], + }, + }); + const renderComponent = (mock_store = mockStore({})) => { + const mock_props = { clearNotifications: jest.fn() }; + render( + + + + ); + }; + + it('should render the component', () => { + renderComponent(mock_store_without_notifications); + expect(screen.getByTestId('dt_notifications_list_wrapper')).toBeInTheDocument(); + }); + + it('should render the "EmptyNotification" component if notifications list is empty', () => { + renderComponent(mock_store_without_notifications); + expect(screen.getByText('No notifications')).toBeInTheDocument(); + expect(screen.getByText('You have yet to receive any notifications')).toBeInTheDocument(); + expect(screen.queryByText('Mock Notification Header')).not.toBeInTheDocument(); + expect(screen.queryByText('Mock Notification Message')).not.toBeInTheDocument(); + expect(screen.queryByText('Mock Notification Action')).not.toBeInTheDocument(); + }); + + it('should render the "NotificationsList" component if notifications list is not empty', () => { + renderComponent(mock_store_with_notifications); + expect(screen.getByText('Mock Notification Header')).toBeInTheDocument(); + expect(screen.getByText('Mock Notification Message')).toBeInTheDocument(); + expect(screen.getByText('Mock Notification Action')).toBeInTheDocument(); + expect(screen.queryByText('No notifications')).not.toBeInTheDocument(); + expect(screen.queryByText('You have yet to receive any notifications')).not.toBeInTheDocument(); + }); + + it('should render the "ClearAllFooter" component', () => { + renderComponent(mock_store_without_notifications); + expect(screen.getByText('Clear All')).toBeInTheDocument(); + }); +}); diff --git a/packages/core/src/App/Containers/NotificationsDialog/__tests__/notifications-clear-all-footer.spec.tsx b/packages/core/src/App/Containers/NotificationsDialog/__tests__/notifications-clear-all-footer.spec.tsx new file mode 100644 index 000000000000..5d4fbcc57e8e --- /dev/null +++ b/packages/core/src/App/Containers/NotificationsDialog/__tests__/notifications-clear-all-footer.spec.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { StoreProvider, mockStore } from '@deriv/stores'; +import { fireEvent, render, screen } from '@testing-library/react'; +import ClearAllFooter from '../notifications-clear-all-footer'; + +describe('ClearAllFooter', () => { + const mock_store_without_notifications = mockStore({ notifications: { notifications: [] } }); + const mock_store_with_notifications = mockStore({ + notifications: { + notifications: [ + { + key: 'mock_security_notification', + header: 'Stronger security for your Deriv account', + message: + 'With two-factor authentication, you’ll protect your account with both your password and your phone - so only you can access your account, even if someone knows your password.', + action: { + route: '/account/two-factor-authentication', + text: 'Secure my account', + }, + type: 'warning', + }, + ], + }, + }); + const mock_props: React.ComponentProps = { clearNotifications: jest.fn() }; + + it('should render the component', () => { + render( + + + + ); + expect(screen.getByTestId('dt_clear_all_footer_button')).toBeInTheDocument(); + }); + + it('should render the button', () => { + render( + + + + ); + expect(screen.getByRole('button', { name: 'Clear All' })).toBeInTheDocument(); + }); + + it('should render the button in disabled state if there are no notifications', () => { + render( + + + + ); + expect(screen.getByRole('button', { name: 'Clear All' })).toBeDisabled(); + }); + + it('should render the button in enabled state if there are notifications available', () => { + render( + + + + ); + expect(screen.getByRole('button', { name: 'Clear All' })).toBeEnabled(); + }); + + it('should fire the "clearNotifications" method on clicking the button', () => { + render( + + + + ); + fireEvent.click(screen.getByRole('button', { name: 'Clear All' })); + expect(mock_props.clearNotifications).toBeCalledTimes(1); + }); +}); diff --git a/packages/core/src/App/Containers/NotificationsDialog/__tests__/notifications-dialog.spec.tsx b/packages/core/src/App/Containers/NotificationsDialog/__tests__/notifications-dialog.spec.tsx new file mode 100644 index 000000000000..37966d9d5939 --- /dev/null +++ b/packages/core/src/App/Containers/NotificationsDialog/__tests__/notifications-dialog.spec.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { isDesktop, isMobile } from '@deriv/shared'; +import { StoreProvider, mockStore } from '@deriv/stores'; +import { render, screen } from '@testing-library/react'; +import NotificationsDialog from '../notifications-dialog'; + +jest.mock('react-transition-group', () => ({ CSSTransition: () => 'MockedCSSTransition' })); +jest.mock('@deriv/components', () => ({ + ...jest.requireActual('@deriv/components'), + MobileDialog: () => 'MockedMobileDialog', +})); +jest.mock('@deriv/shared', () => ({ + ...jest.requireActual('@deriv/shared'), + isDesktop: jest.fn(() => true), + isMobile: jest.fn(() => false), +})); + +describe('NotificationsDialog', () => { + const renderComponent = (mock_store_override = {}) => { + const mock_store = mockStore({ + notifications: { + is_notifications_visible: true, + notifications: [ + { + key: 'mock_notification_key', + header: 'Mock Notification Header', + message: 'Mock Notification Message', + action: { + route: '/mock/route', + text: 'Mock Notification Action', + }, + type: 'mock_notification_type', + }, + ], + toggleNotificationsModal: jest.fn(), + }, + ...mock_store_override, + }); + render( + + + + ); + }; + + it('should render the component CSSTranition in desktop mode', () => { + renderComponent(); + expect(screen.getByText('MockedCSSTransition')).toBeInTheDocument(); + expect(screen.queryByText('MockedMobileDialog')).not.toBeInTheDocument(); + }); + + it('should render the component MobileDialog in mobile mode', () => { + (isDesktop as jest.Mock).mockReturnValue(false); + (isMobile as jest.Mock).mockReturnValue(true); + renderComponent(); + expect(screen.getByText('MockedMobileDialog')).toBeInTheDocument(); + expect(screen.queryByText('MockedCSSTransition')).not.toBeInTheDocument(); + }); +}); diff --git a/packages/core/src/App/Containers/NotificationsDialog/__tests__/notifications-list.spec.tsx b/packages/core/src/App/Containers/NotificationsDialog/__tests__/notifications-list.spec.tsx new file mode 100644 index 000000000000..75d2f7a44cd2 --- /dev/null +++ b/packages/core/src/App/Containers/NotificationsDialog/__tests__/notifications-list.spec.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { StoreProvider, mockStore } from '@deriv/stores'; +import { render, screen } from '@testing-library/react'; +import NotificationsList from '../notifications-list'; + +jest.mock('App/Components/Routes', () => ({ BinaryLink: 'MockedBinaryLink' })); + +describe('NotificationsList', () => { + const renderComponent = (mock_store_override = {}) => { + const mock_store = mockStore({ + notifications: { + notifications: [ + { + key: 'mock_notification_key', + header: 'Mock Notification Header', + message: 'Mock Notification Message', + action: { + route: '/mock/route', + text: 'Mock Notification Action', + }, + type: 'mock_notification_type', + }, + ], + toggleNotificationsModal: jest.fn(), + }, + ...mock_store_override, + }); + render( + + + + ); + }; + + it('should render the notification header', () => { + renderComponent(); + expect(screen.getByText('Mock Notification Header')).toBeInTheDocument(); + }); + + it('should render the notification message', () => { + renderComponent(); + expect(screen.getByText('Mock Notification Message')).toBeInTheDocument(); + }); + + it('should render the notification action button', () => { + renderComponent(); + expect(screen.getByText('Mock Notification Action')).toBeInTheDocument(); + }); +}); diff --git a/packages/core/src/App/Containers/NotificationsDialog/index.js b/packages/core/src/App/Containers/NotificationsDialog/index.js deleted file mode 100644 index be7e438acc3f..000000000000 --- a/packages/core/src/App/Containers/NotificationsDialog/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import NotificationsDialog from './notifications-dialog.jsx'; - -export default NotificationsDialog; diff --git a/packages/core/src/App/Containers/NotificationsDialog/index.ts b/packages/core/src/App/Containers/NotificationsDialog/index.ts new file mode 100644 index 000000000000..3f23a61a41dd --- /dev/null +++ b/packages/core/src/App/Containers/NotificationsDialog/index.ts @@ -0,0 +1 @@ +export { default } from './notifications-dialog'; diff --git a/packages/core/src/App/Containers/NotificationsDialog/notification-list-wrapper.tsx b/packages/core/src/App/Containers/NotificationsDialog/notification-list-wrapper.tsx new file mode 100644 index 000000000000..4dc1e2aa7cd2 --- /dev/null +++ b/packages/core/src/App/Containers/NotificationsDialog/notification-list-wrapper.tsx @@ -0,0 +1,62 @@ +import classNames from 'classnames'; +import React, { LegacyRef } from 'react'; +import { Text, ThemedScrollbars } from '@deriv/components'; +import { isMobile, routes } from '@deriv/shared'; +import { observer, useStore } from '@deriv/stores'; +import { Localize } from '@deriv/translations'; +import EmptyNotification from 'App/Components/Elements/Notifications/empty-notification'; +import ClearAllFooter from './notifications-clear-all-footer'; +import NotificationsList from './notifications-list'; + +type TNotificationListWrapper = { clearNotifications: () => void }; + +const NotificationListWrapperForwardRef = React.forwardRef( + ({ clearNotifications }: TNotificationListWrapper, ref: LegacyRef | undefined) => { + const { notifications } = useStore(); + const { notifications: notifications_array } = notifications; + const is_empty = !notifications_array?.length; + + const traders_hub = window.location.pathname === routes.traders_hub; + + return ( +
+
+ + + +
+
+ + {is_empty ? : } + +
+ +
+ ); + } +); +NotificationListWrapperForwardRef.displayName = 'NotificationListWrapper'; + +const NotificationListWrapper = observer(NotificationListWrapperForwardRef); + +export default NotificationListWrapper; diff --git a/packages/core/src/App/Containers/NotificationsDialog/notifications-clear-all-footer.tsx b/packages/core/src/App/Containers/NotificationsDialog/notifications-clear-all-footer.tsx new file mode 100644 index 000000000000..54464ebc9c23 --- /dev/null +++ b/packages/core/src/App/Containers/NotificationsDialog/notifications-clear-all-footer.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import classNames from 'classnames'; +import { Button, Text } from '@deriv/components'; +import { isMobile } from '@deriv/shared'; +import { localize } from '@deriv/translations'; +import { observer, useStore } from '@deriv/stores'; + +type TClearAllFooter = { + clearNotifications: () => void; +}; + +const ClearAllFooter = observer(({ clearNotifications }: TClearAllFooter) => { + const { notifications } = useStore(); + const { notifications: notifications_array } = notifications; + const is_empty = !notifications_array?.length; + + return ( + +
+
+ +
+ + ); +}); + +export default ClearAllFooter; diff --git a/packages/core/src/App/Containers/NotificationsDialog/notifications-dialog.jsx b/packages/core/src/App/Containers/NotificationsDialog/notifications-dialog.jsx deleted file mode 100644 index dd7323fa05db..000000000000 --- a/packages/core/src/App/Containers/NotificationsDialog/notifications-dialog.jsx +++ /dev/null @@ -1,267 +0,0 @@ -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import React from 'react'; -import { CSSTransition } from 'react-transition-group'; -import { - Button, - DesktopWrapper, - Icon, - MobileDialog, - MobileWrapper, - Text, - ThemedScrollbars, - useOnClickOutside, -} from '@deriv/components'; -import { BinaryLink } from 'App/Components/Routes'; -import { connect } from 'Stores/connect'; -import { localize, Localize } from '@deriv/translations'; -import { isEmptyObject, isMobile, LocalStore, toTitleCase, routes } from '@deriv/shared'; -import { EmptyNotification } from 'App/Components/Elements/Notifications/empty-notification.jsx'; - -const NotificationsList = ({ notifications, toggleDialog }) => { - const getNotificationitemIcon = item => { - const { type } = item; - if (['contract_sold', 'info', 'news', 'promotions'].includes(type)) { - return 'IcAlertInfo'; - } else if (type === 'p2p_completed_order') { - return 'IcAlertAnnounce'; - } - - return `IcAlert${toTitleCase(type)}`; - }; - - const getButtonSettings = item => - ['action', 'secondary_btn', 'cta_btn', 'primary_btn'].find(obj_key => !isEmptyObject(item[obj_key])); - - return ( - - {notifications.map(item => ( -
- - {item.type && ( - - )} - {item.header} - -
{item.message}
-
- {!!getButtonSettings(item) && ( - - {item[getButtonSettings(item)].route ? ( - - - {item[getButtonSettings(item)].text} - - - ) : ( - - )} - - )} -
-
- ))} -
- ); -}; - -const ClearAllFooter = ({ is_empty, clearNotifications }) => { - return ( - -
-
- -
- - ); -}; - -const NotificationListWrapper = React.forwardRef(({ notifications, toggleDialog, clearNotifications }, ref) => { - const is_empty = !notifications?.length; - - const traders_hub = window.location.pathname === routes.traders_hub; - - return ( -
-
- - - -
-
- - {is_empty ? ( - - ) : ( - - )} - -
- -
- ); -}); - -NotificationListWrapper.displayName = 'NotificationListWrapper'; - -const NotificationsDialog = ({ - is_visible, - loginid, - notifications, - toggleDialog, - removeNotificationMessage, - removeNotificationMessageByKey, - removeNotifications, -}) => { - const wrapper_ref = React.useRef(); - - const handleClickOutside = event => { - const notifications_toggle_btn = !event.target.classList.contains('notifications-toggle__icon-wrapper'); - if (!wrapper_ref.current?.contains(event.target) && is_visible && notifications_toggle_btn) { - toggleDialog(); - } - }; - - const clearNotifications = () => { - const p2p_settings = LocalStore.getObject('p2p_settings'); - if (p2p_settings[loginid]) { - p2p_settings[loginid].is_notifications_visible = false; - } - LocalStore.setObject('p2p_settings', p2p_settings); - - return notifications.map(item => { - removeNotificationMessageByKey(item.key); - removeNotificationMessage({ - key: item.key, - should_show_again: item.should_show_again || false, - }); - removeNotifications(true); - }); - }; - - useOnClickOutside(wrapper_ref, handleClickOutside); - - return ( - - - - - - - - - - - - - ); -}; - -NotificationsDialog.propTypes = { - is_visible: PropTypes.bool, - loginid: PropTypes.string, - notifications: PropTypes.array, - toggleDialog: PropTypes.func, - removeNotificationMessage: PropTypes.func, - removeNotificationByKey: PropTypes.func, - removeNotificationMessageByKey: PropTypes.func, - removeNotifications: PropTypes.func, -}; - -export default connect(({ common, client, notifications }) => ({ - app_routing_history: common.app_routing_history, - loginid: client.loginid, - notifications: notifications.notifications, - removeNotificationByKey: notifications.removeNotificationByKey, - removeNotificationMessage: notifications.removeNotificationMessage, - removeNotifications: notifications.removeNotifications, - removeNotificationMessageByKey: notifications.removeNotificationMessageByKey, -}))(NotificationsDialog); diff --git a/packages/core/src/App/Containers/NotificationsDialog/notifications-dialog.tsx b/packages/core/src/App/Containers/NotificationsDialog/notifications-dialog.tsx new file mode 100644 index 000000000000..6e9afc252710 --- /dev/null +++ b/packages/core/src/App/Containers/NotificationsDialog/notifications-dialog.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { CSSTransition } from 'react-transition-group'; +import { DesktopWrapper, MobileDialog, MobileWrapper, useOnClickOutside } from '@deriv/components'; +import { LocalStore } from '@deriv/shared'; +import { observer, useStore } from '@deriv/stores'; +import { localize } from '@deriv/translations'; +import NotificationListWrapper from './notification-list-wrapper'; + +const NotificationsDialog = observer(() => { + const { client, notifications } = useStore(); + const { loginid } = client; + const { + is_notifications_visible, + notifications: notifications_array, + removeNotifications, + removeNotificationMessage, + removeNotificationMessageByKey, + toggleNotificationsModal, + } = notifications; + + const wrapper_ref = React.useRef(null); + + const handleClickOutside = (event: MouseEvent) => { + const notifications_toggle_btn = !(event?.target as Element)?.classList.contains( + 'notifications-toggle__icon-wrapper' + ); + if ( + !wrapper_ref?.current?.contains(event.target as Node) && + is_notifications_visible && + notifications_toggle_btn + ) { + toggleNotificationsModal(); + } + }; + + const clearNotifications = () => { + const p2p_settings = LocalStore.getObject('p2p_settings'); + if (loginid && p2p_settings[loginid]) { + p2p_settings[loginid].is_notifications_visible = false; + } + LocalStore.setObject('p2p_settings', p2p_settings); + + notifications_array.forEach(item => { + removeNotificationMessageByKey({ key: item.key }); + removeNotificationMessage({ + key: item.key, + should_show_again: item?.should_show_again ?? false, + }); + removeNotifications(true); + }); + }; + + useOnClickOutside(wrapper_ref, handleClickOutside); + + return ( + + + + + + + + + + + + + ); +}); + +export default NotificationsDialog; diff --git a/packages/core/src/App/Containers/NotificationsDialog/notifications-list.tsx b/packages/core/src/App/Containers/NotificationsDialog/notifications-list.tsx new file mode 100644 index 000000000000..be1854e4cdf7 --- /dev/null +++ b/packages/core/src/App/Containers/NotificationsDialog/notifications-list.tsx @@ -0,0 +1,101 @@ +import classNames from 'classnames'; +import React from 'react'; +import { Button, Icon, Text } from '@deriv/components'; +import { BinaryLink } from 'App/Components/Routes'; +import { isEmptyObject, toTitleCase } from '@deriv/shared'; +import { observer, useStore } from '@deriv/stores'; +import { TActionProps, TNotificationMessage } from '@deriv/stores/types'; + +const NotificationsList = observer(() => { + const { notifications } = useStore(); + const { notifications: notifications_array, toggleNotificationsModal } = notifications; + + const getNotificationItemIcon = (item: TNotificationMessage) => { + const { type } = item; + if (['contract_sold', 'info', 'news', 'promotions'].includes(type)) { + return 'IcAlertInfo'; + } else if (type === 'p2p_completed_order') { + return 'IcAlertAnnounce'; + } + return `IcAlert${toTitleCase(type)}`; + }; + + const getButtonSettings = (item: TNotificationMessage): TActionProps | undefined => { + const object_key = ['action', 'secondary_btn', 'cta_btn', 'primary_btn'].find( + obj_key => !isEmptyObject(item[obj_key as keyof TNotificationMessage]) + ); + switch (object_key) { + case 'primary_btn': + return item.primary_btn; + case 'cta_btn': + return item.cta_btn; + case 'secondary_btn': + return item.secondary_btn; + case 'action': + return item.action; + default: + return undefined; + } + }; + + return ( + + {notifications_array.map(item => ( +
+ + {item.type && ( + + )} + {item.header} + +
{item.message}
+
+ {!!getButtonSettings(item) && ( + + {getButtonSettings(item)?.route ? ( + + + {getButtonSettings(item)?.text} + + + ) : ( + + )} + + )} +
+
+ ))} +
+ ); +}); + +export default NotificationsList; diff --git a/packages/stores/src/mockStore.ts b/packages/stores/src/mockStore.ts index 4a2185967736..128c8a7038da 100644 --- a/packages/stores/src/mockStore.ts +++ b/packages/stores/src/mockStore.ts @@ -407,8 +407,10 @@ const mock = (): TStores & { is_mock: boolean } => { filterNotificationMessages: jest.fn(), notifications: [], refreshNotifications: jest.fn(), + removeNotifications: jest.fn(), removeNotificationByKey: jest.fn(), removeNotificationMessage: jest.fn(), + removeNotificationMessageByKey: jest.fn(), setP2POrderProps: jest.fn(), showAccountSwitchToRealNotification: jest.fn(), setP2PRedirectTo: jest.fn(), diff --git a/packages/stores/types.ts b/packages/stores/types.ts index 884b8905bce1..2138225c0fd6 100644 --- a/packages/stores/types.ts +++ b/packages/stores/types.ts @@ -167,12 +167,12 @@ type TButtonProps = { text: string; }; -type TNotificationMessage = { - action?: { - onClick: () => void; - route?: string; - text: string; - }; +export type TActionProps = TButtonProps & { + route?: string; +}; + +export type TNotificationMessage = { + action?: TActionProps; className?: string; cta_btn?: TButtonProps; header_popup?: string; @@ -188,6 +188,7 @@ type TNotificationMessage = { primary_btn?: TButtonProps; secondary_btn?: TButtonProps; should_hide_close_btn?: boolean; + should_show_again?: boolean; timeout?: number; timeoutMessage?: (remaining: number | string) => string; type: string; @@ -515,8 +516,10 @@ type TNotificationStore = { filterNotificationMessages: () => void; notifications: TNotificationMessage[]; refreshNotifications: () => void; - removeNotificationByKey: (key: string) => void; - removeNotificationMessage: (key: string, should_show_again?: boolean) => void; + removeNotifications: (should_close_persistent: boolean) => void; + removeNotificationByKey: ({ key }: { key: string }) => void; + removeNotificationMessage: ({ key, should_show_again }: { key: string; should_show_again?: boolean }) => void; + removeNotificationMessageByKey: ({ key }: { key: string }) => void; setP2POrderProps: () => void; showAccountSwitchToRealNotification: (loginid: string, currency: string) => void; setP2PRedirectTo: () => void;