Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
236 changes: 58 additions & 178 deletions src/pages/iou/request/MoneyRequestAccountantSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,35 @@ import lodashPick from 'lodash/pick';
import React, {memo, useCallback, useEffect, useMemo} from 'react';
import type {GestureResponderEvent} from 'react-native';
import EmptySelectionListContent from '@components/EmptySelectionListContent';
import {usePersonalDetails} from '@components/OnyxListItemProvider';
import {useOptionsList} from '@components/OptionListContextProvider';
import InviteMemberListItem from '@components/SelectionList/ListItem/InviteMemberListItem';
import SelectionListWithSections from '@components/SelectionList/SelectionListWithSections';
import type {Section} from '@components/SelectionList/SelectionListWithSections/types';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useDebouncedState from '@hooks/useDebouncedState';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import usePrivateIsArchivedMap from '@hooks/usePrivateIsArchivedMap';
import useReportAttributes from '@hooks/useReportAttributes';
import usePersonalDetailOptions from '@hooks/usePersonalDetailOptions';
import useScreenWrapperTransitionStatus from '@hooks/useScreenWrapperTransitionStatus';
import useUserToInviteReports from '@hooks/useUserToInviteReports';
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
import memoize from '@libs/memoize';
import {
filterAndOrderOptions,
formatSectionsFromSearchTerm,
getEmptyOptions,
getHeaderMessage,
getParticipantsOption,
getPolicyExpenseReportOption,
getValidOptions,
isCurrentUser,
orderOptions,
} from '@libs/OptionsListUtils';
import type {SelectionListSections} from '@libs/OptionsListUtils/types';
import {getHeaderMessage, getValidOptions} from '@libs/PersonalDetailOptionsListUtils';
import type {OptionData} from '@libs/PersonalDetailOptionsListUtils/types';
import {searchUserInServer} from '@userActions/Report';
import type {IOUAction, IOUType} from '@src/CONST';
import type {IOUType} from '@src/CONST';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Accountant} from '@src/types/onyx/IOU';

const memoizedGetValidOptions = memoize(getValidOptions, {maxSize: 5, monitoringName: 'MoneyRequestAccountantSelector.getValidOptions'});

const defaultListOptions = {
userToInvite: null,
recentOptions: [] as OptionData[],
personalDetails: [] as OptionData[],
selectedOptions: [] as OptionData[],
};

type MoneyRequestAccountantSelectorProps = {
/** Callback to request parent modal to go to next step */
onFinish: (value?: string) => void;
Expand All @@ -46,196 +40,71 @@ type MoneyRequestAccountantSelectorProps = {

/** The type of IOU report, i.e. split, request, send, track */
iouType: IOUType;

/** The action of the IOU, i.e. create, split, move */
action: IOUAction;
};

function MoneyRequestAccountantSelector({onFinish, onAccountantSelected, iouType, action}: MoneyRequestAccountantSelectorProps) {
const {translate} = useLocalize();
function MoneyRequestAccountantSelector({onFinish, onAccountantSelected, iouType}: MoneyRequestAccountantSelectorProps) {
const {translate, formatPhoneNumber} = useLocalize();
const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
const {isOffline} = useNetwork();
const personalDetails = usePersonalDetails();
const {didScreenTransitionEnd} = useScreenWrapperTransitionStatus();
const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE);
const [betas] = useOnyx(ONYXKEYS.BETAS);
const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS);
const {options, areOptionsInitialized} = useOptionsList({
shouldInitialize: didScreenTransitionEnd,
});
const offlineMessage: string = isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : '';
const reportAttributesDerived = useReportAttributes();
const [draftComments] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT);
const [nvpDismissedProductTraining] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING);
const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST);
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const currentUserEmail = currentUserPersonalDetails.email ?? '';
const currentUserAccountID = currentUserPersonalDetails.accountID;
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
const privateIsArchivedMap = usePrivateIsArchivedMap();
const {options: personalDetailOptions} = usePersonalDetailOptions({enabled: didScreenTransitionEnd});
const areOptionsInitialized = (personalDetailOptions?.length ?? 0) > 0;
Comment thread
shubham1206agra marked this conversation as resolved.
const offlineMessage: string = isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : '';

useEffect(() => {
searchUserInServer(debouncedSearchTerm.trim());
}, [debouncedSearchTerm]);

