From 5f183525c5472ccba8aa71974dfe06044c79260f Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Sun, 26 Jan 2025 22:45:46 +0700 Subject: [PATCH 1/7] improve phone validation error messages --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- .../PersonalDetails/PhoneNumberPage.tsx | 30 +++++++++++++++---- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 3b55d6fbc75c..98d398e5ca3a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2001,7 +2001,7 @@ const translations = { taxID: 'Please enter a valid tax ID number.', website: 'Please enter a valid website.', zipCode: `Please enter a valid ZIP code using the format: ${CONST.COUNTRY_ZIP_REGEX_DATA.US.samples}.`, - phoneNumber: 'Please enter a valid phone number.', + phoneNumber: `Please enter a valid phone number, with the country code (e.g. ${CONST.EXAMPLE_PHONE_NUMBER})`, email: 'Please enter a valid email address.', companyName: 'Please enter a valid business name.', addressCity: 'Please enter a valid city.', diff --git a/src/languages/es.ts b/src/languages/es.ts index dff3dcd575c0..1979c6710061 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2022,7 +2022,7 @@ const translations = { taxID: 'Por favor, introduce un número de identificación fiscal válido.', website: 'Por favor, introduce un sitio web válido.', zipCode: `Formato de código postal incorrecto. Formato aceptable: ${CONST.COUNTRY_ZIP_REGEX_DATA.US.samples}.`, - phoneNumber: 'Por favor, introduce un teléfono válido.', + phoneNumber: `Introduce un teléfono válido, incluyendo el código del país (p. ej. ${CONST.EXAMPLE_PHONE_NUMBER})`, email: 'Por favor, introduce una dirección de correo electrónico válida.', companyName: 'Por favor, introduce un nombre comercial legal válido.', addressCity: 'Por favor, introduce una ciudad válida.', diff --git a/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx b/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx index 15704221f259..02b8d962ce7d 100644 --- a/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx @@ -43,7 +43,9 @@ function PhoneNumberPage() { // Only call the API if the user has changed their phone number if (phoneNumber !== values?.phoneNumber) { - PersonalDetails.updatePhoneNumber(values?.phoneNumber ?? '', currenPhoneNumber); + const phoneNumberWithCountryCode = LoginUtils.appendCountryCode(values?.phoneNumber ?? ''); + const parsedPhoneNumber = PhoneNumberUtils.parsePhoneNumber(phoneNumberWithCountryCode); + PersonalDetails.updatePhoneNumber(parsedPhoneNumber.number?.e164 ?? '', currenPhoneNumber); } Navigation.goBack(); @@ -52,19 +54,35 @@ function PhoneNumberPage() { const validate = useCallback( (values: FormOnyxValues): FormInputErrors => { const errors: FormInputErrors = {}; + + // Validate that the phone number field is not empty if (!ValidationUtils.isRequiredFulfilled(values[INPUT_IDS.PHONE_NUMBER])) { errors[INPUT_IDS.PHONE_NUMBER] = translate('common.error.fieldRequired'); + return errors; // Early return if field is empty } - const phoneNumberWithCountryCode = LoginUtils.appendCountryCode(values[INPUT_IDS.PHONE_NUMBER]); - const parsedPhoneNumber = PhoneNumberUtils.parsePhoneNumber(values[INPUT_IDS.PHONE_NUMBER]); - if (!parsedPhoneNumber.possible || !Str.isValidE164Phone(phoneNumberWithCountryCode.slice(0))) { + + // Sanitize input: Remove all non-numeric characters except the leading '+' + const sanitizedPhoneNumber = values[INPUT_IDS.PHONE_NUMBER].replace(/[^+\d]/g, ''); + + // Append country code if missing + const phoneNumberWithCountryCode = LoginUtils.appendCountryCode(sanitizedPhoneNumber); + + // Parse and validate the phone number + const parsedPhoneNumber = PhoneNumberUtils.parsePhoneNumber(phoneNumberWithCountryCode); + + // Validate if the phone number was parsed successfully + if (!parsedPhoneNumber || !parsedPhoneNumber.possible) { + errors[INPUT_IDS.PHONE_NUMBER] = translate('bankAccount.error.phoneNumber'); + } else if (!Str.isValidE164Phone(phoneNumberWithCountryCode)) { + // Additional check for E.164 format validity errors[INPUT_IDS.PHONE_NUMBER] = translate('bankAccount.error.phoneNumber'); } - // Clear the error when the user tries to validate the form and there are errors - if (validateLoginError && !!errors) { + // Clear the error if the user tries to validate the form and there are errors + if (validateLoginError && Object.keys(errors).length > 0) { PersonalDetails.clearPhoneNumberError(); } + return errors; }, [translate, validateLoginError], From 40d9af6590d937c695b240702c4864b39f6f1b6a Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Tue, 4 Feb 2025 12:09:55 +0700 Subject: [PATCH 2/7] update regex --- src/CONST.ts | 3 +++ .../Profile/PersonalDetails/PhoneNumberPage.tsx | 11 +++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 1250092cb910..d47b7502fa00 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -474,6 +474,9 @@ const CONST = { // Regex to read violation value from string given by backend VIOLATION_LIMIT_REGEX: /[^0-9]+/g, + // Regex to match and remove all lowercase letters (a-z) + REMOVE_LOWERCASE_REGEX: /[a-z]/g, + MERCHANT_NAME_MAX_LENGTH: 255, MASKED_PAN_PREFIX: 'XXXXXXXXXXXX', diff --git a/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx b/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx index 02b8d962ce7d..75045fde2c08 100644 --- a/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx @@ -35,6 +35,10 @@ function PhoneNumberPage() { const validateLoginError = ErrorUtils.getEarliestErrorField(privatePersonalDetails, 'phoneNumber'); const currenPhoneNumber = privatePersonalDetails?.phoneNumber ?? ''; + const removeLetters = (str: string) => { + return str.replace(CONST.REMOVE_LOWERCASE_REGEX, '').trim(); + }; + const updatePhoneNumber = (values: PrivatePersonalDetails) => { // Clear the error when the user tries to submit the form if (validateLoginError) { @@ -42,9 +46,12 @@ function PhoneNumberPage() { } // Only call the API if the user has changed their phone number - if (phoneNumber !== values?.phoneNumber) { - const phoneNumberWithCountryCode = LoginUtils.appendCountryCode(values?.phoneNumber ?? ''); + if (phoneNumber !== values?.phoneNumber && values?.phoneNumber) { + const phoneNumberWithCountryCode = LoginUtils.appendCountryCode(removeLetters(values?.phoneNumber.toLocaleLowerCase()) ?? ''); const parsedPhoneNumber = PhoneNumberUtils.parsePhoneNumber(phoneNumberWithCountryCode); + console.log('****** phoneNumberWithCountryCode ******', phoneNumberWithCountryCode); + console.log('****** parsedPhoneNumber ******', parsedPhoneNumber); + PersonalDetails.updatePhoneNumber(parsedPhoneNumber.number?.e164 ?? '', currenPhoneNumber); } From eaf181de5a6f51e05a5bae9972657dc3455e8973 Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Tue, 4 Feb 2025 13:44:19 +0700 Subject: [PATCH 3/7] Improve phone validation error message --- src/CONST.ts | 2 ++ .../Profile/PersonalDetails/PhoneNumberPage.tsx | 17 +++++------------ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index d47b7502fa00..8eb60b4942c8 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -477,6 +477,8 @@ const CONST = { // Regex to match and remove all lowercase letters (a-z) REMOVE_LOWERCASE_REGEX: /[a-z]/g, + SANITIZE_PHONE_REGEX: /[^\d+]/g, + MERCHANT_NAME_MAX_LENGTH: 255, MASKED_PAN_PREFIX: 'XXXXXXXXXXXX', diff --git a/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx b/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx index 75045fde2c08..eb5edd02034e 100644 --- a/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx @@ -35,9 +35,7 @@ function PhoneNumberPage() { const validateLoginError = ErrorUtils.getEarliestErrorField(privatePersonalDetails, 'phoneNumber'); const currenPhoneNumber = privatePersonalDetails?.phoneNumber ?? ''; - const removeLetters = (str: string) => { - return str.replace(CONST.REMOVE_LOWERCASE_REGEX, '').trim(); - }; + const sanitizePhoneNumber = (num?: string): string => num?.replace(CONST.SANITIZE_PHONE_REGEX, '') ?? ''; const updatePhoneNumber = (values: PrivatePersonalDetails) => { // Clear the error when the user tries to submit the form @@ -47,10 +45,8 @@ function PhoneNumberPage() { // Only call the API if the user has changed their phone number if (phoneNumber !== values?.phoneNumber && values?.phoneNumber) { - const phoneNumberWithCountryCode = LoginUtils.appendCountryCode(removeLetters(values?.phoneNumber.toLocaleLowerCase()) ?? ''); + const phoneNumberWithCountryCode = LoginUtils.appendCountryCode(sanitizePhoneNumber(values?.phoneNumber)); const parsedPhoneNumber = PhoneNumberUtils.parsePhoneNumber(phoneNumberWithCountryCode); - console.log('****** phoneNumberWithCountryCode ******', phoneNumberWithCountryCode); - console.log('****** parsedPhoneNumber ******', parsedPhoneNumber); PersonalDetails.updatePhoneNumber(parsedPhoneNumber.number?.e164 ?? '', currenPhoneNumber); } @@ -69,7 +65,7 @@ function PhoneNumberPage() { } // Sanitize input: Remove all non-numeric characters except the leading '+' - const sanitizedPhoneNumber = values[INPUT_IDS.PHONE_NUMBER].replace(/[^+\d]/g, ''); + const sanitizedPhoneNumber = sanitizePhoneNumber(values[INPUT_IDS.PHONE_NUMBER]); // Append country code if missing const phoneNumberWithCountryCode = LoginUtils.appendCountryCode(sanitizedPhoneNumber); @@ -77,11 +73,8 @@ function PhoneNumberPage() { // Parse and validate the phone number const parsedPhoneNumber = PhoneNumberUtils.parsePhoneNumber(phoneNumberWithCountryCode); - // Validate if the phone number was parsed successfully - if (!parsedPhoneNumber || !parsedPhoneNumber.possible) { - errors[INPUT_IDS.PHONE_NUMBER] = translate('bankAccount.error.phoneNumber'); - } else if (!Str.isValidE164Phone(phoneNumberWithCountryCode)) { - // Additional check for E.164 format validity + // Check if the phone number is possible and valid in E.164 format + if (!parsedPhoneNumber.possible || !Str.isValidE164Phone(phoneNumberWithCountryCode)) { errors[INPUT_IDS.PHONE_NUMBER] = translate('bankAccount.error.phoneNumber'); } From fc07c1029209541fba615f79e2f8e55911d379cf Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Tue, 4 Feb 2025 14:04:07 +0700 Subject: [PATCH 4/7] Clean up code --- .../PersonalDetails/PhoneNumberPage.tsx | 50 +++++++++---------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx b/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx index eb5edd02034e..fab3b51d8637 100644 --- a/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx @@ -13,12 +13,12 @@ import TextInput from '@components/TextInput'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ErrorUtils from '@libs/ErrorUtils'; -import * as LoginUtils from '@libs/LoginUtils'; +import {getEarliestErrorField} from '@libs/ErrorUtils'; +import {appendCountryCode} from '@libs/LoginUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as PhoneNumberUtils from '@libs/PhoneNumber'; -import * as ValidationUtils from '@libs/ValidationUtils'; -import * as PersonalDetails from '@userActions/PersonalDetails'; +import {parsePhoneNumber} from '@libs/PhoneNumber'; +import {isRequiredFulfilled} from '@libs/ValidationUtils'; +import {clearPhoneNumberError, updatePhoneNumber as updatePhone} from '@userActions/PersonalDetails'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/PersonalDetailsForm'; @@ -32,23 +32,28 @@ function PhoneNumberPage() { const {inputCallbackRef} = useAutoFocusInput(); const phoneNumber = privatePersonalDetails?.phoneNumber ?? ''; - const validateLoginError = ErrorUtils.getEarliestErrorField(privatePersonalDetails, 'phoneNumber'); + const validateLoginError = getEarliestErrorField(privatePersonalDetails, 'phoneNumber'); const currenPhoneNumber = privatePersonalDetails?.phoneNumber ?? ''; const sanitizePhoneNumber = (num?: string): string => num?.replace(CONST.SANITIZE_PHONE_REGEX, '') ?? ''; + const formatPhoneNumber = useCallback((num: string) => { + const phoneNumberWithCountryCode = appendCountryCode(sanitizePhoneNumber(num)); + const parsedPhoneNumber = parsePhoneNumber(phoneNumberWithCountryCode); + + return parsedPhoneNumber; + }, []); const updatePhoneNumber = (values: PrivatePersonalDetails) => { // Clear the error when the user tries to submit the form if (validateLoginError) { - PersonalDetails.clearPhoneNumberError(); + clearPhoneNumberError(); } // Only call the API if the user has changed their phone number if (phoneNumber !== values?.phoneNumber && values?.phoneNumber) { - const phoneNumberWithCountryCode = LoginUtils.appendCountryCode(sanitizePhoneNumber(values?.phoneNumber)); - const parsedPhoneNumber = PhoneNumberUtils.parsePhoneNumber(phoneNumberWithCountryCode); + const formattedPhone = formatPhoneNumber(values.phoneNumber); - PersonalDetails.updatePhoneNumber(parsedPhoneNumber.number?.e164 ?? '', currenPhoneNumber); + updatePhone(formattedPhone.number?.e164 ?? '', currenPhoneNumber); } Navigation.goBack(); @@ -58,34 +63,25 @@ function PhoneNumberPage() { (values: FormOnyxValues): FormInputErrors => { const errors: FormInputErrors = {}; - // Validate that the phone number field is not empty - if (!ValidationUtils.isRequiredFulfilled(values[INPUT_IDS.PHONE_NUMBER])) { + if (!isRequiredFulfilled(values[INPUT_IDS.PHONE_NUMBER])) { errors[INPUT_IDS.PHONE_NUMBER] = translate('common.error.fieldRequired'); - return errors; // Early return if field is empty + return errors; } - // Sanitize input: Remove all non-numeric characters except the leading '+' - const sanitizedPhoneNumber = sanitizePhoneNumber(values[INPUT_IDS.PHONE_NUMBER]); - - // Append country code if missing - const phoneNumberWithCountryCode = LoginUtils.appendCountryCode(sanitizedPhoneNumber); - - // Parse and validate the phone number - const parsedPhoneNumber = PhoneNumberUtils.parsePhoneNumber(phoneNumberWithCountryCode); + const phoneNumberWithCountryCode = appendCountryCode(sanitizePhoneNumber(values[INPUT_IDS.PHONE_NUMBER])); + const parsedPhoneNumber = formatPhoneNumber(values[INPUT_IDS.PHONE_NUMBER]); - // Check if the phone number is possible and valid in E.164 format if (!parsedPhoneNumber.possible || !Str.isValidE164Phone(phoneNumberWithCountryCode)) { errors[INPUT_IDS.PHONE_NUMBER] = translate('bankAccount.error.phoneNumber'); } - // Clear the error if the user tries to validate the form and there are errors if (validateLoginError && Object.keys(errors).length > 0) { - PersonalDetails.clearPhoneNumberError(); + clearPhoneNumberError(); } return errors; }, - [translate, validateLoginError], + [formatPhoneNumber, translate, validateLoginError], ); return ( @@ -113,7 +109,7 @@ function PhoneNumberPage() { PersonalDetails.clearPhoneNumberError()} + onClose={() => clearPhoneNumberError()} > From 1b3346796079e389b83d241b89e8a6049dd10707 Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Tue, 4 Feb 2025 14:17:31 +0700 Subject: [PATCH 5/7] Improve phone number validation --- .../substeps/PhoneNumber.tsx | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/pages/MissingPersonalDetails/substeps/PhoneNumber.tsx b/src/pages/MissingPersonalDetails/substeps/PhoneNumber.tsx index 81f359f403d1..fbc88177ebbf 100644 --- a/src/pages/MissingPersonalDetails/substeps/PhoneNumber.tsx +++ b/src/pages/MissingPersonalDetails/substeps/PhoneNumber.tsx @@ -4,9 +4,9 @@ import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import SingleFieldStep from '@components/SubStepForms/SingleFieldStep'; import useLocalize from '@hooks/useLocalize'; import usePersonalDetailsFormSubmit from '@hooks/usePersonalDetailsFormSubmit'; -import * as LoginUtils from '@libs/LoginUtils'; -import * as PhoneNumberUtils from '@libs/PhoneNumber'; -import * as ValidationUtils from '@libs/ValidationUtils'; +import {appendCountryCode} from '@libs/LoginUtils'; +import {parsePhoneNumber} from '@libs/PhoneNumber'; +import {isRequiredFulfilled} from '@libs/ValidationUtils'; import type {CustomSubStepProps} from '@pages/MissingPersonalDetails/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -17,20 +17,33 @@ const STEP_FIELDS = [INPUT_IDS.PHONE_NUMBER]; function PhoneNumberStep({isEditing, onNext, onMove, personalDetailsValues}: CustomSubStepProps) { const {translate} = useLocalize(); + const sanitizePhoneNumber = (num?: string): string => num?.replace(CONST.SANITIZE_PHONE_REGEX, '') ?? ''; + const formatPhoneNumber = useCallback((num: string) => { + const phoneNumberWithCountryCode = appendCountryCode(sanitizePhoneNumber(num)); + const parsedPhoneNumber = parsePhoneNumber(phoneNumberWithCountryCode); + + return parsedPhoneNumber; + }, []); + const validate = useCallback( (values: FormOnyxValues): FormInputErrors => { const errors: FormInputErrors = {}; - if (!ValidationUtils.isRequiredFulfilled(values[INPUT_IDS.PHONE_NUMBER])) { + + if (!isRequiredFulfilled(values[INPUT_IDS.PHONE_NUMBER])) { errors[INPUT_IDS.PHONE_NUMBER] = translate('common.error.fieldRequired'); + return errors; } - const phoneNumber = LoginUtils.appendCountryCode(values[INPUT_IDS.PHONE_NUMBER]); - const parsedPhoneNumber = PhoneNumberUtils.parsePhoneNumber(phoneNumber); - if (!parsedPhoneNumber.possible || !Str.isValidE164Phone(phoneNumber.slice(0))) { + + const phoneNumberWithCountryCode = appendCountryCode(sanitizePhoneNumber(values[INPUT_IDS.PHONE_NUMBER])); + const parsedPhoneNumber = formatPhoneNumber(values[INPUT_IDS.PHONE_NUMBER]); + + if (!parsedPhoneNumber.possible || !Str.isValidE164Phone(phoneNumberWithCountryCode)) { errors[INPUT_IDS.PHONE_NUMBER] = translate('bankAccount.error.phoneNumber'); } + return errors; }, - [translate], + [formatPhoneNumber, translate], ); const handleSubmit = usePersonalDetailsFormSubmit({ @@ -47,7 +60,9 @@ function PhoneNumberStep({isEditing, onNext, onMove, personalDetailsValues}: Cus formID={ONYXKEYS.FORMS.PERSONAL_DETAILS_FORM} formTitle={translate('privatePersonalDetails.enterPhoneNumber')} validate={validate} - onSubmit={handleSubmit} + onSubmit={(values) => { + handleSubmit({...values, phoneNumber: formatPhoneNumber(values[INPUT_IDS.PHONE_NUMBER]).number?.e164 ?? ''}); + }} inputId={INPUT_IDS.PHONE_NUMBER} inputLabel={translate('common.phoneNumber')} inputMode={CONST.INPUT_MODE.TEL} From 05c93cbaaa644e0cd6307fc4b28a1a6159294d6b Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:30:58 +0700 Subject: [PATCH 6/7] Update phone number validation logic --- src/CONST.ts | 7 ++++--- .../MissingPersonalDetails/substeps/PhoneNumber.tsx | 13 ++++++++++--- .../Profile/PersonalDetails/PhoneNumberPage.tsx | 13 ++++++++++--- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index b2246b53c3d7..4b1fbee18be5 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -479,11 +479,12 @@ const CONST = { // Regex to read violation value from string given by backend VIOLATION_LIMIT_REGEX: /[^0-9]+/g, - // Regex to match and remove all lowercase letters (a-z) - REMOVE_LOWERCASE_REGEX: /[a-z]/g, - + // Removes non-digit/non-plus characters for phone sanitization. SANITIZE_PHONE_REGEX: /[^\d+]/g, + // Validates phone numbers allowing digits, '+', '-', '()', and '.'. + PHONE_NUMBER_PATTERN: /^[0-9+\-().]+$/, + MERCHANT_NAME_MAX_LENGTH: 255, MASKED_PAN_PREFIX: 'XXXXXXXXXXXX', diff --git a/src/pages/MissingPersonalDetails/substeps/PhoneNumber.tsx b/src/pages/MissingPersonalDetails/substeps/PhoneNumber.tsx index fbc88177ebbf..99077c961175 100644 --- a/src/pages/MissingPersonalDetails/substeps/PhoneNumber.tsx +++ b/src/pages/MissingPersonalDetails/substeps/PhoneNumber.tsx @@ -28,14 +28,21 @@ function PhoneNumberStep({isEditing, onNext, onMove, personalDetailsValues}: Cus const validate = useCallback( (values: FormOnyxValues): FormInputErrors => { const errors: FormInputErrors = {}; + const phoneNumberValue = values[INPUT_IDS.PHONE_NUMBER]; - if (!isRequiredFulfilled(values[INPUT_IDS.PHONE_NUMBER])) { + if (!isRequiredFulfilled(phoneNumberValue)) { errors[INPUT_IDS.PHONE_NUMBER] = translate('common.error.fieldRequired'); return errors; } - const phoneNumberWithCountryCode = appendCountryCode(sanitizePhoneNumber(values[INPUT_IDS.PHONE_NUMBER])); - const parsedPhoneNumber = formatPhoneNumber(values[INPUT_IDS.PHONE_NUMBER]); + if (!CONST.PHONE_NUMBER_PATTERN.test(phoneNumberValue)) { + errors[INPUT_IDS.PHONE_NUMBER] = translate('bankAccount.error.phoneNumber'); + return errors; + } + + const sanitizedPhoneNumber = sanitizePhoneNumber(phoneNumberValue); + const phoneNumberWithCountryCode = appendCountryCode(sanitizedPhoneNumber); + const parsedPhoneNumber = formatPhoneNumber(phoneNumberValue); if (!parsedPhoneNumber.possible || !Str.isValidE164Phone(phoneNumberWithCountryCode)) { errors[INPUT_IDS.PHONE_NUMBER] = translate('bankAccount.error.phoneNumber'); diff --git a/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx b/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx index fab3b51d8637..44dd9befa555 100644 --- a/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx @@ -62,14 +62,21 @@ function PhoneNumberPage() { const validate = useCallback( (values: FormOnyxValues): FormInputErrors => { const errors: FormInputErrors = {}; + const phoneNumberValue = values[INPUT_IDS.PHONE_NUMBER]; - if (!isRequiredFulfilled(values[INPUT_IDS.PHONE_NUMBER])) { + if (!isRequiredFulfilled(phoneNumberValue)) { errors[INPUT_IDS.PHONE_NUMBER] = translate('common.error.fieldRequired'); return errors; } - const phoneNumberWithCountryCode = appendCountryCode(sanitizePhoneNumber(values[INPUT_IDS.PHONE_NUMBER])); - const parsedPhoneNumber = formatPhoneNumber(values[INPUT_IDS.PHONE_NUMBER]); + if (!CONST.PHONE_NUMBER_PATTERN.test(phoneNumberValue)) { + errors[INPUT_IDS.PHONE_NUMBER] = translate('bankAccount.error.phoneNumber'); + return errors; + } + + const sanitizedPhoneNumber = sanitizePhoneNumber(phoneNumberValue); + const phoneNumberWithCountryCode = appendCountryCode(sanitizedPhoneNumber); + const parsedPhoneNumber = formatPhoneNumber(phoneNumberValue); if (!parsedPhoneNumber.possible || !Str.isValidE164Phone(phoneNumberWithCountryCode)) { errors[INPUT_IDS.PHONE_NUMBER] = translate('bankAccount.error.phoneNumber'); From e17bc6b13205a0689f1e4cf1dca36cab87d585bf Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Tue, 11 Feb 2025 14:13:16 +0700 Subject: [PATCH 7/7] Prevent consecutive special characters in phone number validation --- src/CONST.ts | 5 ++++- src/pages/MissingPersonalDetails/substeps/PhoneNumber.tsx | 2 +- .../settings/Profile/PersonalDetails/PhoneNumberPage.tsx | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 4b1fbee18be5..24efdfbfede6 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -483,7 +483,10 @@ const CONST = { SANITIZE_PHONE_REGEX: /[^\d+]/g, // Validates phone numbers allowing digits, '+', '-', '()', and '.'. - PHONE_NUMBER_PATTERN: /^[0-9+\-().]+$/, + ACCEPTED_PHONE_CHARACTER_REGEX: /^[0-9+\-().]+$/, + + // Prevents consecutive special characters like '--', '..', '((', '))'. + REPEATED_SPECIAL_CHAR_PATTERN: /([-().])\1+/, MERCHANT_NAME_MAX_LENGTH: 255, diff --git a/src/pages/MissingPersonalDetails/substeps/PhoneNumber.tsx b/src/pages/MissingPersonalDetails/substeps/PhoneNumber.tsx index 99077c961175..0cfd0c452de8 100644 --- a/src/pages/MissingPersonalDetails/substeps/PhoneNumber.tsx +++ b/src/pages/MissingPersonalDetails/substeps/PhoneNumber.tsx @@ -35,7 +35,7 @@ function PhoneNumberStep({isEditing, onNext, onMove, personalDetailsValues}: Cus return errors; } - if (!CONST.PHONE_NUMBER_PATTERN.test(phoneNumberValue)) { + if (!CONST.ACCEPTED_PHONE_CHARACTER_REGEX.test(phoneNumberValue) || CONST.REPEATED_SPECIAL_CHAR_PATTERN.test(phoneNumberValue)) { errors[INPUT_IDS.PHONE_NUMBER] = translate('bankAccount.error.phoneNumber'); return errors; } diff --git a/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx b/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx index 44dd9befa555..a5a009c87e51 100644 --- a/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/PhoneNumberPage.tsx @@ -69,7 +69,7 @@ function PhoneNumberPage() { return errors; } - if (!CONST.PHONE_NUMBER_PATTERN.test(phoneNumberValue)) { + if (!CONST.ACCEPTED_PHONE_CHARACTER_REGEX.test(phoneNumberValue) || CONST.REPEATED_SPECIAL_CHAR_PATTERN.test(phoneNumberValue)) { errors[INPUT_IDS.PHONE_NUMBER] = translate('bankAccount.error.phoneNumber'); return errors; }