Skip to content

Commit 05786d8

Browse files
authored
Merge pull request Expensify#81416 from software-mansion-labs/@zfurtak/migrate-SearchFiltersChatsSelector
Migrate `SearchFiltersParticipantsSelector` and `SearchFiltersChatsSelector`
2 parents 96846d8 + db49fdb commit 05786d8

7 files changed

Lines changed: 128 additions & 137 deletions

File tree

src/components/Search/SearchFiltersChatsSelector.tsx

Lines changed: 80 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import reportsSelector from '@selectors/Attributes';
2-
import React, {useCallback, useEffect, useMemo, useState} from 'react';
2+
import React, {useEffect, useState} from 'react';
33
import {usePersonalDetails} from '@components/OnyxListItemProvider';
44
import {useOptionsList} from '@components/OptionListContextProvider';
5-
// eslint-disable-next-line no-restricted-imports
6-
import SelectionList from '@components/SelectionListWithSections';
7-
import InviteMemberListItem from '@components/SelectionListWithSections/InviteMemberListItem';
5+
import InviteMemberListItem from '@components/SelectionList/ListItem/InviteMemberListItem';
6+
import SelectionListWithSections from '@components/SelectionList/SelectionListWithSections';
87
import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet';
98
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
109
import useDebouncedState from '@hooks/useDebouncedState';
@@ -13,7 +12,8 @@ import useOnyx from '@hooks/useOnyx';
1312
import useScreenWrapperTransitionStatus from '@hooks/useScreenWrapperTransitionStatus';
1413
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
1514
import {createOptionFromReport, filterAndOrderOptions, formatSectionsFromSearchTerm, getAlternateText, getSearchOptions} from '@libs/OptionsListUtils';
16-
import type {Option, Section} from '@libs/OptionsListUtils';
15+
import type {Option} from '@libs/OptionsListUtils';
16+
import type {OptionWithKey, SelectionListSections} from '@libs/OptionsListUtils/types';
1717
import type {OptionData} from '@libs/ReportUtils';
1818
import Navigation from '@navigation/Navigation';
1919
import {searchInServer} from '@userActions/Report';
@@ -59,53 +59,44 @@ function SearchFiltersChatsSelector({initialReportIDs, onFiltersUpdate, isScreen
5959
const [reportAttributesDerived] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {canBeMissing: true, selector: reportsSelector});
6060
const [selectedReportIDs, setSelectedReportIDs] = useState<string[]>(initialReportIDs);
6161
const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
62-
const cleanSearchTerm = useMemo(() => searchTerm.trim().toLowerCase(), [searchTerm]);
62+
const cleanSearchTerm = searchTerm.trim().toLowerCase();
6363
const [draftComments] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT, {canBeMissing: true});
6464
const archivedReportsIdSet = useArchivedReportsIdSet();
6565
const [nvpDismissedProductTraining] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {canBeMissing: true});
6666

