From e2aec0b22576e301856b3c113294f0cd433796e4 Mon Sep 17 00:00:00 2001 From: jczekalski Date: Wed, 12 Apr 2023 18:16:31 +0200 Subject: [PATCH 01/10] fix stale data issue in components that use report data and display options list --- src/pages/NewChatPage.js | 15 +++++++ src/pages/SearchPage.js | 45 +++++++++++++------ .../MoneyRequestParticipantsSelector.js | 15 +++++++ .../MoneyRequestParticipantsSplitSelector.js | 15 +++++++ 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index 0ca8d13fbcc8..f720ec5e4f2d 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -32,6 +32,9 @@ const propTypes = { /** All reports shared with the user */ reports: PropTypes.objectOf(reportPropTypes), + /** Indicates whether the reports data is ready */ + isLoadingReportData: PropTypes.bool, + ...windowDimensionsPropTypes, ...withLocalizePropTypes, @@ -42,6 +45,7 @@ const defaultProps = { betas: [], personalDetails: {}, reports: {}, + isLoadingReportData: true, }; class NewChatPage extends Component { @@ -75,6 +79,13 @@ class NewChatPage extends Component { }; } + componentDidUpdate(prevProps) { + if (prevProps.isLoadingReportData === this.props.isLoadingReportData) { + return; + } + this.updateOptionsWithSearchTerm(this.state.searchTerm); + } + /** * Returns the sections needed for the OptionsSelector * @@ -255,6 +266,7 @@ class NewChatPage extends Component { boldStyle shouldFocusOnSelectRow={this.props.isGroupChat} shouldShowConfirmButton={this.props.isGroupChat} + shouldShowOptions={!this.props.isLoadingReportData} confirmButtonText={this.props.translate('newChatPage.createGroup')} onConfirmSelection={this.createGroup} placeholderText={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} @@ -287,5 +299,8 @@ export default compose( betas: { key: ONYXKEYS.BETAS, }, + isLoadingReportData: { + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + }, }), )(NewChatPage); diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 560ec388ef99..3ac4ed2e193e 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -20,6 +20,7 @@ import compose from '../libs/compose'; import personalDetailsPropType from './personalDetailsPropType'; import reportPropTypes from './reportPropTypes'; import Performance from '../libs/Performance'; +import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; const propTypes = { /* Onyx Props */ @@ -33,6 +34,9 @@ const propTypes = { /** All reports shared with the user */ reports: PropTypes.objectOf(reportPropTypes), + /** Indicates whether report data is ready */ + isLoadingReportData: PropTypes.bool, + /** Window Dimensions Props */ ...windowDimensionsPropTypes, @@ -43,6 +47,7 @@ const defaultProps = { betas: [], personalDetails: {}, reports: {}, + isLoadingReportData: true, }; class SearchPage extends Component { @@ -76,6 +81,13 @@ class SearchPage extends Component { }; } + componentDidUpdate(prevProps) { + if (prevProps.isLoadingReportData === this.props.isLoadingReportData) { + return; + } + this.updateOptions(); + } + onChangeText(searchValue = '') { this.setState({searchValue}, this.debouncedUpdateOptions); } @@ -178,19 +190,23 @@ class SearchPage extends Component { onCloseButtonPress={() => Navigation.dismissModal(true)} /> - + {didScreenTransitionEnd ? ( + + ) : ( + + )} )} @@ -215,5 +231,8 @@ export default compose( betas: { key: ONYXKEYS.BETAS, }, + isLoadingReportData: { + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + }, }), )(SearchPage); diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js index cdb903212876..7bd603404858 100755 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js @@ -27,6 +27,9 @@ const propTypes = { /** All reports shared with the user */ reports: PropTypes.objectOf(reportPropTypes), + /** Indicates whether report data is ready */ + isLoadingReportData: PropTypes.bool, + /** padding bottom style of safe area */ safeAreaPaddingBottomStyle: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.object), @@ -44,6 +47,7 @@ const defaultProps = { personalDetails: {}, reports: {}, betas: [], + isLoadingReportData: true, }; class MoneyRequestParticipantsSelector extends Component { @@ -67,6 +71,13 @@ class MoneyRequestParticipantsSelector extends Component { }; } + componentDidUpdate(prevProps) { + if (prevProps.isLoadingReportData === this.props.isLoadingReportData) { + return; + } + this.updateOptionsWithSearchTerm(this.state.searchTerm); + } + /** * @param {string} searchTerm * @returns {Object} @@ -164,6 +175,7 @@ class MoneyRequestParticipantsSelector extends Component { placeholderText={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} boldStyle safeAreaPaddingBottomStyle={this.props.safeAreaPaddingBottomStyle} + shouldShowOptions={!this.props.isLoadingReportData} /> ); } @@ -184,5 +196,8 @@ export default compose( betas: { key: ONYXKEYS.BETAS, }, + isLoadingReportData: { + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + }, }), )(MoneyRequestParticipantsSelector); diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js index 961ec62bd395..74c4956cf80d 100755 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js @@ -49,6 +49,9 @@ const propTypes = { PropTypes.object, ]), + /** Indicates whether report data is ready */ + isLoadingReportData: PropTypes.bool, + ...withLocalizePropTypes, }; @@ -58,6 +61,7 @@ const defaultProps = { personalDetails: {}, reports: {}, safeAreaPaddingBottomStyle: {}, + isLoadingReportData: true, }; class MoneyRequestParticipantsSplitSelector extends Component { @@ -89,6 +93,13 @@ class MoneyRequestParticipantsSplitSelector extends Component { }; } + componentDidUpdate(prevProps) { + if (prevProps.isLoadingReportData === this.props.isLoadingReportData) { + return; + } + this.updateOptionsWithSearchTerm(this.state.searchTerm); + } + /** * Returns the sections needed for the OptionsSelector * @@ -238,6 +249,7 @@ class MoneyRequestParticipantsSplitSelector extends Component { onConfirmSelection={this.finalizeParticipants} placeholderText={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} safeAreaPaddingBottomStyle={this.props.safeAreaPaddingBottomStyle} + shouldShowOptions={!this.props.isLoadingReportData} /> ); @@ -259,5 +271,8 @@ export default compose( betas: { key: ONYXKEYS.BETAS, }, + isLoadingReportData: { + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + }, }), )(MoneyRequestParticipantsSplitSelector); From ac71ae15d76088624b47d82d71fa4aa2422a8754 Mon Sep 17 00:00:00 2001 From: jczekalski Date: Wed, 12 Apr 2023 18:18:09 +0200 Subject: [PATCH 02/10] add skeleton loading state to options list --- src/components/OptionsList/BaseOptionsList.js | 65 ++++++++++--------- .../OptionsList/optionsListPropTypes.js | 4 ++ ...etonView.js => OptionsListSkeletonView.js} | 8 +-- .../OptionsSelector/BaseOptionsSelector.js | 6 +- src/pages/home/sidebar/SidebarLinks.js | 4 +- 5 files changed, 48 insertions(+), 39 deletions(-) rename src/components/{LHNSkeletonView.js => OptionsListSkeletonView.js} (93%) diff --git a/src/components/OptionsList/BaseOptionsList.js b/src/components/OptionsList/BaseOptionsList.js index 38f0d7c9be3f..cad5d0019eca 100644 --- a/src/components/OptionsList/BaseOptionsList.js +++ b/src/components/OptionsList/BaseOptionsList.js @@ -8,6 +8,7 @@ import OptionRow from '../OptionRow'; import SectionList from '../SectionList'; import Text from '../Text'; import {propTypes as optionsListPropTypes, defaultProps as optionsListDefaultProps} from './optionsListPropTypes'; +import OptionsListSkeletonView from '../OptionsListSkeletonView'; const propTypes = { /** Determines whether the keyboard gets dismissed in response to a drag */ @@ -48,7 +49,7 @@ class BaseOptionsList extends Component { return nextProps.focusedIndex !== this.props.focusedIndex || nextProps.selectedOptions.length !== this.props.selectedOptions.length || nextProps.headerMessage !== this.props.headerMessage - || !_.isEqual(nextProps.sections, this.props.sections) + || nextProps.isLoading !== this.props.isLoading || !_.isEqual(nextProps.sections, this.props.sections); } @@ -209,35 +210,39 @@ class BaseOptionsList extends Component { render() { return ( - {this.props.headerMessage ? ( - - - {this.props.headerMessage} - - - ) : null} - + {this.props.isLoading ? : ( + <> + {this.props.headerMessage ? ( + + + {this.props.headerMessage} + + + ) : null} + + + )} ); } diff --git a/src/components/OptionsList/optionsListPropTypes.js b/src/components/OptionsList/optionsListPropTypes.js index 91aaa5988d57..82aee30fd68f 100644 --- a/src/components/OptionsList/optionsListPropTypes.js +++ b/src/components/OptionsList/optionsListPropTypes.js @@ -65,6 +65,9 @@ const propTypes = { /** Whether to disable the interactivity of the list's option row(s) */ isDisabled: PropTypes.bool, + /** Whether the options list skeleton loading view should be displayed */ + isLoading: PropTypes.bool, + /** Callback to execute when the SectionList lays out */ onLayout: PropTypes.func, @@ -91,6 +94,7 @@ const defaultProps = { innerRef: null, showTitleTooltip: false, isDisabled: false, + isLoading: false, onLayout: undefined, shouldHaveOptionSeparator: false, shouldDisableRowInnerPadding: false, diff --git a/src/components/LHNSkeletonView.js b/src/components/OptionsListSkeletonView.js similarity index 93% rename from src/components/LHNSkeletonView.js rename to src/components/OptionsListSkeletonView.js index efa7a629fe82..f39a52326f2d 100644 --- a/src/components/LHNSkeletonView.js +++ b/src/components/OptionsListSkeletonView.js @@ -16,7 +16,7 @@ const defaultTypes = { shouldAnimate: true, }; -class LHNSkeletonView extends React.Component { +class OptionsListSkeletonView extends React.Component { constructor(props) { super(props); this.state = { @@ -91,7 +91,7 @@ class LHNSkeletonView extends React.Component { } } -LHNSkeletonView.propTypes = propTypes; -LHNSkeletonView.defaultProps = defaultTypes; +OptionsListSkeletonView.propTypes = propTypes; +OptionsListSkeletonView.defaultProps = defaultTypes; -export default LHNSkeletonView; +export default OptionsListSkeletonView; diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 66b613a1ce33..712e4cc44fc9 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -12,7 +12,6 @@ import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import TextInput from '../TextInput'; import ArrowKeyFocusManager from '../ArrowKeyFocusManager'; import KeyboardShortcut from '../../libs/KeyboardShortcut'; -import FullScreenLoadingIndicator from '../FullscreenLoadingIndicator'; import {propTypes as optionsSelectorPropTypes, defaultProps as optionsSelectorDefaultProps} from './optionsSelectorPropTypes'; import setSelection from '../../libs/setSelection'; @@ -287,7 +286,7 @@ class BaseOptionsSelector extends Component { blurOnSubmit={Boolean(this.state.allOptions.length)} /> ); - const optionsList = this.props.shouldShowOptions ? ( + const optionsList = ( this.list = el} optionHoveredStyle={this.props.optionHoveredStyle} @@ -310,8 +309,9 @@ class BaseOptionsSelector extends Component { } }} contentContainerStyles={shouldShowFooter ? undefined : [this.props.safeAreaPaddingBottomStyle]} + isLoading={!this.props.shouldShowOptions} /> - ) : ; + ); return ( ; + const skeletonPlaceholder = ; return ( Date: Thu, 20 Apr 2023 15:07:39 +0200 Subject: [PATCH 03/10] update options whenever personal details or reports data changes --- src/pages/NewChatPage.js | 2 +- src/pages/SearchPage.js | 2 +- .../MoneyRequestParticipantsSelector.js | 2 +- .../MoneyRequestParticipantsSplitSelector.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index f720ec5e4f2d..ce1ff9936d20 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -80,7 +80,7 @@ class NewChatPage extends Component { } componentDidUpdate(prevProps) { - if (prevProps.isLoadingReportData === this.props.isLoadingReportData) { + if (_.isEqual(prevProps.reports, this.props.reports) && _.isEqual(prevProps.personalDetails, this.props.personalDetails)) { return; } this.updateOptionsWithSearchTerm(this.state.searchTerm); diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index cc56e03e9aff..dd6db7ad1360 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -83,7 +83,7 @@ class SearchPage extends Component { } componentDidUpdate(prevProps) { - if (prevProps.isLoadingReportData === this.props.isLoadingReportData) { + if (_.isEqual(prevProps.reports, this.props.reports) && _.isEqual(prevProps.personalDetails, this.props.personalDetails)) { return; } this.updateOptions(); diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js index 7bd603404858..efa6a1ad30d4 100755 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js @@ -72,7 +72,7 @@ class MoneyRequestParticipantsSelector extends Component { } componentDidUpdate(prevProps) { - if (prevProps.isLoadingReportData === this.props.isLoadingReportData) { + if (_.isEqual(prevProps.reports, this.props.reports) && _.isEqual(prevProps.personalDetails, this.props.personalDetails)) { return; } this.updateOptionsWithSearchTerm(this.state.searchTerm); diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js index 74c4956cf80d..75d26a5a0141 100755 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js @@ -94,7 +94,7 @@ class MoneyRequestParticipantsSplitSelector extends Component { } componentDidUpdate(prevProps) { - if (prevProps.isLoadingReportData === this.props.isLoadingReportData) { + if (_.isEqual(prevProps.reports, this.props.reports) && _.isEqual(prevProps.personalDetails, this.props.personalDetails)) { return; } this.updateOptionsWithSearchTerm(this.state.searchTerm); From a6c58d6bcafc3978abcb21f06baf927b5bde2f22 Mon Sep 17 00:00:00 2001 From: jczekalski Date: Thu, 20 Apr 2023 16:33:51 +0200 Subject: [PATCH 04/10] make sure personal details data is ready before attempting to create options --- src/libs/OptionsListUtils.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index e5a5d22bec6e..ca24fe960516 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -528,6 +528,16 @@ function getOptions(reports, personalDetails, { forcePolicyNamePreview = false, includeOwnedWorkspaceChats = false, }) { + const personalDetailsReady = !_.isEmpty(personalDetails) && _.every(_.keys(personalDetails), key => personalDetails[key].login); + + if (!personalDetailsReady) { + return { + recentReports: [], + personalDetails: [], + userToInvite: null, + }; + } + let recentReportOptions = []; let personalDetailsOptions = []; const reportMapForLogins = {}; From a888bdbeef8c24ddf3221edc8c19f8f1712c1ba8 Mon Sep 17 00:00:00 2001 From: jczekalski Date: Thu, 20 Apr 2023 16:58:22 +0200 Subject: [PATCH 05/10] more efficient check --- src/libs/OptionsListUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index ca24fe960516..54140ea7619d 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -528,7 +528,7 @@ function getOptions(reports, personalDetails, { forcePolicyNamePreview = false, includeOwnedWorkspaceChats = false, }) { - const personalDetailsReady = !_.isEmpty(personalDetails) && _.every(_.keys(personalDetails), key => personalDetails[key].login); + const personalDetailsReady = !_.isEmpty(personalDetails) && !_.some(_.keys(personalDetails), key => !personalDetails[key].login); if (!personalDetailsReady) { return { From e1068df5de59d16beedd9da2a096fb4f6b121c63 Mon Sep 17 00:00:00 2001 From: jczekalski Date: Thu, 4 May 2023 11:39:14 +0200 Subject: [PATCH 06/10] wait until report data and personal details ready before hiding skeleton loader --- src/libs/OptionsListUtils.js | 14 +++++++++++--- src/pages/NewChatPage.js | 4 +++- src/pages/SearchPage.js | 4 +++- .../MoneyRequestParticipantsSelector.js | 4 +++- .../MoneyRequestParticipantsSplitSelector.js | 4 +++- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 452b5fa0765c..23d3275b18e1 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -176,6 +176,15 @@ function getPersonalDetailsForLogins(logins, personalDetails) { return personalDetailsForLogins; } +/** + * Return true if personal details data is ready, i.e. report list options can be created. + * @param {Object} personalDetails + * @returns {boolean} + */ +function isPersonalDetailsReady(personalDetails) { + return !_.isEmpty(personalDetails) && !_.some(_.keys(personalDetails), key => !personalDetails[key].login); +} + /** * Get the participant options for a report. * @param {Object} report @@ -529,9 +538,7 @@ function getOptions(reports, personalDetails, { forcePolicyNamePreview = false, includeOwnedWorkspaceChats = false, }) { - const personalDetailsReady = !_.isEmpty(personalDetails) && !_.some(_.keys(personalDetails), key => !personalDetails[key].login); - - if (!personalDetailsReady) { + if (!isPersonalDetailsReady(personalDetails)) { return { recentReports: [], personalDetails: [], @@ -901,6 +908,7 @@ function getHeaderMessage(hasSelectableOptions, hasUserToInvite, searchValue, ma export { addSMSDomainIfPhoneNumber, isCurrentUser, + isPersonalDetailsReady, getSearchOptions, getNewChatOptions, getMemberInviteOptions, diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index e92238438ff0..a615f9160d58 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -243,6 +243,8 @@ class NewChatPage extends Component { this.state.searchTerm, maxParticipantsReached, ); + const isOptionsDataReady = !this.props.isLoadingReportData && OptionsListUtils.isPersonalDetailsReady(this.props.personalDetails); + return ( {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( @@ -266,7 +268,7 @@ class NewChatPage extends Component { boldStyle shouldFocusOnSelectRow={this.props.isGroupChat} shouldShowConfirmButton={this.props.isGroupChat} - shouldShowOptions={!this.props.isLoadingReportData} + shouldShowOptions={isOptionsDataReady} confirmButtonText={this.props.translate('newChatPage.createGroup')} onConfirmSelection={this.createGroup} placeholderText={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index dd6db7ad1360..3b468d9930eb 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -185,6 +185,8 @@ class SearchPage extends Component { render() { const sections = this.getSections(); + const isOptionsDataReady = !this.props.isLoadingReportData && OptionsListUtils.isPersonalDetailsReady(this.props.personalDetails); + return ( {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( @@ -203,7 +205,7 @@ class SearchPage extends Component { headerMessage={this.state.headerMessage} hideSectionHeaders showTitleTooltip - shouldShowOptions={!this.props.isLoadingReportData} + shouldShowOptions={isOptionsDataReady} placeholderText={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} onLayout={this.searchRendered} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js index efa6a1ad30d4..81f2269fa1c0 100755 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js @@ -165,6 +165,8 @@ class MoneyRequestParticipantsSelector extends Component { Boolean(this.state.userToInvite), this.state.searchTerm, ); + const isOptionsDataReady = !this.props.isLoadingReportData && OptionsListUtils.isPersonalDetailsReady(this.props.personalDetails); + return ( ); } diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js index 75d26a5a0141..089dd281dcf1 100755 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js @@ -230,6 +230,8 @@ class MoneyRequestParticipantsSplitSelector extends Component { this.state.searchTerm, maxParticipantsReached, ); + const isOptionsDataReady = !this.props.isLoadingReportData && OptionsListUtils.isPersonalDetailsReady(this.props.personalDetails); + return ( 0 ? this.props.safeAreaPaddingBottomStyle : {})]}> @@ -249,7 +251,7 @@ class MoneyRequestParticipantsSplitSelector extends Component { onConfirmSelection={this.finalizeParticipants} placeholderText={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} safeAreaPaddingBottomStyle={this.props.safeAreaPaddingBottomStyle} - shouldShowOptions={!this.props.isLoadingReportData} + shouldShowOptions={isOptionsDataReady} /> ); From 082d05e619b9c7db636e8915dd4708277b64c836 Mon Sep 17 00:00:00 2001 From: jczekalski Date: Thu, 4 May 2023 12:22:14 +0200 Subject: [PATCH 07/10] handle WorkspaceInvitePage --- src/pages/workspace/WorkspaceInvitePage.js | 100 ++++++++++----------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js index 8aff82ffb281..b97a1c0ffcab 100644 --- a/src/pages/workspace/WorkspaceInvitePage.js +++ b/src/pages/workspace/WorkspaceInvitePage.js @@ -17,7 +17,6 @@ import FormSubmit from '../../components/FormSubmit'; import OptionsSelector from '../../components/OptionsSelector'; import * as OptionsListUtils from '../../libs/OptionsListUtils'; import CONST from '../../CONST'; -import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator'; import * as Link from '../../libs/actions/Link'; import withPolicy, {policyPropTypes, policyDefaultProps} from './withPolicy'; import {withNetwork} from '../../components/OnyxProvider'; @@ -101,6 +100,10 @@ class WorkspaceInvitePage extends React.Component { } componentDidUpdate(prevProps) { + if (!_.isEqual(prevProps.personalDetails, this.props.personalDetails)) { + this.updateOptionsWithSearchTerm(this.props.searchTerm); + } + const isReconnecting = prevProps.network.isOffline && !this.props.network.isOffline; if (!isReconnecting) { return; @@ -283,57 +286,52 @@ class WorkspaceInvitePage extends React.Component { return ( - {({didScreenTransitionEnd}) => ( - Navigation.navigate(ROUTES.SETTINGS_WORKSPACES)} - > - - this.clearErrors(true)} - shouldShowGetAssistanceButton - guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_MEMBERS} - shouldShowBackButton - onBackButtonPress={() => Navigation.goBack()} + Navigation.navigate(ROUTES.SETTINGS_WORKSPACES)} + > + + this.clearErrors(true)} + shouldShowGetAssistanceButton + guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_MEMBERS} + shouldShowBackButton + onBackButtonPress={() => Navigation.goBack()} + /> + + + + + - - {didScreenTransitionEnd ? ( - - ) : ( - - )} - - - - - - - )} + + + ); } From bd9cde23b671ea2a8fbb47f3c3d819b4358ddb33 Mon Sep 17 00:00:00 2001 From: jczekalski Date: Thu, 4 May 2023 15:03:14 +0200 Subject: [PATCH 08/10] fix WorkspaceInvitePage condition for displaying options list --- src/pages/workspace/WorkspaceInvitePage.js | 92 +++++++++++----------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js index b97a1c0ffcab..d68b4194e2f6 100644 --- a/src/pages/workspace/WorkspaceInvitePage.js +++ b/src/pages/workspace/WorkspaceInvitePage.js @@ -286,52 +286,54 @@ class WorkspaceInvitePage extends React.Component { return ( - Navigation.navigate(ROUTES.SETTINGS_WORKSPACES)} - > - - this.clearErrors(true)} - shouldShowGetAssistanceButton - guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_MEMBERS} - shouldShowBackButton - onBackButtonPress={() => Navigation.goBack()} - /> - - ( + Navigation.navigate(ROUTES.SETTINGS_WORKSPACES)} + > + + this.clearErrors(true)} + shouldShowGetAssistanceButton + guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_MEMBERS} + shouldShowBackButton + onBackButtonPress={() => Navigation.goBack()} /> - - - - - - + + + + + + + + + )} ); } From ee09d15d66a21bececcae976aca6a7e8df09a9a9 Mon Sep 17 00:00:00 2001 From: jczekalski Date: Thu, 4 May 2023 17:48:44 +0200 Subject: [PATCH 09/10] resolve crash when deep linking into WorkspaceMembersPage --- src/pages/workspace/WorkspaceMembersPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.js index 348023909907..5934272bca72 100644 --- a/src/pages/workspace/WorkspaceMembersPage.js +++ b/src/pages/workspace/WorkspaceMembersPage.js @@ -368,7 +368,7 @@ class WorkspaceMembersPage extends React.Component { data = _.sortBy(data, value => value.displayName.toLowerCase()); data = this.getMemberOptions(data, this.state.searchValue.trim().toLowerCase()); - data = _.reject(data, (member) => { + data = !_.isEmpty(this.props.policy) && !_.isEmpty(this.props.currentUserPersonalDetails) && _.reject(data, (member) => { // If this policy is owned by Expensify then show all support (expensify.com or team.expensify.com) emails if (PolicyUtils.isExpensifyTeam(lodashGet(this.props.policy, 'owner'))) { return; From cdd8e319af4edbdb426fa3f78306c87b5a03c44a Mon Sep 17 00:00:00 2001 From: jczekalski Date: Thu, 4 May 2023 18:43:09 +0200 Subject: [PATCH 10/10] WorkspaceMembersPage: get specific properties using lodashGet, make sure data ready before trying to access/use it --- src/pages/workspace/WorkspaceMembersPage.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.js index 5934272bca72..0e50cd24cfaf 100644 --- a/src/pages/workspace/WorkspaceMembersPage.js +++ b/src/pages/workspace/WorkspaceMembersPage.js @@ -353,6 +353,8 @@ class WorkspaceMembersPage extends React.Component { render() { const policyMemberList = lodashGet(this.props, 'policyMemberList', {}); + const policyOwner = lodashGet(this.props.policy, 'owner'); + const currentUserLogin = lodashGet(this.props.currentUserPersonalDetails, 'login'); const removableMembers = {}; let data = []; _.each(policyMemberList, (policyMember, email) => { @@ -368,15 +370,19 @@ class WorkspaceMembersPage extends React.Component { data = _.sortBy(data, value => value.displayName.toLowerCase()); data = this.getMemberOptions(data, this.state.searchValue.trim().toLowerCase()); - data = !_.isEmpty(this.props.policy) && !_.isEmpty(this.props.currentUserPersonalDetails) && _.reject(data, (member) => { + data = _.reject(data, (member) => { + if (!policyOwner || !currentUserLogin) { + return; + } + // If this policy is owned by Expensify then show all support (expensify.com or team.expensify.com) emails - if (PolicyUtils.isExpensifyTeam(lodashGet(this.props.policy, 'owner'))) { + if (PolicyUtils.isExpensifyTeam(policyOwner)) { return; } // We don't want to show guides as policy members unless the user is not a guide. Some customers get confused when they // see random people added to their policy, but guides having access to the policies help set them up. - const isCurrentUserExpensifyTeam = PolicyUtils.isExpensifyTeam(this.props.currentUserPersonalDetails.login); + const isCurrentUserExpensifyTeam = PolicyUtils.isExpensifyTeam(currentUserLogin); return !isCurrentUserExpensifyTeam && PolicyUtils.isExpensifyTeam(member.login); });