const defaultOptions = useMemo(() => {
const optionsList = useMemo(() => {
if (!areOptionsInitialized || !didScreenTransitionEnd) {
getEmptyOptions();
return defaultListOptions;
}

const optionList = memoizedGetValidOptions(
{
reports: options.reports,
personalDetails: options.personalDetails,
},
allPolicies,
draftComments,
nvpDismissedProductTraining,
loginList,
currentUserAccountID,
currentUserEmail,
conciergeReportID,
{
betas,
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT,
action,
personalDetails,
countryCode,
},
);

const orderedOptions = orderOptions(optionList);

return {
...optionList,
...orderedOptions,
};
}, [
areOptionsInitialized,
didScreenTransitionEnd,
options.reports,
options.personalDetails,
allPolicies,
draftComments,
nvpDismissedProductTraining,
loginList,
betas,
action,
countryCode,
currentUserAccountID,
currentUserEmail,
conciergeReportID,
personalDetails,
]);

const chatOptions = useMemo(() => {
if (!areOptionsInitialized) {
return {
userToInvite: null,
recentReports: [],
personalDetails: [],
currentUserOption: null,
headerMessage: '',
};
}
const newOptions = filterAndOrderOptions(defaultOptions, debouncedSearchTerm, countryCode, loginList, currentUserEmail, currentUserAccountID, personalDetails, {
return memoizedGetValidOptions(personalDetailOptions ?? [], currentUserEmail, formatPhoneNumber, countryCode, loginList, {
Comment thread
shubham1206agra marked this conversation as resolved.
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT,
maxRecentReportsToShow: CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW,
searchString: debouncedSearchTerm,
includeUserToInvite: true,
});
return newOptions;
}, [areOptionsInitialized, defaultOptions, debouncedSearchTerm, countryCode, loginList, currentUserAccountID, currentUserEmail, personalDetails]);

const {userToInviteExpenseReport} = useUserToInviteReports(chatOptions?.userToInvite);
const userToInviteExpenseReportPolicy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${userToInviteExpenseReport?.policyID}`];
}, [areOptionsInitialized, didScreenTransitionEnd, personalDetailOptions, currentUserEmail, formatPhoneNumber, countryCode, loginList, debouncedSearchTerm]);

/**
* Returns the sections needed for the OptionsSelector
*/
const [sections, header] = useMemo(() => {
const newSections: SelectionListSections = [];
const sections = useMemo(() => {
const newSections: Array<Section<OptionData>> = [];
if (!areOptionsInitialized || !didScreenTransitionEnd) {
return [newSections, ''];
return newSections;
}
const fiveRecents = [...chatOptions.recentReports].slice(0, 5);
const restOfRecents = [...chatOptions.recentReports].slice(5);
const contactsWithRestOfRecents = [...restOfRecents, ...chatOptions.personalDetails];

const formatResults = formatSectionsFromSearchTerm(
debouncedSearchTerm,
[],
chatOptions.recentReports,
chatOptions.personalDetails,
privateIsArchivedMap,
currentUserAccountID,
allPolicies,
personalDetails,
true,
undefined,
reportAttributesDerived,
);
newSections.push({...formatResults.section, sectionIndex: 0});

newSections.push({
title: translate('common.recents'),
data: fiveRecents,
sectionIndex: 1,
});
let sectionIndex = 0;

newSections.push({
title: translate('common.contacts'),
data: contactsWithRestOfRecents,
sectionIndex: 2,
});
if (optionsList.recentOptions.length > 0) {
newSections.push({
title: translate('common.recents'),
data: optionsList.recentOptions,
sectionIndex: sectionIndex++,
});
}

if (
chatOptions.userToInvite &&
!isCurrentUser(
{...chatOptions.userToInvite, accountID: chatOptions.userToInvite?.accountID ?? CONST.DEFAULT_NUMBER_ID, status: chatOptions.userToInvite?.status ?? undefined},
loginList,
currentUserEmail,
)
) {
if (optionsList.personalDetails.length > 0) {
newSections.push({
data: [chatOptions.userToInvite].map((participant) => {
const isPolicyExpenseChat = participant?.isPolicyExpenseChat ?? false;
const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${userToInviteExpenseReport?.reportID}`];
return isPolicyExpenseChat
? getPolicyExpenseReportOption(participant, privateIsArchived, personalDetails, userToInviteExpenseReport, userToInviteExpenseReportPolicy, reportAttributesDerived)
: getParticipantsOption(participant, personalDetails);
}),
sectionIndex: 3,
title: translate('common.contacts'),
data: optionsList.personalDetails,
sectionIndex: sectionIndex++,
});
}

const headerMessage = getHeaderMessage(
(chatOptions.personalDetails ?? []).length + (chatOptions.recentReports ?? []).length !== 0,
!!chatOptions?.userToInvite,
debouncedSearchTerm.trim(),
countryCode,
false,
);
if (optionsList.userToInvite) {
newSections.push({
data: [optionsList.userToInvite],
sectionIndex: sectionIndex++,
});
}

return [newSections, headerMessage];
}, [
areOptionsInitialized,
didScreenTransitionEnd,
chatOptions.recentReports,
chatOptions.personalDetails,
chatOptions.userToInvite,
debouncedSearchTerm,
personalDetails,
userToInviteExpenseReport,
userToInviteExpenseReportPolicy,
reportAttributesDerived,
translate,
loginList,
countryCode,
privateIsArchivedMap,
currentUserAccountID,
currentUserEmail,
allPolicies,
]);
return newSections;
}, [areOptionsInitialized, didScreenTransitionEnd, optionsList.recentOptions, optionsList.personalDetails, optionsList.userToInvite, translate]);

const selectAccountant = useCallback(
(option: Accountant) => {
Expand All @@ -256,6 +125,17 @@ function MoneyRequestAccountantSelector({onFinish, onAccountantSelected, iouType
[selectAccountant],
);

const getHeaderMessageText = () => {
if (sections.length > 0) {
return '';
}
const searchValue = debouncedSearchTerm.trim().toLowerCase();
if (CONST.EXPENSIFY_EMAILS_OBJECT[searchValue]) {
return translate('messages.errorMessageInvalidEmail');
}
return getHeaderMessage(translate, debouncedSearchTerm, countryCode);
};

const shouldShowLoadingPlaceholder = useMemo(() => !areOptionsInitialized || !didScreenTransitionEnd, [areOptionsInitialized, didScreenTransitionEnd]);

const optionLength = useMemo(() => {
Expand All @@ -271,7 +151,7 @@ function MoneyRequestAccountantSelector({onFinish, onAccountantSelected, iouType
value: searchTerm,
label: translate('selectionList.nameEmailOrPhoneNumber'),
onChangeText: setSearchTerm,
headerMessage: header,
headerMessage: getHeaderMessageText(),
hint: offlineMessage,
};

Expand Down
1 change: 0 additions & 1 deletion src/pages/iou/request/step/IOURequestStepAccountant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ function IOURequestStepAccountant({
onFinish={navigateToNextStep}
onAccountantSelected={setAccountant}
iouType={iouType}
action={action}
/>
</StepScreenWrapper>
);
Expand Down
Loading