Skip to content

Commit

Permalink
[WALL] Jim/WALL-4001/ Send email when forgot password is clicked (bin…
Browse files Browse the repository at this point in the history
…ary-com#15107)

* chore: send email when forgot password is clicked

* chore: add sendemail call to modal on mobile

* chore: rename hook

* docs: add tsdoc

* chore: add sendemail to dependency array

* chore: add error handling logic

* chore: add error handling for sent email template

* chore: remove unnecessary dependencies
  • Loading branch information
jim-deriv committed May 16, 2024
1 parent ce809bb commit 8dc26ac
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 45 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { FC, Fragment, useEffect, useState } from 'react';
import { Trans } from 'react-i18next';
import { useCountdown } from 'usehooks-ts';
import { useActiveWalletAccount, useSettings, useVerifyEmail } from '@deriv/api-v2';
import {
DerivLightIcEmailSentIcon,
DerivLightIcEmailSentPasskeyIcon,
Expand All @@ -12,16 +11,18 @@ import {
} from '@deriv/quill-icons';
import { PlatformDetails } from '../../features/cfd/constants';
import useDevice from '../../hooks/useDevice';
import useSendPasswordResetEmail from '../../hooks/useSendPasswordResetEmail';
import { TPlatforms } from '../../types';
import { platformPasswordResetRedirectLink } from '../../utils/cfd';
import { WalletButton, WalletText } from '../Base';
import { WalletError } from '../WalletError';
import { WalletsActionScreen } from '../WalletsActionScreen';
import './SentEmailContent.scss';

type SentEmailContentProps = {
description?: string;
isChangePassword?: boolean; // NOTE: This prop is ONLY used for rendering different email icons between either Change Password/Forgot password email modal
isInvestorPassword?: boolean;
onErrorButtonClick?: () => void;
platform?: TPlatforms.All;
};

Expand Down Expand Up @@ -57,12 +58,12 @@ const SentEmailContent: FC<SentEmailContentProps> = ({
description,
isChangePassword = false,
isInvestorPassword = false,
onErrorButtonClick,
platform,
}) => {
const [shouldShowResendEmailReasons, setShouldShowResendEmailReasons] = useState(false);
const [hasCountdownStarted, setHasCountdownStarted] = useState(false);
const { data } = useSettings();
const { mutate: verifyEmail } = useVerifyEmail();
const { error: resetPasswordError, sendEmail } = useSendPasswordResetEmail();
const { isMobile } = useDevice();
const mt5Platform = PlatformDetails.mt5.platform;
const { title } = PlatformDetails[platform ?? mt5Platform];
Expand All @@ -80,29 +81,25 @@ const SentEmailContent: FC<SentEmailContentProps> = ({
isChangePassword || isInvestorPassword ? DerivLightIcEmailSentIcon : DerivLightIcEmailSentPasskeyIcon;

const resendEmail = () => {
if (data?.email) {
verifyEmail({
type: platform === mt5Platform ? mt5ResetType : 'trading_platform_dxtrade_password_reset',
url_parameters: {
redirect_to: platformPasswordResetRedirectLink(platform ?? mt5Platform, activeWallet?.is_virtual),
},
verify_email: data?.email,
});
resetCountdown();
startCountdown();
setHasCountdownStarted(true);
}
sendEmail({ isInvestorPassword, platform });
resetCountdown();
startCountdown();
setHasCountdownStarted(true);
};

useEffect(() => {
if (count === 0) setHasCountdownStarted(false);
}, [count]);

const { data: activeWallet } = useActiveWalletAccount();

const mt5ResetType = isInvestorPassword
? 'trading_platform_investor_password_reset'
: 'trading_platform_mt5_password_reset';
if (resetPasswordError) {
return (
<WalletError
errorMessage={resetPasswordError?.error.message}
onClick={onErrorButtonClick}
title={resetPasswordError?.error?.code}
/>
);
}

return (
<div className='wallets-sent-email-content'>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useHistory } from 'react-router-dom';
import {
useAccountStatus,
Expand All @@ -10,6 +10,7 @@ import { SentEmailContent, WalletError } from '../../../../components';
import { ModalStepWrapper, ModalWrapper, WalletButton, WalletButtonGroup } from '../../../../components/Base';
import { useModal } from '../../../../components/ModalProvider';
import useDevice from '../../../../hooks/useDevice';
import useSendPasswordResetEmail from '../../../../hooks/useSendPasswordResetEmail';
import { PlatformDetails } from '../../constants';
import { CFDSuccess, CreatePassword, EnterPassword } from '../../screens';
import './DxtradeEnterPasswordModal.scss';
Expand All @@ -19,9 +20,23 @@ const DxtradeEnterPasswordModal = () => {
const { isMobile } = useDevice();
const [password, setPassword] = useState('');
const { data: getAccountStatus, isSuccess: accountStatusSuccess } = useAccountStatus();
const { data: createdAccount, error, isLoading, isSuccess, mutateAsync, status } = useCreateOtherCFDAccount();
const {
data: createdAccount,
error,
isLoading,
isSuccess: isCreateAccountSuccessful,
mutateAsync,
status,
} = useCreateOtherCFDAccount();

const { data: dxtradeAccount, isSuccess: dxtradeAccountListSuccess } = useDxtradeAccountsList();
const { data: activeWallet } = useActiveWalletAccount();
const {
error: resetPasswordError,
isLoading: isResetPasswordLoading,
isSuccess: isResetPasswordSuccessful,
sendEmail,
} = useSendPasswordResetEmail();
const { hide, show } = useModal();
const accountType = activeWallet?.is_virtual ? 'demo' : 'real';
const dxtradePlatform = PlatformDetails.dxtrade.platform;
Expand All @@ -45,12 +60,29 @@ const DxtradeEnterPasswordModal = () => {
: `Transfer funds from your ${activeWallet?.currency} Wallet to your ${PlatformDetails.dxtrade.title} account to start trading.`;
}, [accountType, activeWallet?.currency, activeWallet?.display_balance]);

useEffect(() => {
if (!isResetPasswordSuccessful) return;
if (!isDxtradePasswordNotSet && isMobile) {
show(
<ModalStepWrapper title="We've sent you an email">
<SentEmailContent onErrorButtonClick={hide} platform={dxtradePlatform} />
</ModalStepWrapper>
);
} else if (!isDxtradePasswordNotSet) {
show(
<ModalWrapper>
<SentEmailContent onErrorButtonClick={hide} platform={dxtradePlatform} />
</ModalWrapper>
);
}
}, [dxtradePlatform, hide, isDxtradePasswordNotSet, isMobile, isResetPasswordSuccessful, show]);

const dxtradeBalance = useMemo(() => {
return dxtradeAccount?.find(account => account.market_type === 'all')?.display_balance;
}, [dxtradeAccount]);

const renderFooter = useMemo(() => {
if (isSuccess) {
if (isCreateAccountSuccessful) {
if (accountType === 'demo') {
return (
<div className='wallets-success-btn'>
Expand Down Expand Up @@ -83,12 +115,11 @@ const DxtradeEnterPasswordModal = () => {
<WalletButtonGroup isFullWidth>
<WalletButton
isFullWidth
isLoading={isResetPasswordLoading}
onClick={() => {
show(
<ModalStepWrapper title="We've sent you an email">
<SentEmailContent platform={dxtradePlatform} />
</ModalStepWrapper>
);
sendEmail({
platform: dxtradePlatform,
});
}}
size={isMobile ? 'lg' : 'md'}
variant='outlined'
Expand Down Expand Up @@ -125,17 +156,18 @@ const DxtradeEnterPasswordModal = () => {
dxtradePlatform,
hide,
history,
isCreateAccountSuccessful,
isDxtradePasswordNotSet,
isLoading,
isMobile,
isSuccess,
isResetPasswordLoading,
onSubmit,
password,
show,
sendEmail,
]);

const successComponent = useMemo(() => {
if (isSuccess && dxtradeAccountListSuccess) {
if (isCreateAccountSuccessful && dxtradeAccountListSuccess) {
return (
<CFDSuccess
description={successDescription}
Expand All @@ -150,7 +182,7 @@ const DxtradeEnterPasswordModal = () => {
);
}
}, [
isSuccess,
isCreateAccountSuccessful,
dxtradeAccountListSuccess,
successDescription,
dxtradeBalance,
Expand All @@ -160,7 +192,7 @@ const DxtradeEnterPasswordModal = () => {
]);

const passwordComponent = useMemo(() => {
if (!isSuccess && accountStatusSuccess) {
if (!isCreateAccountSuccessful && accountStatusSuccess) {
return isDxtradePasswordNotSet ? (
<CreatePassword
isLoading={isLoading}
Expand All @@ -171,17 +203,16 @@ const DxtradeEnterPasswordModal = () => {
/>
) : (
<EnterPassword
isForgotPasswordLoading={isResetPasswordLoading}
isLoading={isLoading}
marketType='all'
onPasswordChange={e => setPassword(e.target.value)}
onPrimaryClick={onSubmit}
onSecondaryClick={() =>
show(
<ModalWrapper>
<SentEmailContent platform={dxtradePlatform} />
</ModalWrapper>
)
}
onSecondaryClick={() => {
sendEmail({
platform: dxtradePlatform,
});
}}
password={password}
passwordError={error?.error?.code === 'PasswordError'}
platform={dxtradePlatform}
Expand All @@ -190,20 +221,32 @@ const DxtradeEnterPasswordModal = () => {
);
}
}, [
isSuccess,
isCreateAccountSuccessful,
accountStatusSuccess,
isDxtradePasswordNotSet,
isLoading,
onSubmit,
password,
dxtradePlatform,
isResetPasswordLoading,
error?.error?.code,
show,
sendEmail,
]);

if (status === 'error' && error?.error?.code !== 'PasswordError') {
return <WalletError errorMessage={error?.error.message} onClick={hide} title={error?.error?.code} />;
}

if (resetPasswordError) {
return (
<WalletError
errorMessage={resetPasswordError?.error.message}
onClick={hide}
title={resetPasswordError?.error?.code}
/>
);
}

if (isMobile) {
return (
<ModalStepWrapper renderFooter={() => renderFooter} title={' '}>
Expand All @@ -213,7 +256,7 @@ const DxtradeEnterPasswordModal = () => {
);
}
return (
<ModalWrapper hideCloseButton={isSuccess}>
<ModalWrapper hideCloseButton={isCreateAccountSuccessful}>
{successComponent}
{passwordComponent}
</ModalWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { CFD_PLATFORMS, MarketTypeDetails, PlatformDetails } from '../../constan
import './EnterPassword.scss';

type TProps = {
isForgotPasswordLoading?: boolean;
isLoading?: boolean;
marketType: TMarketTypes.CreateOtherCFDAccount;
onPasswordChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
Expand All @@ -20,6 +21,7 @@ type TProps = {
};

const EnterPassword: React.FC<TProps> = ({
isForgotPasswordLoading,
isLoading,
marketType,
onPasswordChange,
Expand Down Expand Up @@ -70,7 +72,12 @@ const EnterPassword: React.FC<TProps> = ({
</div>
{isDesktop && (
<div className='wallets-enter-password__buttons'>
<WalletButton onClick={onSecondaryClick} size='md' variant='outlined'>
<WalletButton
isLoading={isForgotPasswordLoading}
onClick={onSecondaryClick}
size='md'
variant='outlined'
>
Forgot password?
</WalletButton>
<WalletButton
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { PropsWithChildren } from 'react';
import { APIProvider } from '@deriv/api-v2';
import { renderHook } from '@testing-library/react-hooks';
import WalletsAuthProvider from '../../AuthProvider';
import useSendPasswordResetEmail from '../useSendPasswordResetEmail';

const mockMutate = jest.fn();

jest.mock('@deriv/api-v2', () => ({
...jest.requireActual('@deriv/api-v2'),
useSettings: jest.fn(() => ({
data: {
email: 'test@meme.com',
},
})),
useVerifyEmail: jest.fn(() => ({
mutate: mockMutate,
})),
}));

const wrapper = ({ children }: PropsWithChildren) => (
<APIProvider>
<WalletsAuthProvider>{children}</WalletsAuthProvider>
</APIProvider>
);

describe('useSendPasswordResetEmail', () => {
it('should call mutate when sendEmail is called', () => {
const { result } = renderHook(() => useSendPasswordResetEmail(), { wrapper });
result.current.sendEmail({
isInvestorPassword: true,
platform: 'mt5',
});
expect(mockMutate).toHaveBeenCalledWith({
type: 'trading_platform_investor_password_reset',
url_parameters: {
redirect_to: 10,
},
verify_email: 'test@meme.com',
});
});
});
Loading

0 comments on commit 8dc26ac

Please sign in to comment.