Page MenuHomePhabricator

No OneTemporary

diff --git a/web/account/log-in-form.css b/web/account/log-in-form.css
index b51ef1e34..b4433d576 100644
--- a/web/account/log-in-form.css
+++ b/web/account/log-in-form.css
@@ -1,37 +1,42 @@
div.modal-body {
display: flex;
flex-direction: column;
padding: 20px 40px;
border-radius: 16px;
background-color: var(--modal-bg);
box-shadow: 0 0 40px rgba(126, 87, 194, 0.5);
}
div.form-title {
padding: 6px 6px 0 0;
font-size: var(--s-font-14);
font-weight: var(--bold);
color: var(--fg);
}
div.form-content {
margin-top: 4px;
margin-bottom: 8px;
font-family: var(--font-stack);
color: var(--fg);
}
div.form-footer {
display: flex;
flex-direction: column;
margin-top: 16px;
}
+div.form-footer > button:disabled {
+ min-height: 48px;
+ opacity: 0.5;
+}
+
div.modal-form-error {
margin-top: 16px;
font-size: 14px;
color: var(--error);
font-style: italic;
padding-left: 6px;
align-self: center;
}
diff --git a/web/account/log-in-form.react.js b/web/account/log-in-form.react.js
index 9beca6595..c2a98aa20 100644
--- a/web/account/log-in-form.react.js
+++ b/web/account/log-in-form.react.js
@@ -1,155 +1,163 @@
// @flow
import invariant from 'invariant';
import * as React from 'react';
import { logInActionTypes, logIn } from 'lib/actions/user-actions';
import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors';
import {
oldValidUsernameRegex,
validEmailRegex,
} from 'lib/shared/account-utils';
import type {
LogInExtraInfo,
LogInStartingPayload,
} from 'lib/types/account-types';
import {
useDispatchActionPromise,
useServerCall,
} from 'lib/utils/action-utils';
import Button from '../components/button.react';
+import LoadingIndicator from '../loading-indicator.react';
import Input from '../modals/input.react';
import { useModalContext } from '../modals/modal-provider.react';
import { useSelector } from '../redux/redux-utils';
import { webLogInExtraInfoSelector } from '../selectors/account-selectors';
import css from './log-in-form.css';
const loadingStatusSelector = createLoadingStatusSelector(logInActionTypes);
function LoginForm(): React.Node {
const inputDisabled = useSelector(loadingStatusSelector) === 'loading';
const loginExtraInfo = useSelector(webLogInExtraInfoSelector);
const callLogIn = useServerCall(logIn);
const dispatchActionPromise = useDispatchActionPromise();
const modalContext = useModalContext();
const [username, setUsername] = React.useState<string>('');
const [password, setPassword] = React.useState<string>('');
const [errorMessage, setErrorMessage] = React.useState<string>('');
const usernameInputRef = React.useRef();
const passwordInputRef = React.useRef();
React.useEffect(() => {
usernameInputRef.current?.focus();
}, []);
const onUsernameChange = React.useCallback(e => {
invariant(e.target instanceof HTMLInputElement, 'target not input');
setUsername(e.target.value);
}, []);
const onPasswordChange = React.useCallback(e => {
invariant(e.target instanceof HTMLInputElement, 'target not input');
setPassword(e.target.value);
}, []);
const logInAction = React.useCallback(
async (extraInfo: LogInExtraInfo) => {
try {
const result = await callLogIn({
username,
password,
...extraInfo,
});
modalContext.clearModal();
return result;
} catch (e) {
if (e.message === 'invalid_parameters') {
setUsername('');
setErrorMessage(`user doesn't exist`);
usernameInputRef.current?.focus();
} else if (e.message === 'invalid_credentials') {
setPassword('');
setErrorMessage('wrong password');
passwordInputRef.current?.focus();
} else {
setUsername('');
setPassword('');
setErrorMessage('unknown error');
usernameInputRef.current?.focus();
}
throw e;
}
},
[callLogIn, modalContext, password, username],
);
const onSubmit = React.useCallback(
(event: SyntheticEvent<HTMLElement>) => {
event.preventDefault();
if (username.search(validEmailRegex) > -1) {
setUsername('');
setErrorMessage('usernames only, not emails');
usernameInputRef.current?.focus();
return;
} else if (username.search(oldValidUsernameRegex) === -1) {
setUsername('');
setErrorMessage('alphanumeric usernames only');
usernameInputRef.current?.focus();
return;
}
const extraInfo = loginExtraInfo();
dispatchActionPromise(
logInActionTypes,
logInAction(extraInfo),
undefined,
({ calendarQuery: extraInfo.calendarQuery }: LogInStartingPayload),
);
},
[dispatchActionPromise, logInAction, loginExtraInfo, username],
);
+ let loginButtonContent;
+ if (inputDisabled) {
+ loginButtonContent = <LoadingIndicator status="loading" />;
+ } else {
+ loginButtonContent = 'Log in';
+ }
+
return (
<div className={css['modal-body']}>
<form method="POST">
<div>
<div className={css['form-title']}>Username</div>
<div className={css['form-content']}>
<Input
type="text"
placeholder="Username"
value={username}
onChange={onUsernameChange}
ref={usernameInputRef}
disabled={inputDisabled}
/>
</div>
</div>
<div>
<div className={css['form-title']}>Password</div>
<div className={css['form-content']}>
<Input
type="password"
placeholder="Password"
value={password}
onChange={onPasswordChange}
ref={passwordInputRef}
disabled={inputDisabled}
/>
</div>
</div>
<div className={css['form-footer']}>
<Button type="submit" disabled={inputDisabled} onClick={onSubmit}>
- Log in
+ {loginButtonContent}
</Button>
<div className={css['modal-form-error']}>{errorMessage}</div>
</div>
</form>
</div>
);
}
export default LoginForm;

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 23, 2:18 AM (10 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2690206
Default Alt Text
(6 KB)

Event Timeline