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

Revert "[PhoneNumber Verification] Display only valid numbers in user search." #18100

Merged
merged 1 commit into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -963,10 +963,15 @@ const CONST = {
},
REGEX: {
SPECIAL_CHARS_WITHOUT_NEWLINE: /((?!\n)[()-\s\t])/g,
US_PHONE: /^\+1\d{10}$/,
US_PHONE_WITH_OPTIONAL_COUNTRY_CODE: /^(\+1)?\d{10}$/,
DIGITS_AND_PLUS: /^\+?[0-9]*$/,
PHONE_E164_PLUS: /^\+?[1-9]\d{1,14}$/,
PHONE_WITH_SPECIAL_CHARS: /^\s*(?:\+?(\d{1,3}))?[-. (]*(\d{3})[-. )]*(\d{3})[-. ]*(\d{4})(?: *x(\d+))?\s*$/,
ALPHABETIC_CHARS: /[a-zA-Z]+/,
ALPHABETIC_CHARS_WITH_NUMBER: /^[a-zA-Z0-9 ]*$/,
POSITIVE_INTEGER: /^\d+$/,
NON_ALPHA_NUMERIC: /[^A-Za-z0-9+]/g,
PO_BOX: /\b[P|p]?(OST|ost)?\.?\s*[O|o|0]?(ffice|FFICE)?\.?\s*[B|b][O|o|0]?[X|x]?\.?\s+[#]?(\d+)\b/,
ANY_VALUE: /^.+$/,
ZIP_CODE: /[0-9]{5}(?:[- ][0-9]{4})?/,
Expand All @@ -988,6 +993,7 @@ const CONST = {
// Extract attachment's source from the data's html string
ATTACHMENT_DATA: /(data-expensify-source|data-name)="([^"]+)"/g,

NON_NUMERIC_WITH_PLUS: /[^0-9+]/g,
EMOJI_NAME: /:[\w+-]+:/g,
EMOJI_SUGGESTIONS: /:[a-zA-Z0-9_+-]{1,40}$/,
AFTER_FIRST_LINE_BREAK: /\n.*/g,
Expand Down
16 changes: 15 additions & 1 deletion src/libs/LoginUtils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Str from 'expensify-common/lib/str';
import Onyx from 'react-native-onyx';
import CONST from '../CONST';
import ONYXKEYS from '../ONYXKEYS';
Expand All @@ -18,17 +19,30 @@ function getPhoneNumberWithoutSpecialChars(phone) {
return phone.replace(CONST.REGEX.SPECIAL_CHARS_WITHOUT_NEWLINE, '');
}

/**
* Remove +1 and special chars from the phone number
*
* @param {String} phone
* @return {String}
*/
function getPhoneNumberWithoutUSCountryCodeAndSpecialChars(phone) {
return getPhoneNumberWithoutSpecialChars(phone.replace(/^\+1/, ''));
}

/**
* Append user country code to the phone number
*
* @param {String} phone
* @return {String}
*/
function appendCountryCode(phone) {
return phone.startsWith('+') ? phone : `+${countryCodeByIP}${phone}`;
return (Str.isValidPhone(phone) && !phone.includes('+'))
? `+${countryCodeByIP}${phone}`
: phone;
}

export {
getPhoneNumberWithoutSpecialChars,
getPhoneNumberWithoutUSCountryCodeAndSpecialChars,
appendCountryCode,
};
41 changes: 25 additions & 16 deletions src/libs/OptionsListUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import Onyx from 'react-native-onyx';
import lodashOrderBy from 'lodash/orderBy';
import lodashGet from 'lodash/get';
import Str from 'expensify-common/lib/str';
import {parsePhoneNumber} from 'awesome-phonenumber';
import ONYXKEYS from '../ONYXKEYS';
import CONST from '../CONST';
import * as ReportUtils from './ReportUtils';
Expand Down Expand Up @@ -33,6 +32,12 @@ Onyx.connect({
callback: val => loginList = _.isEmpty(val) ? {} : val,
});

let countryCodeByIP;
Onyx.connect({
key: ONYXKEYS.COUNTRY_CODE,
callback: val => countryCodeByIP = val || 1,
});

let preferredLocale;
Onyx.connect({
key: ONYXKEYS.NVP_PREFERRED_LOCALE,
Expand Down Expand Up @@ -129,9 +134,9 @@ function getPolicyExpenseReportOptions(report) {
* @return {String}
*/
function addSMSDomainIfPhoneNumber(login) {
const parsedPhoneNumber = parsePhoneNumber(login);
if (parsedPhoneNumber.possible && !Str.isValidEmail(login)) {
return parsedPhoneNumber.number.e164 + CONST.SMS.DOMAIN;
if (Str.isValidPhone(login) && !Str.isValidEmail(login)) {
const smsLogin = login + CONST.SMS.DOMAIN;
return smsLogin.includes('+') ? smsLogin : `+${countryCodeByIP}${smsLogin}`;
}
return login;
}
Expand Down Expand Up @@ -527,8 +532,8 @@ function getOptions(reports, personalDetails, {
let recentReportOptions = [];
let personalDetailsOptions = [];
const reportMapForLogins = {};
const parsedPhoneNumber = parsePhoneNumber(LoginUtils.appendCountryCode(searchInputValue));
const searchValue = parsedPhoneNumber.possible ? parsedPhoneNumber.number.e164 : searchInputValue;
const isPhoneNumber = CONST.REGEX.PHONE_WITH_SPECIAL_CHARS.test(searchInputValue);
const searchValue = isPhoneNumber ? searchInputValue.replace(CONST.REGEX.NON_NUMERIC_WITH_PLUS, '') : searchInputValue;

// Filter out all the reports that shouldn't be displayed
const filteredReports = _.filter(reports, report => ReportUtils.shouldReportBeInOptionList(
Expand Down Expand Up @@ -671,21 +676,25 @@ function getOptions(reports, personalDetails, {
const noOptions = (recentReportOptions.length + personalDetailsOptions.length) === 0;
const noOptionsMatchExactly = !_.find(personalDetailsOptions.concat(recentReportOptions), option => option.login === searchValue.toLowerCase());

if (searchValue && (noOptions || noOptionsMatchExactly)
&& !isCurrentUser({login: searchValue})
&& _.every(selectedOptions, option => option.login !== searchValue)
&& ((Str.isValidEmail(searchValue) && !Str.isDomainEmail(searchValue)) || parsedPhoneNumber.possible)
&& (!_.find(loginOptionsToExclude, loginOptionToExclude => loginOptionToExclude.login === addSMSDomainIfPhoneNumber(searchValue).toLowerCase()))
&& (searchValue !== CONST.EMAIL.CHRONOS || Permissions.canUseChronos(betas))
// If the phone number doesn't have an international code then let's prefix it with the
// current user's international code based on their IP address.
const login = LoginUtils.appendCountryCode(searchValue);

if (login && (noOptions || noOptionsMatchExactly)
&& !isCurrentUser({login})
&& _.every(selectedOptions, option => option.login !== login)
&& ((Str.isValidEmail(login) && !Str.isDomainEmail(login)) || Str.isValidPhone(login))
&& (!_.find(loginOptionsToExclude, loginOptionToExclude => loginOptionToExclude.login === addSMSDomainIfPhoneNumber(login).toLowerCase()))
&& (login !== CONST.EMAIL.CHRONOS || Permissions.canUseChronos(betas))
) {
userToInvite = createOption([searchValue], personalDetails, null, reportActions, {
userToInvite = createOption([login], personalDetails, null, reportActions, {
showChatPreviewLine,
});

// If user doesn't exist, use a default avatar
userToInvite.icons = [{
source: ReportUtils.getAvatar('', searchValue),
name: searchValue,
source: ReportUtils.getAvatar('', login),
name: login,
type: CONST.ICON_TYPE_AVATAR,
}];
}
Expand Down Expand Up @@ -855,7 +864,7 @@ function getHeaderMessage(hasSelectableOptions, hasUserToInvite, searchValue, ma
return Localize.translate(preferredLocale, 'common.maxParticipantsReached', {count: CONST.REPORT.MAXIMUM_PARTICIPANTS});
}

const isValidPhone = parsePhoneNumber(LoginUtils.appendCountryCode(searchValue)).possible;
const isValidPhone = Str.isValidPhone(LoginUtils.appendCountryCode(searchValue));

const isValidEmail = Str.isValidEmail(searchValue);

Expand Down
10 changes: 5 additions & 5 deletions src/libs/ValidationUtils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import moment from 'moment';
import _ from 'underscore';
import {URL_REGEX_WITH_REQUIRED_PROTOCOL} from 'expensify-common/lib/Url';
import {parsePhoneNumber} from 'awesome-phonenumber';
import CONST from '../CONST';
import * as CardUtils from './CardUtils';
import * as LoginUtils from './LoginUtils';
Expand Down Expand Up @@ -298,11 +297,12 @@ function validateIdentity(identity) {
* @returns {Boolean}
*/
function isValidUSPhone(phoneNumber = '', isCountryCodeOptional) {
const phone = phoneNumber || '';
const regionCode = isCountryCodeOptional ? CONST.COUNTRY.US : null;
// Remove non alphanumeric characters from the phone number
const sanitizedPhone = (phoneNumber || '').replace(CONST.REGEX.NON_ALPHA_NUMERIC, '');
const isUsPhone = isCountryCodeOptional
? CONST.REGEX.US_PHONE_WITH_OPTIONAL_COUNTRY_CODE.test(sanitizedPhone) : CONST.REGEX.US_PHONE.test(sanitizedPhone);

const parsedPhoneNumber = parsePhoneNumber(phone, {regionCode});
return parsedPhoneNumber.possible && parsedPhoneNumber.regionCode === CONST.COUNTRY.US;
return CONST.REGEX.PHONE_E164_PLUS.test(sanitizedPhone) && isUsPhone;
}

/**
Expand Down
6 changes: 2 additions & 4 deletions src/pages/DetailsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import _ from 'underscore';
import {withOnyx} from 'react-native-onyx';
import Str from 'expensify-common/lib/str';
import lodashGet from 'lodash/get';
import {parsePhoneNumber} from 'awesome-phonenumber';
import styles from '../styles/styles';
import Text from '../components/Text';
import ONYXKEYS from '../ONYXKEYS';
Expand Down Expand Up @@ -74,9 +73,8 @@ const defaultProps = {
*/
const getPhoneNumber = (details) => {
// If the user hasn't set a displayName, it is set to their phone number, so use that
const parsedPhoneNumber = parsePhoneNumber(details.displayName);
if (parsedPhoneNumber.possible) {
return parsedPhoneNumber.number.e164;
if (Str.isValidPhone(details.displayName)) {
return details.displayName;
}

// If the user has set a displayName, get the phone number from the SMS login
Expand Down
4 changes: 2 additions & 2 deletions src/pages/EnablePayments/AdditionalDetailsStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import {View} from 'react-native';
import moment from 'moment/moment';
import {parsePhoneNumber} from 'awesome-phonenumber';
import IdologyQuestions from './IdologyQuestions';
import ScreenWrapper from '../../components/ScreenWrapper';
import HeaderWithCloseButton from '../../components/HeaderWithCloseButton';
Expand All @@ -19,6 +18,7 @@ import TextLink from '../../components/TextLink';
import TextInput from '../../components/TextInput';
import * as Wallet from '../../libs/actions/Wallet';
import * as ValidationUtils from '../../libs/ValidationUtils';
import * as LoginUtils from '../../libs/LoginUtils';
import * as ErrorUtils from '../../libs/ErrorUtils';
import AddressForm from '../ReimbursementAccount/AddressForm';
import DatePicker from '../../components/DatePicker';
Expand Down Expand Up @@ -173,7 +173,7 @@ class AdditionalDetailsStep extends React.Component {
*/
activateWallet(values) {
const personalDetails = {
phoneNumber: parsePhoneNumber(values[INPUT_IDS.PHONE_NUMBER], {regionCode: CONST.COUNTRY.US}).number.significant,
phoneNumber: LoginUtils.getPhoneNumberWithoutUSCountryCodeAndSpecialChars(values[INPUT_IDS.PHONE_NUMBER]),
legalFirstName: values[INPUT_IDS.LEGAL_FIRST_NAME],
legalLastName: values[INPUT_IDS.LEGAL_LAST_NAME],
addressStreet: values[INPUT_IDS.ADDRESS.street],
Expand Down
4 changes: 2 additions & 2 deletions src/pages/ReimbursementAccount/CompanyStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {View} from 'react-native';
import Str from 'expensify-common/lib/str';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import {parsePhoneNumber} from 'awesome-phonenumber';
import HeaderWithCloseButton from '../../components/HeaderWithCloseButton';
import CONST from '../../CONST';
import * as BankAccounts from '../../libs/actions/BankAccounts';
Expand All @@ -19,6 +18,7 @@ import TextLink from '../../components/TextLink';
import StatePicker from '../../components/StatePicker';
import withLocalize from '../../components/withLocalize';
import * as ValidationUtils from '../../libs/ValidationUtils';
import * as LoginUtils from '../../libs/LoginUtils';
import compose from '../../libs/compose';
import ONYXKEYS from '../../ONYXKEYS';
import Picker from '../../components/Picker';
Expand Down Expand Up @@ -148,7 +148,7 @@ class CompanyStep extends React.Component {
// Fields from Company step
...values,
companyTaxID: values.companyTaxID.replace(CONST.REGEX.NON_NUMERIC, ''),
companyPhone: parsePhoneNumber(values.companyPhone, {regionCode: CONST.COUNTRY.US}).number.significant,
companyPhone: LoginUtils.getPhoneNumberWithoutUSCountryCodeAndSpecialChars(values.companyPhone),
};

BankAccounts.updateCompanyInformationForBankAccount(bankAccount);
Expand Down
5 changes: 2 additions & 3 deletions src/pages/settings/Profile/Contacts/NewContactMethodPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {withOnyx} from 'react-native-onyx';
import {compose} from 'underscore';
import lodashGet from 'lodash/get';
import Str from 'expensify-common/lib/str';
import {parsePhoneNumber} from 'awesome-phonenumber';
import Button from '../../../../components/Button';
import FixedFooter from '../../../../components/FixedFooter';
import HeaderWithCloseButton from '../../../../components/HeaderWithCloseButton';
Expand Down Expand Up @@ -69,10 +68,10 @@ function NewContactMethodPage(props) {
}, []);

const isFormValid = useMemo(() => {
const phoneLogin = LoginUtils.appendCountryCode(LoginUtils.getPhoneNumberWithoutSpecialChars(login));
const phoneLogin = LoginUtils.getPhoneNumberWithoutSpecialChars(login);

return (Permissions.canUsePasswordlessLogins(props.betas) || password)
&& (Str.isValidEmail(login) || parsePhoneNumber(phoneLogin).possible);
&& (Str.isValidEmail(login) || Str.isValidPhone(phoneLogin));
}, [login, password, props.betas]);

const submitForm = useCallback(() => {
Expand Down
9 changes: 4 additions & 5 deletions src/pages/signin/LoginForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import _ from 'underscore';
import Str from 'expensify-common/lib/str';
import {parsePhoneNumber} from 'awesome-phonenumber';
import styles from '../../styles/styles';
import Text from '../../components/Text';
import * as Session from '../../libs/actions/Session';
Expand Down Expand Up @@ -144,10 +143,10 @@ class LoginForm extends React.Component {
return;
}

const phoneLogin = LoginUtils.appendCountryCode(LoginUtils.getPhoneNumberWithoutSpecialChars(login));
const parsedPhoneNumber = parsePhoneNumber(phoneLogin);
const phoneLogin = LoginUtils.getPhoneNumberWithoutSpecialChars(login);
const isValidPhoneLogin = Str.isValidPhone(phoneLogin);

if (!Str.isValidEmail(login) && !parsedPhoneNumber.possible) {
if (!Str.isValidEmail(login) && !isValidPhoneLogin) {
if (ValidationUtils.isNumericWithSpecialChars(login)) {
this.setState({formError: 'common.error.phoneNumber'});
} else {
Expand All @@ -161,7 +160,7 @@ class LoginForm extends React.Component {
});

// Check if this login has an account associated with it or not
Session.beginSignIn(parsedPhoneNumber.possible ? parsedPhoneNumber.number.e164 : login);
Session.beginSignIn(isValidPhoneLogin ? phoneLogin : login);
}

render() {
Expand Down