diff --git a/web/splash/splash.react.js b/web/splash/splash.react.js index ecffe48e7..209f8aad4 100644 --- a/web/splash/splash.react.js +++ b/web/splash/splash.react.js @@ -1,276 +1,278 @@ // @flow import invariant from 'invariant'; -import PropTypes from 'prop-types'; import * as React from 'react'; import { requestAccessActionTypes, requestAccess, } from 'lib/actions/user-actions'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors'; import { validEmailRegex } from 'lib/shared/account-utils'; import type { AccessRequest } from 'lib/types/account-types'; import { type DeviceType, assertDeviceType } from 'lib/types/device-types'; +import { type LoadingStatus } from 'lib/types/loading-types'; import { - type LoadingStatus, - loadingStatusPropType, -} from 'lib/types/loading-types'; -import type { DispatchActionPromise } from 'lib/utils/action-utils'; -import { connect } from 'lib/utils/redux-utils'; + type DispatchActionPromise, + useDispatchActionPromise, + useServerCall, +} from 'lib/utils/action-utils'; import LoadingIndicator from '../loading-indicator.react'; import LogInModal from '../modals/account/log-in-modal.react'; -import type { AppState } from '../redux/redux-setup'; +import { useSelector } from '../redux/redux-utils'; import css from './splash.css'; const defaultRequestAccessScrollHeight = 390; -type Props = { - setModal: (modal: ?React.Node) => void, - currentModal: ?React.Node, - // Redux state - loadingStatus: LoadingStatus, - // Redux dispatch functions - dispatchActionPromise: DispatchActionPromise, - // async functions that hit server APIs - requestAccess: (accessRequest: AccessRequest) => Promise, -}; +type BaseProps = {| + +setModal: (modal: ?React.Node) => void, + +currentModal: ?React.Node, +|}; +type Props = {| + ...BaseProps, + +loadingStatus: LoadingStatus, + +dispatchActionPromise: DispatchActionPromise, + +requestAccess: (accessRequest: AccessRequest) => Promise, +|}; type State = {| - platform: DeviceType, - email: string, - error: ?string, - success: ?string, + +platform: DeviceType, + +email: string, + +error: ?string, + +success: ?string, |}; class Splash extends React.PureComponent { - static propTypes = { - setModal: PropTypes.func.isRequired, - currentModal: PropTypes.node, - loadingStatus: loadingStatusPropType.isRequired, - dispatchActionPromise: PropTypes.func.isRequired, - requestAccess: PropTypes.func.isRequired, - }; emailInput: ?HTMLInputElement; bottomContainer: ?HTMLDivElement; state: State = { platform: 'ios', email: '', error: null, success: null, }; componentDidMount() { if ('scrollRestoration' in history) { history.scrollRestoration = 'manual'; } } render() { let androidWarning = null; if (this.state.platform === 'android') { androidWarning = (

Make sure this is the email you use to log in to the Google Play Store!

); } let error = null; if (this.state.error) { error =

{this.state.error}

; } let success = null; if (this.state.success) { success = (

{this.state.success}

); } let submitButtonContent = 'Submit'; if (this.props.loadingStatus === 'loading') { submitButtonContent = ( ); } return (

SquadCal is a chat app with an integrated calendar.

We make it incredibly easy to plan events with your friends.

We're currently alpha testing the first version of our app.

If you'd like to try it out, please let us know!

{androidWarning}
{error} {success}
{this.props.currentModal} ); } bottomContainerRef = (bottomContainer: ?HTMLDivElement) => { this.bottomContainer = bottomContainer; }; emailInputRef = (emailInput: ?HTMLInputElement) => { this.emailInput = emailInput; }; onChangeEmail = (event: SyntheticEvent) => { this.setState({ email: event.currentTarget.value }); }; onChangePlatform = (event: SyntheticEvent) => { this.setState({ platform: assertDeviceType(event.currentTarget.value) }); }; onClickLogIn = (event: SyntheticEvent) => { event.preventDefault(); this.props.setModal(); }; onClickRequestAccess = (event: SyntheticEvent) => { event.preventDefault(); const { bottomContainer } = this; invariant(bottomContainer, 'bottomContainer should exist'); const formHeight = 180; const contentHeight = 790; const guaranteesSpace = contentHeight - window.innerHeight + formHeight; if (bottomContainer.scrollTop < guaranteesSpace) { bottomContainer.scrollTop = Math.max( defaultRequestAccessScrollHeight, guaranteesSpace, ); } if (this.emailInput) { this.emailInput.focus(); } }; onSubmitRequestAccess = (event: SyntheticEvent) => { event.preventDefault(); if (this.state.email.search(validEmailRegex) === -1) { this.setState({ success: null, error: 'Please enter a valid email!' }); invariant(this.emailInput, 'should be set'); this.emailInput.focus(); return; } this.props.dispatchActionPromise( requestAccessActionTypes, this.requestAccessAction(), ); }; async requestAccessAction() { try { await this.props.requestAccess({ email: this.state.email, platform: this.state.platform, }); this.setState({ success: "Thanks for your interest! We'll let you know as soon as " + "we're able to extend an invite.", error: null, }); } catch (e) { this.setState({ success: null, error: 'Unknown error...' }); throw e; } } } const loadingStatusSelector = createLoadingStatusSelector( requestAccessActionTypes, ); -export default connect( - (state: AppState) => ({ - loadingStatus: loadingStatusSelector(state), - }), - { requestAccess }, -)(Splash); +export default React.memo(function ConnectedSplash( + props: BaseProps, +) { + const loadingStatus = useSelector(loadingStatusSelector); + const callRequestAccess = useServerCall(requestAccess); + const dispatchActionPromise = useDispatchActionPromise(); + + return ( + + ); +});