diff --git a/native/account/logged-out-modal.react.js b/native/account/logged-out-modal.react.js --- a/native/account/logged-out-modal.react.js +++ b/native/account/logged-out-modal.react.js @@ -50,19 +50,14 @@ } from '../navigation/route-names.js'; import { useSelector } from '../redux/redux-utils.js'; import { usePersistedStateLoaded } from '../selectors/app-state-selectors.js'; -import { - type DerivedDimensionsInfo, - derivedDimensionsInfoSelector, -} from '../selectors/dimensions-selectors.js'; +import { derivedDimensionsInfoSelector } from '../selectors/dimensions-selectors.js'; import { splashStyleSelector } from '../splash.js'; import { useStyles } from '../themes/colors.js'; import type { KeyboardEvent } from '../types/react-native.js'; -import type { ImageStyle } from '../types/styles.js'; import { runTiming, ratchetAlongWithKeyboardHeight, } from '../utils/animation-utils.js'; -import type { StateContainer } from '../utils/state-container.js'; import EthereumLogo from '../vectors/ethereum-logo.react.js'; let initialAppLoad = true; @@ -218,239 +213,11 @@ }, }; -type BaseProps = { +type Props = { +navigation: RootNavigationProp<'LoggedOutModal'>, +route: NavigationRoute<'LoggedOutModal'>, }; -type Props = { - ...BaseProps, - +logInStateContainer: StateContainer<LogInState>, - +legacyRegisterStateContainer: StateContainer<LegacyRegisterState>, - +mode: Mode, - +contentHeight: Value, - +keyboardHeightValue: Value, - +buttonOpacity: Value, - +panelPaddingTop: Node, - +panelOpacity: Node, - +combinedSetMode: LoggedOutMode => void, - +goBackToPrompt: () => void, - +activeAlertRef: { current: boolean }, - +setActiveAlert: boolean => void, - +resetToPrompt: () => boolean, - // Redux state - +dimensions: DerivedDimensionsInfo, - +splashStyle: ImageStyle, - +styles: $ReadOnly<typeof unboundStyles>, -}; -class LoggedOutModal extends React.PureComponent<Props> { - render(): React.Node { - const { styles } = this.props; - - const siweButton = ( - <> - <TouchableOpacity - onPress={this.onPressSIWE} - style={[styles.button, styles.siweButton]} - activeOpacity={0.6} - > - <View style={styles.siweIcon}> - <EthereumLogo /> - </View> - <Text style={[styles.buttonText, styles.siweButtonText]}> - Sign in with Ethereum - </Text> - </TouchableOpacity> - <View style={styles.siweOr}> - <View style={styles.siweOrLeftHR} /> - <Text style={styles.siweOrText}>or</Text> - <View style={styles.siweOrRightHR} /> - </View> - </> - ); - - let panel = null; - let buttons = null; - if (this.props.mode.curMode === 'log-in') { - panel = ( - <LogInPanel - setActiveAlert={this.props.setActiveAlert} - opacityValue={this.props.panelOpacity} - logInState={this.props.logInStateContainer} - /> - ); - } else if (this.props.mode.curMode === 'register') { - panel = ( - <LegacyRegisterPanel - setActiveAlert={this.props.setActiveAlert} - opacityValue={this.props.panelOpacity} - legacyRegisterState={this.props.legacyRegisterStateContainer} - /> - ); - } else if (this.props.mode.curMode === 'prompt') { - const opacityStyle = { opacity: this.props.buttonOpacity }; - - const registerButtons = []; - registerButtons.push( - <TouchableOpacity - onPress={this.onPressRegister} - style={[styles.button, styles.classicAuthButton]} - activeOpacity={0.6} - key="old" - > - <Text style={[styles.buttonText, styles.classicAuthButtonText]}> - Register - </Text> - </TouchableOpacity>, - ); - if (enableNewRegistrationMode) { - registerButtons.push( - <TouchableOpacity - onPress={this.onPressNewRegister} - style={[styles.button, styles.classicAuthButton]} - activeOpacity={0.6} - key="new" - > - <Text style={[styles.buttonText, styles.classicAuthButtonText]}> - Register (new) - </Text> - </TouchableOpacity>, - ); - } - - const signInButtons = []; - signInButtons.push( - <TouchableOpacity - onPress={this.onPressLogIn} - style={[styles.button, styles.classicAuthButton]} - activeOpacity={0.6} - key="login-form" - > - <Text style={[styles.buttonText, styles.classicAuthButtonText]}> - Sign in - </Text> - </TouchableOpacity>, - ); - if (__DEV__) { - signInButtons.push( - <TouchableOpacity - onPress={this.onPressQRCodeSignIn} - style={[styles.button, styles.classicAuthButton]} - activeOpacity={0.6} - key="qr-code-login" - > - <Text style={[styles.buttonText, styles.classicAuthButtonText]}> - Sign in (QR) - </Text> - </TouchableOpacity>, - ); - } - - buttons = ( - <Animated.View style={[styles.buttonContainer, opacityStyle]}> - <LoggedOutStaffInfo /> - {siweButton} - <View style={styles.signInButtons}>{signInButtons}</View> - <View style={styles.registerButtons}>{registerButtons}</View> - </Animated.View> - ); - } else if (this.props.mode.curMode === 'loading') { - panel = ( - <ActivityIndicator - color="white" - size="large" - style={styles.loadingIndicator} - /> - ); - } - - const windowWidth = this.props.dimensions.width; - const buttonStyle = { - opacity: this.props.panelOpacity, - left: windowWidth < 360 ? 28 : 40, - }; - const padding = { paddingTop: this.props.panelPaddingTop }; - - const animatedContent = ( - <Animated.View style={[styles.animationContainer, padding]}> - <View> - <Text style={styles.header}>Comm</Text> - <Animated.View style={[styles.backButton, buttonStyle]}> - <TouchableOpacity - activeOpacity={0.6} - onPress={this.props.resetToPrompt} - > - <Icon name="arrow-circle-o-left" size={36} color="#FFFFFFAA" /> - </TouchableOpacity> - </Animated.View> - </View> - {panel} - </Animated.View> - ); - - let siwePanel; - if (this.props.mode.curMode === 'siwe') { - siwePanel = ( - <FullscreenSIWEPanel - goBackToPrompt={this.props.goBackToPrompt} - closing={this.props.mode.nextMode === 'prompt'} - /> - ); - } - - const backgroundSource = { uri: splashBackgroundURI }; - return ( - <React.Fragment> - <ConnectedStatusBar barStyle="light-content" /> - <Image - source={backgroundSource} - style={[styles.modalBackground, this.props.splashStyle]} - /> - <SafeAreaView style={styles.container} edges={safeAreaEdges}> - <KeyboardAvoidingView behavior="padding" style={styles.container}> - {animatedContent} - {buttons} - </KeyboardAvoidingView> - </SafeAreaView> - {siwePanel} - </React.Fragment> - ); - } - - onPressSIWE = () => { - this.props.combinedSetMode('siwe'); - }; - - onPressLogIn = () => { - if (Platform.OS !== 'ios') { - // For some strange reason, iOS's password management logic doesn't - // realize that the username and password fields in LogInPanel are related - // if the username field gets focused on mount. To avoid this issue we - // need the username and password fields to both appear on-screen before - // we focus the username field. However, when we set keyboardHeightValue - // to -1 here, we are telling our Reanimated logic to wait until the - // keyboard appears before showing LogInPanel. Since we need LogInPanel - // to appear before the username field is focused, we need to avoid this - // behavior on iOS. - this.props.keyboardHeightValue.setValue(-1); - } - this.props.combinedSetMode('log-in'); - }; - - onPressQRCodeSignIn = () => { - this.props.navigation.navigate(QRCodeSignInNavigatorRouteName); - }; - - onPressRegister = () => { - this.props.keyboardHeightValue.setValue(-1); - this.props.combinedSetMode('register'); - }; - - onPressNewRegister = () => { - this.props.navigation.navigate(RegistrationRouteName); - }; -} - const isForegroundSelector = createIsForegroundSelector( LoggedOutModalRouteName, ); @@ -470,8 +237,8 @@ +nextMode: LoggedOutMode, }; -const ConnectedLoggedOutModal: React.ComponentType<BaseProps> = - React.memo<BaseProps>(function ConnectedLoggedOutModal(props: BaseProps) { +const ConnectedLoggedOutModal: React.ComponentType<Props> = React.memo<Props>( + function ConnectedLoggedOutModal(props: Props) { const mountedRef = React.useRef(false); React.useEffect(() => { mountedRef.current = true; @@ -804,27 +571,207 @@ const splashStyle = useSelector(splashStyleSelector); const styles = useStyles(unboundStyles); + + const onPressSIWE = React.useCallback(() => { + combinedSetMode('siwe'); + }, [combinedSetMode]); + + const onPressLogIn = React.useCallback(() => { + if (Platform.OS !== 'ios') { + // For some strange reason, iOS's password management logic doesn't + // realize that the username and password fields in LogInPanel are + // related if the username field gets focused on mount. To avoid this + // issue we need the username and password fields to both appear + // on-screen before we focus the username field. However, when we set + // keyboardHeightValue to -1 here, we are telling our Reanimated logic + // to wait until the keyboard appears before showing LogInPanel. Since + // we need LogInPanel to appear before the username field is focused, we + //need to avoid this behavior on iOS. + keyboardHeightValue.setValue(-1); + } + combinedSetMode('log-in'); + }, [keyboardHeightValue, combinedSetMode]); + + const { navigate } = props.navigation; + const onPressQRCodeSignIn = React.useCallback(() => { + navigate(QRCodeSignInNavigatorRouteName); + }, [navigate]); + + const onPressRegister = React.useCallback(() => { + keyboardHeightValue.setValue(-1); + combinedSetMode('register'); + }, [keyboardHeightValue, combinedSetMode]); + + const onPressNewRegister = React.useCallback(() => { + navigate(RegistrationRouteName); + }, [navigate]); + + const siweButton = ( + <> + <TouchableOpacity + onPress={onPressSIWE} + style={[styles.button, styles.siweButton]} + activeOpacity={0.6} + > + <View style={styles.siweIcon}> + <EthereumLogo /> + </View> + <Text style={[styles.buttonText, styles.siweButtonText]}> + Sign in with Ethereum + </Text> + </TouchableOpacity> + <View style={styles.siweOr}> + <View style={styles.siweOrLeftHR} /> + <Text style={styles.siweOrText}>or</Text> + <View style={styles.siweOrRightHR} /> + </View> + </> + ); + + let panel = null; + let buttons = null; + if (mode.curMode === 'log-in') { + panel = ( + <LogInPanel + setActiveAlert={setActiveAlert} + opacityValue={panelOpacity} + logInState={logInStateContainer} + /> + ); + } else if (mode.curMode === 'register') { + panel = ( + <LegacyRegisterPanel + setActiveAlert={setActiveAlert} + opacityValue={panelOpacity} + legacyRegisterState={legacyRegisterStateContainer} + /> + ); + } else if (mode.curMode === 'prompt') { + const opacityStyle = { opacity: buttonOpacity }; + + const registerButtons = []; + registerButtons.push( + <TouchableOpacity + onPress={onPressRegister} + style={[styles.button, styles.classicAuthButton]} + activeOpacity={0.6} + key="old" + > + <Text style={[styles.buttonText, styles.classicAuthButtonText]}> + Register + </Text> + </TouchableOpacity>, + ); + if (enableNewRegistrationMode) { + registerButtons.push( + <TouchableOpacity + onPress={onPressNewRegister} + style={[styles.button, styles.classicAuthButton]} + activeOpacity={0.6} + key="new" + > + <Text style={[styles.buttonText, styles.classicAuthButtonText]}> + Register (new) + </Text> + </TouchableOpacity>, + ); + } + + const signInButtons = []; + signInButtons.push( + <TouchableOpacity + onPress={onPressLogIn} + style={[styles.button, styles.classicAuthButton]} + activeOpacity={0.6} + key="login-form" + > + <Text style={[styles.buttonText, styles.classicAuthButtonText]}> + Sign in + </Text> + </TouchableOpacity>, + ); + if (__DEV__) { + signInButtons.push( + <TouchableOpacity + onPress={onPressQRCodeSignIn} + style={[styles.button, styles.classicAuthButton]} + activeOpacity={0.6} + key="qr-code-login" + > + <Text style={[styles.buttonText, styles.classicAuthButtonText]}> + Sign in (QR) + </Text> + </TouchableOpacity>, + ); + } + + buttons = ( + <Animated.View style={[styles.buttonContainer, opacityStyle]}> + <LoggedOutStaffInfo /> + {siweButton} + <View style={styles.signInButtons}>{signInButtons}</View> + <View style={styles.registerButtons}>{registerButtons}</View> + </Animated.View> + ); + } else if (mode.curMode === 'loading') { + panel = ( + <ActivityIndicator + color="white" + size="large" + style={styles.loadingIndicator} + /> + ); + } + + const windowWidth = dimensions.width; + const buttonStyle = { + opacity: panelOpacity, + left: windowWidth < 360 ? 28 : 40, + }; + const padding = { paddingTop: panelPaddingTop }; + + const animatedContent = ( + <Animated.View style={[styles.animationContainer, padding]}> + <View> + <Text style={styles.header}>Comm</Text> + <Animated.View style={[styles.backButton, buttonStyle]}> + <TouchableOpacity activeOpacity={0.6} onPress={resetToPrompt}> + <Icon name="arrow-circle-o-left" size={36} color="#FFFFFFAA" /> + </TouchableOpacity> + </Animated.View> + </View> + {panel} + </Animated.View> + ); + + let siwePanel; + if (mode.curMode === 'siwe') { + siwePanel = ( + <FullscreenSIWEPanel + goBackToPrompt={goBackToPrompt} + closing={mode.nextMode === 'prompt'} + /> + ); + } + + const backgroundSource = { uri: splashBackgroundURI }; return ( - <LoggedOutModal - {...props} - logInStateContainer={logInStateContainer} - legacyRegisterStateContainer={legacyRegisterStateContainer} - mode={mode} - contentHeight={contentHeight} - keyboardHeightValue={keyboardHeightValue} - buttonOpacity={buttonOpacity} - panelPaddingTop={panelPaddingTop} - panelOpacity={panelOpacity} - combinedSetMode={combinedSetMode} - goBackToPrompt={goBackToPrompt} - activeAlertRef={activeAlertRef} - setActiveAlert={setActiveAlert} - resetToPrompt={resetToPrompt} - dimensions={dimensions} - splashStyle={splashStyle} - styles={styles} - /> + <React.Fragment> + <ConnectedStatusBar barStyle="light-content" /> + <Image + source={backgroundSource} + style={[styles.modalBackground, splashStyle]} + /> + <SafeAreaView style={styles.container} edges={safeAreaEdges}> + <KeyboardAvoidingView behavior="padding" style={styles.container}> + {animatedContent} + {buttons} + </KeyboardAvoidingView> + </SafeAreaView> + {siwePanel} + </React.Fragment> ); - }); + }, +); export default ConnectedLoggedOutModal;