diff --git a/web/modals/account/reset-password-modal.react.js b/web/modals/account/reset-password-modal.react.js index a73771654..d8d39582e 100644 --- a/web/modals/account/reset-password-modal.react.js +++ b/web/modals/account/reset-password-modal.react.js @@ -1,225 +1,237 @@ // @flow import invariant from 'invariant'; -import PropTypes from 'prop-types'; import * as React from 'react'; import { resetPasswordActionTypes, resetPassword, } from 'lib/actions/user-actions'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors'; import type { UpdatePasswordInfo, LogInExtraInfo, LogInResult, LogInStartingPayload, } from 'lib/types/account-types'; import { verifyField } from 'lib/types/verify-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'; -type Props = { - onClose: () => void, - onSuccess: () => void, - // Redux state - resetPasswordUsername: string, - verifyCode: string, - inputDisabled: boolean, - logInExtraInfo: () => LogInExtraInfo, - // Redux dispatch functions - dispatchActionPromise: DispatchActionPromise, - // async functions that hit server APIs - resetPassword: (info: UpdatePasswordInfo) => Promise, -}; -type State = { - password: string, - confirmPassword: string, - errorMessage: string, -}; +type BaseProps = {| + +onClose: () => void, + +onSuccess: () => void, +|}; +type Props = {| + ...BaseProps, + +resetPasswordUsername: string, + +verifyCode: ?string, + +inputDisabled: boolean, + +logInExtraInfo: () => LogInExtraInfo, + +dispatchActionPromise: DispatchActionPromise, + +resetPassword: (info: UpdatePasswordInfo) => Promise, +|}; +type State = {| + +password: string, + +confirmPassword: string, + +errorMessage: string, +|}; class ResetPasswordModal extends React.PureComponent { passwordInput: ?HTMLInputElement; constructor(props: Props) { super(props); this.state = { password: '', confirmPassword: '', errorMessage: '', }; } componentDidMount() { invariant(this.passwordInput, 'usernameOrEmail ref unset'); this.passwordInput.focus(); } render() { return (
Username
{this.props.resetPasswordUsername}
Password
{this.state.errorMessage}
); } passwordInputRef = (passwordInput: ?HTMLInputElement) => { this.passwordInput = passwordInput; }; onChangePassword = (event: SyntheticEvent) => { const target = event.target; invariant(target instanceof HTMLInputElement, 'target not input'); this.setState({ password: target.value }); }; onChangeConfirmPassword = (event: SyntheticEvent) => { const target = event.target; invariant(target instanceof HTMLInputElement, 'target not input'); this.setState({ confirmPassword: target.value }); }; onSubmit = (event: SyntheticEvent) => { event.preventDefault(); if (this.state.password === '') { this.setState( { password: '', confirmPassword: '', errorMessage: 'empty password', }, () => { invariant(this.passwordInput, 'passwordInput ref unset'); this.passwordInput.focus(); }, ); return; } if (this.state.password !== this.state.confirmPassword) { this.setState( { password: '', confirmPassword: '', errorMessage: "passwords don't match", }, () => { invariant(this.passwordInput, 'passwordInput ref unset'); this.passwordInput.focus(); }, ); return; } const extraInfo = this.props.logInExtraInfo(); this.props.dispatchActionPromise( resetPasswordActionTypes, this.resetPasswordAction(extraInfo), undefined, ({ calendarQuery: extraInfo.calendarQuery }: LogInStartingPayload), ); }; async resetPasswordAction(extraInfo: LogInExtraInfo) { + if (!this.props.verifyCode) { + return; + } try { const response = await this.props.resetPassword({ code: this.props.verifyCode, password: this.state.password, ...extraInfo, }); this.props.onSuccess(); return response; } catch (e) { this.setState( { password: '', confirmPassword: '', errorMessage: 'unknown error', }, () => { invariant(this.passwordInput, 'passwordInput ref unset'); this.passwordInput.focus(); }, ); throw e; } } } -ResetPasswordModal.propTypes = { - onClose: PropTypes.func.isRequired, - onSuccess: PropTypes.func.isRequired, - resetPasswordUsername: PropTypes.string.isRequired, - verifyCode: PropTypes.string.isRequired, - inputDisabled: PropTypes.bool.isRequired, - logInExtraInfo: PropTypes.func.isRequired, - dispatchActionPromise: PropTypes.func.isRequired, - resetPassword: PropTypes.func.isRequired, -}; - const loadingStatusSelector = createLoadingStatusSelector( resetPasswordActionTypes, ); -export default connect( - (state: AppState) => ({ - resetPasswordUsername: +export default React.memo(function ConnectedResetPasswordModal( + props: BaseProps, +) { + const resetPasswordUsername = useSelector( + (state) => state.serverVerificationResult && state.serverVerificationResult.success && state.serverVerificationResult.field === verifyField.RESET_PASSWORD && state.serverVerificationResult.username, - verifyCode: state.navInfo.verify, - inputDisabled: loadingStatusSelector(state) === 'loading', - logInExtraInfo: webLogInExtraInfoSelector(state), - }), - { resetPassword }, -)(ResetPasswordModal); + ); + const verifyCode = useSelector((state) => state.navInfo.verify); + const inputDisabled = useSelector( + (state) => loadingStatusSelector(state) === 'loading', + ); + const logInExtraInfo = useSelector(webLogInExtraInfoSelector); + const callResetPassword = useServerCall(resetPassword); + const dispatchActionPromise = useDispatchActionPromise(); + invariant(resetPasswordUsername, 'should have reset password username'); + + return ( + + ); +});