Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WALL] george / WALL-4850 / Password change and account creation success message are not shown when changing password #17318

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ import PasswordMeter from './PasswordMeter';
import PasswordViewerIcon from './PasswordViewerIcon';
import './WalletPasswordField.scss';

export const validatePassword = (
password: string,
mt5Policy: boolean,
localize: ReturnType<typeof useTranslations>['localize']
) => {
type TValidatePasswordProps = {
isMT5PasswordNotSet?: boolean;
localize: ReturnType<typeof useTranslations>['localize'];
mt5Policy: boolean;
password: string;
};

export const validatePassword = (values: TValidatePasswordProps) => {
const { isMT5PasswordNotSet, localize, mt5Policy, password } = values;
const score = mt5Policy ? calculateScoreMT5(password) : calculateScoreCFD(password);
let validationErrorMessage = '';

Expand All @@ -38,7 +42,9 @@ export const validatePassword = (
} else {
cfdSchema(localize).validateSync(password);
}
validationErrorMessage = getWarningMessages(localize)[feedback.warning as passwordKeys] ?? '';
validationErrorMessage = isMT5PasswordNotSet
? getWarningMessages(localize)[feedback.warning as passwordKeys] ?? ''
: '';
} catch (err) {
if (err instanceof ValidationError) {
validationErrorMessage = err?.message;
Expand All @@ -50,6 +56,7 @@ export const validatePassword = (

const WalletPasswordField: React.FC<WalletPasswordFieldProps> = ({
autoComplete,
isMT5PasswordNotSet,
label,
mt5Policy = false,
name = 'walletPasswordField',
Expand All @@ -67,8 +74,8 @@ const WalletPasswordField: React.FC<WalletPasswordFieldProps> = ({
const [errorMessage, setErrorMessage] = useState('');

const { score, validationErrorMessage } = useMemo(
() => validatePassword(password, mt5Policy, localize),
[password, mt5Policy, localize]
() => validatePassword({ isMT5PasswordNotSet, localize, mt5Policy, password }),
[password, mt5Policy, localize, isMT5PasswordNotSet]
);
const passwordValidation = mt5Policy ? !validPasswordMT5(password) : !validPassword(password);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Loader } from '@deriv-com/ui';
import { WalletTextFieldProps } from '../WalletTextField/WalletTextField';

export interface WalletPasswordFieldProps extends WalletTextFieldProps {
isMT5PasswordNotSet?: boolean;
mt5Policy?: boolean; // This prop is used to utilize the new password validation for MT5.
password: string;
passwordError?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type TModalContext = {
type TModalOptions = {
defaultRootId?: 'wallets_modal_root' | 'wallets_modal_show_header_root';
rootRef?: React.RefObject<HTMLElement>;
shouldCloseOnClickOutside?: boolean;
shouldHideDerivAppHeader?: boolean;
};

Expand Down Expand Up @@ -84,7 +85,10 @@ const ModalProvider = ({ children }: React.PropsWithChildren<unknown>) => {
}));
};

useOnClickOutside(modalRef, isDesktop ? hide : () => undefined);
const onClickOutsideHandler = () =>
modalOptions?.shouldCloseOnClickOutside === false || !isDesktop ? () => undefined : hide;

useOnClickOutside(modalRef, onClickOutsideHandler);

const modalRootRef = useMemo(() => {
// if they specify their own root, prioritize this first
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { FC } from 'react';
import { DerivLightMt5SuccessPasswordResetIcon } from '@deriv/quill-icons';
import { useTranslations } from '@deriv-com/translations';
import { ActionScreen, Button, useDevice } from '@deriv-com/ui';
import { PlatformDetails } from '../../features/cfd/constants';
import { ModalStepWrapper, ModalWrapper } from '../Base';
import './WalletsChangeMT5Password.scss';

type TWalletSuccessChangeMT5Password = {
onClick: () => void;
};

const WalletSuccessChangeMT5Password: FC<TWalletSuccessChangeMT5Password> = ({ onClick }) => {
const { isDesktop } = useDevice();
const { localize } = useTranslations();

const title = localize('Success');
const description = localize(
'You have a new {{title}} password to log in to your {{title}} accounts on the web and mobile apps.',
{ title: PlatformDetails.mt5.title }
);
const renderButtons = (
<Button isFullWidth={!isDesktop} onClick={onClick} size='lg' textSize='sm'>
{localize('Next')}
</Button>
);

if (isDesktop) {
return (
<ModalWrapper hideCloseButton shouldPreventCloseOnEscape>
<div className='wallets-change-mt5-password'>
<ActionScreen
actionButtons={renderButtons}
description={description}
descriptionSize='sm'
icon={<DerivLightMt5SuccessPasswordResetIcon height={100} width={100} />}
title={title}
/>
</div>
</ModalWrapper>
);
}

return (
<ModalStepWrapper
renderFooter={() => renderButtons}
shouldHideFooter={isDesktop}
shouldHideHeader
shouldPreventCloseOnEscape
>
<div className='wallets-change-mt5-password'>
<ActionScreen
description={description}
descriptionSize='sm'
icon={<DerivLightMt5SuccessPasswordResetIcon height={100} width={100} />}
title={title}
/>
</div>
</ModalStepWrapper>
);
};

export default WalletSuccessChangeMT5Password;
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.wallets-change-mt5-password {
width: 44rem;
padding: 2.4rem;
display: flex;
flex-direction: column;
gap: 2.4rem;
background-color: var(--general-main-1, #ffffff);
border-radius: 0.8rem;

@include mobile-or-tablet-screen {
width: 100%;
padding-inline: 1.6rem;
gap: 1.6rem;
margin: 0 auto;
max-width: 60rem;
}

&__footer {
display: flex;
justify-content: flex-end;
gap: 0.8rem;

@include mobile-or-tablet-screen {
width: 100%;
justify-content: center;
margin: 0 auto;
max-width: 60rem;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as WalletSuccessChangeMT5Password } from './WalletSuccessChangeMT5Password';
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import { SentEmailContent, WalletError } from '../../../../components';
import { ModalStepWrapper, ModalWrapper } from '../../../../components/Base';
import { useModal } from '../../../../components/ModalProvider';
import { WalletSuccessChangeMT5Password } from '../../../../components/WalletsChangeMT5Password';
import { platformPasswordResetRedirectLink } from '../../../../utils/cfd';
import { validPassword, validPasswordMT5 } from '../../../../utils/password-validation';
import { CFD_PLATFORMS, JURISDICTION, MARKET_TYPE, PlatformDetails } from '../../constants';
Expand Down Expand Up @@ -58,13 +59,14 @@
status: emailVerificationStatus,
} = useVerifyEmail();
const { isDesktop } = useDevice();
const { getModalState, hide } = useModal();
const { getModalState, hide, setModalOptions } = useModal();
const { data: settingsData } = useSettings();
const { localize } = useTranslations();

const { email } = settingsData;

const [password, setPassword] = useState('');
const [isPasswordChanged, setIsPasswordChanged] = useState(false);

const marketType = account.market_type ?? 'synthetic';
const platform = account.platform;
Expand All @@ -81,22 +83,10 @@
(createMT5AccountError?.error?.code === 'InvalidTradingPlatformPasswordFormat' ||
createMT5AccountError?.error?.code === 'IncorrectMT5PasswordFormat');

const onSubmit = useCallback(async () => {
// ====== Create MT5 Account ======
// In order to create account, we need to set a password through trading_platform_password_change endpoint first,
// then only mt5_create_account can be called, otherwise it will response an error for password required.
// =================================

const createMT5Account = useCallback(() => {
const accountType = marketType === MARKET_TYPE.SYNTHETIC ? 'gaming' : marketType;
const categoryAccountType = isVirtual ? 'demo' : accountType;

if (isMT5PasswordNotSet) {
await tradingPasswordChangeMutateAsync({
new_password: password,
platform: mt5Platform,
});
}

createMT5AccountMutate({
payload: {
account_type: categoryAccountType,
Expand Down Expand Up @@ -127,14 +117,15 @@
zipCode: settingsData?.address_postcode ?? '',
},
});
}, [

Check warning on line 120 in packages/wallets/src/features/cfd/modals/MT5PasswordModal/MT5PasswordModal.tsx

View workflow job for this annotation

GitHub Actions / Build And Test

React Hook useCallback has an unnecessary dependency: 'isMT5PasswordNotSet'. Either exclude it or remove the dependency array
availableMT5AccountsData,
createMT5AccountMutate,
isVirtual,
isMT5PasswordNotSet,
isVirtual,
marketType,
mt5Platform,
password,
product,
selectedJurisdiction,
settingsData?.address_city,
settingsData?.address_line_1,
settingsData?.address_postcode,
Expand All @@ -143,11 +134,24 @@
settingsData?.email,
settingsData?.first_name,
settingsData?.phone,
tradingPasswordChangeMutateAsync,
selectedJurisdiction,
product,
]);

const onSubmit = useCallback(async () => {
// ====== Create MT5 Account ======
// In order to create account, we need to set a password through trading_platform_password_change endpoint first,
// then only mt5_create_account can be called, otherwise it will response an error for password required.
// =================================

if (isMT5PasswordNotSet) {
await tradingPasswordChangeMutateAsync({
new_password: password,
platform: mt5Platform,
});
}

createMT5Account();
}, [createMT5Account, isMT5PasswordNotSet, mt5Platform, password, tradingPasswordChangeMutateAsync]);

const sendEmailVerification = useCallback(() => {
if (email) {
emailVerificationMutate({
Expand All @@ -166,10 +170,13 @@
new_password: newPassword,
old_password: currentPassword,
platform: mt5Platform,
}).then(() => {
setPassword(newPassword);
setModalOptions(prev => ({ ...prev, shouldCloseOnClickOutside: false }));
setIsPasswordChanged(true);
});
setPassword(newPassword);
},
[mt5Platform, tradingPasswordChangeMutateAsync]
[mt5Platform, setModalOptions, tradingPasswordChangeMutateAsync]
);

const renderTitle = useCallback(() => {
Expand Down Expand Up @@ -289,6 +296,7 @@
<EnterPassword
account={account}
isLoading={tradingPlatformPasswordChangeLoading || createMT5AccountLoading}
isMT5PasswordNotSet={isMT5PasswordNotSet}
isTncChecked={isTncChecked}
isVirtual={isVirtual}
marketType={marketType}
Expand Down Expand Up @@ -353,6 +361,18 @@
);
}

if (isPasswordChanged) {
return (
<WalletSuccessChangeMT5Password
onClick={() => {
createMT5Account();
setIsPasswordChanged(false);
setModalOptions(prev => ({ ...prev, shouldCloseOnClickOutside: true }));
}}
/>
);
}

if (createMT5AccountSuccess) {
return (
<MT5AccountAdded
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Localize, useTranslations } from '@deriv-com/translations';
import { Button, Text, useDevice } from '@deriv-com/ui';
import { WalletPasswordFieldLazy } from '../../../../components/Base';
import { THooks, TMarketTypes, TPlatforms } from '../../../../types';
import { validPassword, validPasswordMT5 } from '../../../../utils/password-validation';
import { CFD_PLATFORMS, getMarketTypeDetails, JURISDICTION, PlatformDetails } from '../../constants';
import { TAvailableMT5Account } from '../../types';
import { MT5LicenceMessage, MT5PasswordModalTnc } from '../components';
Expand All @@ -15,6 +14,7 @@ type TProps = {
account?: TAvailableMT5Account;
isForgotPasswordLoading?: boolean;
isLoading?: boolean;
isMT5PasswordNotSet?: boolean;
isTncChecked?: boolean;
isVirtual?: boolean;
marketType: TMarketTypes.CreateOtherCFDAccount;
Expand All @@ -34,6 +34,7 @@ const EnterPassword: React.FC<TProps> = ({
account,
isForgotPasswordLoading,
isLoading,
isMT5PasswordNotSet,
isTncChecked = true,
isVirtual,
marketType,
Expand All @@ -52,8 +53,6 @@ const EnterPassword: React.FC<TProps> = ({
const { localize } = useTranslations();
const { data } = useActiveWalletAccount();

const isMT5 = platform === CFD_PLATFORMS.MT5;
const disableButton = isMT5 ? !validPasswordMT5(password) : !validPassword(password);
const accountType = data?.is_virtual ? localize('Demo') : localize('Real');
const title = PlatformDetails[platform].title;
const marketTypeTitle =
Expand All @@ -64,6 +63,7 @@ const EnterPassword: React.FC<TProps> = ({
'Hint: You may have entered your Deriv password, which is different from your {{title}} password.',
{ title }
);
const isAddAccountBtnDisabled = !password || isLoading || !isTncChecked;

useEffect(() => {
if (passwordError) {
Expand Down Expand Up @@ -93,6 +93,7 @@ const EnterPassword: React.FC<TProps> = ({
/>
</Text>
<WalletPasswordFieldLazy
isMT5PasswordNotSet={isMT5PasswordNotSet}
label={localize('{{title}} password', { title })}
onChange={onPasswordChange}
password={password}
Expand Down Expand Up @@ -122,7 +123,7 @@ const EnterPassword: React.FC<TProps> = ({
<Localize i18n_default_text='Forgot password?' />
</Button>
<Button
disabled={!password || isLoading || disableButton || !isTncChecked}
disabled={isAddAccountBtnDisabled}
isLoading={isLoading}
onClick={onPrimaryClick}
size='lg'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ describe('EnterPassword', () => {
afterEach(cleanup);

const title = `Enter your ${PlatformDetails.mt5.title} password`;
const shortPassword = 'abcd';
const validPassword = 'Abcd1234!';

const defaultProps = {
Expand Down Expand Up @@ -84,12 +83,6 @@ describe('EnterPassword', () => {
expect(defaultProps.onPrimaryClick).toHaveBeenCalled();
});

it('disables the "Add account" button when password is invalid', () => {
renderComponent({ password: shortPassword });
const addAccountButton = screen.getByRole('button', { name: 'Add account' });
expect(addAccountButton).toBeDisabled();
});

it('disables the "Add account" button when tnc is not checked', () => {
renderComponent({ isTncChecked: false });
const addAccountButton = screen.getByRole('button', { name: 'Add account' });
Expand Down
Loading