diff --git a/src/common/SmartUrlInput.js b/src/common/SmartUrlInput.js deleted file mode 100644 index daab8bbf989..00000000000 --- a/src/common/SmartUrlInput.js +++ /dev/null @@ -1,97 +0,0 @@ -/* @flow strict-local */ -import React, { useState, useRef, useCallback, useContext } from 'react'; -import type { Node } from 'react'; -import { TextInput, View } from 'react-native'; -import { useFocusEffect } from '@react-navigation/native'; -import type { ViewStyleProp } from 'react-native/Libraries/StyleSheet/StyleSheet'; - -import type { AppNavigationProp } from '../nav/AppNavigator'; -import { ThemeContext, createStyleSheet, HALF_COLOR } from '../styles'; - -const styles = createStyleSheet({ - wrapper: { - flexDirection: 'row', - opacity: 0.8, - }, - realmInput: { - flex: 1, - padding: 0, - fontSize: 20, - }, -}); - -type Props = $ReadOnly<{| - // TODO: Currently this type is acceptable because the only - // `navigation` prop we pass to a `SmartUrlInput` instance is the - // one from a component on AppNavigator. - navigation: AppNavigationProp<>, - - style?: ViewStyleProp, - onChangeText: (value: string) => void, - onSubmitEditing: () => Promise, - enablesReturnKeyAutomatically: boolean, -|}>; - -export default function SmartUrlInput(props: Props): Node { - const { style, onChangeText, onSubmitEditing, enablesReturnKeyAutomatically } = props; - - // We should replace the fixme with - // `React$ElementRef` when we can. Currently, that - // would make `.current` be `any(implicit)`, which we don't want; - // this is probably down to bugs in Flow's special support for React. - const textInputRef = useRef<$FlowFixMe>(); - - const [value, setValue] = useState(''); - - const themeContext = useContext(ThemeContext); - - // When the route is focused in the navigation, focus the input. - // Otherwise, if you go back to this screen from the auth screen, the - // input won't be focused. - useFocusEffect( - useCallback(() => { - if (textInputRef.current) { - // Sometimes the effect of this `.focus()` is immediately undone - // (the keyboard is closed) by a Keyboard.dismiss() from React - // Navigation's internals. Seems like a complex bug, but the symptom - // isn't terrible, it just means that on back-navigating to this - // screen, sometimes the keyboard flicks open then closed, instead - // of just opening. Shrug. See - // https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/realm-input/near/1346690 - // - // `.current` is not type-checked; see definition. - textInputRef.current.focus(); - } - }, []), - ); - - const handleChange = useCallback( - (_value: string) => { - setValue(_value); - onChangeText(_value); - }, - [onChangeText], - ); - - return ( - - - - ); -} diff --git a/src/start/RealmInputScreen.js b/src/start/RealmInputScreen.js index 61205495136..2189486160a 100644 --- a/src/start/RealmInputScreen.js +++ b/src/start/RealmInputScreen.js @@ -1,30 +1,26 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React, { useCallback } from 'react'; import type { Node } from 'react'; -import { Keyboard } from 'react-native'; +import { Keyboard, View, TextInput } from 'react-native'; +import { useFocusEffect } from '@react-navigation/native'; import type { RouteProp } from '../react-navigation'; import type { AppNavigationProp } from '../nav/AppNavigator'; import type { ApiResponseServerSettings } from '../api/settings/getServerSettings'; import ErrorMsg from '../common/ErrorMsg'; import ZulipTextIntl from '../common/ZulipTextIntl'; -import SmartUrlInput from '../common/SmartUrlInput'; import Screen from '../common/Screen'; import ZulipButton from '../common/ZulipButton'; import { tryParseUrl } from '../utils/url'; import * as api from '../api'; +import { ThemeContext } from '../styles/theme'; +import { createStyleSheet, HALF_COLOR } from '../styles'; type Props = $ReadOnly<{| navigation: AppNavigationProp<'realm-input'>, route: RouteProp<'realm-input', {| initial: boolean | void |}>, |}>; -type State = {| - realmInputValue: string, - error: string | null, - progress: boolean, -|}; - const urlFromInputValue = (realmInputValue: string): URL | void => { const withScheme = /^https?:\/\//.test(realmInputValue) ? realmInputValue @@ -33,88 +29,126 @@ const urlFromInputValue = (realmInputValue: string): URL | void => { return tryParseUrl(withScheme); }; -export default class RealmInputScreen extends PureComponent { - state: State = { - progress: false, - realmInputValue: '', - error: null, - }; +export default function RealmInputScreen(props: Props): Node { + const { navigation, route } = props; + + const themeContext = React.useContext(ThemeContext); + + const [progress, setProgress] = React.useState(false); + const [realmInputValue, setRealmInputValue] = React.useState(''); + const [error, setError] = React.useState(null); + + const textInputRef = React.useRef | null>(null); - tryRealm: () => Promise = async () => { - const { realmInputValue } = this.state; + // When the route is focused in the navigation, focus the input. + // Otherwise, if you go back to this screen from the auth screen, the + // input won't be focused. + useFocusEffect( + useCallback(() => { + if (textInputRef.current) { + // Sometimes the effect of this `.focus()` is immediately undone + // (the keyboard is closed) by a Keyboard.dismiss() from React + // Navigation's internals. Seems like a complex bug, but the symptom + // isn't terrible, it just means that on back-navigating to this + // screen, sometimes the keyboard flicks open then closed, instead + // of just opening. Shrug. See + // https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/realm-input/near/1346690 + textInputRef.current.focus(); + } + }, []), + ); + const tryRealm = React.useCallback(async () => { const parsedRealm = urlFromInputValue(realmInputValue); if (!parsedRealm) { - this.setState({ error: 'Please enter a valid URL' }); + setError('Please enter a valid URL'); return; } if (parsedRealm.username !== '') { - this.setState({ error: 'Please enter the server URL, not your email' }); + setError('Please enter the server URL, not your email'); return; } - this.setState({ - progress: true, - error: null, - }); + setProgress(true); + setError(null); try { const serverSettings: ApiResponseServerSettings = await api.getServerSettings(parsedRealm); - this.props.navigation.push('auth', { serverSettings }); + navigation.push('auth', { serverSettings }); Keyboard.dismiss(); } catch (errorIllTyped) { const err: mixed = errorIllTyped; // https://github.com/facebook/flow/issues/2470 - this.setState({ error: 'Cannot connect to server' }); + setError('Cannot connect to server'); /* eslint-disable no-console */ console.warn('RealmInputScreen: failed to connect to server:', err); // $FlowFixMe[incompatible-cast]: assuming caught exception was Error console.warn((err: Error).stack); } finally { - this.setState({ progress: false }); + setProgress(false); } - }; + }, [navigation, realmInputValue]); - handleRealmChange: string => void = value => this.setState({ realmInputValue: value }); + const styles = React.useMemo( + () => + createStyleSheet({ + inputWrapper: { + flexDirection: 'row', + opacity: 0.8, + marginTop: 16, + marginBottom: 8, + }, + input: { + flex: 1, + padding: 0, + fontSize: 20, + color: themeContext.color, + }, + hintText: { paddingLeft: 2, fontSize: 12 }, + button: { marginTop: 8 }, + }), + [themeContext], + ); - render(): Node { - const { navigation } = this.props; - const { progress, error, realmInputValue } = this.state; - - const styles = { - input: { marginTop: 16, marginBottom: 8 }, - hintText: { paddingLeft: 2, fontSize: 12 }, - button: { marginTop: 8 }, - }; - - return ( - - - + + + - {error !== null ? ( - - ) : ( - - )} - - - ); - } + + {error !== null ? ( + + ) : ( + + )} + + + ); }