Skip to content

Commit

Permalink
Merge pull request Expensify#30563 from dukenv0307/fix/29640
Browse files Browse the repository at this point in the history
Validate login form whever the input is changed
  • Loading branch information
Beamanator authored Nov 15, 2023
2 parents 3701d19 + 57031a6 commit 3bbb214
Showing 1 changed file with 61 additions and 20 deletions.
81 changes: 61 additions & 20 deletions src/pages/signin/LoginForm/BaseLoginForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import Log from '@libs/Log';
import * as LoginUtils from '@libs/LoginUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ValidationUtils from '@libs/ValidationUtils';
import Visibility from '@libs/Visibility';
import styles from '@styles/styles';
import * as CloseAccount from '@userActions/CloseAccount';
import * as MemoryOnlyKeys from '@userActions/MemoryOnlyKeys/MemoryOnlyKeys';
Expand Down Expand Up @@ -98,18 +99,52 @@ function LoginForm(props) {
const [login, setLogin] = useState(() => Str.removeSMSDomain(props.credentials.login || ''));
const [formError, setFormError] = useState(false);
const prevIsVisible = usePrevious(props.isVisible);
const firstBlurred = useRef(false);

const {translate} = props;

/**
* Handle text input and clear formError upon text change
* Validate the input value and set the error for formError
*
* @param {String} value
*/
const validate = useCallback(
(value) => {
const loginTrim = value.trim();
if (!loginTrim) {
setFormError('common.pleaseEnterEmailOrPhoneNumber');
return false;
}

const phoneLogin = LoginUtils.appendCountryCode(LoginUtils.getPhoneNumberWithoutSpecialChars(loginTrim));
const parsedPhoneNumber = parsePhoneNumber(phoneLogin);

if (!Str.isValidEmail(loginTrim) && !parsedPhoneNumber.possible) {
if (ValidationUtils.isNumericWithSpecialChars(loginTrim)) {
setFormError('common.error.phoneNumber');
} else {
setFormError('loginForm.error.invalidFormatEmailLogin');
}
return false;
}

setFormError(null);
return true;
},
[setFormError],
);

/**
* Handle text input and validate the text input if it is blurred
*
* @param {String} text
*/
const onTextInput = useCallback(
(text) => {
setLogin(text);
setFormError(null);
if (firstBlurred.current) {
validate(text);
}

if (props.account.errors || props.account.message) {
Session.clearAccountMessages();
Expand All @@ -120,7 +155,7 @@ function LoginForm(props) {
CloseAccount.setDefaultData();
}
},
[props.account, props.closeAccount, input, setFormError, setLogin],
[props.account, props.closeAccount, input, setLogin, validate],
);

function getSignInWithStyles() {
Expand All @@ -140,35 +175,30 @@ function LoginForm(props) {
CloseAccount.setDefaultData();
}

const loginTrim = login.trim();
if (!loginTrim) {
setFormError('common.pleaseEnterEmailOrPhoneNumber');
return;
// For native, the single input doesn't lost focus when we click outside.
// So we need to change firstBlurred here to make the validate function is called whenever the text input is changed after the first validation.
if (!firstBlurred.current) {
firstBlurred.current = true;
}

const phoneLogin = LoginUtils.appendCountryCode(LoginUtils.getPhoneNumberWithoutSpecialChars(loginTrim));
const parsedPhoneNumber = parsePhoneNumber(phoneLogin);

if (!Str.isValidEmail(loginTrim) && !parsedPhoneNumber.possible) {
if (ValidationUtils.isNumericWithSpecialChars(loginTrim)) {
setFormError('common.error.phoneNumber');
} else {
setFormError('loginForm.error.invalidFormatEmailLogin');
}
if (!validate(login)) {
return;
}

const loginTrim = login.trim();

// If the user has entered a guide email, then we are going to enable an experimental Onyx mode to help with performance
if (PolicyUtils.isExpensifyGuideTeam(loginTrim)) {
Log.info('Detected guide email in login field, setting memory only keys.');
MemoryOnlyKeys.enable();
}

setFormError(null);
const phoneLogin = LoginUtils.appendCountryCode(LoginUtils.getPhoneNumberWithoutSpecialChars(loginTrim));
const parsedPhoneNumber = parsePhoneNumber(phoneLogin);

// Check if this login has an account associated with it or not
Session.beginSignIn(parsedPhoneNumber.possible ? parsedPhoneNumber.number.e164 : loginTrim);
}, [login, props.account, props.closeAccount, props.network, setFormError]);
}, [login, props.account, props.closeAccount, props.network, validate]);

useEffect(() => {
// Just call clearAccountMessages on the login page (home route), because when the user is in the transition route and not yet authenticated,
Expand Down Expand Up @@ -227,6 +257,13 @@ function LoginForm(props) {
textContentType="username"
id="username"
name="username"
onBlur={() => {
if (firstBlurred.current || !Visibility.isVisible() || !Visibility.hasFocus()) {
return;
}
firstBlurred.current = true;
validate(login);
}}
onChangeText={onTextInput}
onSubmitEditing={validateAndSubmitForm}
autoCapitalize="none"
Expand Down Expand Up @@ -276,8 +313,12 @@ function LoginForm(props) {
</Text>

<View style={props.isSmallScreenWidth ? styles.loginButtonRowSmallScreen : styles.loginButtonRow}>
<AppleSignIn />
<GoogleSignIn />
<View onMouseDown={(e) => e.preventDefault()}>
<AppleSignIn />
</View>
<View onMouseDown={(e) => e.preventDefault()}>
<GoogleSignIn />
</View>
</View>
</View>
)
Expand Down

0 comments on commit 3bbb214

Please sign in to comment.