diff --git a/src/components/ScreenWrapper.js b/src/components/ScreenWrapper.js
index 3e141b3b2379..58e95b1b42e4 100644
--- a/src/components/ScreenWrapper.js
+++ b/src/components/ScreenWrapper.js
@@ -24,8 +24,13 @@ const propTypes = {
// Whether to include padding top
includePaddingTop: PropTypes.bool,
- // react-navigation object that will allow us to goBack()
+ // 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 listner to Navigaton state.
+ addListener: PropTypes.func.isRequired,
// Returns to the previous navigation state e.g. if this is inside a Modal we will dismiss it
goBack: PropTypes.func,
@@ -36,24 +41,39 @@ const defaultProps = {
style: [],
includePaddingBottom: true,
includePaddingTop: true,
+ onTransitionEnd: () => {},
navigation: {
+ addListener: () => {},
goBack: () => {},
},
};
class ScreenWrapper extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ didScreenTransitionEnd: false,
+ };
+ }
+
componentDidMount() {
- this.unsubscribe = KeyboardShortcut.subscribe('Escape', () => {
+ this.unsubscribeEscapeKey = KeyboardShortcut.subscribe('Escape', () => {
this.props.navigation.goBack();
}, [], true);
+
+ this.unsubscribeTransitionEnd = this.props.navigation.addListener('transitionEnd', () => {
+ this.setState({didScreenTransitionEnd: true});
+ this.props.onTransitionEnd();
+ });
}
componentWillUnmount() {
- if (!this.unsubscribe) {
- return;
+ if (this.unsubscribeEscapeKey) {
+ this.unsubscribeEscapeKey();
+ }
+ if (this.unsubscribeTransitionEnd) {
+ this.unsubscribeTransitionEnd();
}
-
- this.unsubscribe();
}
render() {
@@ -81,7 +101,10 @@ class ScreenWrapper extends React.Component {
{// If props.children is a function, call it to provide the insets to the children.
_.isFunction(this.props.children)
- ? this.props.children(insets)
+ ? this.props.children({
+ insets,
+ didScreenTransitionEnd: this.state.didScreenTransitionEnd,
+ })
: this.props.children
}
diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js
index ef06075b3cf3..1f244fabec95 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.js
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.js
@@ -68,10 +68,12 @@ Onyx.connect({
const RootStack = createCustomModalStackNavigator();
-// When modal screen gets focused, update modal visibility in Onyx
+// We want to delay the re-rendering for components(e.g. ReportActionCompose)
+// that depends on modal visibility until Modal is completely closed or its transition has ended
+// When modal screen is focused and animation transition is ended, update modal visibility in Onyx
// https://reactnavigation.org/docs/navigation-events/
const modalScreenListeners = {
- focus: () => {
+ transitionEnd: () => {
setModalVisibility(true);
},
beforeRemove: () => {
@@ -159,7 +161,7 @@ class AuthScreens extends React.Component {
const modalScreenOptions = {
headerShown: false,
cardStyle: getNavigationModalCardStyle(this.props.isSmallScreenWidth),
- cardStyleInterpolator: modalCardStyleInterpolator,
+ cardStyleInterpolator: props => modalCardStyleInterpolator(this.props.isSmallScreenWidth, props),
animationEnabled: true,
gestureDirection: 'horizontal',
cardOverlayEnabled: true,
diff --git a/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js b/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js
index 28b85c68b7ec..8ad5b1603cdd 100644
--- a/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js
+++ b/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js
@@ -1,15 +1,19 @@
import {Animated} from 'react-native';
+import variables from '../../../styles/variables';
-export default ({
- current: {progress},
- inverted,
- layouts: {
- screen,
+export default (
+ isSmallScreen,
+ {
+ current: {progress},
+ inverted,
+ layouts: {
+ screen,
+ },
},
-}) => {
+) => {
const translateX = Animated.multiply(progress.interpolate({
inputRange: [0, 1],
- outputRange: [screen.width, 0],
+ outputRange: [isSmallScreen ? screen.width : variables.sideBarWidth, 0],
extrapolate: 'clamp',
}), inverted);
diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js
index 2eae67d9f2e6..17762d37df91 100755
--- a/src/pages/NewChatPage.js
+++ b/src/pages/NewChatPage.js
@@ -12,6 +12,7 @@ import withWindowDimensions, {windowDimensionsPropTypes} from '../components/wit
import HeaderWithCloseButton from '../components/HeaderWithCloseButton';
import Navigation from '../libs/Navigation/Navigation';
import ScreenWrapper from '../components/ScreenWrapper';
+import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator';
import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
import compose from '../libs/compose';
@@ -52,7 +53,6 @@ class NewChatPage extends Component {
super(props);
this.createNewChat = this.createNewChat.bind(this);
-
const {
personalDetails,
userToInvite,
@@ -117,38 +117,45 @@ class NewChatPage extends Component {
return (
- Navigation.dismissModal(true)}
- />
-
- {
- const {
- personalDetails,
- userToInvite,
- } = getNewChatOptions(
- this.props.reports,
- this.props.personalDetails,
- searchValue,
- );
- this.setState({
- searchValue,
- userToInvite,
- personalDetails,
- });
- }}
- headerMessage={headerMessage}
- hideSectionHeaders
- disableArrowKeysActions
- hideAdditionalOptionStates
- forceTextUnreadStyle
- />
-
-
+ {({didScreenTransitionEnd}) => (
+ <>
+ Navigation.dismissModal(true)}
+ />
+
+
+ {didScreenTransitionEnd && (
+ {
+ const {
+ personalDetails,
+ userToInvite,
+ } = getNewChatOptions(
+ this.props.reports,
+ this.props.personalDetails,
+ searchValue,
+ );
+ this.setState({
+ searchValue,
+ userToInvite,
+ personalDetails,
+ });
+ }}
+ headerMessage={headerMessage}
+ hideSectionHeaders
+ disableArrowKeysActions
+ hideAdditionalOptionStates
+ forceTextUnreadStyle
+ />
+ )}
+
+
+ >
+ )}
);
}
diff --git a/src/pages/NewGroupPage.js b/src/pages/NewGroupPage.js
index f1e83de73ae5..f1584647f1f2 100755
--- a/src/pages/NewGroupPage.js
+++ b/src/pages/NewGroupPage.js
@@ -14,6 +14,7 @@ import withWindowDimensions, {windowDimensionsPropTypes} from '../components/wit
import HeaderWithCloseButton from '../components/HeaderWithCloseButton';
import ScreenWrapper from '../components/ScreenWrapper';
import Navigation from '../libs/Navigation/Navigation';
+import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator';
import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
import compose from '../libs/compose';
@@ -55,7 +56,6 @@ class NewGroupPage extends Component {
this.toggleOption = this.toggleOption.bind(this);
this.createGroup = this.createGroup.bind(this);
-
const {
recentReports,
personalDetails,
@@ -185,60 +185,69 @@ class NewGroupPage extends Component {
);
return (
- Navigation.dismissModal(true)}
- />
-
- {
- const {
- recentReports,
- personalDetails,
- userToInvite,
- } = getNewGroupOptions(
- this.props.reports,
- this.props.personalDetails,
- searchValue,
- [],
- );
- this.setState({
- searchValue,
- userToInvite,
- recentReports,
- personalDetails,
- });
- }}
- headerMessage={headerMessage}
- disableArrowKeysActions
- hideAdditionalOptionStates
- forceTextUnreadStyle
- shouldFocusOnSelectRow
- />
- {this.state.selectedOptions?.length > 0 && (
-
- [
- styles.button,
- styles.buttonSuccess,
- styles.w100,
- hovered && styles.buttonSuccessHovered,
- ]}
- >
-
- {this.props.translate('newGroupPage.createGroup')}
-
-
+ {({didScreenTransitionEnd}) => (
+ <>
+ Navigation.dismissModal(true)}
+ />
+
+
+ {didScreenTransitionEnd && (
+ <>
+ {
+ const {
+ recentReports,
+ personalDetails,
+ userToInvite,
+ } = getNewGroupOptions(
+ this.props.reports,
+ this.props.personalDetails,
+ searchValue,
+ [],
+ );
+ this.setState({
+ searchValue,
+ userToInvite,
+ recentReports,
+ personalDetails,
+ });
+ }}
+ headerMessage={headerMessage}
+ disableArrowKeysActions
+ hideAdditionalOptionStates
+ forceTextUnreadStyle
+ shouldFocusOnSelectRow
+ />
+ {this.state.selectedOptions?.length > 0 && (
+
+ [
+ styles.button,
+ styles.buttonSuccess,
+ styles.w100,
+ hovered && styles.buttonSuccessHovered,
+ ]}
+ >
+
+ {this.props.translate('newGroupPage.createGroup')}
+
+
+
+ )}
+ >
+ )}
- )}
-
-
+
+ >
+ )}
);
}
diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js
index 2ba3ebc84e4a..5405e6d8fd47 100755
--- a/src/pages/SearchPage.js
+++ b/src/pages/SearchPage.js
@@ -15,6 +15,7 @@ import HeaderWithCloseButton from '../components/HeaderWithCloseButton';
import ScreenWrapper from '../components/ScreenWrapper';
import Timing from '../libs/actions/Timing';
import CONST from '../CONST';
+import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator';
import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
import compose from '../libs/compose';
@@ -60,7 +61,6 @@ class SearchPage extends Component {
Timing.start(CONST.TIMING.SEARCH_RENDER);
this.selectReport = this.selectReport.bind(this);
-
const {
recentReports,
personalDetails,
@@ -141,39 +141,46 @@ class SearchPage extends Component {
);
return (
- Navigation.dismissModal(true)}
- />
-
- {
- const {
- recentReports,
- personalDetails,
- userToInvite,
- } = getSearchOptions(
- this.props.reports,
- this.props.personalDetails,
- searchValue,
- );
- this.setState({
- searchValue,
- userToInvite,
- recentReports,
- personalDetails,
- });
- }}
- headerMessage={headerMessage}
- hideSectionHeaders
- hideAdditionalOptionStates
- showTitleTooltip
- />
-
-
+ {({didScreenTransitionEnd}) => (
+ <>
+ Navigation.dismissModal(true)}
+ />
+
+
+ {didScreenTransitionEnd && (
+ {
+ const {
+ recentReports,
+ personalDetails,
+ userToInvite,
+ } = getSearchOptions(
+ this.props.reports,
+ this.props.personalDetails,
+ searchValue,
+ );
+ this.setState({
+ searchValue,
+ userToInvite,
+ recentReports,
+ personalDetails,
+ });
+ }}
+ headerMessage={headerMessage}
+ hideSectionHeaders
+ hideAdditionalOptionStates
+ showTitleTooltip
+ />
+ )}
+
+
+ >
+ )}
);
}
diff --git a/src/pages/home/sidebar/SidebarScreen.js b/src/pages/home/sidebar/SidebarScreen.js
index 1e90b7aa98a4..27533ebf67a4 100755
--- a/src/pages/home/sidebar/SidebarScreen.js
+++ b/src/pages/home/sidebar/SidebarScreen.js
@@ -81,7 +81,7 @@ class SidebarScreen extends Component {
includePaddingBottom={false}
style={[styles.sidebar]}
>
- {insets => (
+ {({insets}) => (
<>
(
-
- {() => (
- // eslint-disable-next-line react/jsx-props-no-spreading
-
- )}
-
-);
+// eslint-disable-next-line react/jsx-props-no-spreading
+export default props => ;
diff --git a/src/pages/iou/IOUModal.js b/src/pages/iou/IOUModal.js
index 05fd51b55c11..a31d9c1acd1d 100755
--- a/src/pages/iou/IOUModal.js
+++ b/src/pages/iou/IOUModal.js
@@ -16,6 +16,8 @@ import ONYXKEYS from '../../ONYXKEYS';
import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize';
import compose from '../../libs/compose';
import {getPersonalDetailsForLogins} from '../../libs/OptionsListUtils';
+import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator';
+import ScreenWrapper from '../../components/ScreenWrapper';
/**
* IOU modal for requesting money and splitting bills.
@@ -37,6 +39,9 @@ const propTypes = {
// Whether or not transaction creation has resulted to error
error: PropTypes.bool,
+
+ // is loading
+ loading: PropTypes.bool,
}).isRequired,
// Personal details of all the users
@@ -78,6 +83,7 @@ class IOUModal extends Component {
this.createTransaction = this.createTransaction.bind(this);
this.updateComment = this.updateComment.bind(this);
this.addParticipants = this.addParticipants.bind(this);
+ this.getReady = this.getReady.bind(this);
const participants = lodashGet(props, 'report.participants', []);
const participantsWithDetails = getPersonalDetailsForLogins(participants, props.personalDetails)
.map(personalDetails => ({
@@ -107,10 +113,6 @@ class IOUModal extends Component {
}
}
- componentDidMount() {
- getPreferredCurrency();
- }
-
componentDidUpdate(prevProps) {
// Successfully close the modal if transaction creation has ended and there is no error
if (prevProps.iou.creatingIOUTransaction && !this.props.iou.creatingIOUTransaction && !this.props.iou.error) {
@@ -118,6 +120,12 @@ class IOUModal extends Component {
}
}
+
+ getReady() {
+ getPreferredCurrency();
+ }
+
+
/**
* Retrieve title for current step, based upon current step and type of IOU
*
@@ -216,67 +224,79 @@ class IOUModal extends Component {
render() {
const currentStep = this.steps[this.state.currentStepIndex];
return (
- <>
-
-
- {this.state.currentStepIndex > 0
- && (
-
+ {({didScreenTransitionEnd}) => (
+ <>
+
+
-
-
- )}
-
-
- Navigation.dismissModal()}
- style={[styles.touchableButtonImage]}
- >
-
-
+ {this.state.currentStepIndex > 0
+ && (
+
+
+
+ )}
+
+
+ Navigation.dismissModal()}
+ style={[styles.touchableButtonImage]}
+ >
+
+
+
+
-
-
- {currentStep === Steps.IOUAmount && (
- {
- this.setState({amount});
- this.navigateToNextStep();
- }}
- currencySelected={this.currencySelected}
- selectedCurrency={this.state.selectedCurrency}
- />
- )}
- {currentStep === Steps.IOUParticipants && (
-
- )}
- {currentStep === Steps.IOUConfirm && (
-
+
+
+ {didScreenTransitionEnd && (
+ <>
+ {currentStep === Steps.IOUAmount && (
+ {
+ this.setState({amount});
+ this.navigateToNextStep();
+ }}
+ currencySelected={this.currencySelected}
+ selectedCurrency={this.state.selectedCurrency}
+ />
+ )}
+ {currentStep === Steps.IOUParticipants && (
+
+ )}
+ {currentStep === Steps.IOUConfirm && (
+
+ )}
+ >
+ )}
+
+
+ >
)}
- >
+
);
}
}
diff --git a/src/pages/iou/IOURequestPage.js b/src/pages/iou/IOURequestPage.js
index 67ee9d0f4dd4..6cacd162c105 100644
--- a/src/pages/iou/IOURequestPage.js
+++ b/src/pages/iou/IOURequestPage.js
@@ -1,12 +1,5 @@
import React from 'react';
import IOUModal from './IOUModal';
-import ScreenWrapper from '../../components/ScreenWrapper';
-export default props => (
-
- {() => (
- // eslint-disable-next-line react/jsx-props-no-spreading
-
- )}
-
-);
+// eslint-disable-next-line react/jsx-props-no-spreading
+export default props => ;