Skip to content

Commit

Permalink
Merge pull request #51663 from getusha/feat-add-countdown-after-magic…
Browse files Browse the repository at this point in the history
…-code-resend

feat: add timer on validate code action modal
  • Loading branch information
mountiny authored Oct 30, 2024
2 parents 4735b58 + 3c7ae6d commit 6b85457
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type ValidateCodeFormProps = {
/** Function to clear error of the form */
clearError: () => void;

/** Function is called when validate code modal is mounted and on magic code resend */
sendValidateCode: () => void;
};

Expand Down Expand Up @@ -90,6 +91,10 @@ function BaseValidateCodeForm({
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- nullish coalescing doesn't achieve the same result in this case
const shouldDisableResendValidateCode = !!isOffline || account?.isLoading;
const focusTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const [timeRemaining, setTimeRemaining] = useState(CONST.REQUEST_CODE_DELAY as number);
const [isResent, setIsResent] = useState(false);

const timerRef = useRef<NodeJS.Timeout>();

useImperativeHandle(innerRef, () => ({
focus() {
Expand Down Expand Up @@ -135,12 +140,28 @@ function BaseValidateCodeForm({
inputValidateCodeRef.current?.clear();
}, [hasMagicCodeBeenSent]);

useEffect(() => {
if (timeRemaining > 0) {
timerRef.current = setTimeout(() => {
setTimeRemaining(timeRemaining - 1);
}, 1000);
}
return () => {
clearTimeout(timerRef.current);
};
}, [timeRemaining]);

/**
* Request a validate code / magic code be sent to verify this contact method
*/
const resendValidateCode = () => {
if (hasMagicCodeBeenSent && !isResent) {
return;
}

sendValidateCode();
inputValidateCodeRef.current?.clear();
setTimeRemaining(CONST.REQUEST_CODE_DELAY);
};

/**
Expand Down Expand Up @@ -177,6 +198,7 @@ function BaseValidateCodeForm({
handleSubmitForm(validateCode);
}, [validateCode, handleSubmitForm]);

const shouldShowTimer = timeRemaining > 0 && !isOffline;
return (
<>
<MagicCodeInput
Expand All @@ -190,35 +212,46 @@ function BaseValidateCodeForm({
onFulfill={validateAndSubmitForm}
autoFocus
/>
{shouldShowTimer && (
<Text style={[styles.mt5]}>
{translate('validateCodeForm.requestNewCode')}
<Text style={[styles.textBlue]}>00:{String(timeRemaining).padStart(2, '0')}</Text>
</Text>
)}
<OfflineWithFeedback
pendingAction={validateCodeAction?.pendingFields?.validateCodeSent}
errors={ErrorUtils.getLatestErrorField(validateCodeAction, 'actionVerified')}
errorRowStyles={[styles.mt2]}
onClose={() => User.clearValidateCodeActionError('actionVerified')}
>
<View style={[styles.mt5, styles.dFlex, styles.flexColumn, styles.alignItemsStart]}>
<PressableWithFeedback
disabled={shouldDisableResendValidateCode}
style={[styles.mr1]}
onPress={resendValidateCode}
underlayColor={theme.componentBG}
hoverDimmingValue={1}
pressDimmingValue={0.2}
role={CONST.ROLE.BUTTON}
accessibilityLabel={translate('validateCodeForm.magicCodeNotReceived')}
>
<Text style={[StyleUtils.getDisabledLinkStyles(shouldDisableResendValidateCode)]}>{translate('validateCodeForm.magicCodeNotReceived')}</Text>
</PressableWithFeedback>
{hasMagicCodeBeenSent && (
<DotIndicatorMessage
type="success"
style={[styles.mt6, styles.flex0]}
// eslint-disable-next-line @typescript-eslint/naming-convention
messages={{0: translate('validateCodeModal.successfulNewCodeRequest')}}
/>
)}
</View>
{!shouldShowTimer && (
<View style={[styles.mt5, styles.dFlex, styles.flexColumn, styles.alignItemsStart]}>
<PressableWithFeedback
disabled={shouldDisableResendValidateCode}
style={[styles.mr1]}
onPress={() => {
resendValidateCode();
setIsResent(true);
}}
underlayColor={theme.componentBG}
hoverDimmingValue={1}
pressDimmingValue={0.2}
role={CONST.ROLE.BUTTON}
accessibilityLabel={translate('validateCodeForm.magicCodeNotReceived')}
>
<Text style={[StyleUtils.getDisabledLinkStyles(shouldDisableResendValidateCode)]}>{translate('validateCodeForm.magicCodeNotReceived')}</Text>
</PressableWithFeedback>
</View>
)}
</OfflineWithFeedback>
{hasMagicCodeBeenSent && (
<DotIndicatorMessage
type="success"
style={[styles.mt6, styles.flex0]}
// eslint-disable-next-line @typescript-eslint/naming-convention
messages={{0: translate('validateCodeModal.successfulNewCodeRequest')}}
/>
)}
<OfflineWithFeedback
pendingAction={validatePendingAction}
errors={validateError}
Expand Down
4 changes: 2 additions & 2 deletions src/components/ValidateCodeActionModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ function ValidateCodeActionModal({
}, [onClose, clearError]);

useEffect(() => {
if (!firstRenderRef.current || !isVisible) {
if (!firstRenderRef.current || !isVisible || hasMagicCodeBeenSent) {
return;
}
firstRenderRef.current = false;

sendValidateCode();
}, [isVisible, sendValidateCode]);
}, [isVisible, sendValidateCode, hasMagicCodeBeenSent]);

return (
<Modal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,6 @@ function ContactMethodDetailsPage({route}: ContactMethodDetailsPageProps) {
User.deleteContactMethod(contactMethod, loginList ?? {}, backTo);
}, [contactMethod, loginList, toggleDeleteModal, backTo]);

const sendValidateCode = () => {
if (loginData?.validateCodeSent) {
return;
}

User.requestContactMethodValidateCode(contactMethod);
};

const prevValidatedDate = usePrevious(loginData?.validatedDate);
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
Expand Down Expand Up @@ -276,7 +268,7 @@ function ContactMethodDetailsPage({route}: ContactMethodDetailsPageProps) {
Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.getRoute(backTo));
setIsValidateCodeActionModalVisible(false);
}}
sendValidateCode={sendValidateCode}
sendValidateCode={() => User.requestContactMethodValidateCode(contactMethod)}
description={translate('contacts.enterMagicCode', {contactMethod})}
footer={() => getMenuItems()}
/>
Expand Down
13 changes: 3 additions & 10 deletions src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,6 @@ function NewContactMethodPage({route}: NewContactMethodPageProps) {
Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.getRoute(navigateBackTo));
}, [navigateBackTo]);

const sendValidateCode = () => {
if (loginData?.validateCodeSent) {
return;
}

User.requestValidateCodeAction();
};

return (
<AccessOrNotFoundWrapper shouldBeBlocked={isActingAsDelegate}>
<ScreenWrapper
Expand Down Expand Up @@ -176,8 +168,9 @@ function NewContactMethodPage({route}: NewContactMethodPageProps) {
setIsValidateCodeActionModalVisible(false);
}}
isVisible={isValidateCodeActionModalVisible}
title={contactMethod}
sendValidateCode={sendValidateCode}
hasMagicCodeBeenSent={!!loginData?.validateCodeSent}
title={translate('delegate.makeSureItIsYou')}
sendValidateCode={() => User.requestValidateCodeAction()}
description={translate('contacts.enterMagicCode', {contactMethod})}
/>
</ScreenWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,15 @@ function DelegateMagicCodeModal({login, role, onClose}: DelegateMagicCodeModalPr
Delegate.clearAddDelegateErrors(currentDelegate?.email ?? '', 'addDelegate');
};

const sendValidateCode = () => {
if (currentDelegate?.validateCodeSent) {
return;
}

User.requestValidateCodeAction();
};

return (
<ValidateCodeActionModal
clearError={clearError}
onClose={onBackButtonPress}
validateError={validateLoginError}
isVisible={isValidateCodeActionModalVisible}
title={translate('delegate.makeSureItIsYou')}
sendValidateCode={sendValidateCode}
sendValidateCode={() => User.requestValidateCodeAction()}
hasMagicCodeBeenSent={!!currentDelegate?.validateCodeSent}
handleSubmitForm={(validateCode) => Delegate.addDelegate(login, role, validateCode)}
description={translate('delegate.enterMagicCode', {contactMethod: account?.primaryLogin ?? ''})}
/>
Expand Down
17 changes: 5 additions & 12 deletions src/pages/settings/Wallet/ExpensifyCardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ function ExpensifyCardPage({
const formattedAvailableSpendAmount = CurrencyUtils.convertToDisplayString(cardsToShow?.at(0)?.availableSpend);
const {limitNameKey, limitTitleKey} = getLimitTypeTranslationKeys(cardsToShow?.at(0)?.nameValuePairs?.limitType);

const primaryLogin = account?.primaryLogin ?? '';
const loginData = loginList?.[primaryLogin];

const goToGetPhysicalCardFlow = () => {
let updatedDraftValues = draftValues;
if (!draftValues) {
Expand All @@ -146,17 +149,6 @@ function ExpensifyCardPage({
GetPhysicalCardUtils.goToNextPhysicalCardRoute(domain, GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(updatedDraftValues, privatePersonalDetails));
};

const sendValidateCode = () => {
const primaryLogin = account?.primaryLogin ?? '';
const loginData = loginList?.[primaryLogin];

if (loginData?.validateCodeSent) {
return;
}

requestValidateCodeAction();
};

if (isNotFound) {
return <NotFoundPage onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_WALLET)} />;
}
Expand Down Expand Up @@ -310,9 +302,10 @@ function ExpensifyCardPage({
<ValidateCodeActionModal
handleSubmitForm={handleRevealDetails}
clearError={() => {}}
sendValidateCode={sendValidateCode}
sendValidateCode={() => requestValidateCodeAction()}
onClose={() => setIsValidateCodeActionModalVisible(false)}
isVisible={isValidateCodeActionModalVisible}
hasMagicCodeBeenSent={!!loginData?.validateCodeSent}
title={translate('cardPage.validateCardTitle')}
description={translate('cardPage.enterMagicCode', {contactMethod: account?.primaryLogin ?? ''})}
/>
Expand Down
1 change: 1 addition & 0 deletions src/pages/settings/Wallet/VerifyAccountPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ function VerifyAccountPage({route}: VerifyAccountPageProps) {
sendValidateCode={() => User.requestValidateCodeAction()}
handleSubmitForm={handleSubmitForm}
validateError={validateLoginError}
hasMagicCodeBeenSent={!!loginData?.validateCodeSent}
isVisible={isValidateCodeActionModalVisible}
title={translate('contacts.validateAccount')}
description={translate('contacts.featureRequiresValidate')}
Expand Down

0 comments on commit 6b85457

Please sign in to comment.