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
141 changes: 76 additions & 65 deletions src/components/BaseVacationDelegateSelectionComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import {View} from 'react-native';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useSearchSelector from '@hooks/useSearchSelector';
import usePersonalDetailSearchSelector from '@hooks/usePersonalDetailSearchSelector';
import useThemeStyles from '@hooks/useThemeStyles';
import {searchUserInServer} from '@libs/actions/Report';
import {formatPhoneNumber} from '@libs/LocalePhoneNumber';
import {getHeaderMessage} from '@libs/OptionsListUtils';
import {filterOption, getHeaderMessage} from '@libs/PersonalDetailOptionsListUtils';
import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
Expand Down Expand Up @@ -67,95 +67,107 @@ function BaseVacationDelegateSelectionComponent({
...additionalExcludeLogins,
};

const {searchTerm, debouncedSearchTerm, setSearchTerm, availableOptions, areOptionsInitialized, onListEndReached} = useSearchSelector({
const {searchTerm, debouncedSearchTerm, setSearchTerm, availableOptions, areOptionsInitialized} = usePersonalDetailSearchSelector({
selectionMode: CONST.SEARCH_SELECTOR.SELECTION_MODE_SINGLE,
maxRecentReportsToShow: CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW,
searchContext: CONST.SEARCH_SELECTOR.SEARCH_CONTEXT_GENERAL,
excludeLogins,
includeUserToInvite: true,
includeRecentReports: true,
getValidOptionsConfig: {
excludeLogins,
includeCurrentUser,
},
includeCurrentUser,
});

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

const sectionsList = [];

if (currentVacationDelegate && delegatePersonalDetails) {
sectionsList.push({
title: undefined,
sectionIndex: 0,
data: [
{
...delegatePersonalDetails,
text: delegatePersonalDetails?.displayName ?? currentVacationDelegate,
alternateText: delegatePersonalDetails?.login ?? currentVacationDelegate,
login: delegatePersonalDetails.login ?? currentVacationDelegate,
keyForList: `vacationDelegate-${delegatePersonalDetails.login}`,
isDisabled: false,
isSelected: true,
shouldShowSubscript: undefined,
icons: [
{
source: delegatePersonalDetails?.avatar ?? icons.FallbackAvatar,
name: formatPhoneNumber(delegatePersonalDetails?.login ?? ''),
type: CONST.ICON_TYPE_AVATAR,
id: delegatePersonalDetails?.accountID,
},
],
},
],
});
}

sectionsList.push({
title: translate('common.recents'),
sectionIndex: 1,
data: availableOptions.recentReports,
});
sectionsList.push({
title: translate('common.contacts'),
sectionIndex: 2,
data: availableOptions.personalDetails,
});

if (availableOptions.userToInvite) {
sectionsList.push({
title: undefined,
sectionIndex: 3,
data: [availableOptions.userToInvite],
});
}
const sectionsList = (() => {
const list = [];

const delegateOption =
currentVacationDelegate && delegatePersonalDetails
? {
...delegatePersonalDetails,
text: delegatePersonalDetails?.displayName ?? currentVacationDelegate,
alternateText: delegatePersonalDetails?.login ?? currentVacationDelegate,
login: delegatePersonalDetails.login ?? currentVacationDelegate,
keyForList: `vacationDelegate-${delegatePersonalDetails.login}`,
isDisabled: false,
isSelected: true,
shouldShowSubscript: undefined,
icons: [
{
source: delegatePersonalDetails?.avatar ?? icons.FallbackAvatar,
name: formatPhoneNumber(delegatePersonalDetails?.login ?? ''),
type: CONST.ICON_TYPE_AVATAR,
id: delegatePersonalDetails?.accountID,
},
],
}
: undefined;

// Only pin the current delegate when it matches the search term, mirroring how the hook filters the other sections
if (delegateOption && filterOption(delegateOption, debouncedSearchTerm)) {
list.push({
title: undefined,
sectionIndex: 0,
data: [delegateOption],
});
}

if (availableOptions.recentOptions.length) {
list.push({
title: translate('common.recents'),
sectionIndex: 1,
data: availableOptions.recentOptions,
});
}

if (availableOptions.personalDetails.length) {
list.push({
title: translate('common.contacts'),
sectionIndex: 2,
data: availableOptions.personalDetails,
});
}

if (availableOptions.userToInvite) {
list.push({
title: undefined,
sectionIndex: 3,
data: [availableOptions.userToInvite],
});
}

return list;
})();

const sections = sectionsList.map((section) => ({
...section,
data: (section.data ?? []).map((option) => ({
...option,
text: option.text ?? option.displayName ?? '',
text: option.text ?? '',
alternateText: option.alternateText ?? option.login ?? undefined,
keyForList: option.keyForList ?? '',
isDisabled: option.isDisabled ?? undefined,
isSelected: option.isSelected ?? undefined,
login: option.login ?? undefined,
shouldShowSubscript: option.shouldShowSubscript ?? undefined,
shouldShowSubscript: undefined,
})),
}));

const searchValue = debouncedSearchTerm.trim().toLowerCase();
const headerMessage = (() => {
if (sections.length > 0) {
return '';
}
return getHeaderMessage(translate, searchValue, countryCode);
})();

const textInputOptions = {
value: searchTerm,
onChangeText: setSearchTerm,
label: translate('selectionList.nameEmailOrPhoneNumber'),
headerMessage: getHeaderMessage(
(availableOptions.recentReports?.length || 0) + (availableOptions.personalDetails?.length || 0) !== 0,
!!availableOptions.userToInvite,
debouncedSearchTerm.trim(),
countryCode,
false,
),
headerMessage,
};

return (
Expand Down Expand Up @@ -186,7 +198,6 @@ function BaseVacationDelegateSelectionComponent({
textInputOptions={textInputOptions}
shouldShowLoadingPlaceholder={!areOptionsInitialized}
isLoadingNewOptions={!!isSearchingForReports}
onEndReached={onListEndReached}
shouldSingleExecuteRowSelect
shouldShowTextInput
/>
Expand Down
86 changes: 53 additions & 33 deletions src/pages/settings/Security/AddDelegate/AddDelegatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import UserListItem from '@components/SelectionList/ListItem/UserListItem';
import SelectionListWithSections from '@components/SelectionList/SelectionListWithSections';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useSearchSelector from '@hooks/useSearchSelector';
import usePersonalDetailSearchSelector from '@hooks/usePersonalDetailSearchSelector';
import useThemeStyles from '@hooks/useThemeStyles';
import {searchUserInServer} from '@libs/actions/Report';
import Navigation from '@libs/Navigation/Navigation';
import {getHeaderMessage} from '@libs/OptionsListUtils';
import type {OptionData} from '@libs/ReportUtils';
import {getHeaderMessage} from '@libs/PersonalDetailOptionsListUtils';
import type {OptionData} from '@libs/PersonalDetailOptionsListUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
Expand All @@ -33,47 +33,60 @@ function AddDelegatePage() {
{} as Record<string, boolean>,
) ?? {};

const {searchTerm, debouncedSearchTerm, setSearchTerm, availableOptions, areOptionsInitialized, setSelectedOptions, onListEndReached} = useSearchSelector({
const {searchTerm, debouncedSearchTerm, setSearchTerm, availableOptions, selectedNonExistingOptions, areOptionsInitialized, toggleSelection} = usePersonalDetailSearchSelector({
selectionMode: CONST.SEARCH_SELECTOR.SELECTION_MODE_SINGLE,
searchContext: CONST.SEARCH_SELECTOR.SEARCH_CONTEXT_GENERAL,
includeUserToInvite: true,
excludeLogins: {...CONST.EXPENSIFY_EMAILS_OBJECT, ...existingDelegates},
includeRecentReports: true,
maxRecentReportsToShow: CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW,
shouldKeepSelectedInAvailableOptions: true,
shouldUpdateSelectedOptionsOnSingleSelect: true,
});

const handleSelectRow = (option: OptionData) => {
setSelectedOptions([option]);
// toggleSelection would deselect an already-selected row on re-tap, so only select when it isn't selected yet
if (!option.isSelected) {
toggleSelection(option);
}
Navigation.navigate(ROUTES.SETTINGS_DELEGATE_ROLE.getRoute(option.login ?? ''));
};

const headerMessage = getHeaderMessage(
(availableOptions.recentReports?.length || 0) + (availableOptions.personalDetails?.length || 0) !== 0,
!!availableOptions.userToInvite,
debouncedSearchTerm,
countryCode,
);
const sectionsList = [
{
title: translate('common.recents'),
sectionIndex: 0,
data: availableOptions.recentReports,
},
{
title: translate('common.contacts'),
sectionIndex: 1,
data: availableOptions.personalDetails,
},
];
const sectionsList = (() => {
const list = [];
if (selectedNonExistingOptions.length > 0) {
list.push({
title: undefined,
sectionIndex: 0,
data: selectedNonExistingOptions,
});
}

if (availableOptions.recentOptions?.length) {
list.push({
title: translate('common.recents'),
sectionIndex: 1,
data: availableOptions.recentOptions,
});
}

if (availableOptions.personalDetails?.length) {
list.push({
title: translate('common.contacts'),
sectionIndex: 2,
data: availableOptions.personalDetails,
});
}

if (availableOptions.userToInvite) {
sectionsList.push({
sectionIndex: 2,
title: '',
data: [availableOptions.userToInvite],
});
}
if (availableOptions.userToInvite) {
list.push({
sectionIndex: 3,
title: '',
data: [availableOptions.userToInvite],
});
}

return list;
})();

const sections = sectionsList.map((section) => ({
...section,
Expand All @@ -84,10 +97,18 @@ function AddDelegatePage() {
keyForList: `${option.keyForList}-${index}`,
isDisabled: option.isDisabled ?? undefined,
login: option.login ?? undefined,
shouldShowSubscript: option.shouldShowSubscript ?? undefined,
shouldShowSubscript: undefined,
})),
}));

const searchValue = debouncedSearchTerm.trim().toLowerCase();
const headerMessage = (() => {
if (sections.length > 0) {
return '';
}
return getHeaderMessage(translate, searchValue, countryCode);
})();

useEffect(() => {
searchUserInServer(debouncedSearchTerm);
}, [debouncedSearchTerm]);
Expand Down Expand Up @@ -117,7 +138,6 @@ function AddDelegatePage() {
shouldShowLoadingPlaceholder={!areOptionsInitialized}
isLoadingNewOptions={!!isSearchingForReports}
shouldShowTextInput
onEndReached={onListEndReached}
/>
</View>
</DelegateNoAccessWrapper>
Expand Down
Loading
Loading