Skip to content

Commit

Permalink
Merge pull request #40370 from getusha/refactor-participant-selector-…
Browse files Browse the repository at this point in the history
…page

Refactor participant selector page
  • Loading branch information
mountiny authored Apr 19, 2024
2 parents 305f12c + cd1893a commit 9a115b6
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 133 deletions.
25 changes: 0 additions & 25 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type {ParamListBase, StackNavigationState} from '@react-navigation/native';
import {format} from 'date-fns';
import fastMerge from 'expensify-common/lib/fastMerge';
import Str from 'expensify-common/lib/str';
Expand Down Expand Up @@ -45,7 +44,6 @@ import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import * as UserUtils from '@libs/UserUtils';
import ViolationsUtils from '@libs/Violations/ViolationsUtils';
import type {NavigationPartialRoute} from '@navigation/types';
import type {IOUAction, IOUType} from '@src/CONST';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
Expand Down Expand Up @@ -357,28 +355,6 @@ function clearMoneyRequest(transactionID: string, skipConfirmation = false) {
Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, null);
}

/**
* Update money expense-related pages IOU type params
*/
function updateMoneyRequestTypeParams(routes: StackNavigationState<ParamListBase>['routes'] | NavigationPartialRoute[], newIouType: string, tab?: string) {
routes.forEach((route) => {
const tabList = [CONST.TAB_REQUEST.DISTANCE, CONST.TAB_REQUEST.MANUAL, CONST.TAB_REQUEST.SCAN] as string[];
if (!route.name.startsWith('Money_Request_') && !tabList.includes(route.name)) {
return;
}
const newParams: Record<string, unknown> = {iouType: newIouType};
if (route.name === 'Money_Request_Create') {
// Both screen and nested params are needed to properly update the nested tab navigator
newParams.params = {...newParams};
newParams.screen = tab;
}
Navigation.setParams(newParams, route.key ?? '');

// Recursively update nested expense tab params
updateMoneyRequestTypeParams(route.state?.routes ?? [], newIouType, tab);
});
}

