diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js index 95fa64723737..327ff39ef248 100644 --- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js +++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js @@ -1,25 +1,30 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useMemo, useState} from 'react'; +import React, {useCallback, useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import Button from '@components/Button'; import FormHelpMessage from '@components/FormHelpMessage'; -import OptionsSelector from '@components/OptionsSelector'; -import refPropTypes from '@components/refPropTypes'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import Icon from '@components/Icon'; +import {Info} from '@components/Icon/Expensicons'; +import {PressableWithFeedback, PressableWithoutFeedback} from '@components/Pressable'; +import SelectCircle from '@components/SelectCircle'; +import SelectionList from '@components/SelectionList'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import * as Report from '@libs/actions/Report'; import * as Browser from '@libs/Browser'; -import compose from '@libs/compose'; +import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as ReportUtils from '@libs/ReportUtils'; import personalDetailsPropType from '@pages/personalDetailsPropType'; import reportPropTypes from '@pages/reportPropTypes'; +import useTheme from '@styles/themes/useTheme'; import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; const propTypes = { /** Beta features list */ @@ -28,9 +33,6 @@ const propTypes = { /** Callback to request parent modal to go to next step, which should be split */ onFinish: PropTypes.func.isRequired, - /** A ref to forward to options selector's text input */ - forwardedRef: refPropTypes, - /** Callback to add participants in MoneyRequestModal */ onParticipantsAdded: PropTypes.func.isRequired, @@ -62,13 +64,10 @@ const propTypes = { /** Whether we are searching for reports in the server */ isSearchingForReports: PropTypes.bool, - - ...withLocalizePropTypes, }; const defaultProps = { participants: [], - forwardedRef: undefined, safeAreaPaddingBottomStyle: {}, personalDetails: {}, reports: {}, @@ -77,12 +76,10 @@ const defaultProps = { }; function MoneyTemporaryForRefactorRequestParticipantsSelector({ - forwardedRef, betas, participants, personalDetails, reports, - translate, onFinish, onParticipantsAdded, safeAreaPaddingBottomStyle, @@ -90,15 +87,14 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ iouRequestType, isSearchingForReports, }) { + const {translate} = useLocalize(); const styles = useThemeStyles(); + const theme = useTheme(); const [searchTerm, setSearchTerm] = useState(''); - const [newChatOptions, setNewChatOptions] = useState({ - recentReports: [], - personalDetails: [], - userToInvite: null, - }); const {isOffline} = useNetwork(); + const offlineMessage = isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; + const maxParticipantsReached = participants.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; /** @@ -106,15 +102,42 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ * * @returns {Array} */ - const sections = useMemo(() => { + const [sections, newChatOptions] = useMemo(() => { const newSections = []; let indexOffset = 0; + const chatOptions = OptionsListUtils.getFilteredOptions( + reports, + personalDetails, + betas, + searchTerm, + participants, + CONST.EXPENSIFY_EMAILS, + + // If we are using this component in the "Request money" flow then we pass the includeOwnedWorkspaceChats argument so that the current user + // sees the option to request money from their admin on their own Workspace Chat. + iouType === CONST.IOU.TYPE.REQUEST, + + // We don't want to include any P2P options like personal details or reports that are not workspace chats for certain features. + iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE, + false, + {}, + [], + false, + {}, + [], + + // We don't want the user to be able to invite individuals when they are in the "Distance request" flow for now. + // This functionality is being built here: https://github.com/Expensify/App/issues/23291 + iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE, + true, + ); + const formatResults = OptionsListUtils.formatSectionsFromSearchTerm( searchTerm, participants, - newChatOptions.recentReports, - newChatOptions.personalDetails, + chatOptions.recentReports, + chatOptions.personalDetails, personalDetails, true, indexOffset, @@ -128,24 +151,24 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ newSections.push({ title: translate('common.recents'), - data: newChatOptions.recentReports, - shouldShow: !_.isEmpty(newChatOptions.recentReports), + data: chatOptions.recentReports, + shouldShow: !_.isEmpty(chatOptions.recentReports), indexOffset, }); - indexOffset += newChatOptions.recentReports.length; + indexOffset += chatOptions.recentReports.length; newSections.push({ title: translate('common.contacts'), - data: newChatOptions.personalDetails, - shouldShow: !_.isEmpty(newChatOptions.personalDetails), + data: chatOptions.personalDetails, + shouldShow: !_.isEmpty(chatOptions.personalDetails), indexOffset, }); - indexOffset += newChatOptions.personalDetails.length; + indexOffset += chatOptions.personalDetails.length; - if (newChatOptions.userToInvite && !OptionsListUtils.isCurrentUser(newChatOptions.userToInvite)) { + if (chatOptions.userToInvite && !OptionsListUtils.isCurrentUser(chatOptions.userToInvite)) { newSections.push({ title: undefined, - data: _.map([newChatOptions.userToInvite], (participant) => { + data: _.map([chatOptions.userToInvite], (participant) => { const isPolicyExpenseChat = lodashGet(participant, 'isPolicyExpenseChat', false); return isPolicyExpenseChat ? OptionsListUtils.getPolicyExpenseReportOption(participant) : OptionsListUtils.getParticipantsOption(participant, personalDetails); }), @@ -154,8 +177,8 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ }); } - return newSections; - }, [maxParticipantsReached, newChatOptions, participants, personalDetails, translate, searchTerm]); + return [newSections, chatOptions]; + }, [reports, personalDetails, betas, searchTerm, participants, iouType, iouRequestType, maxParticipantsReached, translate]); /** * Adds a single participant to the request @@ -213,48 +236,17 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ [participants, onParticipantsAdded], ); - const headerMessage = OptionsListUtils.getHeaderMessage( - newChatOptions.personalDetails.length + newChatOptions.recentReports.length !== 0, - Boolean(newChatOptions.userToInvite), - searchTerm.trim(), - maxParticipantsReached, - _.some(participants, (participant) => participant.searchText.toLowerCase().includes(searchTerm.trim().toLowerCase())), + const headerMessage = useMemo( + () => + OptionsListUtils.getHeaderMessage( + newChatOptions.personalDetails.length + newChatOptions.recentReports.length !== 0, + Boolean(newChatOptions.userToInvite), + searchTerm.trim(), + maxParticipantsReached, + _.some(participants, (participant) => participant.searchText.toLowerCase().includes(searchTerm.trim().toLowerCase())), + ), + [maxParticipantsReached, newChatOptions.personalDetails.length, newChatOptions.recentReports.length, newChatOptions.userToInvite, participants, searchTerm], ); - const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(personalDetails); - - useEffect(() => { - const chatOptions = OptionsListUtils.getFilteredOptions( - reports, - personalDetails, - betas, - searchTerm, - participants, - CONST.EXPENSIFY_EMAILS, - - // If we are using this component in the "Request money" flow then we pass the includeOwnedWorkspaceChats argument so that the current user - // sees the option to request money from their admin on their own Workspace Chat. - iouType === CONST.IOU.TYPE.REQUEST, - - // We don't want to include any P2P options like personal details or reports that are not workspace chats for certain features. - iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE, - false, - {}, - [], - false, - {}, - [], - - // We don't want the user to be able to invite individuals when they are in the "Distance request" flow for now. - // This functionality is being built here: https://github.com/Expensify/App/issues/23291 - iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE, - true, - ); - setNewChatOptions({ - recentReports: chatOptions.recentReports, - personalDetails: chatOptions.personalDetails, - userToInvite: chatOptions.userToInvite, - }); - }, [betas, reports, participants, personalDetails, translate, searchTerm, setNewChatOptions, iouType, iouRequestType]); // When search term updates we will fetch any reports const setSearchTermAndSearchInServer = useCallback((text = '') => { @@ -270,6 +262,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ const hasPolicyExpenseChatParticipant = _.some(participants, (participant) => participant.isPolicyExpenseChat); const shouldShowSplitBillErrorMessage = participants.length > 1 && hasPolicyExpenseChatParticipant; const isAllowedToSplit = iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE; + const referralContentType = iouType === CONST.IOU.TYPE.SEND ? CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY : CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST; const handleConfirmSelection = useCallback(() => { if (shouldShowSplitBillErrorMessage) { @@ -281,6 +274,32 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ const footerContent = ( + + { + Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(referralContentType)); + }} + style={[styles.p5, styles.w100, styles.br2, styles.highlightBG, styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter, {gap: 10}]} + accessibilityLabel="referral" + role={CONST.ACCESSIBILITY_ROLE.BUTTON} + > + + {translate(`referralProgram.${referralContentType}.buttonText1`)} + + {translate(`referralProgram.${referralContentType}.buttonText2`)} + + + + + + {shouldShowSplitBillErrorMessage && ( )} -