diff --git a/packages/account/src/Sections/Security/TwoFactorAuthentication/digit-form.jsx b/packages/account/src/Sections/Security/TwoFactorAuthentication/digit-form.jsx index dfd2270867f5..765e44d53231 100644 --- a/packages/account/src/Sections/Security/TwoFactorAuthentication/digit-form.jsx +++ b/packages/account/src/Sections/Security/TwoFactorAuthentication/digit-form.jsx @@ -2,12 +2,12 @@ import React from 'react'; import classNames from 'classnames'; import { Formik, Form, Field } from 'formik'; import { Input, Button } from '@deriv/components'; -import { localize, getLanguage } from '@deriv/translations'; -import { getPropertyValue, WS, redirectToLogin } from '@deriv/shared'; +import { localize } from '@deriv/translations'; +import { getPropertyValue, WS } from '@deriv/shared'; -const DigitForm = ({ is_enabled, setTwoFAStatus, logoutClient }) => { +const DigitForm = ({ is_enabled, setTwoFAStatus, setTwoFAChangedStatus }) => { const [is_success, setSuccess] = React.useState(false); - const button_text = is_enabled ? localize('Disable 2FA') : localize('Enable'); + const button_text = is_enabled ? localize('Disable') : localize('Enable'); const initial_form = { digit_code: '', @@ -50,7 +50,7 @@ const DigitForm = ({ is_enabled, setTwoFAStatus, logoutClient }) => { setSuccess(true); resetForm(); setTwoFAStatus(is_enabled_response); - logoutClient().then(() => redirectToLogin(false, getLanguage())); + setTwoFAChangedStatus(true); } }; @@ -66,12 +66,14 @@ const DigitForm = ({ is_enabled, setTwoFAStatus, logoutClient }) => { data-lpignore='true' type='text' className='two-factor__input' - label={localize('6 digit code')} + label={localize('Authentication code')} value={values.digit_code} onChange={handleChange} onBlur={handleBlur} required error={touched.digit_code && errors.digit_code} + maxLength='6' + autoComplete='off' /> )} 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 c7f4510d7ca9..c18230f8aec6 100644 --- a/packages/account/src/Sections/Security/TwoFactorAuthentication/two-factor-authentication.jsx +++ b/packages/account/src/Sections/Security/TwoFactorAuthentication/two-factor-authentication.jsx @@ -22,10 +22,11 @@ import TwoFactorAuthenticationArticle from './two-factor-authentication-article. const TwoFactorAuthentication = ({ email_address, is_switching, - logoutClient, setTwoFAStatus, getTwoFAStatus, has_enabled_two_fa, + Notifications, + setTwoFAChangedStatus, }) => { const [is_loading, setLoading] = React.useState(true); const [is_qr_loading, setQrLoading] = React.useState(false); @@ -36,7 +37,7 @@ const TwoFactorAuthentication = ({ React.useEffect(() => { getDigitStatus(); - }, [getDigitStatus]); + }, [getDigitStatus, has_enabled_two_fa]); const generateQrCode = React.useCallback(async () => { setQrLoading(true); @@ -91,7 +92,7 @@ const TwoFactorAuthentication = ({ @@ -140,26 +141,37 @@ const TwoFactorAuthentication = ({ {is_qr_loading ? ( ) : ( - <> -
- -
+ + {qr_secret_key && ( +
+ +
+ )} - - {localize( - 'If you are unable to scan the QR code, you can manually enter this code instead:' - )} - -
- {secret_key} - -
- + {secret_key && ( + + + {localize( + 'If you are unable to scan the QR code, you can manually enter this code instead:' + )} + +
+ {secret_key} + +
+
+ )} +
)} @@ -169,7 +181,7 @@ const TwoFactorAuthentication = ({ @@ -188,6 +200,7 @@ const TwoFactorAuthentication = ({ 'two-factor__wrapper-dashboard': is_appstore, })} > + {Notifications && } {has_enabled_two_fa ? TwoFactorEnabled : TwoFactorDisabled} @@ -197,17 +210,19 @@ const TwoFactorAuthentication = ({ TwoFactorAuthentication.propTypes = { email_address: PropTypes.string, is_switching: PropTypes.bool, - logoutClient: PropTypes.func, setTwoFAStatus: PropTypes.func, getTwoFAStatus: PropTypes.func, has_enabled_two_fa: PropTypes.bool, + Notifications: PropTypes.node, + setTwoFAChangedStatus: PropTypes.func, }; -export default connect(({ client }) => ({ +export default connect(({ client, ui }) => ({ email_address: client.email_address, is_switching: client.is_switching, - logoutClient: client.logout, 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/appstore/src/components/main-title-bar/account-type-dropdown.scss b/packages/appstore/src/components/main-title-bar/account-type-dropdown.scss index 0f7c8674fe78..8b413ba86bc8 100644 --- a/packages/appstore/src/components/main-title-bar/account-type-dropdown.scss +++ b/packages/appstore/src/components/main-title-bar/account-type-dropdown.scss @@ -11,8 +11,8 @@ border: none; & > div.dc-dropdown { &-container { - min-width: 8.2rem; - width: 8.2rem; + min-width: 7.1rem; + width: auto; } } @@ -32,14 +32,17 @@ line-height: 20px; letter-spacing: 0em; text-align: left; - padding-left: 0.6em; + padding-left: 0.4em; + } + & > .dc-dropdown__display-text { + padding-right: 2.3em; } } &--real, &--real:focus, &--real:hover { border: 1px solid var(--status-info); - width: 87%; + min-width: 87%; & > .dc-text { color: var(--status-info); } @@ -52,7 +55,7 @@ width: 90%; & > .dc-text { color: var(--status-success); - padding-left: 0.8em; + padding-left: 0.65em; } } &__icon { @@ -62,11 +65,11 @@ } &--real { --fill-color1: var(--status-info) !important; - right: 1.8rem; + right: 0.7rem; } &--demo { --fill-color1: var(--status-success) !important; - right: 1.5rem; + right: 1.4rem; } } } diff --git a/packages/appstore/src/components/main-title-bar/account-type-dropdown.tsx b/packages/appstore/src/components/main-title-bar/account-type-dropdown.tsx index 72d875adf60e..d3e76abcf44a 100644 --- a/packages/appstore/src/components/main-title-bar/account-type-dropdown.tsx +++ b/packages/appstore/src/components/main-title-bar/account-type-dropdown.tsx @@ -2,14 +2,15 @@ import React from 'react'; import classNames from 'classnames'; import { observer } from 'mobx-react-lite'; import { Dropdown } from '@deriv/components'; -import { account_types } from 'Constants/platform-config'; +import { getAccountTypes } from 'Constants/platform-config'; import { useStores } from 'Stores'; import './account-type-dropdown.scss'; const AccountTypeDropdown = () => { - const { traders_hub, client } = useStores(); + const { traders_hub, client, common } = useStores(); const { selected_account_type, selectAccountType } = traders_hub; const { setPrevAccountType } = client; + const { current_language } = common; return (
@@ -20,7 +21,8 @@ const AccountTypeDropdown = () => { 'account-type-dropdown', `account-type-dropdown--${selected_account_type}` )} - list={account_types} + list={getAccountTypes()} + key={`account-type-dropdown__icon--key-${current_language}`} onChange={async (e: React.ChangeEvent) => { await selectAccountType(e.target.value); await setPrevAccountType(e.target.value); diff --git a/packages/appstore/src/constants/platform-config.ts b/packages/appstore/src/constants/platform-config.ts index 8c95c6b576d4..a7c628273162 100644 --- a/packages/appstore/src/constants/platform-config.ts +++ b/packages/appstore/src/constants/platform-config.ts @@ -5,9 +5,9 @@ import { TAccountCategory, TRegionAvailability } from 'Types'; export type AccountType = { text: 'Real' | 'Demo'; value: TAccountCategory }; export type RegionAvailability = 'Non-EU' | 'EU' | 'All'; -export const account_types: AccountType[] = [ - { text: 'Demo', value: 'demo' }, - { text: 'Real', value: 'real' }, +export const getAccountTypes = (): AccountType[] => [ + { text: localize('Demo'), value: 'demo' }, + { text: localize('Real'), value: 'real' }, ]; export const region_availability: RegionAvailability[] = ['Non-EU', 'EU']; diff --git a/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-content.spec.tsx b/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-content.spec.tsx new file mode 100644 index 000000000000..c57c5c0067e5 --- /dev/null +++ b/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-content.spec.tsx @@ -0,0 +1,311 @@ +import React from 'react'; +import RootStore from 'Stores/index'; +import { render, screen } from '@testing-library/react'; +import { Jurisdiction } from '@deriv/shared'; +import JurisdictionModalContent from '../jurisdiction-modal-content'; + +describe('JurisdictionModalContent', () => { + const mock_store = { + common: {}, + client: {}, + ui: {}, + }; + const mock_context = new RootStore(mock_store); + const mock_props = { + account_type: '', + is_virtual: false, + jurisdiction_selected_shortcode: '', + setJurisdictionSelectedShortcode: jest.fn(), + synthetic_available_accounts: [ + { + market_type: 'gaming' as const, + name: '', + requirements: { + after_first_deposit: { + financial_assessment: [''], + }, + compliance: { + mt5: [''], + tax_information: [''], + }, + signup: [''], + }, + shortcode: Jurisdiction.SVG, + sub_account_type: '', + }, + { + market_type: 'gaming' as const, + name: '', + requirements: { + after_first_deposit: { + financial_assessment: [''], + }, + compliance: { + mt5: [''], + tax_information: [''], + }, + signup: [''], + }, + shortcode: Jurisdiction.BVI, + sub_account_type: '', + }, + { + market_type: 'gaming' as const, + name: '', + requirements: { + after_first_deposit: { + financial_assessment: [''], + }, + compliance: { + mt5: [''], + tax_information: [''], + }, + signup: [''], + }, + shortcode: Jurisdiction.VANUATU, + sub_account_type: '', + }, + ], + financial_available_accounts: [ + { + market_type: 'financial' as const, + name: '', + requirements: { + after_first_deposit: { + financial_assessment: [''], + }, + compliance: { + mt5: [''], + tax_information: [''], + }, + signup: [''], + }, + shortcode: Jurisdiction.SVG, + sub_account_type: '', + }, + { + market_type: 'financial' as const, + name: '', + requirements: { + after_first_deposit: { + financial_assessment: [''], + }, + compliance: { + mt5: [''], + tax_information: [''], + }, + signup: [''], + }, + shortcode: Jurisdiction.BVI, + sub_account_type: '', + }, + { + market_type: 'financial' as const, + name: '', + requirements: { + after_first_deposit: { + financial_assessment: [''], + }, + compliance: { + mt5: [''], + tax_information: [''], + }, + signup: [''], + }, + shortcode: Jurisdiction.VANUATU, + sub_account_type: '', + }, + { + market_type: 'financial' as const, + name: '', + requirements: { + after_first_deposit: { + financial_assessment: [''], + }, + compliance: { + mt5: [''], + tax_information: [''], + }, + signup: [''], + }, + shortcode: Jurisdiction.LABUAN, + sub_account_type: '', + }, + ], + context: mock_context, + real_synthetic_accounts_existing_data: [], + real_financial_accounts_existing_data: [], + card_flip_status: { + svg: false, + bvi: false, + vanuatu: false, + labuan: false, + maltainvest: false, + }, + flipCard: jest.fn(), + }; + + it('should display cfd-jurisdiction-card--synthetic__wrapper in class name', () => { + render(); + const container = screen.getByTestId('dt-jurisdiction-modal-content'); + expect(container).toHaveClass('cfd-jurisdiction-card--synthetic__wrapper'); + }); + + it('should display 3 types of jurisdiction card for synthetics account', () => { + render(); + expect(screen.getByText('St. Vincent & Grenadines')).toBeInTheDocument(); + expect(screen.getByText('British Virgin Islands')).toBeInTheDocument(); + expect(screen.getByText('Vanuatu')).toBeInTheDocument(); + }); + + it('should display content of 3 types of jurisdiction correctly for synthetics account', () => { + render(); + expect(screen.getAllByText('Assets')).toHaveLength(3); + expect(screen.getAllByText('Synthetics, Basket indices and Derived FX')).toHaveLength(3); + expect(screen.getAllByText('40+')).toHaveLength(3); + expect(screen.getAllByText('Leverage')).toHaveLength(3); + expect(screen.getAllByText('1:1000')).toHaveLength(3); + expect(screen.getAllByText('Verifications')).toHaveLength(3); + expect( + screen.getByText('You will need to submit proof of identity and address once you reach certain thresholds.') + ).toBeInTheDocument(); + expect(screen.getAllByText('Learn more')).toHaveLength(2); + expect(screen.getAllByText('about verifications needed.')).toHaveLength(2); + expect(screen.getAllByText('Regulator/EDR')).toHaveLength(3); + expect(screen.getByText('Deriv (SVG) LLC (company no. 273 LLC 2020)')).toBeInTheDocument(); + expect( + screen.getByText('British Virgin Islands Financial Services Commission (License no. SIBA/L/18/1114)') + ).toBeInTheDocument(); + expect(screen.getByText('Vanuatu Financial Services Commission')).toBeInTheDocument(); + }); + + it('should display cfd-jurisdiction-card--financial__wrapper in class name', () => { + render(); + const container = screen.getByTestId('dt-jurisdiction-modal-content'); + expect(container).toHaveClass('cfd-jurisdiction-card--financial__wrapper'); + }); + + it('should display 4 types of jurisdiction card for financial account', () => { + render(); + expect(screen.getByText('St. Vincent & Grenadines')).toBeInTheDocument(); + expect(screen.getByText('British Virgin Islands')).toBeInTheDocument(); + expect(screen.getByText('Vanuatu')).toBeInTheDocument(); + expect(screen.getByText('Labuan')).toBeInTheDocument(); + }); + + it('should display content of 4 types of jurisdiction correctly for synthetics account', () => { + render(); + expect(screen.getAllByText('Assets')).toHaveLength(4); + expect(screen.getAllByText('Forex, Stocks, Stock indices, Commodities, and Cryptocurrencies')).toHaveLength(2); + expect(screen.getByText('Forex, Stock indices, Commodities and Cryptocurrencies')).toBeInTheDocument(); + expect(screen.getByText('Forex and Cryptocurrencies')).toBeInTheDocument(); + expect(screen.getAllByText('Leverage')).toHaveLength(4); + expect(screen.getAllByText('1:1000')).toHaveLength(3); + expect(screen.getByText('1:100')).toBeInTheDocument(); + expect(screen.getAllByText('Spreads from')).toHaveLength(4); + expect(screen.getAllByText('0.6 pips')).toHaveLength(2); + expect(screen.getAllByText('0.5 pips')).toHaveLength(2); + expect(screen.getAllByText('Verifications')).toHaveLength(4); + expect( + screen.getByText('You will need to submit proof of identity and address once you reach certain thresholds.') + ).toBeInTheDocument(); + expect(screen.getAllByText('Learn more')).toHaveLength(3); + expect(screen.getAllByText('about verifications needed.')).toHaveLength(3); + expect(screen.getAllByText('Regulator/EDR')).toHaveLength(4); + expect(screen.getByText('Deriv (SVG) LLC (company no. 273 LLC 2020)')).toBeInTheDocument(); + expect( + screen.getByText('British Virgin Islands Financial Services Commission (License no. SIBA/L/18/1114)') + ).toBeInTheDocument(); + expect(screen.getByText('Vanuatu Financial Services Commission')).toBeInTheDocument(); + expect(screen.getByText('Labuan Financial Services Authority (licence no. MB/18/0024)')).toBeInTheDocument(); + }); + + it('should display only financial maltainvest for MF clients', () => { + mock_props.financial_available_accounts = [ + { ...mock_props.financial_available_accounts[0], shortcode: Jurisdiction.MALTA_INVEST }, + ]; + mock_props.synthetic_available_accounts = []; + render(); + const container = screen.getByTestId('dt-jurisdiction-modal-content'); + expect(container).toHaveClass('cfd-jurisdiction-card--financial__wrapper'); + expect(screen.queryByText('St. Vincent & Grenadines')).not.toBeInTheDocument(); + expect(screen.queryByText('British Virgin Islands')).not.toBeInTheDocument(); + expect(screen.queryByText('Vanuatu')).not.toBeInTheDocument(); + expect(screen.queryByText('Labuan')).not.toBeInTheDocument(); + expect(screen.getByText('Malta')).toBeInTheDocument(); + expect(screen.getByText('Assets')).toBeInTheDocument(); + expect(screen.getByText('140+')).toBeInTheDocument(); + expect( + screen.getByText('Synthetics, Forex, Stocks, Stock indices, Commodities, and Cryptocurrencies') + ).toBeInTheDocument(); + expect(screen.getByText('Leverage')).toBeInTheDocument(); + expect(screen.getByText('1:30')).toBeInTheDocument(); + expect(screen.getByText('Spreads from')).toBeInTheDocument(); + expect(screen.getByText('0.5 pips')).toBeInTheDocument(); + expect(screen.getByText('Verifications')).toBeInTheDocument(); + expect(screen.getByText('Learn more')).toBeInTheDocument(); + expect(screen.getByText('about verifications needed.')).toBeInTheDocument(); + expect(screen.getByText('Regulator/EDR')).toBeInTheDocument(); + expect( + screen.getByText('Malta Financial Services Authority (MFSA) (licence no. IS/70156)') + ).toBeInTheDocument(); + }); + + it('should display only financial svg for highrisk clients', () => { + mock_props.financial_available_accounts = [ + { ...mock_props.financial_available_accounts[0], shortcode: Jurisdiction.SVG }, + ]; + mock_props.synthetic_available_accounts = [ + { ...mock_props.synthetic_available_accounts[0], shortcode: Jurisdiction.SVG }, + ]; + render(); + const container = screen.getByTestId('dt-jurisdiction-modal-content'); + expect(container).toHaveClass('cfd-jurisdiction-card--financial__wrapper'); + expect(screen.queryByText('British Virgin Islands')).not.toBeInTheDocument(); + expect(screen.queryByText('Vanuatu')).not.toBeInTheDocument(); + expect(screen.queryByText('Labuan')).not.toBeInTheDocument(); + expect(screen.queryByText('Malta')).not.toBeInTheDocument(); + expect(screen.getByText('St. Vincent & Grenadines')).toBeInTheDocument(); + expect(screen.getByText('Assets')).toBeInTheDocument(); + expect(screen.getByText('Leverage')).toBeInTheDocument(); + expect(screen.getByText('1:1000')).toBeInTheDocument(); + expect(screen.getByText('Verifications')).toBeInTheDocument(); + expect( + screen.getByText('You will need to submit proof of identity and address once you reach certain thresholds.') + ).toBeInTheDocument(); + expect(screen.getByText('Regulator/EDR')).toBeInTheDocument(); + expect(screen.getByText('Deriv (SVG) LLC (company no. 273 LLC 2020)')).toBeInTheDocument(); + expect(screen.getByText('170+')).toBeInTheDocument(); + expect(screen.getByText('Forex, Stocks, Stock indices, Commodities, and Cryptocurrencies')).toBeInTheDocument(); + expect(screen.getByText('Spreads from')).toBeInTheDocument(); + expect(screen.getByText('0.6 pips')).toBeInTheDocument(); + }); + + it('should display only synthetic svg for highrisk clients', () => { + mock_props.financial_available_accounts = [ + { ...mock_props.financial_available_accounts[0], shortcode: Jurisdiction.SVG }, + ]; + mock_props.synthetic_available_accounts = [ + { ...mock_props.synthetic_available_accounts[0], shortcode: Jurisdiction.SVG }, + ]; + render(); + const container = screen.getByTestId('dt-jurisdiction-modal-content'); + expect(container).toHaveClass('cfd-jurisdiction-card--synthetic__wrapper'); + expect(screen.queryByText('British Virgin Islands')).not.toBeInTheDocument(); + expect(screen.queryByText('Vanuatu')).not.toBeInTheDocument(); + expect(screen.queryByText('Labuan')).not.toBeInTheDocument(); + expect(screen.queryByText('Malta')).not.toBeInTheDocument(); + expect(screen.getByText('St. Vincent & Grenadines')).toBeInTheDocument(); + expect(screen.getByText('Assets')).toBeInTheDocument(); + expect(screen.getByText('Leverage')).toBeInTheDocument(); + expect(screen.getByText('1:1000')).toBeInTheDocument(); + expect(screen.getByText('Verifications')).toBeInTheDocument(); + expect( + screen.getByText('You will need to submit proof of identity and address once you reach certain thresholds.') + ).toBeInTheDocument(); + expect(screen.getByText('Regulator/EDR')).toBeInTheDocument(); + expect(screen.getByText('Deriv (SVG) LLC (company no. 273 LLC 2020)')).toBeInTheDocument(); + expect(screen.getByText('40+')).toBeInTheDocument(); + expect(screen.getByText('Synthetics, Basket indices and Derived FX')).toBeInTheDocument(); + }); +}); diff --git a/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-foot-note.spec.tsx b/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-foot-note.spec.tsx index d8b32540ebf9..ff4227723950 100644 --- a/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-foot-note.spec.tsx +++ b/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-foot-note.spec.tsx @@ -28,7 +28,7 @@ describe('JurisdictionModalFootNote', () => { account_type: '', context: mock_context, card_classname: '', - jurisdiction_selected_shortcode: '', + jurisdiction_selected_shortcode: Jurisdiction.SVG, should_restrict_bvi_account_creation: false, should_restrict_vanuatu_account_creation: false, }; @@ -205,4 +205,9 @@ describe('JurisdictionModalFootNote', () => { ) ).toBeInTheDocument(); }); + + it('should not render JurisdictionModalFootNote when jurisdiction_shortcode is empty', () => { + render(); + expect(screen.queryByTestId('dt-jurisdiction-footnote')).not.toBeInTheDocument(); + }); }); diff --git a/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal-content.tsx b/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal-content.tsx index c96a4a89cff8..bf855a2acd8b 100644 --- a/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal-content.tsx +++ b/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal-content.tsx @@ -40,7 +40,7 @@ const JurisdictionModalContent = ({ ]; return ( -
+
{jurisdiction_cards_array.map( card => cardsToBeShown(card) && ( diff --git a/packages/core/src/App/Containers/app-notification-messages.jsx b/packages/core/src/App/Containers/app-notification-messages.jsx index 7a2eac069332..81d370a3e3a7 100644 --- a/packages/core/src/App/Containers/app-notification-messages.jsx +++ b/packages/core/src/App/Containers/app-notification-messages.jsx @@ -135,6 +135,7 @@ const AppNotificationMessages = ({ 'poa_address_mismatch_failure', 'svg_needs_poi_poa', 'svg_needs_poa', + 'has_changed_two_fa', 'svg_needs_poi', 'svg_poi_expired', 'switched_to_real', diff --git a/packages/core/src/Stores/Helpers/client-notifications.js b/packages/core/src/Stores/Helpers/client-notifications.js index 5fa45f9c591b..870a7d3b25d6 100644 --- a/packages/core/src/Stores/Helpers/client-notifications.js +++ b/packages/core/src/Stores/Helpers/client-notifications.js @@ -62,7 +62,7 @@ export const getCashierValidations = cashier_arr => { }; export const excluded_notifications = isMobile() - ? ['contract_sold', 'switched_to_real'] + ? ['contract_sold', 'switched_to_real', 'has_changed_two_fa'] : [ 'you_are_offline', 'password_changed', @@ -76,6 +76,7 @@ export const excluded_notifications = isMobile() 'svg_needs_poi', 'svg_poi_expired', 'switched_to_real', + 'has_changed_two_fa', ]; export const priority_toast_messages = ['svg', 'need_fa', 'p2p_daily_limit_increase']; diff --git a/packages/core/src/Stores/client-store.js b/packages/core/src/Stores/client-store.js index ef6bf7c41fef..178284dad34c 100644 --- a/packages/core/src/Stores/client-store.js +++ b/packages/core/src/Stores/client-store.js @@ -77,6 +77,7 @@ export default class ClientStore extends BaseStore { is_landing_company_loaded = false; is_account_setting_loaded = false; has_enabled_two_fa = false; + has_changed_two_fa = false; // this will store the landing_company API response, including // financial_company: {} // gaming_company: {} @@ -186,6 +187,7 @@ export default class ClientStore extends BaseStore { is_landing_company_loaded: observable, is_account_setting_loaded: observable, has_enabled_two_fa: observable, + has_changed_two_fa: observable, landing_companies: observable, standpoint: observable, upgradeable_landing_companies: observable, @@ -383,6 +385,7 @@ export default class ClientStore extends BaseStore { fetchFinancialAssessment: action.bound, setFinancialAndTradingAssessment: action.bound, setTwoFAStatus: action.bound, + setTwoFAChangedStatus: action.bound, is_eu_or_multipliers_only: computed, getTwoFAStatus: action.bound, updateMT5Status: action.bound, @@ -2612,6 +2615,10 @@ export default class ClientStore extends BaseStore { }); } + setTwoFAChangedStatus(status) { + this.has_changed_two_fa = status; + } + async updateMT5Status() { this.updateAccountStatus(); await WS.authorized.mt5LoginList().then(this.root_store.client.responseMt5LoginList); diff --git a/packages/core/src/Stores/notification-store.js b/packages/core/src/Stores/notification-store.js index bf2c81c9dac8..375ed4dba7d0 100644 --- a/packages/core/src/Stores/notification-store.js +++ b/packages/core/src/Stores/notification-store.js @@ -100,6 +100,7 @@ export default class NotificationStore extends BaseStore { root_store.common?.selected_contract_type, root_store.client.is_eu, root_store.client.has_enabled_two_fa, + root_store.client.has_changed_two_fa, this.p2p_order_props.order_id, root_store.client.p2p_advertiser_info, ], @@ -289,6 +290,7 @@ export default class NotificationStore extends BaseStore { obj_total_balance, website_status, has_enabled_two_fa, + has_changed_two_fa, is_poi_dob_mismatch, is_financial_information_incomplete, has_restricted_mt5_account, @@ -374,6 +376,10 @@ export default class NotificationStore extends BaseStore { this.addNotificationMessage(this.client_notifications.acuity_mt5_download); } + if (has_changed_two_fa) { + this.addNotificationMessage(this.client_notifications.has_changed_two_fa); + } + const client = accounts[loginid]; if (client && !client.is_virtual) { if (isEmptyObject(account_status)) return; @@ -708,6 +714,8 @@ export default class NotificationStore extends BaseStore { setClientNotifications(client_data = {}) { const { ui } = this.root_store; + const { has_enabled_two_fa, setTwoFAChangedStatus } = this.root_store.client; + const two_fa_status = has_enabled_two_fa ? localize('enabled') : localize('disabled'); const mx_mlt_custom_header = this.custom_notifications.mx_mlt_notification.header(); const mx_mlt_custom_content = this.custom_notifications.mx_mlt_notification.main(); @@ -1288,6 +1296,22 @@ export default class NotificationStore extends BaseStore { ), type: 'warning', }, + has_changed_two_fa: { + key: 'has_changed_two_fa', + header: localize('Logging out on other devices'), + message: ( + + ), + type: 'info', + delay: 4000, + is_auto_close: true, + closeOnClick: () => { + setTwoFAChangedStatus(false); + }, + }, two_f_a: { key: 'two_f_a', header: localize('Stronger security for your Deriv account'), diff --git a/packages/stores/src/mockStore.ts b/packages/stores/src/mockStore.ts index 772848070c61..e2aa6981e1e6 100644 --- a/packages/stores/src/mockStore.ts +++ b/packages/stores/src/mockStore.ts @@ -190,6 +190,10 @@ const mock = (): TStores => { switch_broadcast: false, switchEndSignal: jest.fn(), is_crypto: false, + has_enabled_two_fa: false, + setTwoFAStatus: jest.fn(), + has_changed_two_fa: false, + setTwoFAChangedStatus: jest.fn(), }, common: { error: { diff --git a/packages/stores/types.ts b/packages/stores/types.ts index a760e6d0859b..04b8cd820714 100644 --- a/packages/stores/types.ts +++ b/packages/stores/types.ts @@ -180,6 +180,10 @@ type TClientStore = { logout: () => Promise; should_allow_authentication: boolean; is_crypto: boolean; + has_enabled_two_fa: boolean; + setTwoFAStatus: (status: boolean) => void; + has_changed_two_fa: boolean; + setTwoFAChangedStatus: (status: boolean) => void; }; type TCommonStoreError = {