diff --git a/src/components/KeyboardAvoidingView/index.ios.js b/src/components/KeyboardAvoidingView/index.ios.js new file mode 100644 index 000000000000..aeeb32e417bc --- /dev/null +++ b/src/components/KeyboardAvoidingView/index.ios.js @@ -0,0 +1,14 @@ +/* + * The KeyboardAvoidingView is only used on ios + */ +import React from 'react'; +import {KeyboardAvoidingView as KeyboardAvoidingViewComponent} from 'react-native'; + +const KeyboardAvoidingView = props => ( + // eslint-disable-next-line react/jsx-props-no-spreading + +); + +KeyboardAvoidingView.displayName = 'KeyboardAvoidingView'; + +export default KeyboardAvoidingView; diff --git a/src/components/KeyboardAvoidingView/index.js b/src/components/KeyboardAvoidingView/index.js new file mode 100644 index 000000000000..76fe55058fb4 --- /dev/null +++ b/src/components/KeyboardAvoidingView/index.js @@ -0,0 +1,18 @@ +/* + * The KeyboardAvoidingView is only used on ios + */ +import React from 'react'; +import {View} from 'react-native'; +import _ from 'underscore'; + +const KeyboardAvoidingView = (props) => { + const viewProps = _.omit(props, ['behavior', 'contentContainerStyle', 'enabled', 'keyboardVerticalOffset']); + return ( + // eslint-disable-next-line react/jsx-props-no-spreading + + ); +}; + +KeyboardAvoidingView.displayName = 'KeyboardAvoidingView'; + +export default KeyboardAvoidingView; diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index fb12cc02d2b7..cc4db7b97078 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -1,6 +1,7 @@ import React, {Component} from 'react'; -import {TouchableWithoutFeedback, View, KeyboardAvoidingView} from 'react-native'; +import {TouchableWithoutFeedback, View} from 'react-native'; import PDF from 'react-native-pdf'; +import KeyboardAvoidingView from '../KeyboardAvoidingView'; import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; import FullScreenLoadingIndicator from '../FullscreenLoadingIndicator'; diff --git a/src/components/ScreenWrapper/BaseScreenWrapper.js b/src/components/ScreenWrapper/BaseScreenWrapper.js deleted file mode 100644 index 83408e6a36fe..000000000000 --- a/src/components/ScreenWrapper/BaseScreenWrapper.js +++ /dev/null @@ -1,125 +0,0 @@ -import {KeyboardAvoidingView, View} from 'react-native'; -import React from 'react'; -import {SafeAreaInsetsContext} from 'react-native-safe-area-context'; -import _ from 'underscore'; -import {withOnyx} from 'react-native-onyx'; -import CONST from '../../CONST'; -import KeyboardShortcut from '../../libs/KeyboardShortcut'; -import Navigation from '../../libs/Navigation/Navigation'; -import onScreenTransitionEnd from '../../libs/onScreenTransitionEnd'; -import * as StyleUtils from '../../styles/StyleUtils'; -import styles from '../../styles/styles'; -import HeaderGap from '../HeaderGap'; -import OfflineIndicator from '../OfflineIndicator'; -import compose from '../../libs/compose'; -import withNavigation from '../withNavigation'; -import withWindowDimensions from '../withWindowDimensions'; -import ONYXKEYS from '../../ONYXKEYS'; -import {withNetwork} from '../OnyxProvider'; -import {propTypes, defaultProps} from './propTypes'; - -class BaseScreenWrapper extends React.Component { - constructor(props) { - super(props); - - this.state = { - didScreenTransitionEnd: false, - }; - } - - componentDidMount() { - const shortcutConfig = CONST.KEYBOARD_SHORTCUTS.ESCAPE; - this.unsubscribeEscapeKey = KeyboardShortcut.subscribe(shortcutConfig.shortcutKey, () => { - if (this.props.modal.willAlertModalBecomeVisible) { - return; - } - - Navigation.dismissModal(); - }, shortcutConfig.descriptionKey, shortcutConfig.modifiers, true); - - this.unsubscribeTransitionEnd = onScreenTransitionEnd(this.props.navigation, () => { - this.setState({didScreenTransitionEnd: true}); - this.props.onTransitionEnd(); - }); - } - - /** - * We explicitly want to ignore if props.modal changes, and only want to rerender if - * any of the other props **used for the rendering output** is changed. - * @param {Object} nextProps - * @param {Object} nextState - * @returns {boolean} - */ - shouldComponentUpdate(nextProps, nextState) { - return !_.isEqual(this.state, nextState) - || !_.isEqual(_.omit(this.props, 'modal'), _.omit(nextProps, 'modal')); - } - - componentWillUnmount() { - if (this.unsubscribeEscapeKey) { - this.unsubscribeEscapeKey(); - } - if (this.unsubscribeTransitionEnd) { - this.unsubscribeTransitionEnd(); - } - } - - render() { - return ( - - {(insets) => { - const {paddingTop, paddingBottom} = StyleUtils.getSafeAreaPadding(insets); - const paddingStyle = {}; - - if (this.props.includePaddingTop) { - paddingStyle.paddingTop = paddingTop; - } - - // We always need the safe area padding bottom if we're showing the offline indicator since it is bottom-docked. - if (this.props.includePaddingBottom || this.props.network.isOffline) { - paddingStyle.paddingBottom = paddingBottom; - } - - return ( - - - - {// If props.children is a function, call it to provide the insets to the children. - _.isFunction(this.props.children) - ? this.props.children({ - insets, - didScreenTransitionEnd: this.state.didScreenTransitionEnd, - }) - : this.props.children - } - {this.props.isSmallScreenWidth && ( - - )} - - - ); - }} - - ); - } -} - -BaseScreenWrapper.propTypes = propTypes; -BaseScreenWrapper.defaultProps = defaultProps; - -export default compose( - withNavigation, - withWindowDimensions, - withOnyx({ - modal: { - key: ONYXKEYS.MODAL, - }, - }), - withNetwork(), -)(BaseScreenWrapper); diff --git a/src/components/ScreenWrapper/index.android.js b/src/components/ScreenWrapper/index.android.js deleted file mode 100644 index e4b64a51bb40..000000000000 --- a/src/components/ScreenWrapper/index.android.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import BaseScreenWrapper from './BaseScreenWrapper'; -import {defaultProps, propTypes} from './propTypes'; - -const ScreenWrapper = props => ( - - {props.children} - -); - -defaultProps.keyboardAvoidingViewBehavior = 'height'; - -ScreenWrapper.propTypes = propTypes; -ScreenWrapper.defaultProps = defaultProps; -ScreenWrapper.displayName = 'ScreenWrapper'; - -export default ScreenWrapper; diff --git a/src/components/ScreenWrapper/index.js b/src/components/ScreenWrapper/index.js index 187e75159561..52d9aa5e6b62 100644 --- a/src/components/ScreenWrapper/index.js +++ b/src/components/ScreenWrapper/index.js @@ -1,17 +1,126 @@ +import {View} from 'react-native'; import React from 'react'; -import BaseScreenWrapper from './BaseScreenWrapper'; -import {defaultProps, propTypes} from './propTypes'; - -const ScreenWrapper = props => ( - - {props.children} - -); +import {SafeAreaInsetsContext} from 'react-native-safe-area-context'; +import _ from 'underscore'; +import {withOnyx} from 'react-native-onyx'; +import KeyboardAvoidingView from '../KeyboardAvoidingView'; +import CONST from '../../CONST'; +import KeyboardShortcut from '../../libs/KeyboardShortcut'; +import Navigation from '../../libs/Navigation/Navigation'; +import onScreenTransitionEnd from '../../libs/onScreenTransitionEnd'; +import * as StyleUtils from '../../styles/StyleUtils'; +import styles from '../../styles/styles'; +import HeaderGap from '../HeaderGap'; +import OfflineIndicator from '../OfflineIndicator'; +import compose from '../../libs/compose'; +import withNavigation from '../withNavigation'; +import withWindowDimensions from '../withWindowDimensions'; +import ONYXKEYS from '../../ONYXKEYS'; +import {withNetwork} from '../OnyxProvider'; +import {propTypes, defaultProps} from './propTypes'; + +class ScreenWrapper extends React.Component { + constructor(props) { + super(props); + + this.state = { + didScreenTransitionEnd: false, + }; + } + + componentDidMount() { + const shortcutConfig = CONST.KEYBOARD_SHORTCUTS.ESCAPE; + this.unsubscribeEscapeKey = KeyboardShortcut.subscribe(shortcutConfig.shortcutKey, () => { + if (this.props.modal.willAlertModalBecomeVisible) { + return; + } + + Navigation.dismissModal(); + }, shortcutConfig.descriptionKey, shortcutConfig.modifiers, true); + + this.unsubscribeTransitionEnd = onScreenTransitionEnd(this.props.navigation, () => { + this.setState({didScreenTransitionEnd: true}); + this.props.onTransitionEnd(); + }); + } + + /** + * We explicitly want to ignore if props.modal changes, and only want to rerender if + * any of the other props **used for the rendering output** is changed. + * @param {Object} nextProps + * @param {Object} nextState + * @returns {boolean} + */ + shouldComponentUpdate(nextProps, nextState) { + return !_.isEqual(this.state, nextState) + || !_.isEqual(_.omit(this.props, 'modal'), _.omit(nextProps, 'modal')); + } + + componentWillUnmount() { + if (this.unsubscribeEscapeKey) { + this.unsubscribeEscapeKey(); + } + if (this.unsubscribeTransitionEnd) { + this.unsubscribeTransitionEnd(); + } + } + + render() { + return ( + + {(insets) => { + const {paddingTop, paddingBottom} = StyleUtils.getSafeAreaPadding(insets); + const paddingStyle = {}; + + if (this.props.includePaddingTop) { + paddingStyle.paddingTop = paddingTop; + } + + // We always need the safe area padding bottom if we're showing the offline indicator since it is bottom-docked. + if (this.props.includePaddingBottom || this.props.network.isOffline) { + paddingStyle.paddingBottom = paddingBottom; + } + + return ( + + + + {// If props.children is a function, call it to provide the insets to the children. + _.isFunction(this.props.children) + ? this.props.children({ + insets, + didScreenTransitionEnd: this.state.didScreenTransitionEnd, + }) + : this.props.children + } + {this.props.isSmallScreenWidth && ( + + )} + + + ); + }} + + ); + } +} + ScreenWrapper.propTypes = propTypes; ScreenWrapper.defaultProps = defaultProps; -ScreenWrapper.displayName = 'ScreenWrapper'; -export default ScreenWrapper; +export default compose( + withNavigation, + withWindowDimensions, + withOnyx({ + modal: { + key: ONYXKEYS.MODAL, + }, + }), + withNetwork(), +)(ScreenWrapper); diff --git a/src/pages/settings/AddSecondaryLoginPage.js b/src/pages/settings/AddSecondaryLoginPage.js index 01e60852a578..892a75153d55 100755 --- a/src/pages/settings/AddSecondaryLoginPage.js +++ b/src/pages/settings/AddSecondaryLoginPage.js @@ -107,7 +107,8 @@ class AddSecondaryLoginPage extends Component { onBackButtonPress={() => Navigation.navigate(ROUTES.SETTINGS_PROFILE)} onCloseButtonPress={() => Navigation.dismissModal()} /> - + {/* We use keyboardShouldPersistTaps="handled" to prevent the keyboard from being hidden when switching focus on input fields */} + {this.props.translate(this.formType === CONST.LOGIN_TYPE.PHONE ? 'addSecondaryLoginPage.enterPreferredPhoneNumberToSendValidationLink' diff --git a/src/pages/signin/SignInPageLayout/SignInPageContent.js b/src/pages/signin/SignInPageLayout/SignInPageContent.js index f90778d203d8..35c51c259b24 100755 --- a/src/pages/signin/SignInPageLayout/SignInPageContent.js +++ b/src/pages/signin/SignInPageLayout/SignInPageContent.js @@ -1,9 +1,10 @@ import React from 'react'; -import {ScrollView, View, KeyboardAvoidingView} from 'react-native'; +import {ScrollView, View} from 'react-native'; import PropTypes from 'prop-types'; import {withSafeAreaInsets} from 'react-native-safe-area-context'; import styles from '../../../styles/styles'; import variables from '../../../styles/variables'; +import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView'; import ExpensifyCashLogo from '../../../components/ExpensifyCashLogo'; import Text from '../../../components/Text'; import TermsAndLicenses from '../TermsAndLicenses';