diff --git a/packages/cashier/src/components/email-verification-empty-state/__tests__/email-verification-empty-state.test.tsx b/packages/cashier/src/components/email-verification-empty-state/__tests__/email-verification-empty-state.test.tsx index 87a157bc470a..7dfd6b4a24e2 100644 --- a/packages/cashier/src/components/email-verification-empty-state/__tests__/email-verification-empty-state.test.tsx +++ b/packages/cashier/src/components/email-verification-empty-state/__tests__/email-verification-empty-state.test.tsx @@ -2,6 +2,8 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import EmailVerificationEmptyState from '../email-verification-empty-state'; import { TRootStore } from 'Types'; +import { useVerifyEmail } from '@deriv/hooks'; +import { VerifyEmail } from '@deriv/api-types'; import CashierProviders from '../../../cashier-providers'; const mock_store: DeepPartial = { @@ -11,8 +13,18 @@ const mock_store: DeepPartial = { }; describe('EmailVerificationEmptyState', () => { + const verify: ReturnType = { + is_loading: false, + error: '', + data: {} as VerifyEmail, + counter: 58, + is_counter_running: true, + sent_count: 2, + has_been_sent: true, + send: jest.fn(), + }; test('should disable resend button after sending the request', () => { - render(, { + render(, { wrapper: ({ children }) => {children}, }); diff --git a/packages/cashier/src/components/email-verification-empty-state/email-verification-empty-state.tsx b/packages/cashier/src/components/email-verification-empty-state/email-verification-empty-state.tsx index 479417cee08a..9494cc8b8bda 100644 --- a/packages/cashier/src/components/email-verification-empty-state/email-verification-empty-state.tsx +++ b/packages/cashier/src/components/email-verification-empty-state/email-verification-empty-state.tsx @@ -1,17 +1,15 @@ import React from 'react'; -import { useVerifyEmail, TEmailVerificationType } from '@deriv/hooks'; +import { useVerifyEmail } from '@deriv/hooks'; import { localize } from '@deriv/translations'; import EmptyState from 'Components/empty-state'; import EmailVerificationResendEmptyState from './email-verification-resend-empty-state'; import './email-verification-empty-state.scss'; type TEmailVerificationEmptyStateProps = { - type: TEmailVerificationType; + verify: ReturnType; }; -const EmailVerificationEmptyState = ({ type }: TEmailVerificationEmptyStateProps) => { - const verify = useVerifyEmail(type); - +const EmailVerificationEmptyState = ({ verify }: TEmailVerificationEmptyStateProps) => { const action = { label: localize("Didn't receive the email?"), onClick: verify.send, diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-list/withdrawal-tab.tsx b/packages/cashier/src/pages/payment-agent/payment-agent-list/withdrawal-tab.tsx index 265e2f567265..2de771ef96a2 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-list/withdrawal-tab.tsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-list/withdrawal-tab.tsx @@ -23,8 +23,7 @@ const WithdrawalTab = observer(() => { // match the behavior of the `Withdrawal` page and first inform the user. if (verify.error && 'code' in verify.error) return ; - if (!verify.is_loading && verify.has_been_sent) - return ; + if (!verify.is_loading && verify.has_been_sent) return ; if (verification_code || payment_agent.is_withdraw) return ; diff --git a/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/withdrawal-verification-email.tsx b/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/withdrawal-verification-email.tsx index 678cca20fc09..58342dcc63bf 100644 --- a/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/withdrawal-verification-email.tsx +++ b/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/withdrawal-verification-email.tsx @@ -21,7 +21,7 @@ const WithdrawalVerificationEmail = observer(() => { if (verify.error) return ; - if (verify.has_been_sent) return ; + if (verify.has_been_sent) return ; return ( <> diff --git a/packages/core/src/Stores/client-store.js b/packages/core/src/Stores/client-store.js index 73d325152247..cbf3f3660c6f 100644 --- a/packages/core/src/Stores/client-store.js +++ b/packages/core/src/Stores/client-store.js @@ -129,6 +129,7 @@ export default class ClientStore extends BaseStore { account_limits = {}; self_exclusion = {}; + sent_verify_emails_data = {}; local_currency_config = { currency: '', @@ -206,6 +207,7 @@ export default class ClientStore extends BaseStore { new_email: observable, account_limits: observable, self_exclusion: observable, + sent_verify_emails_data: observable, local_currency_config: observable, has_cookie_account: observable, financial_assessment: observable, @@ -310,6 +312,7 @@ export default class ClientStore extends BaseStore { setCookieAccount: action.bound, setCFDScore: action.bound, setIsCFDScoreAvailable: action.bound, + setSentVerifyEmailsData: action.bound, updateSelfExclusion: action.bound, responsePayoutCurrencies: action.bound, responseAuthorize: action.bound, @@ -1180,6 +1183,11 @@ export default class ClientStore extends BaseStore { LocalStore.setObject(LANGUAGE_KEY, lang); }; + setSentVerifyEmailsData(sent_verify_emails_data) { + this.sent_verify_emails_data = sent_verify_emails_data; + LocalStore.setObject('sent_verify_emails_data', sent_verify_emails_data); + } + setCookieAccount() { const domain = /deriv\.(com|me)/.test(window.location.hostname) ? deriv_urls.DERIV_HOST_NAME : 'binary.sx'; @@ -1589,6 +1597,7 @@ export default class ClientStore extends BaseStore { this.setLoginId(LocalStore.get('active_loginid')); this.setAccounts(LocalStore.getObject(storage_key)); + this.setSentVerifyEmailsData(LocalStore.getObject('sent_verify_emails_data')); this.setSwitched(''); const client = this.accounts[this.loginid]; // If there is an authorize_response, it means it was the first login diff --git a/packages/hooks/src/useVerifyEmail.ts b/packages/hooks/src/useVerifyEmail.ts index 2116418a4089..abac330c47b1 100644 --- a/packages/hooks/src/useVerifyEmail.ts +++ b/packages/hooks/src/useVerifyEmail.ts @@ -1,4 +1,3 @@ -import { useState } from 'react'; import { useWS } from '@deriv/api'; import { useStore } from '@deriv/stores'; import type { TSocketEndpoints } from '@deriv/api/types'; @@ -10,9 +9,19 @@ export type TEmailVerificationType = TSocketEndpoints['verify_email']['request'] const useVerifyEmail = (type: TEmailVerificationType) => { const WS = useWS('verify_email'); - const counter = useCountdown({ from: RESEND_COUNTDOWN }); const { client } = useStore(); - const [sent_count, setSentCount] = useState(0); + const { setSentVerifyEmailsData, sent_verify_emails_data } = client; + const { last_time_sent_seconds = 0, sent_count = 0 } = sent_verify_emails_data[type] || {}; + const time_now_seconds = Math.floor(Date.now() / 1000); + const seconds_left = last_time_sent_seconds + RESEND_COUNTDOWN - time_now_seconds; + const should_not_allow_resend = + last_time_sent_seconds && time_now_seconds < last_time_sent_seconds + RESEND_COUNTDOWN; + const countdown = should_not_allow_resend ? seconds_left : RESEND_COUNTDOWN; + const counter = useCountdown({ from: countdown }); + + if (!counter.is_running && should_not_allow_resend) { + counter.start(); + } const send = () => { if (!client.email) return; @@ -20,8 +29,11 @@ const useVerifyEmail = (type: TEmailVerificationType) => { counter.reset(); counter.start(); - - setSentCount(old => old + 1); + const sent_emails_data = { + ...sent_verify_emails_data, + [type]: { last_time_sent_seconds: time_now_seconds, sent_count: sent_count + 1 }, + }; + setSentVerifyEmailsData(sent_emails_data); WS.send({ verify_email: client.email, type }); }; diff --git a/packages/stores/src/mockStore.ts b/packages/stores/src/mockStore.ts index 048d9a24fc55..6f433ca96fc4 100644 --- a/packages/stores/src/mockStore.ts +++ b/packages/stores/src/mockStore.ts @@ -154,6 +154,8 @@ const mock = (): TRootStore => { residence: '', responseMt5LoginList: jest.fn(), responseTradingPlatformAccountsList: jest.fn(), + sent_verify_emails_data: {}, + setSentVerifyEmailsData: jest.fn(), standpoint: { iom: '', }, diff --git a/packages/stores/types.ts b/packages/stores/types.ts index eb77b0da8c30..da4823dd53d0 100644 --- a/packages/stores/types.ts +++ b/packages/stores/types.ts @@ -1,4 +1,5 @@ import type { GetAccountStatus, Authorize, DetailsOfEachMT5Loginid, LogOutResponse, GetLimits } from '@deriv/api-types'; +import type { TEmailVerificationType } from '@deriv/hooks'; import type { RouteComponentProps } from 'react-router'; type TAccount = NonNullable[0]; @@ -167,7 +168,9 @@ type TClientStore = { trading_platform_dxtrade_password_reset: string; trading_platform_mt5_password_reset: string; }; + sent_verify_emails_data: TSentVerifyEmailsData; email: string; + setSentVerifyEmailsData: (sent_verify_emails_data: TSentVerifyEmailsData) => void; setVerificationCode: (code: string, action: string) => void; updateAccountStatus: () => Promise; is_authentication_needed: boolean; @@ -179,6 +182,16 @@ type TClientStore = { is_crypto: boolean; }; +type TSentVerifyEmailsData = Partial< + Record< + TEmailVerificationType, + { + last_time_sent_seconds: number; + sent_count: number; + } + > +>; + type TCommonStoreError = { header: string | JSX.Element; message: string | JSX.Element;