diff --git a/web/modals/account/log-in-modal.react.js b/web/modals/account/log-in-modal.react.js index 7be9053a3..cd885e272 100644 --- a/web/modals/account/log-in-modal.react.js +++ b/web/modals/account/log-in-modal.react.js @@ -1,247 +1,261 @@ // @flow import invariant from 'invariant'; import PropTypes from 'prop-types'; 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 { LogInInfo, LogInExtraInfo, LogInResult, LogInStartingPayload, } from 'lib/types/account-types'; -import type { DispatchActionPromise } from 'lib/utils/action-utils'; -import { connect } from 'lib/utils/redux-utils'; +import { + type DispatchActionPromise, + useDispatchActionPromise, + useServerCall, +} from 'lib/utils/action-utils'; -import type { AppState } from '../../redux/redux-setup'; +import { useSelector } from '../../redux/redux-utils'; import { webLogInExtraInfoSelector } from '../../selectors/account-selectors'; import css from '../../style.css'; import Modal from '../modal.react'; import ForgotPasswordModal from './forgot-password-modal.react'; -type Props = {| +type BaseProps = {| +setModal: (modal: ?React.Node) => void, - // Redux state +|}; +type Props = {| + ...BaseProps, +inputDisabled: boolean, +logInExtraInfo: () => LogInExtraInfo, - // Redux dispatch functions +dispatchActionPromise: DispatchActionPromise, - // async functions that hit server APIs +logIn: (logInInfo: LogInInfo) => Promise, |}; type State = {| +usernameOrEmail: string, +password: string, +errorMessage: string, |}; class LogInModal extends React.PureComponent { usernameOrEmailInput: ?HTMLInputElement; passwordInput: ?HTMLInputElement; constructor(props: Props) { super(props); this.state = { usernameOrEmail: '', password: '', errorMessage: '', }; } componentDidMount() { invariant(this.usernameOrEmailInput, 'usernameOrEmail ref unset'); this.usernameOrEmailInput.focus(); } render() { return (
Username
{this.state.errorMessage}
); } usernameOrEmailInputRef = (usernameOrEmailInput: ?HTMLInputElement) => { this.usernameOrEmailInput = usernameOrEmailInput; }; passwordInputRef = (passwordInput: ?HTMLInputElement) => { this.passwordInput = passwordInput; }; onChangeUsernameOrEmail = (event: SyntheticEvent) => { const target = event.target; invariant(target instanceof HTMLInputElement, 'target not input'); this.setState({ usernameOrEmail: target.value }); }; onChangePassword = (event: SyntheticEvent) => { const target = event.target; invariant(target instanceof HTMLInputElement, 'target not input'); this.setState({ password: target.value }); }; onClickForgotPassword = (event: SyntheticEvent) => { event.preventDefault(); this.props.setModal(); }; onSubmit = (event: SyntheticEvent) => { event.preventDefault(); if ( this.state.usernameOrEmail.search(oldValidUsernameRegex) === -1 && this.state.usernameOrEmail.search(validEmailRegex) === -1 ) { this.setState( { usernameOrEmail: '', errorMessage: 'alphanumeric usernames or emails only', }, () => { invariant( this.usernameOrEmailInput, 'usernameOrEmailInput ref unset', ); this.usernameOrEmailInput.focus(); }, ); return; } const extraInfo = this.props.logInExtraInfo(); this.props.dispatchActionPromise( logInActionTypes, this.logInAction(extraInfo), undefined, ({ calendarQuery: extraInfo.calendarQuery }: LogInStartingPayload), ); }; async logInAction(extraInfo: LogInExtraInfo) { try { const result = await this.props.logIn({ usernameOrEmail: this.state.usernameOrEmail, password: this.state.password, ...extraInfo, }); this.clearModal(); return result; } catch (e) { if (e.message === 'invalid_parameters') { this.setState( { usernameOrEmail: '', errorMessage: "user doesn't exist", }, () => { invariant( this.usernameOrEmailInput, 'usernameOrEmailInput ref unset', ); this.usernameOrEmailInput.focus(); }, ); } else if (e.message === 'invalid_credentials') { this.setState( { password: '', errorMessage: 'wrong password', }, () => { invariant(this.passwordInput, 'passwordInput ref unset'); this.passwordInput.focus(); }, ); } else { this.setState( { usernameOrEmail: '', password: '', errorMessage: 'unknown error', }, () => { invariant( this.usernameOrEmailInput, 'usernameOrEmailInput ref unset', ); this.usernameOrEmailInput.focus(); }, ); } throw e; } } clearModal = () => { this.props.setModal(null); }; } LogInModal.propTypes = { setModal: PropTypes.func.isRequired, inputDisabled: PropTypes.bool.isRequired, logInExtraInfo: PropTypes.func.isRequired, dispatchActionPromise: PropTypes.func.isRequired, logIn: PropTypes.func.isRequired, }; const loadingStatusSelector = createLoadingStatusSelector(logInActionTypes); -export default connect( - (state: AppState) => ({ - inputDisabled: loadingStatusSelector(state) === 'loading', - logInExtraInfo: webLogInExtraInfoSelector(state), - }), - { logIn }, -)(LogInModal); +export default React.memo(function ConnectedLoginModal( + props: BaseProps, +) { + const inputDisabled = useSelector(loadingStatusSelector) === 'loading'; + const loginExtraInfo = useSelector(webLogInExtraInfoSelector); + const callLogIn = useServerCall(logIn); + const dispatchActionPromise = useDispatchActionPromise(); + + return ( + + ); +});