// eslint-disable-next-line @typescript-eslint/naming-convention
function startMoneyRequest(iouType: ValueOf<typeof CONST.IOU.TYPE>, reportID: string, requestType?: IOURequestType, skipConfirmation = false) {
clearMoneyRequest(CONST.IOU.OPTIMISTIC_TRANSACTION_ID, skipConfirmation);
Expand Down Expand Up @@ -6061,6 +6037,5 @@ export {
updateMoneyRequestTag,
updateMoneyRequestTaxAmount,
updateMoneyRequestTaxRate,
updateMoneyRequestTypeParams,
};
export type {GPSPoint as GpsPoint, IOURequestType};
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,19 @@ function FloatingActionButtonAndPopover(
),
),
},
{
icon: Expensicons.Transfer,
text: translate('iou.splitExpense'),
onSelected: () =>
interceptAnonymousUser(() =>
IOU.startMoneyRequest(
CONST.IOU.TYPE.SPLIT,
// When starting to create a money request from the global FAB, there is not an existing report yet. A random optimistic reportID is generated and used
// for all of the routes in the creation flow.
ReportUtils.generateReportID(),
),
),
},
{
icon: Expensicons.Send,
text: translate('iou.paySomeone', {}),
Expand Down
20 changes: 5 additions & 15 deletions src/pages/iou/request/IOURequestStartPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {useFocusEffect, useNavigation} from '@react-navigation/native';
import {useFocusEffect} from '@react-navigation/native';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
Expand All @@ -11,7 +11,6 @@ import ScreenWrapper from '@components/ScreenWrapper';
import TabSelector from '@components/TabSelector/TabSelector';
import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
import usePrevious from '@hooks/usePrevious';
import useThemeStyles from '@hooks/useThemeStyles';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as IOUUtils from '@libs/IOUUtils';
Expand Down Expand Up @@ -60,7 +59,6 @@ function IOURequestStartPage({
}: IOURequestStartPageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const navigation = useNavigation();
const [isDraggingOver, setIsDraggingOver] = useState(false);
const tabTitles = {
[CONST.IOU.TYPE.REQUEST]: translate('iou.submitExpense'),
Expand All @@ -69,7 +67,6 @@ function IOURequestStartPage({
[CONST.IOU.TYPE.TRACK_EXPENSE]: translate('iou.trackExpense'),
};
const transactionRequestType = useRef(TransactionUtils.getRequestType(transaction));
const previousIOURequestType = usePrevious(transactionRequestType.current);
const {canUseP2PDistanceRequests} = usePermissions(iouType);
const isFromGlobalCreate = isEmptyObject(report?.reportID);

Expand Down Expand Up @@ -98,7 +95,7 @@ function IOURequestStartPage({

const isExpenseChat = ReportUtils.isPolicyExpenseChat(report);
const isExpenseReport = ReportUtils.isExpenseReport(report);
const shouldDisplayDistanceRequest = !!canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || isFromGlobalCreate;
const shouldDisplayDistanceRequest = (!!canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || isFromGlobalCreate) && iouType !== CONST.IOU.TYPE.SPLIT;

// Allow the user to submit the expense if we are submitting the expense in global menu or the report can create the exoense
const isAllowedToCreateRequest = isEmptyObject(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType);
Expand All @@ -108,17 +105,10 @@ function IOURequestStartPage({
};

const resetIOUTypeIfChanged = useCallback(
(newIouType: IOURequestType) => {
if (newIouType === previousIOURequestType) {
return;
}
if (iouType === CONST.IOU.TYPE.SPLIT && transaction?.isFromGlobalCreate) {
IOU.updateMoneyRequestTypeParams(navigation.getState()?.routes ?? [], CONST.IOU.TYPE.REQUEST, newIouType);
}
IOU.initMoneyRequest(reportID, policy, isFromGlobalCreate, newIouType);
transactionRequestType.current = newIouType;
(newIOUType: IOURequestType) => {
IOU.initMoneyRequest(reportID, policy, isFromGlobalCreate, newIOUType);
},
[policy, previousIOURequestType, reportID, isFromGlobalCreate, iouType, navigation, transaction?.isFromGlobalCreate],
[policy, reportID, isFromGlobalCreate],
);

if (!transaction?.transactionID) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@ import * as Illustrations from '@components/Icon/Illustrations';
import OfflineIndicator from '@components/OfflineIndicator';
import {usePersonalDetails} from '@components/OnyxProvider';
import {useOptionsList} from '@components/OptionListContextProvider';
import {PressableWithFeedback} from '@components/Pressable';
import ReferralProgramCTA from '@components/ReferralProgramCTA';
import SelectCircle from '@components/SelectCircle';
import SelectionList from '@components/SelectionList';
import UserListItem from '@components/SelectionList/UserListItem';
import InviteMemberListItem from '@components/SelectionList/InviteMemberListItem';
import useDebouncedState from '@hooks/useDebouncedState';
import useDismissedReferralBanners from '@hooks/useDismissedReferralBanners';
import useLocalize from '@hooks/useLocalize';
Expand Down Expand Up @@ -85,6 +83,8 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
const maxParticipantsReached = participants.length === CONST.REPORT.MAXIMUM_PARTICIPANTS;
const {isSmallScreenWidth} = useWindowDimensions();

const isIOUSplit = iouType === CONST.IOU.TYPE.SPLIT;

useEffect(() => {
Report.searchInServer(debouncedSearchTerm.trim());
}, [debouncedSearchTerm]);
Expand All @@ -109,7 +109,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF

// If we are using this component in the "Submit expense" flow then we pass the includeOwnedWorkspaceChats argument so that the current user
// sees the option to submit an expense from their admin on their own Workspace Chat.
iouType === CONST.IOU.TYPE.REQUEST && action !== CONST.IOU.ACTION.REQUEST,
(iouType === CONST.IOU.TYPE.REQUEST || iouType === CONST.IOU.TYPE.SPLIT) && action !== CONST.IOU.ACTION.REQUEST,

(canUseP2PDistanceRequests || iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE) && ![CONST.IOU.ACTION.CATEGORIZE, CONST.IOU.ACTION.SHARE].includes(action),
false,
Expand Down Expand Up @@ -322,40 +322,6 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
);
}, [handleConfirmSelection, participants.length, isDismissed, referralContentType, shouldShowSplitBillErrorMessage, styles, translate]);

const itemRightSideComponent = useCallback(
(item) => {
if (!isAllowedToSplit) {
return null;
}
if (item.isSelected) {
return (
<PressableWithFeedback
onPress={() => addParticipantToSelection(item)}
disabled={item.isDisabled}
role={CONST.ACCESSIBILITY_ROLE.CHECKBOX}
accessibilityLabel={CONST.ACCESSIBILITY_ROLE.CHECKBOX}
style={[styles.flexRow, styles.alignItemsCenter, styles.ml5, styles.optionSelectCircle]}
>
<SelectCircle
isChecked={item.isSelected}
selectCircleStyles={styles.ml0}
/>
</PressableWithFeedback>
);
}

return (
<Button
onPress={() => addParticipantToSelection(item)}
style={[styles.pl2]}
text={translate('iou.split')}
small
/>
);
},
[addParticipantToSelection, isAllowedToSplit, styles, translate],
);

const renderEmptyWorkspaceView = () => (
<>
<BlockingView
Expand Down Expand Up @@ -386,17 +352,17 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
<SelectionList
onConfirm={handleConfirmSelection}
sections={areOptionsInitialized ? sections : CONST.EMPTY_ARRAY}
ListItem={UserListItem}
ListItem={InviteMemberListItem}
textInputValue={searchTerm}
textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')}
textInputHint={offlineMessage}
onChangeText={setSearchTerm}
shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
onSelectRow={addSingleParticipant}
onSelectRow={(item) => (isIOUSplit ? addParticipantToSelection(item) : addSingleParticipant(item))}
footerContent={footerContent}
headerMessage={headerMessage}
showLoadingPlaceholder={!areOptionsInitialized || !didScreenTransitionEnd}
rightHandSideComponent={itemRightSideComponent}
canSelectMultiple={isIOUSplit && isAllowedToSplit}
/>
);
}
Expand Down
64 changes: 12 additions & 52 deletions src/pages/iou/request/step/IOURequestStepParticipants.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import {useNavigation} from '@react-navigation/native';
import lodashIsEqual from 'lodash/isEqual';
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
Expand Down Expand Up @@ -39,7 +37,6 @@ function IOURequestStepParticipants({
}: IOURequestStepParticipantsProps) {
const participants = transaction?.participants;
const {translate} = useLocalize();
const navigation = useNavigation();
const selectedReportID = useRef<string>(reportID);
const numberOfParticipants = useRef(participants?.length ?? 0);
const iouRequestType = TransactionUtils.getRequestType(transaction);
Expand Down Expand Up @@ -79,24 +76,6 @@ function IOURequestStepParticipants({
IOU.navigateToStartStepIfScanFileCannotBeRead(receiptFilename ?? '', receiptPath ?? '', () => {}, iouRequestType, iouType, transactionID, reportID, receiptType ?? '');
}, [receiptType, receiptPath, receiptFilename, iouRequestType, iouType, transactionID, reportID, action]);

const updateRouteParams = useCallback(() => {
const navigationState = navigation.getState();
if (!navigationState || !newIouType.current) {
return;
}
IOU.updateMoneyRequestTypeParams(navigationState.routes, newIouType.current);
}, [navigation]);

useEffect(() => {
if (!newIouType.current) {
return;
}
// Participants can be added as normal or split participants. We want to wait for the participants' data to be updated before
// updating the expense type route params reducing the overhead of the thread and preventing possible jitters in UI.
updateRouteParams();
newIouType.current = null;
}, [participants, updateRouteParams]);

const addParticipant = useCallback(
(val: Participant[], selectedIouType: IOUType) => {
const isSplit = selectedIouType === CONST.IOU.TYPE.SPLIT;
Expand All @@ -110,13 +89,6 @@ function IOURequestStepParticipants({
newIouType.current = CONST.IOU.TYPE.REQUEST;
}

// If the Onyx participants has the same items as the selected participants (val), Onyx won't update it
// thus this component won't rerender, so we can immediately update the route params.
if (newIouType.current && lodashIsEqual(participants, val)) {
updateRouteParams();
newIouType.current = null;
}

IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val);
numberOfParticipants.current = val.length;

Expand All @@ -130,33 +102,21 @@ function IOURequestStepParticipants({
// When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step.
selectedReportID.current = val[0]?.reportID ?? reportID;
},
[reportID, transactionID, iouType, participants, updateRouteParams],
[reportID, transactionID, iouType],
);

const goToNextStep = useCallback(
(selectedIouType: IOUType) => {
const isSplit = selectedIouType === CONST.IOU.TYPE.SPLIT;
let nextStepIOUType: IOUType = CONST.IOU.TYPE.REQUEST;

if (isSplit && iouType !== CONST.IOU.TYPE.REQUEST) {
nextStepIOUType = CONST.IOU.TYPE.SPLIT;
} else if (iouType === CONST.IOU.TYPE.SEND) {
nextStepIOUType = CONST.IOU.TYPE.SEND;
}

const isCategorizing = action === CONST.IOU.ACTION.CATEGORIZE;
const goToNextStep = useCallback(() => {
const isCategorizing = action === CONST.IOU.ACTION.CATEGORIZE;

IOU.setMoneyRequestTag(transactionID, '');
IOU.setMoneyRequestCategory(transactionID, '');
const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, nextStepIOUType, transactionID, selectedReportID.current || reportID);
if (isCategorizing) {
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, nextStepIOUType, transactionID, selectedReportID.current || reportID, iouConfirmationPageRoute));
} else {
Navigation.navigate(iouConfirmationPageRoute);
}
},
[iouType, transactionID, reportID, action],
);
IOU.setMoneyRequestTag(transactionID, '');
IOU.setMoneyRequestCategory(transactionID, '');
const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, iouType, transactionID, selectedReportID.current || reportID);
if (isCategorizing) {
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, transactionID, selectedReportID.current || reportID, iouConfirmationPageRoute));
} else {
Navigation.navigate(iouConfirmationPageRoute);
}
}, [iouType, transactionID, reportID, action]);

const navigateBack = useCallback(() => {
IOUUtils.navigateToStartMoneyRequestStep(iouRequestType, iouType, transactionID, reportID, action);
Expand Down

0 comments on commit 9a115b6

Please sign in to comment.