Skip to content

Commit

Permalink
P2p 2fa feature (binary-com#6422)
Browse files Browse the repository at this point in the history
* carol/ P2P: 2FA (binary-com#6009)

* add: icons

* add: one more icon

* add email verification modal

* add invalid verification link modal

* add email verified

* add email blocked modal

* add email blocked modal

* use align prop instead

* carol/ P2P: Email verification for orders (binary-com#6299)

* fix: quotes

* add: email verification

* cleanup

* don't kill me

* add response checks

* add comment

* add: amount + currency

* fixed loading of order details and chat

* fixed design on responsive

* fixed verification modal in responsive

* show modal if error

* fix

* fixed truncated modal in ios

* fix time

* fix invalid verification modal

* fix

* fix

* fixed verification modal in desktop

* fix responsive +logged out user

* fix: design for seller

* the solution to all my problems

* fix: modal

* fix logout + modal

* hide extra modal

* i got 99 problems and 2fa is all of em

* fix

* fix

* fix: amount

* fix: amount

* fix: add modal

* fix: rating modal

Co-authored-by: Farrah Mae Ochoa <farrah@deriv.com>

Co-authored-by: Nijil Nirmal <nijil@deriv.com>
Co-authored-by: Farrah Mae Ochoa <farrah@deriv.com>
  • Loading branch information
3 people committed Sep 8, 2022
1 parent 451abb4 commit b142e79
Show file tree
Hide file tree
Showing 35 changed files with 769 additions and 117 deletions.
44 changes: 37 additions & 7 deletions packages/cashier/src/pages/p2p-cashier/p2p-cashier.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ const P2PCashier = ({
setOnRemount,
}) => {
const [order_id, setOrderId] = React.useState(null);
const [action_param, setActionParam] = React.useState();
const [code_param, setCodeParam] = React.useState();

const server_time = {
get,
init,
Expand All @@ -42,21 +45,46 @@ const P2PCashier = ({

React.useEffect(() => {
const url_params = new URLSearchParams(location.search);
const passed_order_id = url_params.get('order');
let passed_order_id;

setActionParam(url_params.get('action'));
if (is_mobile) {
setCodeParam(localStorage.getItem('verification_code.p2p_order_confirm'));
} else if (!code_param) {
if (url_params.has('code')) {
setCodeParam(url_params.get('code'));
} else if (localStorage.getItem('verification_code.p2p_order_confirm')) {
setCodeParam(localStorage.getItem('verification_code.p2p_order_confirm'));
}
}

// Different emails give us different params (order / order_id),
// don't remove order_id since it's consistent for mobile and web for 2FA
if (url_params.has('order_id')) {
passed_order_id = url_params.get('order_id');
} else if (url_params.has('order')) {
passed_order_id = url_params.get('order');
}

if (passed_order_id) {
setQueryOrder(passed_order_id);
}

return () => setQueryOrder(null);
}, [location.search, setQueryOrder]);
}, [setQueryOrder]);

const setQueryOrder = React.useCallback(
input_order_id => {
const current_query_params = new URLSearchParams(location.search);

if (current_query_params.has('order')) {
if (is_mobile) {
current_query_params.delete('action');
current_query_params.delete('code');
}

if (current_query_params.has('order_id') || current_query_params.has('order')) {
current_query_params.delete('order');
current_query_params.delete('order_id');
}

if (input_order_id) {
Expand Down Expand Up @@ -91,8 +119,9 @@ const P2PCashier = ({
return (
<P2P
addNotificationMessage={addNotificationMessage}
client={{ currency, local_currency_config, is_virtual, residence, loginid }}
balance={balance}
client={{ currency, local_currency_config, is_virtual, residence, loginid }}
current_focus={current_focus}
filterNotificationMessages={filterNotificationMessages}
history={history}
is_dark_mode_on={is_dark_mode_on}
Expand All @@ -107,13 +136,14 @@ const P2PCashier = ({
removeNotificationByKey={removeNotificationByKey}
removeNotificationMessage={removeNotificationMessage}
server_time={server_time}
setCurrentFocus={setCurrentFocus}
setNotificationCount={setNotificationCount}
setOnRemount={setOnRemount}
setOrderId={setQueryOrder}
setOnRemount={setOnRemount}
should_show_verification={/verification/.test(location.hash)}
verification_action={action_param}
verification_code={code_param}
websocket_api={WS}
current_focus={current_focus}
setCurrentFocus={setCurrentFocus}
/>
);
};
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions packages/components/src/components/icon/icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,13 @@ import './common/ic-edit.svg';
import './common/ic-email-firewall.svg';
import './common/ic-email-outline.svg';
import './common/ic-email-sent-dashboard.svg';
import './common/ic-email-sent-p2p.svg';
import './common/ic-email-sent.svg';
import './common/ic-email-spam.svg';
import './common/ic-email-typo.svg';
import './common/ic-email-verification-link-blocked.svg';
import './common/ic-email-verification-link-invalid.svg';
import './common/ic-email-verification-link-valid.svg';
import './common/ic-email-verified.svg';
import './common/ic-email.svg';
import './common/ic-empty-folder.svg';
Expand Down
4 changes: 4 additions & 0 deletions packages/components/stories/icon/icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,13 @@ export const icons =
'IcEmailFirewall',
'IcEmailOutline',
'IcEmailSentDashboard',
'IcEmailSentP2p',
'IcEmailSent',
'IcEmailSpam',
'IcEmailTypo',
'IcEmailVerificationLinkBlocked',
'IcEmailVerificationLinkInvalid',
'IcEmailVerificationLinkValid',
'IcEmailVerified',
'IcEmail',
'IcEmptyFolder',
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/App/Containers/Redirect/redirect.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ const Redirect = ({
redirected_to_route = true;
break;
}
case 'p2p_order_confirm': {
history.push({
pathname: routes.cashier_p2p,
search: url_query_string,
});
redirected_to_route = true;
break;
}

default:
break;
Expand Down
2 changes: 1 addition & 1 deletion packages/p2p/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = {
'/crowdin/',
// TODO: Update the test files once the major features are done
// This is a temporary change, I hope
'/src/components/order-details/',
'/src/components/order*',
],
coveragePathIgnorePatterns: [
'<rootDir>/.eslintrc.js',
Expand Down
43 changes: 30 additions & 13 deletions packages/p2p/src/components/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,19 @@ import './app.scss';

const App = props => {
const { general_store, order_store } = useStores();
const { balance, className, history, lang, Notifications, order_id, server_time, websocket_api, setOnRemount } =
props;
const {
balance,
className,
history,
lang,
Notifications,
order_id,
server_time,
verification_action,
verification_code,
websocket_api,
setOnRemount,
} = props;

React.useEffect(() => {
general_store.setAppProps(props);
Expand Down Expand Up @@ -68,6 +79,20 @@ const App = props => {
setLanguage(lang);
}, [lang]);

React.useEffect(() => {
if (verification_code) {
// We need an extra state since we delete the code from the query params.
// Do not remove.
order_store.setVerificationCode(verification_code);
}
if (verification_action && verification_code) {
order_store.setIsLoadingModalOpen(true);
order_store.verifyEmailVerificationCode(verification_action, verification_code);
}

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [verification_action, verification_code]);

return (
<main className={classNames('p2p-cashier', className)}>
<Notifications />
Expand All @@ -77,25 +102,17 @@ const App = props => {
};

App.propTypes = {
balance: PropTypes.string,
className: PropTypes.string,
client: PropTypes.shape({
currency: PropTypes.string.isRequired,
is_virtual: PropTypes.bool.isRequired,
local_currency_config: PropTypes.shape({
currency: PropTypes.string.isRequired,
decimal_places: PropTypes.number,
}).isRequired,
loginid: PropTypes.string.isRequired,
residence: PropTypes.string.isRequired,
}),
history: PropTypes.object,
balance: PropTypes.string,
lang: PropTypes.string,
modal_root_id: PropTypes.string.isRequired,
order_id: PropTypes.string,
server_time: PropTypes.object,
setNotificationCount: PropTypes.func,
setOnRemount: PropTypes.func,
verification_action: PropTypes.string,
verification_code: PropTypes.string,
websocket_api: PropTypes.object.isRequired,
};

Expand Down
2 changes: 1 addition & 1 deletion packages/p2p/src/components/buy-sell/buy-sell-modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ const BuySellModal = ({ table_type, selected_ad, should_show_popup, setShouldSho
};

const onConfirmClick = order_info => {
order_store.setOrderId(order_info.id);
general_store.redirectTo('orders', { nav: { location: 'buy_sell' } });
order_store.setOrderId(order_info.id);
setShouldShowPopup(false);
buy_sell_store.setShowAdvertiserPage(false);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Icon, Modal, Text } from '@deriv/components';
import { Localize } from 'Components/i18next';

const EmailLinkBlockedModal = ({
// TODO: Uncomment when time is available in BE response
// blocked_for_minutes,
email_link_blocked_modal_error_message,
is_email_link_blocked_modal_open,
setIsEmailLinkBlockedModalOpen,
}) => {
return (
<Modal
has_close_icon
is_open={is_email_link_blocked_modal_open}
renderTitle={() => <></>}
toggleModal={() => setIsEmailLinkBlockedModalOpen(false)}
width='440px'
>
<Modal.Body className='email-link-blocked-modal'>
<Icon icon='IcEmailVerificationLinkBlocked' size='128' />
<Text className='email-link-blocked-modal--text' color='prominent' size='s' weight='bold'>
<Localize i18n_default_text='Too many failed attempts' />
</Text>
<Text align='center' color='prominent' size='s'>
{email_link_blocked_modal_error_message}
</Text>
</Modal.Body>
</Modal>
);
};

EmailLinkBlockedModal.propTypes = {
// TODO: Uncomment when time is available in BE response
// blocked_for_minutes: PropTypes.number,
email_link_blocked_modal_error_message: PropTypes.string,
is_email_link_blocked_modal_open: PropTypes.bool,
setIsEmailLinkBlockedModalOpen: PropTypes.func,
};

export default EmailLinkBlockedModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.email-link-blocked-modal {
align-items: center;
display: flex;
flex-direction: column;

&--text {
margin: 2.4rem 0;
}
}
4 changes: 4 additions & 0 deletions packages/p2p/src/components/email-link-blocked-modal/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import EmailLinkBlockedModal from './email-link-blocked-modal.jsx';
import './email-link-blocked-modal.scss';

export default EmailLinkBlockedModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Icon, Modal, Text } from '@deriv/components';
import { Localize } from 'Components/i18next';

const EmailLinkVerifiedModal = ({
amount,
currency,
is_email_link_verified_modal_open,
onClickConfirm,
setIsEmailLinkVerifiedModalOpen,
}) => {
return (
<Modal
has_close_icon
is_open={is_email_link_verified_modal_open}
renderTitle={() => <></>}
toggleModal={() => setIsEmailLinkVerifiedModalOpen(false)}
width='440px'
>
<Modal.Body className='email-verified-modal'>
<Icon icon='IcEmailVerificationLinkValid' size='128' />
<Text className='email-verified-modal--text' color='prominent' size='s' weight='bold'>
<Localize i18n_default_text="We've verified your order" />
</Text>
<Text align='center' color='prominent' size='s'>
<Localize
i18n_default_text="Please ensure you've received {{amount}} {{currency}} in your account and hit Confirm to complete the transaction."
values={{ amount, currency }}
/>
</Text>
</Modal.Body>
<Modal.Footer className='email-verified-modal--footer'>
<Button
large
primary
onClick={() => {
setIsEmailLinkVerifiedModalOpen(false);
onClickConfirm();
}}
>
<Localize i18n_default_text='Confirm' />
</Button>
</Modal.Footer>
</Modal>
);
};

EmailLinkVerifiedModal.propTypes = {
amount: PropTypes.string,
currency: PropTypes.string,
is_email_link_verified_modal_open: PropTypes.bool,
onClickConfirm: PropTypes.func,
setIsEmailLinkVerifiedModalOpen: PropTypes.func,
};

export default EmailLinkVerifiedModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.email-verified-modal {
align-items: center;
display: flex;
flex-direction: column;

&--footer {
align-self: center;
}

&--text {
margin: 2.4rem 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import EmailLinkVerifiedModal from './email-link-verified-modal.jsx';
import './email-link-verified-modal.scss';

export default EmailLinkVerifiedModal;
Loading

0 comments on commit b142e79

Please sign in to comment.