67-
const selectedOptions = useMemo<OptionData[]>(() => {
68-
return selectedReportIDs.map((id) => {
69-
const report = getSelectedOptionData(
70-
createOptionFromReport({...reports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`], reportID: id}, personalDetails, currentUserAccountID, reportAttributesDerived),
71-
);
72-
const isReportArchived = archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`);
73-
const alternateText = getAlternateText(report, {}, isReportArchived, currentUserAccountID, {});
74-
return {...report, alternateText};
75-
});
76-
}, [archivedReportsIdSet, personalDetails, reportAttributesDerived, reports, selectedReportIDs, currentUserAccountID]);
77-
78-
const defaultOptions = useMemo(() => {
79-
if (!areOptionsInitialized || !isScreenTransitionEnd) {
80-
return defaultListOptions;
81-
}
82-
return getSearchOptions({
83-
options,
84-
draftComments,
85-
nvpDismissedProductTraining,
86-
betas: undefined,
87-
isUsedInChatFinder: false,
88-
countryCode,
89-
loginList,
90-
currentUserAccountID,
91-
currentUserEmail,
92-
personalDetails,
93-
});
94-
}, [areOptionsInitialized, isScreenTransitionEnd, options, draftComments, nvpDismissedProductTraining, countryCode, loginList, currentUserAccountID, currentUserEmail, personalDetails]);
67+
const selectedOptions: OptionData[] = selectedReportIDs.map((id) => {
68+
const report = getSelectedOptionData(
69+
createOptionFromReport({...reports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`], reportID: id}, personalDetails, currentUserAccountID, reportAttributesDerived),
70+
);
71+
const isReportArchived = archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`);
72+
const alternateText = getAlternateText(report, {}, isReportArchived, currentUserAccountID, {});
73+
return {...report, alternateText};
74+
});
9575

96-
const chatOptions = useMemo(() => {
97-
return filterAndOrderOptions(defaultOptions, cleanSearchTerm, countryCode, loginList, currentUserEmail, currentUserAccountID, personalDetails, {
98-
selectedOptions,
99-
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT,
100-
});
101-
}, [defaultOptions, cleanSearchTerm, countryCode, loginList, selectedOptions, currentUserAccountID, currentUserEmail, personalDetails]);
76+
const defaultOptions =
77+
!areOptionsInitialized || !isScreenTransitionEnd
78+
? defaultListOptions
79+
: getSearchOptions({
80+
options,
81+
draftComments,
82+
nvpDismissedProductTraining,
83+
betas: undefined,
84+
isUsedInChatFinder: false,
85+
countryCode,
86+
loginList,
87+
currentUserAccountID,
88+
currentUserEmail,
89+
personalDetails,
90+
});
91+
92+
const chatOptions = filterAndOrderOptions(defaultOptions, cleanSearchTerm, countryCode, loginList, currentUserEmail, currentUserAccountID, personalDetails, {
93+
selectedOptions,
94+
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT,
95+
});
10296

103-
const {sections, headerMessage} = useMemo(() => {
104-
const newSections: Section[] = [];
105-
if (!areOptionsInitialized) {
106-
return {sections: [], headerMessage: undefined};
107-
}
97+
const sections: SelectionListSections = [];
10898

99+
if (areOptionsInitialized) {
109100
const formattedResults = formatSectionsFromSearchTerm(
110101
cleanSearchTerm,
111102
selectedOptions,
@@ -118,102 +109,80 @@ function SearchFiltersChatsSelector({initialReportIDs, onFiltersUpdate, isScreen
118109
reportAttributesDerived,
119110
);
120111

121-
newSections.push(formattedResults.section);
112+
sections.push(formattedResults.section);
122113

123114
const visibleReportsWhenSearchTermNonEmpty = chatOptions.recentReports.map((report) => (selectedReportIDs.includes(report.reportID) ? getSelectedOptionData(report) : report));
124115
const visibleReportsWhenSearchTermEmpty = chatOptions.recentReports.filter((report) => !selectedReportIDs.includes(report.reportID));
125116
const reportsFiltered = cleanSearchTerm === '' ? visibleReportsWhenSearchTermEmpty : visibleReportsWhenSearchTermNonEmpty;
126117

127-
newSections.push({
118+
sections.push({
128119
title: undefined,
129120
data: reportsFiltered,
130-
shouldShow: chatOptions.recentReports.length > 0,
121+
sectionIndex: 1,
131122
});
132-
133-
const areResultsFound = didScreenTransitionEnd && formattedResults.section.data.length === 0 && reportsFiltered.length === 0;
134-
const message = areResultsFound ? translate('common.noResultsFound') : undefined;
135-
136-
return {
137-
sections: newSections,
138-
headerMessage: message,
139-
};
140-
}, [
141-
areOptionsInitialized,
142-
chatOptions.personalDetails,
143-
chatOptions.recentReports,
144-
cleanSearchTerm,
145-
didScreenTransitionEnd,
146-
personalDetails,
147-
reportAttributesDerived,
148-
selectedOptions,
149-
selectedReportIDs,
150-
translate,
151-
currentUserAccountID,
152-
]);
123+
}
124+
const noResultsFound = didScreenTransitionEnd && sections.at(0)?.data.length === 0 && sections.at(1)?.data.length === 0;
125+
const headerMessage = noResultsFound ? translate('common.noResultsFound') : undefined;
153126

154127
useEffect(() => {
155128
searchInServer(debouncedSearchTerm.trim());
156129
}, [debouncedSearchTerm]);
157130

158-
const handleParticipantSelection = useCallback(
159-
(selectedOption: Option) => {
160-
const optionReportID = selectedOption.reportID;
161-
if (!optionReportID) {
162-
return;
163-
}
164-
const foundOptionIndex = selectedReportIDs.findIndex((reportID: string) => {
165-
return reportID && reportID !== '' && selectedOption.reportID === reportID;
166-
});
167-
168-
if (foundOptionIndex < 0) {
169-
setSelectedReportIDs([...selectedReportIDs, optionReportID]);
170-
} else {
171-
const newSelectedReports = [...selectedReportIDs.slice(0, foundOptionIndex), ...selectedReportIDs.slice(foundOptionIndex + 1)];
172-
setSelectedReportIDs(newSelectedReports);
173-
}
174-
},
175-
[selectedReportIDs],
176-
);
131+
const handleParticipantSelection = (selectedOption: OptionWithKey) => {
132+
const optionReportID = selectedOption.reportID;
133+
if (!optionReportID) {
134+
return;
135+
}
136+
const foundOptionIndex = selectedReportIDs.findIndex((reportID: string) => {
137+
return reportID && reportID !== '' && selectedOption.reportID === reportID;
138+
});
177139

178-
const applyChanges = useCallback(() => {
140+
if (foundOptionIndex < 0) {
141+
setSelectedReportIDs([...selectedReportIDs, optionReportID]);
142+
} else {
143+
const newSelectedReports = [...selectedReportIDs.slice(0, foundOptionIndex), ...selectedReportIDs.slice(foundOptionIndex + 1)];
144+
setSelectedReportIDs(newSelectedReports);
145+
}
146+
};
147+
148+
const applyChanges = () => {
179149
onFiltersUpdate(selectedReportIDs);
180150
Navigation.goBack(ROUTES.SEARCH_ADVANCED_FILTERS.getRoute());
181-
}, [onFiltersUpdate, selectedReportIDs]);
151+
};
182152

183-
const resetChanges = useCallback(() => {
153+
const resetChanges = () => {
184154
setSelectedReportIDs([]);
185-
}, []);
186-
187-
const footerContent = useMemo(
188-
() => (
189-
<SearchFilterPageFooterButtons
190-
applyChanges={applyChanges}
191-
resetChanges={resetChanges}
192-
/>
193-
),
194-
[resetChanges, applyChanges],
155+
};
156+
157+
const footerContent = (
158+
<SearchFilterPageFooterButtons
159+
applyChanges={applyChanges}
160+
resetChanges={resetChanges}
161+
/>
195162
);
196163

197164
const isLoadingNewOptions = !!isSearchingForReports;
198165
const showLoadingPlaceholder = !didScreenTransitionEnd || !areOptionsInitialized || !initialReportIDs || !personalDetails;
199166

167+
const textInputOptions = {
168+
value: searchTerm,
169+
label: translate('selectionList.nameEmailOrPhoneNumber'),
170+
onChangeText: setSearchTerm,
171+
headerMessage,
172+
};
173+
200174
return (
201-
<SelectionList
202-
canSelectMultiple
175+
<SelectionListWithSections
203176
sections={sections}
177+
onSelectRow={handleParticipantSelection}
204178
ListItem={InviteMemberListItem}
205-
textInputLabel={translate('selectionList.nameEmailOrPhoneNumber')}
206-
headerMessage={headerMessage}
207-
textInputValue={searchTerm}
208179
footerContent={footerContent}
209-
showScrollIndicator
180+
canSelectMultiple
210181
shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()}
211-
onChangeText={(value) => {
212-
setSearchTerm(value);
213-
}}
214-
onSelectRow={handleParticipantSelection}
182+
textInputOptions={textInputOptions}
215183
isLoadingNewOptions={isLoadingNewOptions}
216184
showLoadingPlaceholder={showLoadingPlaceholder}
185+
shouldShowTextInput
217186
/>
218187
);
219188
}

src/components/Search/SearchFiltersParticipantsSelector.tsx

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ import reportsSelector from '@selectors/Attributes';
22
import React, {useCallback, useEffect, useMemo, useState} from 'react';
33
import {usePersonalDetails} from '@components/OnyxListItemProvider';
44
import {useOptionsList} from '@components/OptionListContextProvider';
5-
// eslint-disable-next-line no-restricted-imports
6-
import SelectionList from '@components/SelectionListWithSections';
7-
import UserSelectionListItem from '@components/SelectionListWithSections/Search/UserSelectionListItem';
5+
import UserSelectionListItem from '@components/SelectionList/ListItem/UserSelectionListItem';
6+
import SelectionListWithSections from '@components/SelectionList/SelectionListWithSections';
87
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
98
import useLocalize from '@hooks/useLocalize';
109
import useOnyx from '@hooks/useOnyx';
1110
import useScreenWrapperTransitionStatus from '@hooks/useScreenWrapperTransitionStatus';
1211
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
1312
import memoize from '@libs/memoize';
1413
import {filterAndOrderOptions, filterSelectedOptions, formatSectionsFromSearchTerm, getFilteredRecentAttendees, getValidOptions} from '@libs/OptionsListUtils';
15-
import type {Option, Section} from '@libs/OptionsListUtils';
14+
import type {Option} from '@libs/OptionsListUtils';
15+
import type {SelectionListSections} from '@libs/OptionsListUtils/types';
1616
import type {OptionData} from '@libs/ReportUtils';
1717
import {getDisplayNameForParticipant} from '@libs/ReportUtils';
1818
import Navigation from '@navigation/Navigation';
@@ -193,7 +193,7 @@ function SearchFiltersParticipantsSelector({initialAccountIDs, onFiltersUpdate,
193193
}, [unselectedOptions, cleanSearchTerm, countryCode, loginList, selectedOptions, shouldAllowNameOnlyOptions, searchTerm, currentUserEmail, currentUserAccountID, personalDetails]);
194194

195195
const {sections, headerMessage} = useMemo(() => {
196-
const newSections: Section[] = [];
196+
const newSections: SelectionListSections = [];
197197
if (!areOptionsInitialized) {
198198
return {sections: [], headerMessage: undefined};
199199
}
@@ -231,7 +231,7 @@ function SearchFiltersParticipantsSelector({initialAccountIDs, onFiltersUpdate,
231231
newSections.push({
232232
title: '',
233233
data: [chatOptions.currentUserOption],
234-
shouldShow: true,
234+
sectionIndex: 0,
235235
});
236236
}
237237

@@ -247,13 +247,13 @@ function SearchFiltersParticipantsSelector({initialAccountIDs, onFiltersUpdate,
247247
newSections.push({
248248
title: '',
249249
data: filteredRecentReports,
250-
shouldShow: filteredRecentReports.length > 0,
250+
sectionIndex: 1,
251251
});
252252

253253
newSections.push({
254254
title: '',
255255
data: chatOptions.personalDetails,
256-
shouldShow: chatOptions.personalDetails.length > 0,
256+
sectionIndex: 2,
257257
});
258258

259259
const noResultsFound = chatOptions.personalDetails.length === 0 && chatOptions.recentReports.length === 0 && !chatOptions.currentUserOption;
@@ -406,23 +406,28 @@ function SearchFiltersParticipantsSelector({initialAccountIDs, onFiltersUpdate,
406406
const isLoadingNewOptions = !!isSearchingForReports;
407407
const showLoadingPlaceholder = !didScreenTransitionEnd || !areOptionsInitialized || !initialAccountIDs || !personalDetails;
408408

409+
const textInputOptions = useMemo(
410+
() => ({
411+
value: searchTerm,
412+
label: translate('selectionList.nameEmailOrPhoneNumber'),
413+
onChangeText: setSearchTerm,
414+
headerMessage,
415+
}),
416+
[searchTerm, translate, setSearchTerm, headerMessage],
417+
);
418+
409419
return (
410-
<SelectionList
411-
canSelectMultiple
420+
<SelectionListWithSections
412421
sections={sections}
413422
ListItem={UserSelectionListItem}
414-
textInputLabel={translate('selectionList.nameEmailOrPhoneNumber')}
415-
headerMessage={headerMessage}
416-
textInputValue={searchTerm}
423+
textInputOptions={textInputOptions}
424+
shouldShowTextInput
417425
footerContent={footerContent}
418-
showScrollIndicator
419426
shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()}
420-
onChangeText={(value) => {
421-
setSearchTerm(value);
422-
}}
423427
onSelectRow={handleParticipantSelection}
424428
isLoadingNewOptions={isLoadingNewOptions}
425429
showLoadingPlaceholder={showLoadingPlaceholder}
430+
canSelectMultiple
426431
/>
427432
);
428433
}

0 commit comments

Comments
 (0)