diff --git a/src/CONST.js b/src/CONST.js index b981c6e92624..63a705738b34 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -477,6 +477,7 @@ const CONST = { EMOJI_PICKER_ITEM_HEIGHT: 40, EMOJI_PICKER_HEADER_HEIGHT: 38, COMPOSER_MAX_HEIGHT: 125, + CHAT_FOOTER_MIN_HEIGHT: 65, CHAT_SKELETON_VIEW: { AVERAGE_ROW_HEIGHT: 80, HEIGHT_FOR_ROW_COUNT: { diff --git a/src/components/ScreenWrapper.js b/src/components/ScreenWrapper.js deleted file mode 100644 index bc6a416dbd01..000000000000 --- a/src/components/ScreenWrapper.js +++ /dev/null @@ -1,147 +0,0 @@ -import _ from 'underscore'; -import React from 'react'; -import PropTypes from 'prop-types'; -import {View} from 'react-native'; -import {SafeAreaInsetsContext} from 'react-native-safe-area-context'; -import {withOnyx} from 'react-native-onyx'; -import styles from '../styles/styles'; -import * as StyleUtils from '../styles/StyleUtils'; -import HeaderGap from './HeaderGap'; -import KeyboardShortcutsModal from './KeyboardShortcutsModal'; -import KeyboardShortcut from '../libs/KeyboardShortcut'; -import onScreenTransitionEnd from '../libs/onScreenTransitionEnd'; -import Navigation from '../libs/Navigation/Navigation'; -import compose from '../libs/compose'; -import ONYXKEYS from '../ONYXKEYS'; -import CONST from '../CONST'; -import withNavigation from './withNavigation'; - -const propTypes = { - /** Array of additional styles to add */ - style: PropTypes.arrayOf(PropTypes.object), - - /** Returns a function as a child to pass insets to or a node to render without insets */ - children: PropTypes.oneOfType([ - PropTypes.node, - PropTypes.func, - ]).isRequired, - - /** Whether to include padding bottom */ - includePaddingBottom: PropTypes.bool, - - /** Whether to include padding top */ - includePaddingTop: PropTypes.bool, - - // Called when navigated Screen's transition is finished. - onTransitionEnd: PropTypes.func, - - // react-navigation navigation object available to screen components - navigation: PropTypes.shape({ - // Method to attach listener to Navigation state. - addListener: PropTypes.func.isRequired, - }), - - /** Details about any modals being used */ - modal: PropTypes.shape({ - /** Indicates when an Alert modal is about to be visible */ - willAlertModalBecomeVisible: PropTypes.bool, - }), - -}; - -const defaultProps = { - style: [], - includePaddingBottom: true, - includePaddingTop: true, - onTransitionEnd: () => {}, - navigation: { - addListener: () => {}, - }, - modal: {}, -}; - -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(); - }); - } - - 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; - } - - if (this.props.includePaddingBottom) { - 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 - } - - - ); - }} - - ); - } -} - -ScreenWrapper.propTypes = propTypes; -ScreenWrapper.defaultProps = defaultProps; - -export default compose( - withNavigation, - withOnyx({ - modal: { - key: ONYXKEYS.MODAL, - }, - }), -)(ScreenWrapper); diff --git a/src/components/ScreenWrapper/BaseScreenWrapper.js b/src/components/ScreenWrapper/BaseScreenWrapper.js new file mode 100644 index 000000000000..a80a013a1c66 --- /dev/null +++ b/src/components/ScreenWrapper/BaseScreenWrapper.js @@ -0,0 +1,115 @@ +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 KeyboardShortcutsModal from '../KeyboardShortcutsModal'; +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(); + }); + } + + 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 new file mode 100644 index 000000000000..55067bd50923 --- /dev/null +++ b/src/components/ScreenWrapper/index.android.js @@ -0,0 +1,17 @@ +import React from 'react'; +import BaseScreenWrapper from './BaseScreenWrapper'; +import {defaultProps, propTypes} from './propTypes'; + +const ScreenWrapper = props => ( + + {props.children} + +); +ScreenWrapper.propTypes = propTypes; +ScreenWrapper.defaultProps = defaultProps; + +export default ScreenWrapper; diff --git a/src/components/ScreenWrapper/index.js b/src/components/ScreenWrapper/index.js new file mode 100644 index 000000000000..29c537442985 --- /dev/null +++ b/src/components/ScreenWrapper/index.js @@ -0,0 +1,16 @@ +import React from 'react'; +import BaseScreenWrapper from './BaseScreenWrapper'; +import {defaultProps, propTypes} from './propTypes'; + +const ScreenWrapper = props => ( + + {props.children} + +); +ScreenWrapper.propTypes = propTypes; +ScreenWrapper.defaultProps = defaultProps; + +export default ScreenWrapper; diff --git a/src/components/ScreenWrapper/propTypes.js b/src/components/ScreenWrapper/propTypes.js new file mode 100644 index 000000000000..ce1858ef20bd --- /dev/null +++ b/src/components/ScreenWrapper/propTypes.js @@ -0,0 +1,42 @@ +import PropTypes from 'prop-types'; + +const propTypes = { + /** Array of additional styles to add */ + style: PropTypes.arrayOf(PropTypes.object), + + /** Returns a function as a child to pass insets to or a node to render without insets */ + children: PropTypes.oneOfType([ + PropTypes.node, + PropTypes.func, + ]).isRequired, + + /** Whether to include padding bottom */ + includePaddingBottom: PropTypes.bool, + + /** Whether to include padding top */ + includePaddingTop: PropTypes.bool, + + // Called when navigated Screen's transition is finished. + onTransitionEnd: PropTypes.func, + + /** The behavior to pass to the KeyboardAvoidingView, requires some trial and error depending on the layout/devices used. + * Search 'switch(behavior)' in ./node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js for more context */ + keyboardAvoidingViewBehavior: PropTypes.oneOf(['padding', 'height', 'position']), + + /** Details about any modals being used */ + modal: PropTypes.shape({ + /** Indicates when an Alert modal is about to be visible */ + willAlertModalBecomeVisible: PropTypes.bool, + }), +}; + +const defaultProps = { + style: [], + includePaddingBottom: true, + includePaddingTop: true, + onTransitionEnd: () => {}, + modal: {}, + keyboardAvoidingViewBehavior: 'padding', +}; + +export {propTypes, defaultProps}; diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index 71daa5f13f72..d2ab86fccc9e 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -3,9 +3,7 @@ import _ from 'underscore'; import React from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; -import { - View, KeyboardAvoidingView, -} from 'react-native'; +import {View} from 'react-native'; import IdologyQuestions from './IdologyQuestions'; import ScreenWrapper from '../../components/ScreenWrapper'; import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; @@ -254,19 +252,17 @@ class AdditionalDetailsStep extends React.Component { render() { if (!_.isEmpty(this.props.walletAdditionalDetails.questions)) { return ( - - - Navigation.dismissModal()} - shouldShowBackButton - onBackButtonPress={() => Wallet.setAdditionalDetailsQuestions(null)} - /> - - + + Navigation.dismissModal()} + shouldShowBackButton + onBackButtonPress={() => Wallet.setAdditionalDetailsQuestions(null)} + /> + ); } @@ -277,127 +273,125 @@ class AdditionalDetailsStep extends React.Component { const {firstName, lastName} = PersonalDetails.extractFirstAndLastNameFromAvailableDetails(this.props.currentUserPersonalDetails); return ( - - - Navigation.dismissModal()} - /> - - - {this.props.translate('additionalDetailsStep.helpText')} - - {this.props.translate('additionalDetailsStep.helpLink')} - - - this.form = el}> - - - this.clearErrorAndSetValue('legalFirstName', val)} - value={this.props.walletAdditionalDetailsDraft.legalFirstName || firstName} - errorText={this.getErrorText('legalFirstName')} - /> - this.clearErrorAndSetValue('legalLastName', val)} - value={this.props.walletAdditionalDetailsDraft.legalLastName || lastName} - errorText={this.getErrorText('legalLastName')} - /> - { - const renamedFields = { - street: 'addressStreet', - state: 'addressState', - zipCode: 'addressZip', - city: 'addressCity', - }; - _.each(values, (value, inputKey) => { - const renamedInputKey = lodashGet(renamedFields, inputKey, inputKey); - this.clearErrorAndSetValue(renamedInputKey, value); - }); - }} - errorText={this.getErrorText('addressStreet')} - hint={this.props.translate('common.noPO')} - /> - {this.props.walletAdditionalDetailsDraft.addressStreet ? ( - <> - {/** Once the user has started entering his address, show the other address fields (city, state, zip) */} - {/** We'll autofill them when the user selects a full address from the google autocomplete */} - this.clearErrorAndSetValue('addressCity', val)} - value={this.props.walletAdditionalDetailsDraft.addressCity || ''} - errorText={this.getErrorText('addressCity')} - /> - this.clearErrorAndSetValue('addressState', val)} - value={this.props.walletAdditionalDetailsDraft.addressState || ''} - errorText={this.getErrorText('addressState')} - /> - this.clearErrorAndSetValue('addressZip', val)} - value={this.props.walletAdditionalDetailsDraft.addressZip || ''} - errorText={this.getErrorText('addressZip')} - /> - - ) : null} - + + Navigation.dismissModal()} + /> + + + {this.props.translate('additionalDetailsStep.helpText')} + + {this.props.translate('additionalDetailsStep.helpLink')} + + + this.form = el}> + + this.clearErrorAndSetValue('phoneNumber', val)} - value={this.props.walletAdditionalDetailsDraft.phoneNumber || ''} - placeholder={this.props.translate('common.phoneNumberPlaceholder')} - errorText={this.getErrorText('phoneNumber')} + label={this.props.translate(this.fieldNameTranslationKeys.legalFirstName)} + onChangeText={val => this.clearErrorAndSetValue('legalFirstName', val)} + value={this.props.walletAdditionalDetailsDraft.legalFirstName || firstName} + errorText={this.getErrorText('legalFirstName')} /> - this.clearDateErrorsAndSetValue(val)} - defaultValue={this.props.walletAdditionalDetailsDraft.dob || ''} - placeholder={this.props.translate('common.dob')} - errorText={this.getErrorText('dob') || this.getErrorText('age')} - maximumDate={new Date()} + label={this.props.translate(this.fieldNameTranslationKeys.legalLastName)} + onChangeText={val => this.clearErrorAndSetValue('legalLastName', val)} + value={this.props.walletAdditionalDetailsDraft.legalLastName || lastName} + errorText={this.getErrorText('legalLastName')} /> - this.clearSSNErrorAndSetValue(val)} - value={this.props.walletAdditionalDetailsDraft.ssn || ''} - errorText={this.getErrorText('ssnFull9') || this.getErrorText('ssn')} - maxLength={shouldAskForFullSSN ? 9 : 4} - keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD} + onInputChange={(values) => { + const renamedFields = { + street: 'addressStreet', + state: 'addressState', + zipCode: 'addressZip', + city: 'addressCity', + }; + _.each(values, (value, inputKey) => { + const renamedInputKey = lodashGet(renamedFields, inputKey, inputKey); + this.clearErrorAndSetValue(renamedInputKey, value); + }); + }} + errorText={this.getErrorText('addressStreet')} + hint={this.props.translate('common.noPO')} /> + {this.props.walletAdditionalDetailsDraft.addressStreet ? ( + <> + {/** Once the user has started entering his address, show the other address fields (city, state, zip) */} + {/** We'll autofill them when the user selects a full address from the google autocomplete */} + this.clearErrorAndSetValue('addressCity', val)} + value={this.props.walletAdditionalDetailsDraft.addressCity || ''} + errorText={this.getErrorText('addressCity')} + /> + this.clearErrorAndSetValue('addressState', val)} + value={this.props.walletAdditionalDetailsDraft.addressState || ''} + errorText={this.getErrorText('addressState')} + /> + this.clearErrorAndSetValue('addressZip', val)} + value={this.props.walletAdditionalDetailsDraft.addressZip || ''} + errorText={this.getErrorText('addressZip')} + /> + + ) : null} - { - this.form.scrollTo({y: 0, animated: true}); - }} - message={this.props.walletAdditionalDetails.additionalErrorMessage} - isLoading={this.props.walletAdditionalDetails.loading} - buttonText={this.props.translate('common.saveAndContinue')} + this.clearErrorAndSetValue('phoneNumber', val)} + value={this.props.walletAdditionalDetailsDraft.phoneNumber || ''} + placeholder={this.props.translate('common.phoneNumberPlaceholder')} + errorText={this.getErrorText('phoneNumber')} /> - - - + this.clearDateErrorsAndSetValue(val)} + defaultValue={this.props.walletAdditionalDetailsDraft.dob || ''} + placeholder={this.props.translate('common.dob')} + errorText={this.getErrorText('dob') || this.getErrorText('age')} + maximumDate={new Date()} + /> + this.clearSSNErrorAndSetValue(val)} + value={this.props.walletAdditionalDetailsDraft.ssn || ''} + errorText={this.getErrorText('ssnFull9') || this.getErrorText('ssn')} + maxLength={shouldAskForFullSSN ? 9 : 4} + keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD} + /> + + { + this.form.scrollTo({y: 0, animated: true}); + }} + message={this.props.walletAdditionalDetails.additionalErrorMessage} + isLoading={this.props.walletAdditionalDetails.loading} + buttonText={this.props.translate('common.saveAndContinue')} + /> + + ); } diff --git a/src/pages/EnablePayments/EnablePaymentsPage.js b/src/pages/EnablePayments/EnablePaymentsPage.js index f1ac80846a05..53f25dfeffd7 100644 --- a/src/pages/EnablePayments/EnablePaymentsPage.js +++ b/src/pages/EnablePayments/EnablePaymentsPage.js @@ -1,7 +1,6 @@ import _ from 'underscore'; import React from 'react'; import {withOnyx} from 'react-native-onyx'; -import {KeyboardAvoidingView} from 'react-native'; import ScreenWrapper from '../../components/ScreenWrapper'; import * as BankAccounts from '../../libs/actions/BankAccounts'; import ONYXKEYS from '../../ONYXKEYS'; @@ -59,14 +58,12 @@ class EnablePaymentsPage extends React.Component { if (this.props.userWallet.shouldShowFailedKYC) { return ( - - - Navigation.dismissModal()} - /> - - + + Navigation.dismissModal()} + /> + ); } diff --git a/src/pages/GetAssistancePage.js b/src/pages/GetAssistancePage.js index 030ea26bac8a..4a07075b0d3f 100644 --- a/src/pages/GetAssistancePage.js +++ b/src/pages/GetAssistancePage.js @@ -3,7 +3,6 @@ import {View} from 'react-native'; import PropTypes from 'prop-types'; import ScreenWrapper from '../components/ScreenWrapper'; import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; -import KeyboardAvoidingView from '../components/KeyboardAvoidingView'; import HeaderWithCloseButton from '../components/HeaderWithCloseButton'; import Section from '../components/Section'; import Navigation from '../libs/Navigation/Navigation'; @@ -28,36 +27,34 @@ const propTypes = { const GetAssistancePage = props => ( - - Navigation.dismissModal(true)} - shouldShowBackButton - onBackButtonPress={() => Navigation.goBack()} - /> -
Report.navigateToConciergeChat(), - icon: Expensicons.ChatBubble, - shouldShowRightIcon: true, - }, - { - title: props.translate('getAssistancePage.requestSetupCall'), - onPress: () => Navigation.navigate(ROUTES.getRequestCallRoute(props.route.params.taskID)), - icon: Expensicons.Phone, - shouldShowRightIcon: true, - }, - ]} - > - - {props.translate('getAssistancePage.description')} - -
-
+ Navigation.dismissModal(true)} + shouldShowBackButton + onBackButtonPress={() => Navigation.goBack()} + /> +
Report.navigateToConciergeChat(), + icon: Expensicons.ChatBubble, + shouldShowRightIcon: true, + }, + { + title: props.translate('getAssistancePage.requestSetupCall'), + onPress: () => Navigation.navigate(ROUTES.getRequestCallRoute(props.route.params.taskID)), + icon: Expensicons.Phone, + shouldShowRightIcon: true, + }, + ]} + > + + {props.translate('getAssistancePage.description')} + +
); diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index 0e55bdbccfbc..2c8a0231c8cb 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -16,7 +16,6 @@ import ScreenWrapper from '../components/ScreenWrapper'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; import compose from '../libs/compose'; -import KeyboardAvoidingView from '../components/KeyboardAvoidingView'; import personalDetailsPropType from './personalDetailsPropType'; const propTypes = { @@ -211,9 +210,9 @@ class NewChatPage extends Component { maxParticipantsReached, ); return ( - + {({didScreenTransitionEnd}) => ( - + <> )} - + )} ); diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index 21a15777b738..9f022bd00bc4 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -14,7 +14,6 @@ import CONST from '../../CONST'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import compose from '../../libs/compose'; import styles from '../../styles/styles'; -import KeyboardAvoidingView from '../../components/KeyboardAvoidingView'; import getPlaidOAuthReceivedRedirectURI from '../../libs/getPlaidOAuthReceivedRedirectURI'; import Text from '../../components/Text'; import {withNetwork} from '../../components/OnyxProvider'; @@ -219,34 +218,32 @@ class ReimbursementAccountPage extends React.Component { } return ( - - {currentStep === CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT && ( - - )} - {currentStep === CONST.BANK_ACCOUNT.STEP.COMPANY && ( - - )} - {currentStep === CONST.BANK_ACCOUNT.STEP.REQUESTOR && ( - - )} - {currentStep === CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT && ( - - )} - {currentStep === CONST.BANK_ACCOUNT.STEP.VALIDATION && ( - - )} - {currentStep === CONST.BANK_ACCOUNT.STEP.ENABLE && ( - - )} - - + {currentStep === CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT && ( + + )} + {currentStep === CONST.BANK_ACCOUNT.STEP.COMPANY && ( + + )} + {currentStep === CONST.BANK_ACCOUNT.STEP.REQUESTOR && ( + + )} + {currentStep === CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT && ( + + )} + {currentStep === CONST.BANK_ACCOUNT.STEP.VALIDATION && ( + + )} + {currentStep === CONST.BANK_ACCOUNT.STEP.ENABLE && ( + + )} + ); } diff --git a/src/pages/RequestCallPage.js b/src/pages/RequestCallPage.js index cbfd52a4390c..d2b8fa817e8c 100644 --- a/src/pages/RequestCallPage.js +++ b/src/pages/RequestCallPage.js @@ -23,7 +23,6 @@ import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, import TextInput from '../components/TextInput'; import Text from '../components/Text'; import Section from '../components/Section'; -import KeyboardAvoidingView from '../components/KeyboardAvoidingView'; import * as Illustrations from '../components/Icon/Illustrations'; import * as Expensicons from '../components/Icon/Expensicons'; import * as LoginUtils from '../libs/LoginUtils'; @@ -285,86 +284,84 @@ class RequestCallPage extends Component { return ( - - Navigation.goBack()} - onCloseButtonPress={() => Navigation.dismissModal(true)} - /> - {this.props.requestCallForm.didRequestCallSucceed - ? ( - - ) : ( - <> - - -
- - {this.props.translate('requestCallPage.description')} - - this.setState({firstName})} - onChangeLastName={lastName => this.setState({lastName})} - style={[styles.mv4]} - /> - this.setState({phoneNumber})} - /> - this.setState({phoneExtension})} - containerStyles={[styles.mt4]} - /> - {this.getWaitTimeMessage()} -
-
-
- - {isBlockedFromConcierge && ( - - - {this.props.translate('requestCallPage.blockedFromConcierge')} - - )} - {!_.isEmpty(this.props.requestCallForm.error) && ( - - {this.props.requestCallForm.error} + Navigation.goBack()} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> + {this.props.requestCallForm.didRequestCallSucceed + ? ( + + ) : ( + <> + + +
+ + {this.props.translate('requestCallPage.description')} - )} - - - - )} - + this.setState({firstName})} + onChangeLastName={lastName => this.setState({lastName})} + style={[styles.mv4]} + /> + this.setState({phoneNumber})} + /> + this.setState({phoneExtension})} + containerStyles={[styles.mt4]} + /> + {this.getWaitTimeMessage()} +
+
+
+ + {isBlockedFromConcierge && ( + + + {this.props.translate('requestCallPage.blockedFromConcierge')} + + )} + {!_.isEmpty(this.props.requestCallForm.error) && ( + + {this.props.requestCallForm.error} + + )} + + + + )}
); } diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 9a5756240e50..3f8534ca2b5f 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -7,7 +7,6 @@ import OptionsSelector from '../components/OptionsSelector'; import * as OptionsListUtils from '../libs/OptionsListUtils'; import ONYXKEYS from '../ONYXKEYS'; import styles from '../styles/styles'; -import KeyboardSpacer from '../components/KeyboardSpacer'; import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; import withWindowDimensions, {windowDimensionsPropTypes} from '../components/withWindowDimensions'; @@ -189,7 +188,6 @@ class SearchPage extends Component { shouldShowOptions={didScreenTransitionEnd} /> - )}
diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index fd9282a88021..c0490d0ebff1 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -16,13 +16,15 @@ import Permissions from '../../libs/Permissions'; import * as ReportUtils from '../../libs/ReportUtils'; import ReportActionsView from './report/ReportActionsView'; import ReportActionCompose from './report/ReportActionCompose'; -import KeyboardAvoidingView from '../../components/KeyboardAvoidingView'; import SwipeableView from '../../components/SwipeableView'; import CONST from '../../CONST'; import ReportActionsSkeletonView from '../../components/ReportActionsSkeletonView'; import reportActionPropTypes from './report/reportActionPropTypes'; import ArchivedReportFooter from '../../components/ArchivedReportFooter'; import toggleReportActionComposeView from '../../libs/toggleReportActionComposeView'; +import {withNetwork} from '../../components/OnyxProvider'; +import compose from '../../libs/compose'; +import networkPropTypes from '../../components/networkPropTypes'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -77,6 +79,9 @@ const propTypes = { /** The type of the policy */ type: PropTypes.string, })).isRequired, + + /** Information about the network */ + network: networkPropTypes.isRequired, }; const defaultProps = { @@ -149,12 +154,8 @@ class ReportScreen extends React.Component { Report.addComment(getReportID(this.props.route), text); } - /** - * @param {SyntheticEvent} e - */ - updateViewportOffsetTop(e) { - const viewportOffsetTop = lodashGet(e, 'target.offsetTop', 0); - this.setState({viewportOffsetTop}); + setChatFooterStyles(isOffline) { + return {...styles.chatFooter, minHeight: !isOffline ? CONST.CHAT_FOOTER_MIN_HEIGHT : 0}; } /** @@ -181,6 +182,14 @@ class ReportScreen extends React.Component { Report.updateCurrentlyViewedReportID(reportID); } + /** + * @param {SyntheticEvent} e + */ + updateViewportOffsetTop(e) { + const viewportOffsetTop = lodashGet(e, 'target.offsetTop', 0); + this.setState({viewportOffsetTop}); + } + render() { if (!this.props.isSidebarLoaded) { return null; @@ -206,57 +215,55 @@ class ReportScreen extends React.Component { } return ( - - Navigation.navigate(ROUTES.HOME)} - /> - - this.setState({skeletonViewContainerHeight: event.nativeEvent.layout.height})} - > - {this.shouldShowLoader() - ? ( - - ) - : ( - - )} - {(isArchivedRoom || this.props.session.shouldShowComposeInput) && ( - - { - isArchivedRoom - ? ( - Navigation.navigate(ROUTES.HOME)} + /> + + this.setState({skeletonViewContainerHeight: event.nativeEvent.layout.height})} + > + {this.shouldShowLoader() + ? ( + + ) + : ( + + )} + {(isArchivedRoom || this.props.session.shouldShowComposeInput) && ( + + { + isArchivedRoom + ? ( + + ) : ( + + - ) : ( - - - - ) - } - - )} - - + + ) + } + + )} + ); } @@ -265,7 +272,7 @@ class ReportScreen extends React.Component { ReportScreen.propTypes = propTypes; ReportScreen.defaultProps = defaultProps; -export default withOnyx({ +export default compose(withNetwork(), withOnyx({ isSidebarLoaded: { key: ONYXKEYS.IS_SIDEBAR_LOADED, }, @@ -292,4 +299,4 @@ export default withOnyx({ policies: { key: ONYXKEYS.COLLECTION.POLICY, }, -})(ReportScreen); +}))(ReportScreen); diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 6e58ae1f5715..51791c723367 100755 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -496,7 +496,11 @@ class ReportActionCompose extends React.Component { const hasExceededMaxCommentLength = this.comment.length > CONST.MAX_COMMENT_LENGTH; return ( - + {shouldShowReportRecipientLocalTime && } - - + + {!this.props.isSmallScreenWidth && } diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 424353b09ca6..5ef4b3603808 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -103,7 +103,7 @@ class ReportActionsList extends React.Component { const minimumReportActionHeight = styles.chatItem.paddingTop + styles.chatItem.paddingBottom + variables.fontSizeNormalHeight; const availableHeight = this.props.windowHeight - - (styles.chatFooter.minHeight + variables.contentHeaderHeight); + - (CONST.CHAT_FOOTER_MIN_HEIGHT + variables.contentHeaderHeight); return Math.ceil(availableHeight / minimumReportActionHeight); } diff --git a/src/pages/home/report/ReportTypingIndicator.js b/src/pages/home/report/ReportTypingIndicator.js index 0ba4e2463b2e..2ee31974690d 100755 --- a/src/pages/home/report/ReportTypingIndicator.js +++ b/src/pages/home/report/ReportTypingIndicator.js @@ -67,7 +67,7 @@ class ReportTypingIndicator extends React.Component { leadingText={PersonalDetails.getDisplayName(this.state.usersTyping[0])} trailingText={` ${this.props.translate('reportTypingIndicator.isTyping')}`} textStyle={[styles.chatItemComposeSecondaryRowSubText]} - wrapperStyle={styles.chatItemComposeSecondaryRow} + wrapperStyle={[styles.chatItemComposeSecondaryRow]} leadingTextParentStyle={styles.chatItemComposeSecondaryRowOffset} /> ); @@ -78,6 +78,7 @@ class ReportTypingIndicator extends React.Component { style={[ styles.chatItemComposeSecondaryRowSubText, styles.chatItemComposeSecondaryRowOffset, + ...this.props.containerStyles, ]} numberOfLines={1} > diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index b8b408b4c815..d0f9861ee819 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -10,7 +10,6 @@ import ScreenWrapper from '../../components/ScreenWrapper'; import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; import compose from '../../libs/compose'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; -import KeyboardAvoidingView from '../../components/KeyboardAvoidingView'; import * as IOU from '../../libs/actions/IOU'; import * as CurrencySymbolUtils from '../../libs/CurrencySymbolUtils'; import {withNetwork} from '../../components/OnyxProvider'; @@ -117,21 +116,19 @@ class IOUCurrencySelection extends Component { const headerMessage = this.state.searchValue.trim() && !this.state.currencyData.length ? this.props.translate('common.noResultsFound') : ''; return ( - - - - + + ); } diff --git a/src/pages/iou/IOUModal.js b/src/pages/iou/IOUModal.js index 14a24b473aad..508456ebe9a4 100755 --- a/src/pages/iou/IOUModal.js +++ b/src/pages/iou/IOUModal.js @@ -23,7 +23,6 @@ import AnimatedStep from '../../components/AnimatedStep'; import ScreenWrapper from '../../components/ScreenWrapper'; import Tooltip from '../../components/Tooltip'; import CONST from '../../CONST'; -import KeyboardAvoidingView from '../../components/KeyboardAvoidingView'; import * as PersonalDetails from '../../libs/actions/PersonalDetails'; import withCurrentUserPersonalDetails from '../../components/withCurrentUserPersonalDetails'; import ROUTES from '../../ROUTES'; @@ -174,7 +173,7 @@ class IOUModal extends Component { * Decides our animation type based on whether we're increasing or decreasing * our step index. * @returns {String} - */ + */ getDirection() { if (this.state.previousStepIndex < this.state.currentStepIndex) { return 'in'; @@ -358,7 +357,7 @@ class IOUModal extends Component { return ( {({didScreenTransitionEnd}) => ( - + <> )} - + )} ); diff --git a/src/pages/settings/AddSecondaryLoginPage.js b/src/pages/settings/AddSecondaryLoginPage.js index 7a15526de572..afffdb7cfdbd 100755 --- a/src/pages/settings/AddSecondaryLoginPage.js +++ b/src/pages/settings/AddSecondaryLoginPage.js @@ -14,7 +14,6 @@ import ONYXKEYS from '../../ONYXKEYS'; import Button from '../../components/Button'; import ROUTES from '../../ROUTES'; import CONST from '../../CONST'; -import KeyboardAvoidingView from '../../components/KeyboardAvoidingView'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import compose from '../../libs/compose'; import FixedFooter from '../../components/FixedFooter'; @@ -101,62 +100,60 @@ class AddSecondaryLoginPage extends Component { this.phoneNumberInputRef.focus(); }} > - - Navigation.navigate(ROUTES.SETTINGS_PROFILE)} - onCloseButtonPress={() => Navigation.dismissModal()} - /> - - - {this.props.translate(this.formType === CONST.LOGIN_TYPE.PHONE - ? 'addSecondaryLoginPage.enterPreferredPhoneNumberToSendValidationLink' - : 'addSecondaryLoginPage.enterPreferredEmailToSendValidationLink')} - - - this.phoneNumberInputRef = el} - value={this.state.login} - onChangeText={this.onSecondaryLoginChange} - keyboardType={this.formType === CONST.LOGIN_TYPE.PHONE - ? CONST.KEYBOARD_TYPE.PHONE_PAD : undefined} - returnKeyType="done" - /> - - - this.setState({password})} - secureTextEntry - autoCompleteType="password" - textContentType="password" - onSubmitEditing={this.submitForm} - /> - - {!_.isEmpty(this.props.user.error) && ( - - {this.props.user.error} - - )} - - -