From 742616c3159076746bd4a7f31878216958b72125 Mon Sep 17 00:00:00 2001 From: Carol Sachdeva <58209918+carol-deriv@users.noreply.github.com> Date: Mon, 20 Feb 2023 15:10:20 +0800 Subject: [PATCH 01/25] Revert "build: Update @deriv/deriv-charts to 1.1.2 (#7637)" (#7660) This reverts commit dbe55f5d1673771739e6db68515695cd6ab3c1d2. --- package-lock.json | 25 +++++++------------------ packages/bot-web-ui/package.json | 2 +- packages/core/package.json | 2 +- packages/trader/package.json | 2 +- 4 files changed, 10 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 32bc7bf227b3..d887953bb164 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@contentpass/zxcvbn": "^4.4.3", "@deriv/api-types": "^1.0.54", "@deriv/deriv-api": "^1.0.8", - "@deriv/deriv-charts": "1.1.2", + "@deriv/deriv-charts": "1.1.1", "@deriv/js-interpreter": "^3.0.0", "@deriv/ui": "^0.0.15", "@enykeev/react-virtualized": "^9.22.4-mirror.1", @@ -31,7 +31,6 @@ "@types/classnames": "^2.2.11", "@types/js-cookie": "^3.0.1", "@types/jsdom": "^20.0.0", - "@types/loadjs": "^4.0.1", "@types/lodash.merge": "^4.6.7", "@types/lodash.throttle": "^4.1.7", "@types/object.fromentries": "^2.0.0", @@ -2810,9 +2809,9 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@deriv/deriv-charts": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@deriv/deriv-charts/-/deriv-charts-1.1.2.tgz", - "integrity": "sha512-w0h/pUcFVtvanKT2EObsaRgR7WPVMoHpbUbpwu9ec41G6NSKW1mmpo+M4SVts/ROirZKvNEwKVzp9yXKQFmSsw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@deriv/deriv-charts/-/deriv-charts-1.1.1.tgz", + "integrity": "sha512-szcJ1ITn/rkJJICAqe/4BFp6PQqynn70hfvZDz8UShXFbVas/HVh37rQKjVVdx6Ocpd56D+To8f6ITgMrsuCcA==", "dependencies": { "@welldone-software/why-did-you-render": "^3.3.8", "classnames": "^2.3.1", @@ -15299,11 +15298,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/loadjs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/loadjs/-/loadjs-4.0.1.tgz", - "integrity": "sha512-UEMOleWwITwDvj+kI6T4etC9yMjmejH6UdJRAa1MWZwzDIW+Iz7T6z6Zc96N/5FeFsxAEBA/zP1ki+HluXPcHQ==" - }, "node_modules/@types/lodash": { "version": "4.14.191", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", @@ -49081,9 +49075,9 @@ } }, "@deriv/deriv-charts": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@deriv/deriv-charts/-/deriv-charts-1.1.2.tgz", - "integrity": "sha512-w0h/pUcFVtvanKT2EObsaRgR7WPVMoHpbUbpwu9ec41G6NSKW1mmpo+M4SVts/ROirZKvNEwKVzp9yXKQFmSsw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@deriv/deriv-charts/-/deriv-charts-1.1.1.tgz", + "integrity": "sha512-szcJ1ITn/rkJJICAqe/4BFp6PQqynn70hfvZDz8UShXFbVas/HVh37rQKjVVdx6Ocpd56D+To8f6ITgMrsuCcA==", "requires": { "@welldone-software/why-did-you-render": "^3.3.8", "classnames": "^2.3.1", @@ -58425,11 +58419,6 @@ "version": "0.0.29", "dev": true }, - "@types/loadjs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/loadjs/-/loadjs-4.0.1.tgz", - "integrity": "sha512-UEMOleWwITwDvj+kI6T4etC9yMjmejH6UdJRAa1MWZwzDIW+Iz7T6z6Zc96N/5FeFsxAEBA/zP1ki+HluXPcHQ==" - }, "@types/lodash": { "version": "4.14.191", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", diff --git a/packages/bot-web-ui/package.json b/packages/bot-web-ui/package.json index bd2d184fe886..e3f865c50e11 100644 --- a/packages/bot-web-ui/package.json +++ b/packages/bot-web-ui/package.json @@ -66,7 +66,7 @@ "dependencies": { "@deriv/bot-skeleton": "^1.0.0", "@deriv/components": "^1.0.0", - "@deriv/deriv-charts": "1.1.2", + "@deriv/deriv-charts": "1.1.1", "@deriv/shared": "^1.0.0", "@deriv/translations": "^1.0.0", "classnames": "^2.2.6", diff --git a/packages/core/package.json b/packages/core/package.json index 13cad2398149..2f721930eb78 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -95,7 +95,7 @@ "@deriv/cfd": "^1.0.0", "@deriv/components": "^1.0.0", "@deriv/deriv-api": "^1.0.8", - "@deriv/deriv-charts": "1.1.2", + "@deriv/deriv-charts": "1.1.1", "@deriv/hooks": "^1.0.0", "@deriv/p2p": "^0.7.3", "@deriv/reports": "^1.0.0", diff --git a/packages/trader/package.json b/packages/trader/package.json index ca4c7963b335..4f0100779738 100644 --- a/packages/trader/package.json +++ b/packages/trader/package.json @@ -82,7 +82,7 @@ "@deriv/api-types": "^1.0.54", "@deriv/components": "^1.0.0", "@deriv/deriv-api": "^1.0.8", - "@deriv/deriv-charts": "1.1.2", + "@deriv/deriv-charts": "1.1.1", "@deriv/reports": "^1.0.0", "@deriv/shared": "^1.0.0", "@deriv/translations": "^1.0.0", From 8a4f30a6c268bdf479b6f278310c10f34d0fdde7 Mon Sep 17 00:00:00 2001 From: Aizad Ridzo <103104395+aizad-deriv@users.noreply.github.com> Date: Mon, 20 Feb 2023 15:53:58 +0800 Subject: [PATCH 02/25] chore: add characters for regex in address line 1 and 2 in account signup (#7568) --- packages/shared/src/utils/validation/regex-validation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared/src/utils/validation/regex-validation.ts b/packages/shared/src/utils/validation/regex-validation.ts index 837d1cc4e136..739585be8a1e 100644 --- a/packages/shared/src/utils/validation/regex-validation.ts +++ b/packages/shared/src/utils/validation/regex-validation.ts @@ -1,8 +1,8 @@ export const regex_checks = { address_details: { address_city: /^\p{L}[\p{L}\s'.-]{0,99}$/u, - address_line_1: /^([a-zA-Z0-9'.,:;()@#/-]+\s)*[a-zA-Z0-9'.,:;()@#/-]{1,70}$/, - address_line_2: /^([a-zA-Z0-9'.,:;()@#/-]+\s)*[a-zA-Z0-9'.,:;()@#/-]{0,70}$/, + address_line_1: /^([a-zA-Z0-9’'.,:;()@#/-]+\s)*[a-zA-Z0-9’'.,:;()@#/-]{1,70}$/, + address_line_2: /^([a-zA-Z0-9’'.,:;()@#/-]+\s)*[a-zA-Z0-9’'.,:;()@#/-]{0,70}$/, address_postcode: /^[a-zA-Z0-9\s-]{0,20}$/, address_state: /^[\w\s\W'.;,-]{0,99}$/, non_jersey_postcode: /^(?!\s*je.*)[a-zA-Z0-9\s-]*/i, From 0f87af7a04e34d6bcb1f76d065249888cd92542a Mon Sep 17 00:00:00 2001 From: Shaheer <122449658+shaheer-deriv@users.noreply.github.com> Date: Tue, 21 Feb 2023 07:25:28 +0400 Subject: [PATCH 03/25] Refactor shaheer/87591/app modals (#7626) * refactor: :art: minimize if checks, refactored individual if checks to if-else-if checks * refactor: :art: Refactored and seperated modal --- .../src/App/Containers/Modals/app-modals.jsx | 69 +++++++------------ .../Modals/risk-accept-test-warning-modal.jsx | 59 ++++++++++++++++ .../trading-assessment-existing-user.jsx | 53 ++------------ 3 files changed, 90 insertions(+), 91 deletions(-) create mode 100644 packages/core/src/App/Containers/Modals/risk-accept-test-warning-modal.jsx diff --git a/packages/core/src/App/Containers/Modals/app-modals.jsx b/packages/core/src/App/Containers/Modals/app-modals.jsx index 261165b48831..85283ed70d6a 100644 --- a/packages/core/src/App/Containers/Modals/app-modals.jsx +++ b/packages/core/src/App/Containers/Modals/app-modals.jsx @@ -9,6 +9,7 @@ import TradingAssessmentExistingUser from './trading-assessment-existing-user.js import CompletedAssessmentModal from './completed-assessment-modal.jsx'; import DerivRealAccountRequiredModal from 'App/Components/Elements/Modals/deriv-real-account-required-modal.jsx'; import ExitTradersHubModal from './exit-traders-hub-modal'; +import RiskAcceptTestWarningModal from './risk-accept-test-warning-modal'; const AccountSignupModal = React.lazy(() => moduleLoader(() => import(/* webpackChunkName: "account-signup-modal" */ '../AccountSignupModal')) @@ -65,12 +66,13 @@ const AppModals = ({ is_trading_assessment_for_new_user_enabled, fetchFinancialAssessment, setCFDScore, - cfd_score, content_flag, active_account_landing_company, is_deriv_account_needed_modal_visible, is_warning_scam_message_modal_visible, is_exit_traders_hub_modal_visible, + is_trading_experience_incomplete, + should_show_risk_accept_modal, }) => { const url_params = new URLSearchParams(useLocation().search); const url_action_param = url_params.get('action'); @@ -110,59 +112,39 @@ const AppModals = ({ break; } - if (is_acuity_modal_open) { - ComponentToLoad = ; - } - - if (is_close_mx_mlt_account_modal_visible) { - ComponentToLoad = ; - } - - if (is_close_uk_account_modal_visible) { - ComponentToLoad = ; - } - - if (is_warning_scam_message_modal_visible) { - ComponentToLoad = ; - } - - if (is_welcome_modal_visible) { - ComponentToLoad = ; - } - - if (is_account_needed_modal_on) { - ComponentToLoad = ; - } - - if (is_reality_check_visible) { - ComponentToLoad = ; - } - if ( is_logged_in && active_account_landing_company === 'maltainvest' && !is_trading_assessment_for_new_user_enabled && - cfd_score === 0 && + is_trading_experience_incomplete && content_flag !== ContentFlag.LOW_RISK_CR_EU && content_flag !== ContentFlag.LOW_RISK_CR_NON_EU ) { ComponentToLoad = ; - } - - if (should_show_cooldown_modal) { + } else if (is_acuity_modal_open) { + ComponentToLoad = ; + } else if (is_close_mx_mlt_account_modal_visible) { + ComponentToLoad = ; + } else if (is_close_uk_account_modal_visible) { + ComponentToLoad = ; + } else if (is_warning_scam_message_modal_visible) { + ComponentToLoad = ; + } else if (is_welcome_modal_visible) { + ComponentToLoad = ; + } else if (is_account_needed_modal_on) { + ComponentToLoad = ; + } else if (is_reality_check_visible) { + ComponentToLoad = ; + } else if (should_show_cooldown_modal) { ComponentToLoad = ; - } - - if (should_show_assessment_complete_modal) { + } else if (should_show_assessment_complete_modal) { ComponentToLoad = ; - } - - if (is_deriv_account_needed_modal_visible) { + } else if (is_deriv_account_needed_modal_visible) { ComponentToLoad = ; - } - - if (is_exit_traders_hub_modal_visible) { + } else if (is_exit_traders_hub_modal_visible) { ComponentToLoad = ; + } else if (should_show_risk_accept_modal) { + ComponentToLoad = ; } return ( @@ -186,7 +168,6 @@ export default connect(({ client, ui, traders_hub }) => ({ has_maltainvest_account: client.has_maltainvest_account, fetchFinancialAssessment: client.fetchFinancialAssessment, setCFDScore: client.setCFDScore, - cfd_score: client.cfd_score, setShouldShowVerifiedAccount: ui.setShouldShowVerifiedAccount, should_show_cooldown_modal: ui.should_show_cooldown_modal, should_show_assessment_complete_modal: ui.should_show_assessment_complete_modal, @@ -196,4 +177,6 @@ export default connect(({ client, ui, traders_hub }) => ({ is_warning_scam_message_modal_visible: ui.is_warning_scam_message_modal_visible, is_exit_traders_hub_modal_visible: ui.is_exit_traders_hub_modal_visible, content_flag: traders_hub.content_flag, + is_trading_experience_incomplete: client.is_trading_experience_incomplete, + should_show_risk_accept_modal: ui.should_show_risk_accept_modal, }))(AppModals); diff --git a/packages/core/src/App/Containers/Modals/risk-accept-test-warning-modal.jsx b/packages/core/src/App/Containers/Modals/risk-accept-test-warning-modal.jsx new file mode 100644 index 000000000000..6a8e7e9401d0 --- /dev/null +++ b/packages/core/src/App/Containers/Modals/risk-accept-test-warning-modal.jsx @@ -0,0 +1,59 @@ +import { TestWarningModal } from '@deriv/account'; +import { Button, Text } from '@deriv/components'; +import { Localize, localize } from '@deriv/translations'; + +import { routes } from '@deriv/shared'; +import React from 'react'; +import { connect } from 'Stores/connect'; + +const RiskAcceptTestWarningModal = ({ + should_show_risk_accept_modal, + setShouldShowWarningModal, + setShouldShowAssessmentCompleteModal, + setIsTradingAssessmentForNewUserEnabled, +}) => { + const handleAcceptAppropriatenessTestWarning = () => { + setShouldShowWarningModal(false); + if (window.location.href.includes(routes.trading_assessment)) { + setShouldShowAssessmentCompleteModal(false); + } else { + setShouldShowAssessmentCompleteModal(true); + setIsTradingAssessmentForNewUserEnabled(true); + } + }; + + return ( + + ]} + /> + ]} + /> + + + } + footer_content={ + )} - - - - - - {my_ads_store.activate_deactivate_error_message} - - - - - - - - ); -}; - -CancelAddPaymentMethodModal.propTypes = { - is_floating: PropTypes.bool, -}; - -export default observer(CancelAddPaymentMethodModal); diff --git a/packages/p2p/src/components/my-profile/payment-methods/add-payment-method/select-payment-method.jsx b/packages/p2p/src/components/my-profile/payment-methods/add-payment-method/select-payment-method.jsx index 37acec080d83..f7ea14e4a03c 100644 --- a/packages/p2p/src/components/my-profile/payment-methods/add-payment-method/select-payment-method.jsx +++ b/packages/p2p/src/components/my-profile/payment-methods/add-payment-method/select-payment-method.jsx @@ -31,7 +31,7 @@ const SelectPaymentMethod = () => { label={localize('Payment method')} list_items={my_profile_store.payment_methods_list_items} onItemSelection={({ value }) => { - my_profile_store.setSelectedPaymentMethod(value); + setTimeout(() => my_profile_store.setSelectedPaymentMethod(value), 0); }} required trailing_icon={} diff --git a/packages/p2p/src/components/my-profile/payment-methods/payment-methods-list/edit-payment-method-form.jsx b/packages/p2p/src/components/my-profile/payment-methods/payment-methods-list/edit-payment-method-form.jsx index eff872701498..d0abeaa3dce9 100644 --- a/packages/p2p/src/components/my-profile/payment-methods/payment-methods-list/edit-payment-method-form.jsx +++ b/packages/p2p/src/components/my-profile/payment-methods/payment-methods-list/edit-payment-method-form.jsx @@ -1,17 +1,18 @@ -import PropTypes from 'prop-types'; import classNames from 'classnames'; import React from 'react'; import { observer } from 'mobx-react-lite'; -import { Field, Form, Formik } from 'formik'; -import { Button, DesktopWrapper, Input, Loading, Modal, Text } from '@deriv/components'; +import { Field, Form } from 'formik'; +import { Button, DesktopWrapper, Input, Loading, Text } from '@deriv/components'; import { isDesktop, isMobile } from '@deriv/shared'; import { Localize, localize } from 'Components/i18next'; import { useStores } from 'Stores'; -import CancelEditPaymentMethodModal from './cancel-edit-payment-method-modal.jsx'; import PageReturn from 'Components/page-return/page-return.jsx'; +import { useModalManagerContext } from 'Components/modal-manager/modal-manager-context'; +import ModalForm from 'Components/modal-manager/modal-form'; -const EditPaymentMethodForm = ({ formik_ref }) => { +const EditPaymentMethodForm = () => { const { general_store, my_profile_store } = useStores(); + const { showModal } = useModalManagerContext(); React.useEffect(() => { return () => { @@ -27,10 +28,8 @@ const EditPaymentMethodForm = ({ formik_ref }) => { return ( - - { { if (dirty) { - my_profile_store.setIsCancelEditPaymentMethodModalOpen(true); + showModal({ + key: 'CancelEditPaymentMethodModal', + }); } else { my_profile_store.setShouldShowEditPaymentMethodForm(false); } @@ -114,7 +115,9 @@ const EditPaymentMethodForm = ({ formik_ref }) => { large onClick={() => { if (dirty) { - my_profile_store.setIsCancelEditPaymentMethodModalOpen(true); + showModal({ + key: 'CancelEditPaymentMethodModal', + }); } else { my_profile_store.setPaymentMethodToEdit(null); my_profile_store.setShouldShowEditPaymentMethodForm(false); @@ -137,34 +140,9 @@ const EditPaymentMethodForm = ({ formik_ref }) => { ); }} - - - - - {my_profile_store.add_payment_method_error_message} - - - - - ); } diff --git a/packages/p2p/src/components/order-details/order-details.jsx b/packages/p2p/src/components/order-details/order-details.jsx index 1f0f729f825f..2da26a6aa716 100644 --- a/packages/p2p/src/components/order-details/order-details.jsx +++ b/packages/p2p/src/components/order-details/order-details.jsx @@ -5,8 +5,6 @@ import { formatMoney, isDesktop, isMobile } from '@deriv/shared'; import { observer } from 'mobx-react-lite'; import { Localize, localize } from 'Components/i18next'; import Chat from 'Components/orders/chat/chat.jsx'; -import EmailVerificationModal from 'Components/email-verification-modal'; -import RatingModal from 'Components/rating-modal'; import StarRating from 'Components/star-rating'; import UserRatingButton from 'Components/user-rating-button'; import OrderDetailsFooter from 'Components/order-details/order-details-footer.jsx'; @@ -19,15 +17,14 @@ import PaymentMethodAccordionHeader from './payment-method-accordion-header.jsx' import PaymentMethodAccordionContent from './payment-method-accordion-content.jsx'; import MyProfileSeparatorContainer from '../my-profile/my-profile-separator-container'; import { setDecimalPlaces, removeTrailingZeros, roundOffDecimal } from 'Utils/format-value'; -import LoadingModal from '../loading-modal'; -import InvalidVerificationLinkModal from '../invalid-verification-link-modal'; import EmailLinkBlockedModal from '../email-link-blocked-modal'; -import EmailLinkVerifiedModal from '../email-link-verified-modal'; import { getDateAfterHours } from 'Utils/date-time'; -import './order-details.scss'; +import { useModalManagerContext } from 'Components/modal-manager/modal-manager-context'; +import 'Components/order-details/order-details.scss'; const OrderDetails = observer(() => { const { general_store, my_profile_store, order_store, sendbird_store } = useStores(); + const { hideModal, showModal, useRegisterModalProps } = useModalManagerContext(); const { account_currency, @@ -72,6 +69,12 @@ const OrderDetails = observer(() => { const rating_average_decimal = review_details ? Number(review_details.rating).toFixed(1) : undefined; + const showRatingModal = () => { + showModal({ + key: 'RatingModal', + }); + }; + React.useEffect(() => { const disposeListeners = sendbird_store.registerEventListeners(); const disposeReactions = sendbird_store.registerMobXReactions(); @@ -97,7 +100,7 @@ const OrderDetails = observer(() => { general_store.props.setP2POrderProps({ order_id: order_store.order_id, redirectToOrderDetails: general_store.redirectToOrderDetails, - setIsRatingModalOpen: order_store.setIsRatingModalOpen, + setIsRatingModalOpen: is_open => (is_open ? showRatingModal : hideModal), }); }; }, []); // eslint-disable-line react-hooks/exhaustive-deps @@ -110,6 +113,28 @@ const OrderDetails = observer(() => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [completion_time]); + useRegisterModalProps({ + key: 'RatingModal', + props: { + is_buy_order_for_user, + is_user_recommended_previously, + onClickDone: () => { + order_store.setOrderRating(id); + general_store.props.removeNotificationMessage({ + key: `order-${id}`, + }); + general_store.props.removeNotificationByKey({ + key: `order-${id}`, + }); + }, + onClickSkip: () => { + order_store.setRatingValue(0); + hideModal(); + }, + previous_recommendation, + }, + }); + if (sendbird_store.should_show_chat_on_orders) { return ; } @@ -121,28 +146,6 @@ const OrderDetails = observer(() => { return ( - {is_active_order && ( - order_store.setIsRecommended(null)} - onClickDone={() => { - order_store.setOrderRating(id); - general_store.props.removeNotificationMessage({ key: `order-${id}` }); - general_store.props.removeNotificationByKey({ key: `order-${id}` }); - }} - onClickNotRecommended={() => order_store.setIsRecommended(0)} - onClickRecommended={() => order_store.setIsRecommended(1)} - onClickSkip={() => { - order_store.setRatingValue(0); - order_store.setIsRatingModalOpen(false); - }} - onClickStar={order_store.handleRating} - previous_recommendation={previous_recommendation} - rating_value={order_store.rating_value} - /> - )} {should_show_lost_funds_banner && (
{ )} {!is_buy_order_for_user && ( - order_store.confirmOrderRequest(id)} - setIsEmailVerificationModalOpen={order_store.setIsEmailVerificationModalOpen} - /> - order_store.confirmOrder(is_buy_order_for_user)} - setIsEmailLinkVerifiedModalOpen={order_store.setIsEmailLinkVerifiedModalOpen} - /> - order_store.confirmOrderRequest(id)} - /> - )}
@@ -319,26 +302,6 @@ const OrderDetails = observer(() => { )} {is_completed_order && !review_details && ( - order_store.setIsRecommended(null)} - onClickDone={() => { - order_store.setOrderRating(id); - general_store.props.removeNotificationMessage({ key: `order-${id}` }); - general_store.props.removeNotificationByKey({ key: `order-${id}` }); - }} - onClickNotRecommended={() => order_store.setIsRecommended(0)} - onClickRecommended={() => order_store.setIsRecommended(1)} - onClickSkip={() => { - order_store.setRatingValue(0); - order_store.setIsRatingModalOpen(false); - }} - onClickStar={order_store.handleRating} - previous_recommendation={previous_recommendation} - rating_value={order_store.rating_value} - />
{ } is_disabled={!is_reviewable} large - onClick={() => order_store.setIsRatingModalOpen(true)} + onClick={showRatingModal} />
diff --git a/packages/p2p/src/components/orders/order-table/order-table-row.jsx b/packages/p2p/src/components/orders/order-table/order-table-row.jsx index 8a0a57784baf..fe076edad526 100644 --- a/packages/p2p/src/components/orders/order-table/order-table-row.jsx +++ b/packages/p2p/src/components/orders/order-table/order-table-row.jsx @@ -10,7 +10,7 @@ import { DesktopWrapper, Icon, MobileWrapper, Table, Text } from '@deriv/compone import { formatMoney } from '@deriv/shared'; import { localize } from 'Components/i18next'; import RatingCellRenderer from 'Components/rating-cell-renderer'; -import RatingModal from 'Components/rating-modal'; +import { useModalManagerContext } from 'Components/modal-manager/modal-manager-context'; const Title = ({ send_amount, currency, order_purchase_datetime, order_type }) => { return ( @@ -37,7 +37,7 @@ const OrderRow = ({ row: order }) => { const [order_state, setOrderState] = React.useState(order); // Use separate state to force refresh when (FE-)expired. const [is_timer_visible, setIsTimerVisible] = React.useState(); const should_show_order_details = React.useRef(true); - const [should_show_rating_modal, setShouldShowRatingModal] = React.useState(false); // Need a separate state to prevent re-render. DON'T REMOVE! + const { showModal, hideModal } = useModalManagerContext(); const { account_currency, @@ -81,6 +81,32 @@ const OrderRow = ({ row: order }) => { return () => {}; }; + const showRatingModal = () => { + showModal({ + key: 'RatingModal', + props: { + is_buy_order_for_user, + is_user_recommended_previously, + onClickDone: () => { + order_store.setOrderRating(id); + hideModal(); + should_show_order_details.current = true; + order_store.setRatingValue(0); + general_store.props.removeNotificationMessage({ key: `order-${id}` }); + general_store.props.removeNotificationByKey({ key: `order-${id}` }); + order_store.setIsLoading(true); + order_store.setOrders([]); + order_store.loadMoreOrders({ startIndex: 0 }); + }, + onClickSkip: () => { + order_store.setRatingValue(0); + hideModal(); + should_show_order_details.current = true; + }, + }, + }); + }; + React.useEffect(() => { const countDownTimer = () => { const { distance, label } = getTimeLeft(order_expiry_milliseconds); @@ -103,32 +129,6 @@ const OrderRow = ({ row: order }) => { return ( - order_store.setIsRecommended(null)} - onClickDone={() => { - order_store.setOrderRating(id); - setShouldShowRatingModal(false); - should_show_order_details.current = true; - order_store.setRatingValue(0); - general_store.props.removeNotificationMessage({ key: `order-${id}` }); - general_store.props.removeNotificationByKey({ key: `order-${id}` }); - order_store.setIsLoading(true); - order_store.setOrders([]); - order_store.loadMoreOrders({ startIndex: 0 }); - }} - onClickNotRecommended={() => order_store.setIsRecommended(0)} - onClickRecommended={() => order_store.setIsRecommended(1)} - onClickSkip={() => { - order_store.setRatingValue(0); - setShouldShowRatingModal(false); - should_show_order_details.current = true; - }} - onClickStar={order_store.handleRating} - rating_value={order_store.rating_value} - />
{ rating={rating} onClickUserRatingButton={() => { should_show_order_details.current = false; - setShouldShowRatingModal(true); + showRatingModal(); }} /> ) @@ -235,7 +235,7 @@ const OrderRow = ({ row: order }) => { rating={rating} onClickUserRatingButton={() => { should_show_order_details.current = false; - setShouldShowRatingModal(true); + showRatingModal(); }} /> )} diff --git a/packages/p2p/src/components/recommended-by/recommended-by.jsx b/packages/p2p/src/components/recommended-by/recommended-by.jsx index 6b5697068d7e..0a93c1f2aae8 100644 --- a/packages/p2p/src/components/recommended-by/recommended-by.jsx +++ b/packages/p2p/src/components/recommended-by/recommended-by.jsx @@ -3,10 +3,11 @@ import PropTypes from 'prop-types'; import { Icon, Popover, Text } from '@deriv/components'; import { isMobile } from '@deriv/shared'; import { localize } from 'Components/i18next'; -import RecommendedModal from './recommended-modal.jsx'; +import { useModalManagerContext } from 'Components/modal-manager/modal-manager-context'; const RecommendedBy = ({ recommended_average, recommended_count }) => { - const [is_recommended_modal_open, setIsRecommendedModalOpen] = React.useState(false); + const { showModal } = useModalManagerContext(); + const getRecommendedMessage = () => { if (recommended_count) { if (recommended_count === 1) { @@ -23,16 +24,21 @@ const RecommendedBy = ({ recommended_average, recommended_count }) => { return ( - setIsRecommendedModalOpen(true) : () => {}} + onClick={ + isMobile() + ? () => + showModal({ + key: 'RecommendedModal', + props: { + message: getRecommendedMessage(), + }, + }) + : () => {} + } > ( - - - - {message} - - - -
); - if ((is_eu_user && has_mf_mt5_account) || is_currency_switcher_disabled_for_mf) { - return null; - } else if (is_demo) { + if ((is_eu_user && has_mf_mt5_account) || is_demo) { return null; } return icon_dropdown; diff --git a/packages/appstore/src/components/currency-switcher-card/real/real-account-card.tsx b/packages/appstore/src/components/currency-switcher-card/real/real-account-card.tsx index 13c43b42a4a6..1618f61b4c4c 100644 --- a/packages/appstore/src/components/currency-switcher-card/real/real-account-card.tsx +++ b/packages/appstore/src/components/currency-switcher-card/real/real-account-card.tsx @@ -18,7 +18,7 @@ const RealAccountCard = () => { const { accounts, loginid } = client; const { current_list } = modules.cfd; - const { openModal, is_eu_user, is_currency_switcher_disabled_for_mf } = traders_hub; + const { openModal, is_eu_user } = traders_hub; const { balance, currency } = accounts[loginid] || default_balance; const has_mf_mt5_account = Object.keys(current_list) @@ -35,9 +35,7 @@ const RealAccountCard = () => { } icon={currency} onClick={() => { - if (is_currency_switcher_disabled_for_mf) { - return null; - } else if (!is_eu_user && !has_mf_mt5_account) { + if (!is_eu_user && !has_mf_mt5_account) { openModal('currency_selection'); } return openModal('currency_selection'); diff --git a/packages/appstore/src/components/currency-switcher-card/real/real-account-switcher.tsx b/packages/appstore/src/components/currency-switcher-card/real/real-account-switcher.tsx index 468b618f1bb2..761e8092c155 100644 --- a/packages/appstore/src/components/currency-switcher-card/real/real-account-switcher.tsx +++ b/packages/appstore/src/components/currency-switcher-card/real/real-account-switcher.tsx @@ -9,50 +9,44 @@ import './real-account-switcher.scss'; type AccountNeedsVerificationProps = { multipliers_account_status: string; - is_currency_switcher_disabled_for_mf: boolean; }; -const AccountNeedsVerification = observer( - ({ multipliers_account_status, is_currency_switcher_disabled_for_mf }: AccountNeedsVerificationProps) => { - const { client, traders_hub } = useStores(); - const { account_list, loginid } = client; +const AccountNeedsVerification = observer(({ multipliers_account_status }: AccountNeedsVerificationProps) => { + const { client, traders_hub } = useStores(); + const { account_list, loginid } = client; - const { openModal, openFailedVerificationModal } = traders_hub; + const { openModal, openFailedVerificationModal } = traders_hub; - const title = account_list.find((acc: { loginid: string }) => loginid === acc.loginid).title; - const icon = account_list.find((acc: { loginid: string }) => loginid === acc.loginid).icon; + const title = account_list.find((acc: { loginid: string }) => loginid === acc.loginid).title; + const icon = account_list.find((acc: { loginid: string }) => loginid === acc.loginid).icon; - return ( - - {title} -
- } - icon={icon} - onClick={() => { - if (is_currency_switcher_disabled_for_mf) { - return null; - } - return openModal('currency_selection'); - }} - > - - - ); - } -); + return ( + + {title} + + } + icon={icon} + onClick={() => { + return openModal('currency_selection'); + }} + > + + + ); +}); const RealAccountSwitcher = () => { const { client, traders_hub } = useStores(); const { is_logging_in, is_switching, has_maltainvest_account } = client; const { multipliers_account_status, - is_currency_switcher_disabled_for_mf, + is_eu_user, no_CR_account, no_MF_account, @@ -71,12 +65,7 @@ const RealAccountSwitcher = () => { } if (multipliers_account_status && is_eu_user) { - return ( - - ); + return ; } if (has_maltainvest_account && is_eu_user) { From d62b72a4b26dfce1a3c8cc42f448eec10d591ef5 Mon Sep 17 00:00:00 2001 From: thisyahlen <104053934+thisyahlen-deriv@users.noreply.github.com> Date: Wed, 22 Feb 2023 14:08:22 +0800 Subject: [PATCH 08/25] Thisyahlen/87555/switching to mf account upon login if no cr account for low risk (#7519) * fix: exit traders hub * fix: When login back to the account with has EU only the data for MF is showing * fix: exit from non-eu from traders hub, multipliers is still the active account --- .../Modals/exit-traders-hub-modal/exit-traders-hub-modal.jsx | 2 +- packages/core/src/Stores/client-store.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/core/src/App/Containers/Modals/exit-traders-hub-modal/exit-traders-hub-modal.jsx b/packages/core/src/App/Containers/Modals/exit-traders-hub-modal/exit-traders-hub-modal.jsx index df02512c6bb2..036583c7a742 100644 --- a/packages/core/src/App/Containers/Modals/exit-traders-hub-modal/exit-traders-hub-modal.jsx +++ b/packages/core/src/App/Containers/Modals/exit-traders-hub-modal/exit-traders-hub-modal.jsx @@ -48,7 +48,7 @@ const ExitTradersHubModal = ({ const cr_account = active_accounts.some(acc => acc.landing_company_shortcode === 'svg'); toggleExitTradersHubModal(); - if (content_flag === ContentFlag.LOW_RISK_CR_EU) { + if (content_flag === ContentFlag.LOW_RISK_CR_EU || content_flag === ContentFlag.LOW_RISK_CR_NON_EU) { if (!cr_account) { await switchAccount(account_list.find(acc => acc.loginid.startsWith('VRTC'))?.loginid); } diff --git a/packages/core/src/Stores/client-store.js b/packages/core/src/Stores/client-store.js index 12a4d03fe9e1..f49889c76abf 100644 --- a/packages/core/src/Stores/client-store.js +++ b/packages/core/src/Stores/client-store.js @@ -1616,6 +1616,11 @@ export default class ClientStore extends BaseStore { if (!this.is_virtual) { this.setPrevRealAccountLoginid(this.loginid); } + const no_cr_account = this.active_accounts.some(acc => acc.landing_company_shortcode === 'svg'); + + if (!no_cr_account && this.is_low_risk) { + this.switchAccount(this.virtual_account_loginid); + } } this.selectCurrency(''); From f758006fd15f7295e146b14e7ce6fe5110c3c611 Mon Sep 17 00:00:00 2001 From: Aizad Ridzo <103104395+aizad-deriv@users.noreply.github.com> Date: Wed, 22 Feb 2023 14:13:36 +0800 Subject: [PATCH 09/25] Aizad/87648/scroll-issue-onboarding-tradershub (#7615) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: change stylings add more padding below to how content in different screen size 📱 * fix: scroll issue on smaller screens * feat: testing sikit * fix: change onboarding sizing to fit mobile * fix: remove max-height on footer * fix: added more height to the body * fix: readjust body height (again😀) --- .../onboarding-new/static-dashboard.scss | 16 +++++++++++----- .../src/modules/onboarding/onboarding.scss | 1 - 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/appstore/src/components/onboarding-new/static-dashboard.scss b/packages/appstore/src/components/onboarding-new/static-dashboard.scss index d950e911b7d4..fcc0799ccf1d 100644 --- a/packages/appstore/src/components/onboarding-new/static-dashboard.scss +++ b/packages/appstore/src/components/onboarding-new/static-dashboard.scss @@ -5,6 +5,10 @@ @include desktop { min-width: 132rem; } + @include mobile { + position: relative; + bottom: 2rem; + } &--deposit-button-blurry { opacity: 0.2; @@ -44,11 +48,6 @@ border: 1px solid var(--general-hover); padding: 2rem; border-radius: 2.4rem; - - @include mobile { - padding: 3rem; - border: none; - } } &__bordered--with-margin { @@ -56,9 +55,16 @@ padding: 2.4rem; border-radius: 2.4rem; margin-bottom: 2rem; + } + &__bordered, + &__bordered--with-margin { @include mobile { + padding: 3rem 3rem 15rem; + height: 36.5rem; border: none; + overflow-y: scroll; + overflow-x: hidden; } } diff --git a/packages/appstore/src/modules/onboarding/onboarding.scss b/packages/appstore/src/modules/onboarding/onboarding.scss index d8205a670bb8..208d97f5b7e1 100644 --- a/packages/appstore/src/modules/onboarding/onboarding.scss +++ b/packages/appstore/src/modules/onboarding/onboarding.scss @@ -21,7 +21,6 @@ height: 100%; @include mobile { - overflow-y: scroll; height: 60%; } } From 310470a8af98d92b5271433eeb0ed202e0180edf Mon Sep 17 00:00:00 2001 From: Niloofar Sadeghi <93518187+niloofar-deriv@users.noreply.github.com> Date: Wed, 22 Feb 2023 16:49:50 +0330 Subject: [PATCH 10/25] fix: security-issues: Client-side URL redirect (#7600) --- packages/reports/src/Containers/open-positions.jsx | 4 +++- packages/reports/src/Containers/profit-table.jsx | 4 +++- packages/reports/src/Containers/statement.jsx | 4 +++- packages/trader/src/App/Containers/Modals/trade-modals.jsx | 3 ++- .../src/Modules/Contract/Containers/contract-replay.jsx | 3 ++- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/reports/src/Containers/open-positions.jsx b/packages/reports/src/Containers/open-positions.jsx index 048a2a8f5784..a203ff43c357 100644 --- a/packages/reports/src/Containers/open-positions.jsx +++ b/packages/reports/src/Containers/open-positions.jsx @@ -203,6 +203,8 @@ export const OpenPositionsTable = ({
); +const portfoliows_href = urlFor('user/portfoliows', { legacy: true }); + const getRowAction = row_obj => row_obj.is_unsupported ? { @@ -218,7 +220,7 @@ const getRowAction = row_obj => className='link link--orange' rel='noopener noreferrer' target='_blank' - href={urlFor('user/portfoliows', { legacy: true })} + href={portfoliows_href} />, ]} /> diff --git a/packages/reports/src/Containers/profit-table.jsx b/packages/reports/src/Containers/profit-table.jsx index e5368fbf6054..d55804c04a47 100644 --- a/packages/reports/src/Containers/profit-table.jsx +++ b/packages/reports/src/Containers/profit-table.jsx @@ -16,6 +16,8 @@ import PlaceholderComponent from '../Components/placeholder-component.jsx'; import { ReportsMeta } from '../Components/reports-meta.jsx'; import { getProfitTableColumnsTemplate } from 'Constants/data-table-constants'; +const profit_tablews_href = urlFor('user/profit_tablews', { legacy: true }); + const getRowAction = row_obj => getSupportedContracts()[extractInfoFromShortcode(row_obj.shortcode).category.toUpperCase()] && !isForwardStarting(row_obj.shortcode, row_obj.purchase_time_unix) @@ -33,7 +35,7 @@ const getRowAction = row_obj => className='link link--orange' rel='noopener noreferrer' target='_blank' - href={urlFor('user/profit_tablews', { legacy: true })} + href={profit_tablews_href} />, ]} /> diff --git a/packages/reports/src/Containers/statement.jsx b/packages/reports/src/Containers/statement.jsx index 71ab00441f6e..9a4daa24f9e6 100644 --- a/packages/reports/src/Containers/statement.jsx +++ b/packages/reports/src/Containers/statement.jsx @@ -52,6 +52,8 @@ const DetailsComponent = ({ message = '', action_type = '' }) => { ); }; +const statementws_href = urlFor('user/statementws', { legacy: true }); + const getRowAction = row_obj => { let action; if (row_obj.id && ['buy', 'sell'].includes(row_obj.action_type)) { @@ -72,7 +74,7 @@ const getRowAction = row_obj => { className='link link--orange' rel='noopener noreferrer' target='_blank' - href={urlFor('user/statementws', { legacy: true })} + href={statementws_href} />, ]} /> diff --git a/packages/trader/src/App/Containers/Modals/trade-modals.jsx b/packages/trader/src/App/Containers/Modals/trade-modals.jsx index 8de4fbdf5f41..494e95485b37 100644 --- a/packages/trader/src/App/Containers/Modals/trade-modals.jsx +++ b/packages/trader/src/App/Containers/Modals/trade-modals.jsx @@ -47,7 +47,8 @@ const TradeModals = ({ }; const unsupportedContractOnClose = () => { - window.open(urlFor('user/portfoliows', { legacy: true }), '_blank'); + const portfoliows_url = urlFor('user/portfoliows', { legacy: true }); + window.open(portfoliows_url, '_blank'); unsupportedContractOnConfirm(false); }; diff --git a/packages/trader/src/Modules/Contract/Containers/contract-replay.jsx b/packages/trader/src/Modules/Contract/Containers/contract-replay.jsx index 27d0dd047f00..587bed6d7d10 100644 --- a/packages/trader/src/Modules/Contract/Containers/contract-replay.jsx +++ b/packages/trader/src/Modules/Contract/Containers/contract-replay.jsx @@ -98,7 +98,8 @@ const ContractReplay = ({ }; const unsupportedContractOnClose = () => { - window.open(urlFor('user/statementws', { legacy: true }), '_blank'); + const statementws_url = urlFor('user/statementws', { legacy: true }); + window.open(statementws_url, '_blank'); }; return ( From dd6d4ee77b09bd38f04e46d6c848df87b0a92d56 Mon Sep 17 00:00:00 2001 From: Niloofar Sadeghi <93518187+niloofar-deriv@users.noreply.github.com> Date: Wed, 22 Feb 2023 16:51:15 +0330 Subject: [PATCH 11/25] fix: Incomplete URL substring sanitization (#7597) --- packages/shared/src/utils/url/url.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/src/utils/url/url.ts b/packages/shared/src/utils/url/url.ts index f7ef9304dbbb..8b762438935a 100644 --- a/packages/shared/src/utils/url/url.ts +++ b/packages/shared/src/utils/url/url.ts @@ -98,7 +98,7 @@ export const urlForCurrentDomain = (href: string) => { const url_object = new URL(href); if (Object.keys(host_map).includes(url_object.hostname)) { url_object.hostname = host_map[url_object.hostname as keyof typeof host_map]; - } else if (url_object.hostname.indexOf(default_domain) !== -1) { + } else if (url_object.hostname.match(default_domain)) { // to keep all non-Binary links unchanged, we use default domain for all Binary links in the codebase (javascript and templates) url_object.hostname = url_object.hostname.replace( new RegExp(`\\.${default_domain}`, 'i'), From e1f48efcc08fc0bb4baa5d4a127521a6f8c84c1c Mon Sep 17 00:00:00 2001 From: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> Date: Tue, 28 Feb 2023 22:22:11 +0800 Subject: [PATCH 12/25] Farzin/85955/Extract `replaceCashierMenuOnclick` method from cashier `GeneralStore` to reusable hook in `@deriv/hooks` (#7434) * Shayan/52349/react17 migration (#6908) * refactor: react version is upgraded to version 17 * fix: fixed typo * fix: changed declaration file location * fix: temporarily commented our two test cases that are failing * fix: fixed react-content-loader props * fix: fixed some bugs * fix: fixed z-index issue for popover in DBot page * fix: fixed popover position issue in DBot page * chore: an small change on how to turn string to array * fix: merge upstream develop into my branch and resolved conflicts * fix: resolved pr comments * fix: removed rc-drawer and refactored mobile drawer * fix: fixed test cases * fix: resolved pr comments * fix: resolved pr comment * fix: fixed typo * fix: resolved pr comments * fix: fixed slide-in component bug * fix: resolved pr comments * fix: resolved pr comments * fix: removed unnecessary lines * fix: resolved pr comments * Update packages/account/src/Components/personal-details/__tests__/personal-details.spec.js Co-authored-by: Niloofar Sadeghi <93518187+niloo-fs@users.noreply.github.com> * Update packages/account/src/Components/personal-details/__tests__/personal-details.spec.js Co-authored-by: Niloofar Sadeghi <93518187+niloo-fs@users.noreply.github.com> * Update packages/account/src/Components/personal-details/personal-details.jsx Co-authored-by: Niloofar Sadeghi <93518187+niloo-fs@users.noreply.github.com> * fix: fixed mt5 modal not appear on screen when clicking on trade button * fix: fixed Bug #84787 Co-authored-by: Shayan Khaleghparast <100833613+iman-fs@users.noreply.github.com> Co-authored-by: Niloofar Sadeghi <93518187+niloo-fs@users.noreply.github.com> * fix: add optional chaining in getMinDuration function (#7344) * fix: :bug: resolved issue with trade. odal (#7291) * Revert "fix: :bug: resolved issue with trade. odal (#7291)" (#7364) This reverts commit b6f7e4c91b463c0e5c9431e101d4e3734671e632. * feat(core): :sparkles: add `@deriv/hooks` and `@deriv/stores` to `@deriv/core` * refactor(core): :fire: remove `ToggleMenuDrawer` prop drilling and use `useStore` hook * refactor(core): :fire: remove dead props * refactor(core): :fire: remove unnecessary ref in `ToggleMenuDrawer` * refactor(core): :fire: remove `react-import-loader` from `@deriv/core` * refactor(core): :fire: remove `platform_switcher` prop from `ToggleMenuDrawer` * Farzin/85054/Call `resetWithrawForm` on `CryptoWithdrawForm` component `onunmount` (#7331) * fix(cashier): :bug: call `resetWithrawForm` on `CryptoWithdrawForm` component `onunmount` * test(cashier): :white_check_mark: fix failing test Co-authored-by: Farzin Mirzaie * Niloofar Sadeghi / Task - Refactor tests in the language.spec.js file (#7325) * refactor: language tests * fix: typo Co-authored-by: Niloofar Sadeghi * refactor: proposal tests (#7327) Co-authored-by: Niloofar Sadeghi * Niloofar Sadeghi / Task - Refactor tests in the error.spec.js file (#7324) * refactor: errors validator tests * fix: typo Co-authored-by: Niloofar Sadeghi * Niloofar Sadeghi / Task - Refactor tests in the binary-link.spec.tsx file (#7288) * refactor: binary-link tests * refactor: improve testids namings Co-authored-by: Niloofar Sadeghi * refactor: money tests (#7353) * refactor: toggle-positions tests (#7287) Co-authored-by: Niloofar Sadeghi * refactor: toggle-button tests (#7328) Co-authored-by: Niloofar Sadeghi * Niloofar Sadeghi / Task - Refactor tests in the toggle-button-group.spec.tsx file (#7330) * refactor: toggle-button-group tests * fix: test issue Co-authored-by: Niloofar Sadeghi * refactor: open-positions-table tests (#7374) * Niloofar Sadeghi / Task - Refactor tests in the marker-spot-label.spec.tsx file (#7355) * refactor: remove extra files from reports * refactor: marker-spot-label tests * Niloofar Sadeghi / Task - Refactor tests in the binary-link.spec.tsx file (#7286) * refactor: binary-link tests * test: added more tests Co-authored-by: Niloofar Sadeghi * Niloofar Sadeghi / Task - Refactor tests in the contract-type-dialog.spec.tsx file (#7285) * refactor: contract-type-dialog tests * test: added more tests * fix: circle/ci issue Co-authored-by: Niloofar Sadeghi * Niloofar Sadeghi / Task - Refactor tests in the platform-dropdown.spec.tsx file (#7282) * refactor: platform-dropdown tests * refactor: improve testids namings Co-authored-by: Niloofar Sadeghi * fix(core): :memo: resolve conflicts * fix(core): :memo: resolve conflicts * feat(hooks): :sparkles: add `useP2PNotificationCount` hook * feat(hooks): :sparkles: add `useHasSetCurrency` and `useHasActiveRealAccount` hooks * refactor(core): :recycle: refactor `MenuLinks` component * refactor(cashier): :fire: remove `CashierNotifications` component * refactor(core): :fire: remove `MenuStore` store * refactor(cashier): :fire: remove `attachCashierToMenu` and `replaceCashierMenuOnclick` methods * fix(stores): :green_heart: add missing default value to `mockStore` --------- Co-authored-by: Shayan Khaleghparast <100833613+shayan-deriv@users.noreply.github.com> Co-authored-by: Shayan Khaleghparast <100833613+iman-fs@users.noreply.github.com> Co-authored-by: Niloofar Sadeghi <93518187+niloo-fs@users.noreply.github.com> Co-authored-by: kate-deriv <121025168+kate-deriv@users.noreply.github.com> Co-authored-by: Likhith Kolayari <98398322+likhith-deriv@users.noreply.github.com> Co-authored-by: Farrah Mae Ochoa Co-authored-by: Matin shafiei Co-authored-by: Farzin Mirzaie Co-authored-by: Niloofar Sadeghi <93518187+niloofar-deriv@users.noreply.github.com> Co-authored-by: Niloofar Sadeghi --- .../__tests__/cashier-notifications.spec.tsx | 28 ----- .../cashier-notifications.tsx | 15 --- .../components/cashier-notifications/index.ts | 3 - .../stores/__tests__/general-store.spec.ts | 72 +------------ packages/cashier/src/stores/general-store.ts | 40 ------- .../Components/Layout/Header/menu-links.jsx | 101 +++++++++--------- .../Components/Layout/Header/menu-links.scss | 7 ++ .../Layout/header/default-header.jsx | 14 +-- .../Layout/header/dtrader-header.jsx | 13 +-- .../Layout/header/trading-hub-header.jsx | 24 +---- .../RealAccountSignup/real-account-signup.jsx | 4 - packages/core/src/Stores/index.js | 2 - packages/core/src/Stores/menu-store.js | 46 -------- packages/hooks/src/index.ts | 3 + packages/hooks/src/useHasActiveRealAccount.ts | 12 +++ packages/hooks/src/useHasSetCurrency.ts | 17 +++ packages/hooks/src/useP2PNotificationCount.ts | 46 ++++++++ packages/p2p/src/stores/general-store.js | 1 + packages/stores/types.ts | 1 + 19 files changed, 146 insertions(+), 303 deletions(-) delete mode 100644 packages/cashier/src/components/cashier-notifications/__tests__/cashier-notifications.spec.tsx delete mode 100644 packages/cashier/src/components/cashier-notifications/cashier-notifications.tsx delete mode 100644 packages/cashier/src/components/cashier-notifications/index.ts create mode 100644 packages/core/src/App/Components/Layout/Header/menu-links.scss delete mode 100644 packages/core/src/Stores/menu-store.js create mode 100644 packages/hooks/src/useHasActiveRealAccount.ts create mode 100644 packages/hooks/src/useHasSetCurrency.ts create mode 100644 packages/hooks/src/useP2PNotificationCount.ts diff --git a/packages/cashier/src/components/cashier-notifications/__tests__/cashier-notifications.spec.tsx b/packages/cashier/src/components/cashier-notifications/__tests__/cashier-notifications.spec.tsx deleted file mode 100644 index ac47061d5c8a..000000000000 --- a/packages/cashier/src/components/cashier-notifications/__tests__/cashier-notifications.spec.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import CashierNotifications from '../cashier-notifications'; - -jest.mock('@deriv/components', () => { - const original_module = jest.requireActual('@deriv/components') as any; - - return { - ...original_module, - Icon: jest.fn(() => 'mockedIcon'), - Counter: jest.fn(() => 'mockedCounter'), - }; -}); - -describe('', () => { - it('should only show the icon if there are no p2p notifications', () => { - render(); - - expect(screen.getByText('mockedIcon')).toBeInTheDocument(); - }); - - it('should show the icon and no. of notifications if there are p2p notifications', () => { - render(); - - expect(screen.getByText(/mockedIcon/i)).toBeInTheDocument(); - expect(screen.getByText(/mockedCounter/i)).toBeInTheDocument(); - }); -}); diff --git a/packages/cashier/src/components/cashier-notifications/cashier-notifications.tsx b/packages/cashier/src/components/cashier-notifications/cashier-notifications.tsx deleted file mode 100644 index d8281f1aeba0..000000000000 --- a/packages/cashier/src/components/cashier-notifications/cashier-notifications.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { Icon, Counter } from '@deriv/components'; - -type TCashierNotificationsProps = { - p2p_notification_count: number; -}; - -const CashierNotifications = ({ p2p_notification_count }: TCashierNotificationsProps) => ( - - - {!!p2p_notification_count && } - -); - -export default CashierNotifications; diff --git a/packages/cashier/src/components/cashier-notifications/index.ts b/packages/cashier/src/components/cashier-notifications/index.ts deleted file mode 100644 index d4d62d36f900..000000000000 --- a/packages/cashier/src/components/cashier-notifications/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import CashierNotifications from './cashier-notifications'; - -export default CashierNotifications; diff --git a/packages/cashier/src/stores/__tests__/general-store.spec.ts b/packages/cashier/src/stores/__tests__/general-store.spec.ts index faae35a4a70c..758893e665bb 100644 --- a/packages/cashier/src/stores/__tests__/general-store.spec.ts +++ b/packages/cashier/src/stores/__tests__/general-store.spec.ts @@ -2,24 +2,11 @@ import { configure } from 'mobx'; import { waitFor } from '@testing-library/react'; import { routes } from '@deriv/shared'; import GeneralStore from '../general-store'; -import CashierNotifications from 'Components/cashier-notifications'; import type { TWebSocket, TRootStore } from 'Types'; -type TMenuItem = { - icon: JSX.Element; - id: string; - link_to: string | boolean; - login_only: boolean; - onClick: boolean | (() => void); - text: () => string; -}; - configure({ safeDescriptors: false }); -let cashier_menu: TMenuItem, - general_store: GeneralStore, - root_store: DeepPartial, - WS: DeepPartial; +let general_store: GeneralStore, root_store: DeepPartial, WS: DeepPartial; beforeEach(() => { root_store = { @@ -97,23 +84,9 @@ beforeEach(() => { wait: jest.fn(), }; general_store = new GeneralStore(WS as TWebSocket, root_store as TRootStore); - - cashier_menu = { - id: 'dt_cashier_tab', - icon: CashierNotifications({ p2p_notification_count: general_store.p2p_notification_count }), - text: expect.any(Function), - link_to: routes.cashier, - onClick: false, - login_only: true, - }; }); describe('GeneralStore', () => { - it('should set has_set_currency equal to true and attach cashier menu with proper data, if "when" reaction was called in constructor', () => { - expect(general_store.has_set_currency).toBeTruthy(); - expect(general_store.root_store.menu.attach).toHaveBeenCalledWith(cashier_menu); - }); - it('should set function on remount', () => { // TODO: Check this // const remountFunc = () => 'function'; @@ -180,49 +153,6 @@ describe('GeneralStore', () => { expect(general_store.show_p2p_in_cashier_onboarding).toBeTruthy(); }); - it('should call setHasSetCurrency method if has_set_currency is equal to false and attach cashier menu with proper properties', () => { - general_store.has_set_currency = false; - const spySetHasSetCurrency = jest.spyOn(general_store, 'setHasSetCurrency'); - general_store.attachCashierToMenu(); - - expect(spySetHasSetCurrency).toHaveBeenCalledTimes(1); - expect(general_store.root_store.menu.attach).toHaveBeenCalledWith(cashier_menu); - }); - - it('should attach cashier menu and set onClick property to ui.toggleSetCurrencyModal and link_to = false if the client did not set the currency', () => { - general_store.has_set_currency = false; - general_store.root_store.client.account_list = [{ is_virtual: 0, title: 'Real' }]; - general_store.root_store.client.has_active_real_account = true; - general_store.attachCashierToMenu(); - - expect(general_store.root_store.menu.attach).toHaveBeenCalledWith({ - ...cashier_menu, - link_to: false, - onClick: general_store.root_store.ui.toggleSetCurrencyModal, - }); - }); - - it('should replace cashier menu onClick to false if the client set the currency', () => { - general_store.replaceCashierMenuOnclick(); - - expect(general_store.root_store.menu.update).toHaveBeenCalledWith(cashier_menu, 1); - }); - - it('should replace cashier menu onClick to ui.toggleSetCurrencyModal if the client did not set the currency', () => { - general_store.root_store.client.account_list = [{ is_virtual: 0, title: 'Real' }]; - general_store.root_store.client.has_active_real_account = true; - general_store.replaceCashierMenuOnclick(); - - expect(general_store.root_store.menu.update).toHaveBeenCalledWith( - { - ...cashier_menu, - link_to: false, - onClick: general_store.root_store.ui.toggleSetCurrencyModal, - }, - 1 - ); - }); - it('should set has_set_currency equal to true if the client has real USD account', () => { general_store.setHasSetCurrency(); diff --git a/packages/cashier/src/stores/general-store.ts b/packages/cashier/src/stores/general-store.ts index 88d87d760f62..8aaa2f61a504 100644 --- a/packages/cashier/src/stores/general-store.ts +++ b/packages/cashier/src/stores/general-store.ts @@ -1,9 +1,7 @@ import { action, computed, observable, reaction, when, makeObservable } from 'mobx'; import { isCryptocurrency, isEmptyObject, getPropertyValue, routes, ContentFlag } from '@deriv/shared'; import type { P2PAdvertInfo } from '@deriv/api-types'; -import { localize } from '@deriv/translations'; import Constants from 'Constants/constants'; -import CashierNotifications from 'Components/cashier-notifications'; import BaseStore from './base-store'; import PaymentAgentStore from './payment-agent-store'; import type { TRootStore, TWebSocket } from 'Types'; @@ -13,7 +11,6 @@ export default class GeneralStore extends BaseStore { super({ root_store }); makeObservable(this, { - attachCashierToMenu: action.bound, calculatePercentage: action.bound, cashier_route_tab_index: observable, changeSetCurrencyModalTitle: action.bound, @@ -42,7 +39,6 @@ export default class GeneralStore extends BaseStore { percentage: observable, percentageSelectorSelectionStatus: action.bound, payment_agent: observable, - replaceCashierMenuOnclick: action.bound, setAccountSwitchListener: action.bound, setActiveTab: action.bound, setCashierTabIndex: action.bound, @@ -68,7 +64,6 @@ export default class GeneralStore extends BaseStore { () => this.root_store.client.is_logged_in, () => { this.setHasSetCurrency(); - this.attachCashierToMenu(); } ); @@ -161,41 +156,6 @@ export default class GeneralStore extends BaseStore { } } - attachCashierToMenu(): void { - const { menu, ui } = this.root_store; - - if (!this.has_set_currency) { - this.setHasSetCurrency(); - } - - menu.attach({ - id: 'dt_cashier_tab', - icon: CashierNotifications({ p2p_notification_count: this.p2p_notification_count }), - text: () => localize('Cashier'), - link_to: this.has_set_currency && routes.cashier, - onClick: !this.has_set_currency && ui.toggleSetCurrencyModal, - login_only: true, - }); - } - - replaceCashierMenuOnclick(): void { - const { menu, ui } = this.root_store; - - this.setHasSetCurrency(); - - menu.update( - { - id: 'dt_cashier_tab', - icon: CashierNotifications({ p2p_notification_count: this.p2p_notification_count }), - text: () => localize('Cashier'), - link_to: this.has_set_currency && routes.cashier, - onClick: !this.has_set_currency ? ui.toggleSetCurrencyModal : false, - login_only: true, - }, - 1 - ); - } - setHasSetCurrency(): void { const { account_list, has_active_real_account } = this.root_store.client; diff --git a/packages/core/src/App/Components/Layout/Header/menu-links.jsx b/packages/core/src/App/Components/Layout/Header/menu-links.jsx index f8b706cd23be..2d7e904546c2 100644 --- a/packages/core/src/App/Components/Layout/Header/menu-links.jsx +++ b/packages/core/src/App/Components/Layout/Header/menu-links.jsx @@ -1,70 +1,71 @@ -import PropTypes from 'prop-types'; import React from 'react'; -import { Text } from '@deriv/components'; +import { Text, Icon, Counter } from '@deriv/components'; import { BinaryLink } from '../../Routes'; +import { observer, useStore } from '@deriv/stores'; +import { routes } from '@deriv/shared'; +import { localize } from '@deriv/translations'; +import { useP2PNotificationCount } from '@deriv/hooks'; +import './menu-links.scss'; -const MenuItems = ({ item, hide_menu_item }) => { - const { id, link_to, href, text, image, logo, icon } = item; - return hide_menu_item ? null : ( +const MenuItems = ({ id, text, icon, link_to }) => { + return ( - + {icon} - {text()} - {logo} + {text} - - {image} - {logo} - ); }; -const MenuLinks = ({ is_logged_in, is_mobile, items, is_pre_appstore }) => ( - - {!!items.length && ( -
- {items.map(item => { - return ( - is_logged_in && ( - - ) - ); - })} -
- )} -
+const ReportTab = () => ( + } + text={localize('Reports')} + link_to={routes.reports} + /> ); -MenuLinks.propTypes = { - items: PropTypes.arrayOf( - PropTypes.shape({ - icon: PropTypes.shape({ - className: PropTypes.string, - }), - is_logged_in: PropTypes.bool, - link_to: PropTypes.string, - text: PropTypes.func, - }) - ), - is_mobile: PropTypes.bool, - is_logged_in: PropTypes.bool, - is_pre_appstore: PropTypes.bool, -}; +const CashierTab = observer(() => { + const p2p_notification_count = useP2PNotificationCount(); + + return ( + + + {p2p_notification_count > 0 && ( + + )} + + } + text={localize('Cashier')} + link_to={routes.cashier} + /> + ); +}); + +const MenuLinks = observer(() => { + const { client, ui } = useStore(); + const { is_logged_in, is_pre_appstore } = client; + const { is_mobile } = ui; + + if (!is_logged_in) return <>; + + return ( +
+ {!is_pre_appstore && } + {!is_pre_appstore && !is_mobile && } +
+ ); +}); export { MenuLinks }; diff --git a/packages/core/src/App/Components/Layout/Header/menu-links.scss b/packages/core/src/App/Components/Layout/Header/menu-links.scss new file mode 100644 index 000000000000..3923e75c0812 --- /dev/null +++ b/packages/core/src/App/Components/Layout/Header/menu-links.scss @@ -0,0 +1,7 @@ +.cashier { + &__counter { + position: absolute; + top: 0.4rem; + right: -1.5rem; + } +} diff --git a/packages/core/src/App/Containers/Layout/header/default-header.jsx b/packages/core/src/App/Containers/Layout/header/default-header.jsx index 1ae635fde512..9847a92a2587 100644 --- a/packages/core/src/App/Containers/Layout/header/default-header.jsx +++ b/packages/core/src/App/Containers/Layout/header/default-header.jsx @@ -41,12 +41,10 @@ const DefaultHeader = ({ is_route_modal_on, is_trading_assessment_for_existing_user_enabled, is_virtual, - menu_items, notifications_count, openRealAccountSignup, platform, removeNotificationMessage, - replaceCashierMenuOnclick, setIsPreAppStore, toggleAccountsDialog, toggleNotifications, @@ -59,10 +57,6 @@ const DefaultHeader = ({ [removeNotificationMessage] ); - React.useEffect(() => { - if (is_logged_in) replaceCashierMenuOnclick(); - }, [is_logged_in]); - React.useEffect(() => { document.addEventListener('IgnorePWAUpdate', removeUpdateNotification); return () => document.removeEventListener('IgnorePWAUpdate', removeUpdateNotification); @@ -140,7 +134,7 @@ const DefaultHeader = ({
{header_extension}
)} - +
{is_logging_in ? null @@ -230,18 +224,16 @@ DefaultHeader.propTypes = { openRealAccountSignup: PropTypes.func, platform: PropTypes.string, removeNotificationMessage: PropTypes.func, - replaceCashierMenuOnclick: PropTypes.func, toggleAccountsDialog: PropTypes.func, toggleNotifications: PropTypes.func, country_standpoint: PropTypes.object, history: PropTypes.object, - menu_items: PropTypes.array, setIsPreAppStore: PropTypes.func, is_landing_company_loaded: PropTypes.bool, is_switching: PropTypes.bool, }; -export default connect(({ client, common, ui, menu, modules, notifications }) => ({ +export default connect(({ client, common, ui, notifications }) => ({ acc_switcher_disabled_message: ui.account_switcher_disabled_message, account_type: client.account_type, addNotificationMessage: notifications.addNotificationMessage, @@ -265,10 +257,8 @@ export default connect(({ client, common, ui, menu, modules, notifications }) => is_notifications_visible: notifications.is_notifications_visible, is_route_modal_on: ui.is_route_modal_on, is_virtual: client.is_virtual, - menu_items: menu.extensions, notifications_count: notifications.notifications.length, openRealAccountSignup: ui.openRealAccountSignup, - replaceCashierMenuOnclick: modules.cashier.general_store.replaceCashierMenuOnclick, platform: common.platform, removeNotificationMessage: notifications.removeNotificationMessage, toggleAccountsDialog: ui.toggleAccountsDialog, diff --git a/packages/core/src/App/Containers/Layout/header/dtrader-header.jsx b/packages/core/src/App/Containers/Layout/header/dtrader-header.jsx index d2c2a5e07def..9f3f9b1740c9 100644 --- a/packages/core/src/App/Containers/Layout/header/dtrader-header.jsx +++ b/packages/core/src/App/Containers/Layout/header/dtrader-header.jsx @@ -17,8 +17,6 @@ import { TradersHubHomeButton } from './trading-hub-header'; const Divider = () =>
; -const MemoizedMenuLinks = React.memo(MenuLinks); - const DTraderHeader = ({ acc_switcher_disabled_message, account_type, @@ -45,11 +43,9 @@ const DTraderHeader = ({ is_pre_appstore, is_route_modal_on, is_virtual, - menu_items, notifications_count, openRealAccountSignup, platform, - replaceCashierMenuOnclick, removeNotificationMessage, toggleAccountsDialog, toggleNotifications, @@ -111,8 +107,7 @@ const DTraderHeader = ({ - {menu_items && is_logged_in && replaceCashierMenuOnclick()} - +
({ +export default connect(({ client, common, ui, notifications }) => ({ acc_switcher_disabled_message: ui.account_switcher_disabled_message, account_type: client.account_type, addNotificationMessage: notifications.addNotificationMessage, @@ -230,10 +223,8 @@ export default connect(({ client, common, ui, menu, modules, notifications }) => is_notifications_visible: notifications.is_notifications_visible, is_route_modal_on: ui.is_route_modal_on, is_virtual: client.is_virtual, - menu_items: menu.extensions, notifications_count: notifications.notifications.length, openRealAccountSignup: ui.openRealAccountSignup, - replaceCashierMenuOnclick: modules.cashier.general_store.replaceCashierMenuOnclick, platform: common.platform, removeNotificationMessage: notifications.removeNotificationMessage, toggleAccountsDialog: ui.toggleAccountsDialog, diff --git a/packages/core/src/App/Containers/Layout/header/trading-hub-header.jsx b/packages/core/src/App/Containers/Layout/header/trading-hub-header.jsx index ca6f7b89e42d..c7652526b357 100644 --- a/packages/core/src/App/Containers/Layout/header/trading-hub-header.jsx +++ b/packages/core/src/App/Containers/Layout/header/trading-hub-header.jsx @@ -113,7 +113,7 @@ const ShowNotifications = ({ is_notifications_visible, notifications_count, togg
); }; -const MemoizedMenuLinks = React.memo(MenuLinks); + const TradingHubHeader = ({ content_flag, header_extension, @@ -121,15 +121,11 @@ const TradingHubHeader = ({ is_eu_country, is_eu, is_logged_in, - is_mobile, is_mt5_allowed, is_notifications_visible, - is_pre_appstore, loginid, - menu_items, modal_data, notifications_count, - replaceCashierMenuOnclick, setIsOnboardingVisited, setIsPreAppStore, should_show_exit_traders_modal, @@ -165,13 +161,7 @@ const TradingHubHeader = ({ - {menu_items && is_logged_in && replaceCashierMenuOnclick()} - +
@@ -263,16 +253,12 @@ TradingHubHeader.propTypes = { is_eu_country: PropTypes.bool, is_eu: PropTypes.bool, is_logged_in: PropTypes.bool, - is_mobile: PropTypes.bool, is_mt5_allowed: PropTypes.bool, is_notifications_visible: PropTypes.bool, - is_pre_appstore: PropTypes.bool, is_settings_modal_on: PropTypes.bool, loginid: PropTypes.string, - menu_items: PropTypes.array, modal_data: PropTypes.object, notifications_count: PropTypes.number, - replaceCashierMenuOnclick: PropTypes.func, setIsPreAppStore: PropTypes.func, setIsOnboardingVisited: PropTypes.func, settings_extension: PropTypes.array, @@ -284,22 +270,18 @@ TradingHubHeader.propTypes = { switchToCRAccount: PropTypes.func, }; -export default connect(({ client, modules, notifications, ui, menu, traders_hub }) => ({ +export default connect(({ client, notifications, ui, traders_hub }) => ({ header_extension: ui.header_extension, is_dark_mode: ui.is_dark_mode_on, is_eu_country: client.is_eu_country, is_eu: client.is_eu, is_logged_in: client.is_logged_in, - is_mobile: ui.is_mobile, is_mt5_allowed: client.is_mt5_allowed, is_notifications_visible: notifications.is_notifications_visible, - is_pre_appstore: client.is_pre_appstore, modal_data: traders_hub.modal_data, notifications_count: notifications.notifications.length, toggleNotifications: notifications.toggleNotificationsModal, loginid: client.loginid, - menu_items: menu.extensions, - replaceCashierMenuOnclick: modules.cashier.general_store.replaceCashierMenuOnclick, setIsOnboardingVisited: traders_hub.setIsOnboardingVisited, setIsPreAppStore: client.setIsPreAppStore, should_show_exit_traders_modal: traders_hub.should_show_exit_traders_modal, diff --git a/packages/core/src/App/Containers/RealAccountSignup/real-account-signup.jsx b/packages/core/src/App/Containers/RealAccountSignup/real-account-signup.jsx index 4cff6b1edaab..7c1d5240da15 100644 --- a/packages/core/src/App/Containers/RealAccountSignup/real-account-signup.jsx +++ b/packages/core/src/App/Containers/RealAccountSignup/real-account-signup.jsx @@ -112,7 +112,6 @@ const RealAccountSignup = ({ is_real_acc_signup_on, real_account_signup_target, realAccountSignup, - replaceCashierMenuOnclick, routing_history, setIsDeposit, setIsTradingAssessmentForNewUserEnabled, @@ -291,7 +290,6 @@ const RealAccountSignup = ({ }; const closeModalThenOpenCashier = () => { - replaceCashierMenuOnclick(); closeRealAccountSignup(); history.push(routes.cashier_deposit); }; @@ -360,7 +358,6 @@ const RealAccountSignup = ({ }, [is_from_restricted_country, is_real_acc_signup_on]); const closeModal = e => { - replaceCashierMenuOnclick(); // Do not close modal on external link and popover click event if ( e?.target.getAttribute('rel') === 'noopener noreferrer' || @@ -643,7 +640,6 @@ export default connect(({ ui, client, common, traders_hub, modules }) => ({ is_real_acc_signup_on: ui.is_real_acc_signup_on, real_account_signup_target: ui.real_account_signup_target, realAccountSignup: client.realAccountSignup, - replaceCashierMenuOnclick: modules.cashier.general_store.replaceCashierMenuOnclick, routing_history: common.app_routing_history, setCFDScore: client.setCFDScore, setIsDeposit: modules.cashier.general_store.setIsDeposit, diff --git a/packages/core/src/Stores/index.js b/packages/core/src/Stores/index.js index 0c1e8e15cfb3..0a6dad2e6b9d 100644 --- a/packages/core/src/Stores/index.js +++ b/packages/core/src/Stores/index.js @@ -4,7 +4,6 @@ import GTMStore from './gtm-store'; import RudderStackStore from './rudderstack-store'; import PushWooshStore from './pushwoosh-store'; import ModulesStore from './Modules'; -import MenuStore from './menu-store'; import NotificationStore from './notification-store'; import UIStore from './ui-store'; import ActiveSymbolsStore from './active-symbols-store'; @@ -22,7 +21,6 @@ export default class RootStore { this.ui = new UIStore(this); this.gtm = new GTMStore(this); this.rudderstack = new RudderStackStore(this); - this.menu = new MenuStore(this); this.pushwoosh = new PushWooshStore(this); this.notifications = new NotificationStore(this); this.active_symbols = new ActiveSymbolsStore(this); diff --git a/packages/core/src/Stores/menu-store.js b/packages/core/src/Stores/menu-store.js deleted file mode 100644 index 42cf4ef91dfa..000000000000 --- a/packages/core/src/Stores/menu-store.js +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import { action, observable, makeObservable } from 'mobx'; -import { Icon } from '@deriv/components'; -import { localize } from '@deriv/translations'; -import { routes } from '@deriv/shared'; -import BaseStore from './base-store'; - -export default class MenuStore extends BaseStore { - extensions = [ - { - id: 'dt_reports_tab', - icon: , - text: () => localize('Reports'), - link_to: routes.reports, - login_only: true, - }, - ]; - - constructor() { - // TODO: [mobx-undecorate] verify the constructor arguments and the arguments of this automatically generated super call - super(); - - makeObservable(this, { - extensions: observable, - attach: action.bound, - detach: action.bound, - }); - } - - attach(menu) { - if (!(menu instanceof Object)) { - throw new TypeError('Menu is not an instance of object.'); - } - this.extensions.push(menu); - } - - update(menu, index) { - if (this.extensions[index]) { - this.extensions[index] = menu; - } - } - - detach(menu) { - this.extensions = this.extensions.filter(extension => extension.id !== menu); - } -} diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts index 4417d0dd313a..0961e4908618 100644 --- a/packages/hooks/src/index.ts +++ b/packages/hooks/src/index.ts @@ -5,4 +5,7 @@ export { default as useNeedFinancialAssessment } from './useNeedFinancialAssessm export { default as useRealSTPAccount } from './useRealSTPAccount'; export { default as useNeedTNC } from './useNeedTNC'; export { default as useDepositLocked } from './useDepositLocked'; +export { default as useHasSetCurrency } from './useHasSetCurrency'; +export { default as useHasActiveRealAccount } from './useHasActiveRealAccount'; +export { default as useP2PNotificationCount } from './useP2PNotificationCount'; export { default as useOnrampVisible } from './useOnrampVisible'; diff --git a/packages/hooks/src/useHasActiveRealAccount.ts b/packages/hooks/src/useHasActiveRealAccount.ts new file mode 100644 index 000000000000..df58321dfc95 --- /dev/null +++ b/packages/hooks/src/useHasActiveRealAccount.ts @@ -0,0 +1,12 @@ +import { useStore } from '@deriv/stores'; + +const useHasActiveRealAccount = () => { + const { client } = useStore(); + const { active_accounts } = client; + + const has_active_real_account = active_accounts.some(account => account.is_virtual === 0); + + return has_active_real_account; +}; + +export default useHasActiveRealAccount; diff --git a/packages/hooks/src/useHasSetCurrency.ts b/packages/hooks/src/useHasSetCurrency.ts new file mode 100644 index 000000000000..702323dad4a5 --- /dev/null +++ b/packages/hooks/src/useHasSetCurrency.ts @@ -0,0 +1,17 @@ +import { useStore } from '@deriv/stores'; +import useHasActiveRealAccount from './useHasActiveRealAccount'; + +const useHasSetCurrency = () => { + const { client } = useStore(); + const { account_list } = client; + const has_active_real_account = useHasActiveRealAccount(); + const has_real_account = account_list + .filter(account => !account.is_virtual) + .some(account => account.title !== 'Real'); + + const has_set_currency = has_real_account || !has_active_real_account; + + return has_set_currency; +}; + +export default useHasSetCurrency; diff --git a/packages/hooks/src/useP2PNotificationCount.ts b/packages/hooks/src/useP2PNotificationCount.ts new file mode 100644 index 000000000000..ef193a648628 --- /dev/null +++ b/packages/hooks/src/useP2PNotificationCount.ts @@ -0,0 +1,46 @@ +import { useStore } from '@deriv/stores'; +import { useEffect, useState } from 'react'; + +type TNotification = { + order_id: string; + is_seen: boolean; + is_active: boolean; +}; + +type TClientData = { + is_cached: boolean; + notifications: TNotification[]; +}; + +type TP2PSettings = Record; + +const useP2PNotificationCount = () => { + const [p2p_settings, setP2PSettings] = useState( + JSON.parse(localStorage.getItem('p2p_settings') || '{}') + ); + const { client } = useStore(); + const { loginid } = client; + const notifications = loginid ? p2p_settings[loginid]?.notifications : null; + + useEffect(() => { + const onStorageChanged = () => { + const data = localStorage.getItem('p2p_settings'); + + if (data) { + setP2PSettings(JSON.parse(data)); + } + }; + + window.addEventListener('storage', onStorageChanged); + + return () => { + window.removeEventListener('storage', onStorageChanged); + }; + }, []); + + const p2p_notification_count = notifications?.filter(notification => !notification.is_seen).length || 0; + + return p2p_notification_count; +}; + +export default useP2PNotificationCount; diff --git a/packages/p2p/src/stores/general-store.js b/packages/p2p/src/stores/general-store.js index 2f275e2986ca..a63c36269ccf 100644 --- a/packages/p2p/src/stores/general-store.js +++ b/packages/p2p/src/stores/general-store.js @@ -889,6 +889,7 @@ export default class GeneralStore extends BaseStore { p2p_settings[this.client.loginid] = user_settings; localStorage.setItem('p2p_settings', JSON.stringify(p2p_settings)); + window.dispatchEvent(new Event('storage')); this.setNotificationCount(notification_count); this.setActiveNotificationCount(active_notification_count); diff --git a/packages/stores/types.ts b/packages/stores/types.ts index 01b2c79601d2..20d6d864fc34 100644 --- a/packages/stores/types.ts +++ b/packages/stores/types.ts @@ -30,6 +30,7 @@ type TAccountsList = { // balance is missing in @deriv/api-types type TActiveAccount = TAccount & { balance?: number; + is_virtual: number; }; type TAuthenticationStatus = { document_status: string; identity_status: string }; From f01ec12c66089908b5c1a51db3804f1d8eabd6b0 Mon Sep 17 00:00:00 2001 From: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> Date: Wed, 1 Mar 2023 13:12:27 +0800 Subject: [PATCH 13/25] Farzin/87070/Extract `is_account_transfer_visible` method from cashier `AccountTransferStore` to reusable hook (#7445) * feat(hooks): :sparkles: add `useAccountTransferVisible` and `useHasMaltaInvestAccount` hooks * refactor(core): :recycle: replace `is_account_transfer_visible` with `useAccountTransferVisible` * refactor(cashier): :recycle: replace `is_account_transfer_visible` with `useAccountTransferVisible` * refactor(cashier): :fire: remove `is_account_transfer_visible` method * test(cashier): :white_check_mark: fix failing test * fix(stores): :green_heart: add missing default value to `mockStore` --------- Co-authored-by: Farzin Mirzaie --- .../cashier/__tests__/cashier.spec.tsx | 27 +++++-------------- .../src/containers/cashier/cashier.tsx | 5 ++-- .../__tests__/account-transfer-store.spec.ts | 17 ------------ .../src/stores/account-transfer-store.ts | 8 ------ .../Layout/Header/toggle-menu-drawer.jsx | 6 ++--- packages/hooks/src/index.ts | 2 ++ .../hooks/src/useAccountTransferVisible.ts | 14 ++++++++++ .../hooks/src/useHasMaltaInvestAccount.ts | 13 +++++++++ packages/stores/types.ts | 1 + 9 files changed, 41 insertions(+), 52 deletions(-) create mode 100644 packages/hooks/src/useAccountTransferVisible.ts create mode 100644 packages/hooks/src/useHasMaltaInvestAccount.ts diff --git a/packages/cashier/src/containers/cashier/__tests__/cashier.spec.tsx b/packages/cashier/src/containers/cashier/__tests__/cashier.spec.tsx index 48ed92719727..93048484d180 100644 --- a/packages/cashier/src/containers/cashier/__tests__/cashier.spec.tsx +++ b/packages/cashier/src/containers/cashier/__tests__/cashier.spec.tsx @@ -61,6 +61,7 @@ describe('', () => { is_account_setting_loaded: false, is_logged_in: false, is_logging_in: true, + active_accounts: [], }, modules: { cashier: { @@ -77,9 +78,6 @@ describe('', () => { setCashierTabIndex: jest.fn(), cashier_route_tab_index: 0, }, - account_transfer: { - is_account_transfer_visible: false, - }, transaction_history: { is_crypto_transactions_visible: false, }, @@ -115,6 +113,7 @@ describe('', () => { is_account_setting_loaded: true, is_logged_in: true, is_logging_in: true, + active_accounts: [], is_virtual: false, is_crypto: true, }, @@ -133,9 +132,6 @@ describe('', () => { setCashierTabIndex: jest.fn(), cashier_route_tab_index: 0, }, - account_transfer: { - is_account_transfer_visible: true, - }, transaction_history: { is_crypto_transactions_visible: true, }, @@ -180,6 +176,7 @@ describe('', () => { is_account_setting_loaded: true, is_logged_in: true, is_logging_in: true, + active_accounts: [], }, modules: { cashier: { @@ -196,9 +193,6 @@ describe('', () => { setCashierTabIndex: jest.fn(), cashier_route_tab_index: 0, }, - account_transfer: { - is_account_transfer_visible: true, - }, transaction_history: { is_crypto_transactions_visible: true, }, @@ -254,9 +248,6 @@ describe('', () => { // setCashierTabIndex: jest.fn(), // cashier_route_tab_index: 0, // }, - // account_transfer: { - // is_account_transfer_visible: true, - // }, // transaction_history: { // is_crypto_transactions_visible: false, // }, @@ -296,6 +287,7 @@ describe('', () => { is_account_setting_loaded: true, is_logged_in: true, is_logging_in: true, + active_accounts: [], }, modules: { cashier: { @@ -312,9 +304,6 @@ describe('', () => { setCashierTabIndex: jest.fn(), cashier_route_tab_index: 0, }, - account_transfer: { - is_account_transfer_visible: true, - }, transaction_history: { is_crypto_transactions_visible: true, }, @@ -353,6 +342,7 @@ describe('', () => { is_account_setting_loaded: true, is_logged_in: true, is_logging_in: true, + active_accounts: [], }, modules: { cashier: { @@ -369,9 +359,6 @@ describe('', () => { setCashierTabIndex: jest.fn(), cashier_route_tab_index: 0, }, - account_transfer: { - is_account_transfer_visible: true, - }, transaction_history: { is_crypto_transactions_visible: true, }, @@ -409,6 +396,7 @@ describe('', () => { is_account_setting_loaded: true, is_logged_in: true, is_logging_in: false, + active_accounts: [], }, modules: { cashier: { @@ -425,9 +413,6 @@ describe('', () => { setCashierTabIndex: jest.fn(), cashier_route_tab_index: 0, }, - account_transfer: { - is_account_transfer_visible: true, - }, transaction_history: { is_crypto_transactions_visible: true, }, diff --git a/packages/cashier/src/containers/cashier/cashier.tsx b/packages/cashier/src/containers/cashier/cashier.tsx index 8a94cf6eea93..02cab631c3fc 100644 --- a/packages/cashier/src/containers/cashier/cashier.tsx +++ b/packages/cashier/src/containers/cashier/cashier.tsx @@ -11,7 +11,7 @@ import { VerticalTab, Loading, } from '@deriv/components'; -import { useOnrampVisible } from '@deriv/hooks'; +import { useOnrampVisible, useAccountTransferVisible } from '@deriv/hooks'; import { getSelectedRoute, getStaticUrl, isMobile, routes, WS } from '@deriv/shared'; import AccountPromptDialog from '../../components/account-prompt-dialog'; import ErrorDialog from '../../components/error-dialog'; @@ -48,7 +48,6 @@ const Cashier = observer(({ history, location, routes: routes_config }: TCashier const { withdraw, general_store, - account_transfer, transaction_history, payment_agent_transfer, payment_agent, @@ -65,7 +64,6 @@ const Cashier = observer(({ history, location, routes: routes_config }: TCashier setCashierTabIndex: setTabIndex, cashier_route_tab_index: tab_index, } = general_store; - const { is_account_transfer_visible } = account_transfer; const { is_crypto_transactions_visible } = transaction_history; const { is_payment_agent_transfer_visible } = payment_agent_transfer; const { is_payment_agent_visible } = payment_agent; @@ -73,6 +71,7 @@ const Cashier = observer(({ history, location, routes: routes_config }: TCashier const { routeBackInApp, is_from_derivgo } = common; const { is_cashier_visible: is_visible, toggleCashier } = ui; const { is_account_setting_loaded, is_logged_in, is_logging_in, is_pre_appstore } = client; + const is_account_transfer_visible = useAccountTransferVisible(); const is_onramp_visible = useOnrampVisible(); React.useEffect(() => { diff --git a/packages/cashier/src/stores/__tests__/account-transfer-store.spec.ts b/packages/cashier/src/stores/__tests__/account-transfer-store.spec.ts index d27c2933c396..80b0a0e9afbd 100644 --- a/packages/cashier/src/stores/__tests__/account-transfer-store.spec.ts +++ b/packages/cashier/src/stores/__tests__/account-transfer-store.spec.ts @@ -196,23 +196,6 @@ jest.mock('@deriv/shared', () => ({ })); describe('AccountTransferStore', () => { - it('account transfer tab should be visible', () => { - expect(account_transfer_store.is_account_transfer_visible).toBeTruthy(); - }); - - it('account transfer tab should not be visible for "iom" clients', () => { - account_transfer_store.root_store.client.residence = 'im'; - - expect(account_transfer_store.is_account_transfer_visible).toBeFalsy(); - }); - - it('account transfer tab should not be visible if landing_company_shortcode is equal to "malta" and the client has not maltainvest account ', () => { - account_transfer_store.root_store.client.landing_company_shortcode = 'malta'; - account_transfer_store.root_store.client.has_maltainvest_account = false; - - expect(account_transfer_store.is_account_transfer_visible).toBeFalsy(); - }); - it('should not lock the transfer if there is no any account statuses', () => { account_transfer_store.root_store.client.account_status.status = []; diff --git a/packages/cashier/src/stores/account-transfer-store.ts b/packages/cashier/src/stores/account-transfer-store.ts index 741c3cd3be56..10b6483374d1 100644 --- a/packages/cashier/src/stores/account-transfer-store.ts +++ b/packages/cashier/src/stores/account-transfer-store.ts @@ -41,7 +41,6 @@ export default class AccountTransferStore { should_switch_account: observable, transfer_fee: observable, transfer_limit: observable, - is_account_transfer_visible: computed, is_transfer_locked: computed, setBalanceByLoginId: action.bound, setBalanceSelectedFrom: action.bound, @@ -90,13 +89,6 @@ export default class AccountTransferStore { transfer_fee?: number | null = null; transfer_limit: { min?: string | null; max?: string | null } = {}; - get is_account_transfer_visible() { - const { has_maltainvest_account, landing_company_shortcode, residence } = this.root_store.client; - // cashier Transfer account tab is hidden for iom clients - // check for residence to hide the tab before creating a real money account - return residence !== 'im' && (landing_company_shortcode !== 'malta' || has_maltainvest_account); - } - get is_transfer_locked() { const { is_financial_account, diff --git a/packages/core/src/App/Components/Layout/Header/toggle-menu-drawer.jsx b/packages/core/src/App/Components/Layout/Header/toggle-menu-drawer.jsx index 66a84c3fa734..067a162134e0 100644 --- a/packages/core/src/App/Components/Layout/Header/toggle-menu-drawer.jsx +++ b/packages/core/src/App/Components/Layout/Header/toggle-menu-drawer.jsx @@ -1,7 +1,7 @@ import classNames from 'classnames'; import React from 'react'; import { Div100vhContainer, Icon, MobileDrawer, ToggleSwitch, Text, Button } from '@deriv/components'; -import { useOnrampVisible } from '@deriv/hooks'; +import { useOnrampVisible, useAccountTransferVisible } from '@deriv/hooks'; import { routes, PlatformContext, getStaticUrl, whatsapp_url, ContentFlag } from '@deriv/shared'; import { observer, useStore } from '@deriv/stores'; import { localize, getAllowedLanguages, getLanguage } from '@deriv/translations'; @@ -126,12 +126,12 @@ const ToggleMenuDrawer = observer(({ platform_config }) => { is_eu, } = client; const { cashier } = modules; - const { general_store, payment_agent_transfer, payment_agent, account_transfer } = cashier; + const { general_store, payment_agent_transfer, payment_agent } = cashier; const { is_p2p_enabled } = general_store; const { is_payment_agent_transfer_visible } = payment_agent_transfer; const { is_payment_agent_visible } = payment_agent; - const { is_account_transfer_visible } = account_transfer; const { content_flag, should_show_exit_traders_modal, switchToCRAccount, show_eu_related_content } = traders_hub; + const is_account_transfer_visible = useAccountTransferVisible(); const is_onramp_visible = useOnrampVisible(); const liveChat = useLiveChat(); diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts index 0961e4908618..91ac00fa5c7e 100644 --- a/packages/hooks/src/index.ts +++ b/packages/hooks/src/index.ts @@ -5,6 +5,8 @@ export { default as useNeedFinancialAssessment } from './useNeedFinancialAssessm export { default as useRealSTPAccount } from './useRealSTPAccount'; export { default as useNeedTNC } from './useNeedTNC'; export { default as useDepositLocked } from './useDepositLocked'; +export { default as useAccountTransferVisible } from './useAccountTransferVisible'; +export { default as useHasMaltaInvestAccount } from './useHasMaltaInvestAccount'; export { default as useHasSetCurrency } from './useHasSetCurrency'; export { default as useHasActiveRealAccount } from './useHasActiveRealAccount'; export { default as useP2PNotificationCount } from './useP2PNotificationCount'; diff --git a/packages/hooks/src/useAccountTransferVisible.ts b/packages/hooks/src/useAccountTransferVisible.ts new file mode 100644 index 000000000000..43f433a2d309 --- /dev/null +++ b/packages/hooks/src/useAccountTransferVisible.ts @@ -0,0 +1,14 @@ +import { useStore } from '@deriv/stores'; +import useHasMaltaInvestAccount from './useHasMaltaInvestAccount'; + +const useAccountTransferVisible = () => { + const { client } = useStore(); + const { landing_company_shortcode, residence } = client; + const has_malta_invest_account = useHasMaltaInvestAccount(); + const is_account_transfer_visible = + residence !== 'im' && (landing_company_shortcode !== 'malta' || has_malta_invest_account); + + return is_account_transfer_visible; +}; + +export default useAccountTransferVisible; diff --git a/packages/hooks/src/useHasMaltaInvestAccount.ts b/packages/hooks/src/useHasMaltaInvestAccount.ts new file mode 100644 index 000000000000..4251c51b2115 --- /dev/null +++ b/packages/hooks/src/useHasMaltaInvestAccount.ts @@ -0,0 +1,13 @@ +import { useStore } from '@deriv/stores'; + +const useHasMaltaInvestAccount = () => { + const { client } = useStore(); + const { active_accounts } = client; + const has_malta_invest_account = active_accounts.some( + account => account.landing_company_shortcode === 'maltainvest' + ); + + return has_malta_invest_account; +}; + +export default useHasMaltaInvestAccount; diff --git a/packages/stores/types.ts b/packages/stores/types.ts index 20d6d864fc34..51a4d572c092 100644 --- a/packages/stores/types.ts +++ b/packages/stores/types.ts @@ -30,6 +30,7 @@ type TAccountsList = { // balance is missing in @deriv/api-types type TActiveAccount = TAccount & { balance?: number; + landing_company_shortcode: 'svg' | 'costarica' | 'maltainvest' | 'malta' | 'iom'; is_virtual: number; }; From a30afc530185f84d8ffe34b3584a9f2df0259dca Mon Sep 17 00:00:00 2001 From: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> Date: Wed, 1 Mar 2023 13:13:36 +0800 Subject: [PATCH 14/25] Farzin/88636/Debounce `p2p_order_list` call (#7613) * perf(p2p): :zap: debounce `p2p_order_list` call and check if p2p is available before making the call * fix(core): :bug: use `WS` from services * fix(core): :bug: add timeout before fetching `p2p_order_list` * fix(core): :bug: fix cannot read properties of undefined `authorized` * fix(core): :bug: fix cannot read properties of undefined `authorized` * fix(core): :bug: fix cannot read properties of undefined `authorized` * fix(core): :bug: fix cannot read properties of undefined `authorized` --------- Co-authored-by: Farzin Mirzaie --- packages/cashier/src/stores/general-store.ts | 18 ------------- .../core/src/Stores/notification-store.js | 27 +++++++++++++++---- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/packages/cashier/src/stores/general-store.ts b/packages/cashier/src/stores/general-store.ts index 8aaa2f61a504..d640e635fc1a 100644 --- a/packages/cashier/src/stores/general-store.ts +++ b/packages/cashier/src/stores/general-store.ts @@ -1,6 +1,5 @@ import { action, computed, observable, reaction, when, makeObservable } from 'mobx'; import { isCryptocurrency, isEmptyObject, getPropertyValue, routes, ContentFlag } from '@deriv/shared'; -import type { P2PAdvertInfo } from '@deriv/api-types'; import Constants from 'Constants/constants'; import BaseStore from './base-store'; import PaymentAgentStore from './payment-agent-store'; @@ -18,7 +17,6 @@ export default class GeneralStore extends BaseStore { continueRoute: action.bound, deposit_target: observable, getAdvertizerError: action.bound, - getP2pCompletedOrders: action.bound, has_set_currency: observable, init: action.bound, is_cashier_locked: computed, @@ -33,7 +31,6 @@ export default class GeneralStore extends BaseStore { onMountCommon: action.bound, onRemount: observable, p2p_advertiser_error: observable, - p2p_completed_orders: observable, p2p_notification_count: observable, p2p_unseen_notifications: computed, percentage: observable, @@ -51,7 +48,6 @@ export default class GeneralStore extends BaseStore { setNotificationCount: action.bound, setOnRemount: action.bound, setP2pAdvertiserError: action.bound, - setP2pCompletedOrders: action.bound, setShouldShowAllAvailableCurrencies: action.bound, should_percentage_reset: observable, should_set_currency_modal_title_change: observable, @@ -94,7 +90,6 @@ export default class GeneralStore extends BaseStore { is_populating_values = false; onRemount: VoidFunction = () => this; p2p_advertiser_error?: string = undefined; - p2p_completed_orders: P2PAdvertInfo[] | null = null; p2p_notification_count = 0; percentage = 0; payment_agent: PaymentAgentStore | null = null; @@ -278,19 +273,6 @@ export default class GeneralStore extends BaseStore { this.setIsP2pVisible(!(is_p2p_restricted || this.root_store.client.is_virtual)); } - setP2pCompletedOrders(p2p_completed_orders: P2PAdvertInfo[]): void { - this.p2p_completed_orders = p2p_completed_orders; - } - - async getP2pCompletedOrders() { - await this.WS.authorized.send?.({ p2p_order_list: 1, active: 0 }).then(response => { - if (!response?.error) { - const { p2p_order_list } = response; - this.setP2pCompletedOrders(p2p_order_list?.list || []); - } - }); - } - async onMountCommon(should_remount?: boolean) { const { client, common, modules } = this.root_store; const { is_from_derivgo, routeTo } = common; diff --git a/packages/core/src/Stores/notification-store.js b/packages/core/src/Stores/notification-store.js index 21b05b73d57e..3ba18e8839df 100644 --- a/packages/core/src/Stores/notification-store.js +++ b/packages/core/src/Stores/notification-store.js @@ -1,3 +1,4 @@ +import debounce from 'lodash.debounce'; import { StaticUrl } from '@deriv/components'; import { daysSince, @@ -38,6 +39,7 @@ export default class NotificationStore extends BaseStore { client_notifications = {}; should_show_popups = true; p2p_order_props = {}; + p2p_completed_orders = null; constructor(root_store) { super({ root_store }); @@ -73,8 +75,12 @@ export default class NotificationStore extends BaseStore { toggleNotificationsModal: action.bound, unmarkNotificationMessage: action.bound, updateNotifications: action.bound, + p2p_completed_orders: observable, + getP2pCompletedOrders: action.bound, }); + const debouncedGetP2pCompletedOrders = debounce(this.getP2pCompletedOrders, 1000); + reaction( () => root_store.common.app_routing_history.map(i => i.pathname), () => { @@ -95,10 +101,13 @@ export default class NotificationStore extends BaseStore { async () => { if ( root_store.client.is_logged_in && + !root_store.client.is_virtual && Object.keys(root_store.client.account_status).length > 0 && - Object.keys(root_store.client.landing_companies).length > 0 - ) - await root_store.modules?.cashier?.general_store?.getP2pCompletedOrders(); + Object.keys(root_store.client.landing_companies).length > 0 && + root_store.modules?.cashier?.general_store?.is_p2p_visible + ) { + await debouncedGetP2pCompletedOrders(); + } if ( !root_store.client.is_logged_in || @@ -271,7 +280,7 @@ export default class NotificationStore extends BaseStore { has_restricted_mt5_account, has_mt5_account_with_rejected_poa, } = this.root_store.client; - const { is_p2p_visible, p2p_completed_orders } = this.root_store.modules.cashier.general_store; + const { is_p2p_visible } = this.root_store.modules.cashier.general_store; const { is_10k_withdrawal_limit_reached } = this.root_store.modules.cashier.withdraw; const { current_language, selected_contract_type } = this.root_store.common; const malta_account = landing_company_shortcode === 'maltainvest'; @@ -481,7 +490,7 @@ export default class NotificationStore extends BaseStore { if (is_p2p_visible) { this.addNotificationMessage(this.client_notifications.dp2p); - p2p_completed_orders?.map(order => { + this.p2p_completed_orders?.map(order => { const { advertiser_details, client_details, @@ -1474,4 +1483,12 @@ export default class NotificationStore extends BaseStore { platform: 'Account', }); }; + + async getP2pCompletedOrders() { + const response = await WS.send?.({ p2p_order_list: 1, active: 0 }); + + if (!response?.error) { + this.p2p_completed_orders = response.p2p_order_list?.list || []; + } + } } From 6486f4e788aa708e91c75cf9840e17e48eadad3b Mon Sep 17 00:00:00 2001 From: nada-deriv <122768621+nada-deriv@users.noreply.github.com> Date: Wed, 1 Mar 2023 11:20:49 +0400 Subject: [PATCH 15/25] fix: shows cancel dialog on selecting payment method (#7429) --- .../components/src/components/dropdown-list/dropdown-list.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/components/src/components/dropdown-list/dropdown-list.jsx b/packages/components/src/components/dropdown-list/dropdown-list.jsx index 49be41f18c07..292dc737ffdd 100644 --- a/packages/components/src/components/dropdown-list/dropdown-list.jsx +++ b/packages/components/src/components/dropdown-list/dropdown-list.jsx @@ -19,7 +19,8 @@ const ListItem = ({
{ + onMouseDown={event => { + event.stopPropagation(); onItemSelection(item); setActiveIndex(index); }} From f2576567127cdffbc1a796e4204c71cb26270d4e Mon Sep 17 00:00:00 2001 From: Carol Sachdeva <58209918+carol-deriv@users.noreply.github.com> Date: Wed, 1 Mar 2023 15:36:14 +0800 Subject: [PATCH 16/25] refactor: move search box to components package (#7664) --- .../{p2p => components}/src/components/search-box/index.js | 0 .../src/components/search-box/search-box.jsx | 3 ++- .../src/components/search-box/search-box.scss | 0 packages/components/src/index.js | 1 + packages/p2p/src/components/buy-sell/buy-sell-header.jsx | 3 +-- .../my-profile/block-user/block-user-list/block-user-list.jsx | 3 +-- 6 files changed, 5 insertions(+), 5 deletions(-) rename packages/{p2p => components}/src/components/search-box/index.js (100%) rename packages/{p2p => components}/src/components/search-box/search-box.jsx (98%) rename packages/{p2p => components}/src/components/search-box/search-box.scss (100%) diff --git a/packages/p2p/src/components/search-box/index.js b/packages/components/src/components/search-box/index.js similarity index 100% rename from packages/p2p/src/components/search-box/index.js rename to packages/components/src/components/search-box/index.js diff --git a/packages/p2p/src/components/search-box/search-box.jsx b/packages/components/src/components/search-box/search-box.jsx similarity index 98% rename from packages/p2p/src/components/search-box/search-box.jsx rename to packages/components/src/components/search-box/search-box.jsx index 1081641544d6..b07935d1529e 100644 --- a/packages/p2p/src/components/search-box/search-box.jsx +++ b/packages/components/src/components/search-box/search-box.jsx @@ -2,7 +2,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import { Field as FormField, Formik, Form } from 'formik'; -import { Icon, Input } from '@deriv/components'; +import Icon from '../icon'; +import Input from '../input'; const SearchBox = ({ className, onClear, onSearch, placeholder }) => { const onSearchClear = setFieldValue => { diff --git a/packages/p2p/src/components/search-box/search-box.scss b/packages/components/src/components/search-box/search-box.scss similarity index 100% rename from packages/p2p/src/components/search-box/search-box.scss rename to packages/components/src/components/search-box/search-box.scss diff --git a/packages/components/src/index.js b/packages/components/src/index.js index f2c263ed0646..c80e7963990e 100644 --- a/packages/components/src/index.js +++ b/packages/components/src/index.js @@ -92,6 +92,7 @@ export { default as StatusBadge } from './components/status-badge'; export { default as SwipeableWrapper } from './components/swipeable-wrapper'; export { default as RelativeDatepicker } from './components/relative-datepicker'; export { default as RemainingTime } from './components/remaining-time'; +export { default as SearchBox } from './components/search-box'; export { default as Table } from './components/table'; export { default as Tabs } from './components/tabs'; export { default as Text } from './components/text'; diff --git a/packages/p2p/src/components/buy-sell/buy-sell-header.jsx b/packages/p2p/src/components/buy-sell/buy-sell-header.jsx index e583655e3617..0bba2dd84730 100644 --- a/packages/p2p/src/components/buy-sell/buy-sell-header.jsx +++ b/packages/p2p/src/components/buy-sell/buy-sell-header.jsx @@ -1,14 +1,13 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import debounce from 'lodash.debounce'; -import { ButtonToggle, Icon } from '@deriv/components'; +import { ButtonToggle, Icon, SearchBox } from '@deriv/components'; import { isDesktop } from '@deriv/shared'; import { observer } from 'mobx-react-lite'; import classNames from 'classnames'; import { buy_sell } from 'Constants/buy-sell'; import { localize } from 'Components/i18next'; import ToggleContainer from 'Components/misc/toggle-container.jsx'; -import SearchBox from 'Components/search-box'; import SortDropdown from 'Components/buy-sell/sort-dropdown.jsx'; import { useStores } from 'Stores'; import CurrencyDropdown from 'Components/buy-sell/currency-dropdown.jsx'; diff --git a/packages/p2p/src/components/my-profile/block-user/block-user-list/block-user-list.jsx b/packages/p2p/src/components/my-profile/block-user/block-user-list/block-user-list.jsx index bba999ac149a..6c7e33d6399c 100644 --- a/packages/p2p/src/components/my-profile/block-user/block-user-list/block-user-list.jsx +++ b/packages/p2p/src/components/my-profile/block-user/block-user-list/block-user-list.jsx @@ -1,12 +1,11 @@ import React from 'react'; import { observer } from 'mobx-react-lite'; import { useStores } from 'Stores'; -import { Text, Loading } from '@deriv/components'; +import { Loading, SearchBox, Text } from '@deriv/components'; import { isMobile } from '@deriv/shared'; import BlockUserDropdown from '../block-user-dropdown'; import BlockUserTable from '../block-user-table'; import BlockUserTableError from '../block-user-table/block-user-table-error'; -import SearchBox from 'Components/search-box'; import debounce from 'lodash.debounce'; import { localize } from 'Components/i18next'; import './block-user-list.scss'; From aa83be934a4461d279034ec9fb7ed00dd128c099 Mon Sep 17 00:00:00 2001 From: Jim Daniels Wasswa <104334373+jim-deriv@users.noreply.github.com> Date: Thu, 2 Mar 2023 13:53:42 +0800 Subject: [PATCH 17/25] Jim/85458/allow poo submission on valid data (#7592) * feat: enable button when valid information is provided * chore: add check for status * refactor: combine conditionals * chore: seperate conditionals * chore: replace nullish coalescing with or operator * chore: update based on reviews * chore: refactor based on code reviews --- .../__test__/proof-of-ownership-form.spec.js | 2 - .../Verification/ProofOfOwnership/card.jsx | 10 +- .../ProofOfOwnership/expanded-card.jsx | 40 ++-- .../ProofOfOwnership/file-uploader.jsx | 18 +- .../proof-of-ownership-form.jsx | 209 +++++++++++------- .../ProofOfOwnership/proof-of-ownership.jsx | 22 +- .../core/src/Stores/notification-store.js | 3 +- 7 files changed, 172 insertions(+), 132 deletions(-) diff --git a/packages/account/src/Sections/Verification/ProofOfOwnership/__test__/proof-of-ownership-form.spec.js b/packages/account/src/Sections/Verification/ProofOfOwnership/__test__/proof-of-ownership-form.spec.js index fade43373a3d..47d6991b8b11 100644 --- a/packages/account/src/Sections/Verification/ProofOfOwnership/__test__/proof-of-ownership-form.spec.js +++ b/packages/account/src/Sections/Verification/ProofOfOwnership/__test__/proof-of-ownership-form.spec.js @@ -8,7 +8,6 @@ describe('proof-of-ownership-form.jsx', () => { render( { render( { +const Card = ({ card, error, index, setFieldValue, updateErrors, values }) => { const [is_open, setIsOpen] = React.useState(false); const onClickHandler = e => { e.preventDefault(); @@ -43,13 +43,10 @@ const Card = ({ card, error, handleBlur, handleChange, index, setFieldValue, upd {is_open && ( )} @@ -59,13 +56,10 @@ const Card = ({ card, error, handleBlur, handleChange, index, setFieldValue, upd Card.propTypes = { card: PropTypes.object, - error: PropTypes.array, - handleBlur: PropTypes.func, - handleChange: PropTypes.func, + error: PropTypes.object, index: PropTypes.number, setFieldValue: PropTypes.func, updateErrors: PropTypes.func, - validateField: PropTypes.func, values: PropTypes.object, }; diff --git a/packages/account/src/Sections/Verification/ProofOfOwnership/expanded-card.jsx b/packages/account/src/Sections/Verification/ProofOfOwnership/expanded-card.jsx index af7f115d9bc5..4ba73987815b 100644 --- a/packages/account/src/Sections/Verification/ProofOfOwnership/expanded-card.jsx +++ b/packages/account/src/Sections/Verification/ProofOfOwnership/expanded-card.jsx @@ -7,22 +7,24 @@ import SampleCreditCardModal from 'Components/sample-credit-card-modal'; import classNames from 'classnames'; import { IDENTIFIER_TYPES, VALIDATIONS } from './constants/constants.js'; -const ExpandedCard = ({ card_details, error, index, setFieldValue, updateErrors, validateField, values }) => { +const ExpandedCard = ({ card_details, error, index, setFieldValue, updateErrors, values }) => { const [is_sample_modal_open, setIsSampleModalOpen] = useState(false); - const handleUploadedFile = (name, file) => { - setFieldValue(name, file); + const handleUploadedFile = async (name, file) => { + await setFieldValue(name, file); }; - const handleBlur = (name, payment_method_identifier, item_id, item_index) => { + const handleBlur = (name, payment_method_identifier, identifier_type, item_id, item_index, documents_required) => { handleIdentifierChange( name, - formatIdentifier(payment_method_identifier, card_details?.identifier_type), + formatIdentifier(payment_method_identifier, identifier_type), item_id, - item_index + item_index, + documents_required ); }; - const handleIdentifierChange = (name, payment_method_identifier, item_id, item_index) => { + const handleIdentifierChange = (name, payment_method_identifier, item_id, item_index, documents_required) => { setFieldValue(`${name}`, { ...values.data?.[index]?.[item_index], + documents_required, id: item_id, payment_method_identifier, is_generic_pm: card_details.is_generic_pm, @@ -92,16 +94,19 @@ const ExpandedCard = ({ card_details, error, index, setFieldValue, updateErrors, `data[${index}].[${item_index}]`, e.currentTarget.value.trim(), item.id, - item_index + item_index, + card_details.documents_required ); }} - value={values?.data?.[index]?.[item_index]?.payment_method_identifier ?? ''} + value={values?.data?.[index]?.[item_index]?.payment_method_identifier || ''} onBlur={e => { handleBlur( `data[${index}].[${item_index}]`, e.currentTarget.value.trim(), + card_details?.identifier_type, item.id, - item_index + item_index, + card_details.documents_required ); }} data-testid='dt_payment_method_identifier' @@ -129,9 +134,10 @@ const ExpandedCard = ({ card_details, error, index, setFieldValue, updateErrors, onChange={e => { handleIdentifierChange( `data[${index}].[${item_index}]`, - e.currentTarget.value, + e.currentTarget.value.trim(), item.id, - item_index + item_index, + card_details.documents_required ); }} value={ @@ -141,9 +147,11 @@ const ExpandedCard = ({ card_details, error, index, setFieldValue, updateErrors, onBlur={e => { handleBlur( `data[${index}].[${item_index}]`, - e.currentTarget.value, + e.currentTarget.value.trim(), + card_details?.identifier_type, item.id, - item_index + item_index, + card_details.documents_required ); }} data-testid='dt_payment_method_identifier' @@ -163,7 +171,6 @@ const ExpandedCard = ({ card_details, error, index, setFieldValue, updateErrors, class_name='proof-of-ownership__card-open-inputs-photo' name={`data[${index}].[${item_index}].files[${i}]`} error={error?.[item_index]?.files?.[i]} - validateField={validateField} index={index} item_index={item_index} sub_index={i} @@ -189,11 +196,10 @@ const ExpandedCard = ({ card_details, error, index, setFieldValue, updateErrors, ExpandedCard.propTypes = { card_details: PropTypes.object, - error: PropTypes.array, + error: PropTypes.object, index: PropTypes.number, setFieldValue: PropTypes.func, updateErrors: PropTypes.func, - validateField: PropTypes.func, values: PropTypes.object, }; diff --git a/packages/account/src/Sections/Verification/ProofOfOwnership/file-uploader.jsx b/packages/account/src/Sections/Verification/ProofOfOwnership/file-uploader.jsx index 3a08535839f3..eda175ad4938 100644 --- a/packages/account/src/Sections/Verification/ProofOfOwnership/file-uploader.jsx +++ b/packages/account/src/Sections/Verification/ProofOfOwnership/file-uploader.jsx @@ -15,32 +15,33 @@ const FileUploader = ({ name, sub_index, updateErrors, - validateField, }) => { const [show_browse_button, setShowBrowseButton] = React.useState(!file_name); // Create a reference to the hidden file input element const hidden_file_input = React.useRef(null); const handleClick = e => { - e.stopPropagation(); + e.nativeEvent.preventDefault(); + e.nativeEvent.stopPropagation(); e.nativeEvent.stopImmediatePropagation(); hidden_file_input.current.click(); }; const handleChange = async event => { - event.stopPropagation(); + event.nativeEvent.preventDefault(); + event.nativeEvent.stopPropagation(); event.nativeEvent.stopImmediatePropagation(); const file_to_upload = await compressImageFiles([event.target.files[0]]); handleFile(name, file_to_upload[0]); setShowBrowseButton(!file_to_upload[0]); }; - const handleIconClick = e => { - e.stopPropagation(); + const handleIconClick = async e => { + e.nativeEvent.preventDefault(); + e.nativeEvent.stopPropagation(); e.nativeEvent.stopImmediatePropagation(); hidden_file_input.current.value = ''; - handleFile(name, ''); + await handleFile(name, ''); setShowBrowseButton(prevState => !prevState); - updateErrors(index, item_index, sub_index); - validateField('files'); + await updateErrors(index, item_index, sub_index); }; return (
@@ -98,7 +99,6 @@ FileUploader.propTypes = { name: PropTypes.string, sub_index: PropTypes.number, updateErrors: PropTypes.func, - validateField: PropTypes.func, }; export default FileUploader; diff --git a/packages/account/src/Sections/Verification/ProofOfOwnership/proof-of-ownership-form.jsx b/packages/account/src/Sections/Verification/ProofOfOwnership/proof-of-ownership-form.jsx index 64a1dea2bf2e..5f3b310f4085 100644 --- a/packages/account/src/Sections/Verification/ProofOfOwnership/proof-of-ownership-form.jsx +++ b/packages/account/src/Sections/Verification/ProofOfOwnership/proof-of-ownership-form.jsx @@ -1,5 +1,5 @@ import classNames from 'classnames'; -import React, { useRef, useState, useEffect } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import { Button, useStateCallback } from '@deriv/components'; import { Formik } from 'formik'; @@ -22,109 +22,149 @@ const ProofOfOwnershipForm = ({ client_email, grouped_payment_method_data, refreshNotifications, - total_documents_required, updateAccountStatus, }) => { const grouped_payment_method_data_keys = Object.keys(grouped_payment_method_data); const initial_values = {}; - const [is_submit_button_disabled, setIsSubmitButtonDisabled] = React.useState(true); const [form_state, setFormState] = useStateCallback({ should_show_form: true }); - const form_ref = useRef(); - const [document_upload_errors, setDocumentUploadErrors] = useState(); - useEffect(() => { - setDocumentUploadErrors([]); - }, []); + const form_ref = React.useRef(); const fileReadErrorMessage = filename => { return localize('Unable to read file {{name}}', { name: filename }); }; - const validateFields = values => { - const errors = {}; - errors.data = [...document_upload_errors]; + let errors = {}; + errors.data = [...(form_ref?.current?.errors?.data || [])]; let total_documents_uploaded = 0; let has_errors = false; + let are_files_uploaded = false; const cards = values?.data; Object.keys(cards)?.forEach?.(card_key => { const items = cards?.[card_key] ?? {}; - errors.data[card_key] = errors.data?.[card_key] ?? []; - Object.keys(items)?.forEach?.(item_key => { - errors.data[card_key][item_key] = errors.data?.[card_key]?.[item_key] ?? {}; - const payment_method = items?.[item_key]; - const payment_method_identifier = payment_method?.payment_method_identifier?.trim(); - const is_pm_identifier_provided = - payment_method?.is_generic_pm || payment_method_identifier?.length > 0; - const is_credit_or_debit_card = payment_method?.identifier_type === IDENTIFIER_TYPES.card_number; - total_documents_uploaded += payment_method?.files?.filter(Boolean)?.length ?? 0; - payment_method?.files?.forEach((file, i) => { - errors.data[card_key][item_key].files = errors?.data?.[card_key]?.[item_key]?.files ?? []; - if (file?.type && !/(image|application)\/(jpe?g|pdf|png)$/.test(file?.type)) { - errors.data[card_key][item_key].files[i] = localize( - "That file format isn't supported. Please upload .pdf, .png, .jpg, or .jpeg files only." - ); + const item_keys = Object.keys(items); + item_keys?.forEach?.(item_key => { + if (!has_errors) { + errors.data[card_key] = errors.data?.[card_key] ?? {}; + errors.data[card_key][item_key] = errors.data?.[card_key]?.[item_key] ?? {}; + const payment_method = items?.[item_key]; + const payment_method_identifier = payment_method?.payment_method_identifier?.trim(); + const is_payment_method_identifier_provided = + payment_method?.is_generic_pm || payment_method_identifier?.length > 0; + const is_credit_or_debit_card = payment_method?.identifier_type === IDENTIFIER_TYPES.card_number; + total_documents_uploaded = payment_method?.files?.filter(Boolean)?.length ?? 0; + if (is_payment_method_identifier_provided) { + are_files_uploaded = total_documents_uploaded === payment_method.documents_required; + } else if ( + (!payment_method?.documents_required && total_documents_uploaded === 0) || + (!is_payment_method_identifier_provided && total_documents_uploaded === 0) + ) { + are_files_uploaded = true; + } else if ( + (payment_method?.documents_required && + is_payment_method_identifier_provided && + total_documents_uploaded === 0) || + (!is_payment_method_identifier_provided && + total_documents_uploaded === payment_method?.documents_required * 0.5) + ) { + are_files_uploaded = false; } - if (file?.size / 1024 > 8000) { - errors.data[card_key][item_key].files[i] = localize( - 'That file is too big (only up to 8MB allowed). Please upload another file.' - ); - } - if (!is_pm_identifier_provided) { + delete errors.data[card_key][item_key].payment_method_identifier; + payment_method?.files?.forEach((file, i) => { + errors.data[card_key][item_key].files = errors?.data?.[card_key]?.[item_key]?.files ?? []; + if (file?.type && !/(image|application)\/(jpe?g|pdf|png)$/.test(file?.type)) { + errors.data[card_key][item_key].files[i] = localize( + "That file format isn't supported. Please upload .pdf, .png, .jpg, or .jpeg files only." + ); + } + if (file?.size / 1024 > 8000) { + errors.data[card_key][item_key].files[i] = localize( + 'That file is too big (only up to 8MB allowed). Please upload another file.' + ); + } + if (errors.data[card_key][item_key].files.length === 0) { + delete errors.data[card_key][item_key].files; + } + if ( + !is_payment_method_identifier_provided && + (total_documents_uploaded === payment_method?.documents_required || + (!payment_method?.documents_required && total_documents_uploaded > 0) || + total_documents_uploaded === payment_method?.documents_required * 0.5) + ) { + errors.data[card_key][item_key].payment_method_identifier = + localize('Please complete this field.'); + } + }); + if ( + is_credit_or_debit_card && + ((payment_method_identifier?.length !== 0 && + (payment_method_identifier?.length !== 16 || payment_method_identifier?.length > 19) && + !VALIDATIONS.is_formated_card_number(payment_method_identifier)) || + (payment_method_identifier?.length === 16 && + VALIDATIONS.has_invalid_characters(payment_method_identifier))) + ) { errors.data[card_key][item_key].payment_method_identifier = - localize('Please complete this field.'); + localize('Enter your full card number'); } - }); - if ( - (is_credit_or_debit_card && - payment_method_identifier?.length !== 0 && - (payment_method_identifier?.length !== 16 || payment_method_identifier?.length > 19) && - !VALIDATIONS.is_formated_card_number(payment_method_identifier)) || - (is_credit_or_debit_card && - payment_method_identifier?.length === 16 && - VALIDATIONS.has_invalid_characters(payment_method_identifier)) - ) { - errors.data[card_key][item_key].payment_method_identifier = localize('Enter your full card number'); + if (!payment_method_identifier && total_documents_uploaded === 0) { + delete form_ref.current?.values?.data?.[card_key]?.[item_key]; + if ((form_ref.current?.values?.data?.[card_key]?.filter?.(Boolean)?.length || 0) === 0) { + delete form_ref.current?.values?.data?.[card_key]; + } + } + if (Object.keys(errors?.data?.[card_key]?.[item_key] || {}).length === 0) { + delete errors?.data?.[card_key]?.[item_key]; + if (Object.keys(errors?.data?.[card_key])?.length === 0) { + delete errors?.data?.[card_key]; + } + } + has_errors = + has_errors || + errors?.data?.[card_key]?.[item_key]?.payment_method_identifier?.trim?.()?.length > 0 || + errors?.data?.[card_key]?.[item_key]?.files?.length > 0 || + !are_files_uploaded; } - has_errors = - has_errors || - errors?.data?.[card_key]?.[item_key]?.payment_method_identifier?.trim?.()?.length > 0 || - errors?.data?.[card_key]?.[item_key]?.files?.length > 0; }); + if ((form_ref.current?.values?.data?.[card_key]?.filter(Boolean)?.length || 0) === 0) { + delete form_ref.current?.values?.data?.[card_key]; + } }); - setIsSubmitButtonDisabled(total_documents_required !== total_documents_uploaded || has_errors); + has_errors = has_errors || (form_ref.current?.values?.data?.filter(Boolean).length || 0) === 0; + if (!has_errors) { + errors = {}; + } return errors; }; - const updateErrors = (index, item_index, sub_index) => { + const updateErrors = async (index, item_index, sub_index) => { let error_count = 0; const errors = {}; - errors.data = [...document_upload_errors]; + errors.data = [...(form_ref?.current?.errors?.data || [])]; if (typeof errors.data[index] === 'object') { - delete errors.data[index][item_index].files[sub_index]; + delete errors?.data?.[index]?.[item_index]?.files?.[sub_index]; const has_other_errors = errors?.data[index]?.[item_index]?.files?.some(error => error !== null); if (!has_other_errors) { delete errors?.data[index]?.[item_index]; } - document_upload_errors.forEach(e => { - error_count += Object.keys(e).length; + errors.data.forEach(e => { + error_count += Object.keys(e || {}).length; }); if (error_count === 0) { errors.data = []; } } - setDocumentUploadErrors(errors?.data); - setIsSubmitButtonDisabled(true); + await form_ref.current.setErrors(errors); + await form_ref.current.validateForm(); }; - const handleSubmit = () => { + const handleFormSubmit = ({ data: form_values }) => { try { setFormState({ ...form_state, ...{ is_btn_loading: true } }); - const { data: formValues } = form_ref.current.values; const uploader = new DocumentUploader({ connection: WS.getSocket() }); if (form_ref.current.errors.length > 0) { // Only upload if no errors and a file has been attached return; } - Object.keys(formValues).forEach(card_key => { - Object.keys(formValues[card_key]).forEach(async card_item_key => { - const payment_method_details = formValues[card_key][card_item_key]; + Object.keys(form_values).forEach(card_key => { + Object.keys(form_values[card_key]).forEach(async card_item_key => { + const payment_method_details = form_values[card_key][card_item_key]; if (payment_method_details.files.length > 0) { const processed_files = await readFiles(payment_method_details.files, fileReadErrorMessage, { documentType: DOCUMENT_TYPE.proof_of_ownership, @@ -140,19 +180,21 @@ const ProofOfOwnershipForm = ({ const response = await uploader.upload(processed_file); if (response.warning) { if (response.warning.trim() === 'DuplicateUpload') { - form_ref.current.errors.data = document_upload_errors; - const { data: form_errors } = form_ref?.current?.errors; - if (typeof form_errors[card_key] !== 'object') { - form_errors[card_key] = {}; + let { errors: form_errors } = form_ref?.current; + if (!form_errors?.data) { + form_errors = {}; + form_errors.data = []; + form_errors.data[card_key] = {}; + } else if (!form_errors.data?.[card_key]) { + form_errors.data[card_key] = {}; } - form_errors[card_key][card_item_key] = - form_errors?.[card_key]?.[card_item_key] ?? {}; - form_errors[card_key][card_item_key].files = - form_errors?.[card_key]?.[card_item_key]?.files ?? []; - form_errors[card_key][card_item_key].files[sub_index] = response.message; // Document already uploaded - setDocumentUploadErrors(form_errors); - form_ref.current.setErrors(form_errors); - form_ref.current.validateForm(); + form_errors.data[card_key][card_item_key] = + form_errors?.data?.[card_key]?.[card_item_key] ?? {}; + form_errors.data[card_key][card_item_key].files = + form_errors?.data?.[card_key]?.[card_item_key]?.files ?? []; + form_errors.data[card_key][card_item_key].files[sub_index] = response.message; // Document already uploaded + await form_ref.current.setErrors(form_errors); + await form_ref.current.validateForm(); setFormState({ ...form_state, ...{ is_btn_loading: false } }); } else { setFormState({ ...form_state, ...{ is_btn_loading: false } }); @@ -170,15 +212,21 @@ const ProofOfOwnershipForm = ({ } }; return ( - - {({ values, errors, handleChange, handleBlur, setFieldValue, validateField }) => ( + + {({ values, errors, setFieldValue, handleSubmit, isValid, dirty }) => (
{ - e.preventDefault(); - e.stopPropagation(); + e.nativeEvent.preventDefault(); + e.nativeEvent.stopPropagation(); e.nativeEvent.stopImmediatePropagation(); + handleSubmit(e); }} > @@ -201,12 +249,9 @@ const ProofOfOwnershipForm = ({
@@ -218,14 +263,13 @@ const ProofOfOwnershipForm = ({