Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
85e40e5
Stop scroll jump on search filter participant selection
MelvinBot May 29, 2026
67b72ec
Remove unused selectedNonExistingOptions section
MelvinBot May 29, 2026
2d755e8
Fix: keep original sectionIndex after removing selected section
MelvinBot May 29, 2026
3a19cd5
Fix: stop scroll jump on chats filter selection
MelvinBot May 29, 2026
2bd5dcb
Add section for selected options not visible in Recents / Contacts
MelvinBot May 29, 2026
d6f84ac
Pin pre-selected items at top in long lists
MelvinBot May 29, 2026
633c81a
Filter pre-selected snapshot by search term
MelvinBot May 29, 2026
8eb5725
Extract useFrozenPreSelection hook and section helpers
MelvinBot May 30, 2026
93973db
Generalize excludeFrozenItems jsdoc
MelvinBot May 30, 2026
06b1f45
Simplify comments to be more human readable
MelvinBot May 30, 2026
60ebfe6
Replace 'checkmarks' with 'selection indicators' to fix spell check
MelvinBot May 30, 2026
4c7e679
Drop useMemo from useFrozenPreSelection in favor of React Compiler
MelvinBot May 30, 2026
f1bd017
Filter chats selector frozen/extra sections by search term + base thr…
MelvinBot May 30, 2026
0985494
Address 8 review findings: noResultsFound flash, stale hydration, thr…
MelvinBot May 30, 2026
764f4da
Simplify comments and add unit tests for frozen pre-selection
MelvinBot May 30, 2026
2bc9e42
Fix lint: replace 'as Option' casts with hoisted refs
MelvinBot May 30, 2026
d84c780
Drop manual memoization in favor of React Compiler
MelvinBot May 31, 2026
9074f5e
Stop clearing search input on selection in search filter selectors
MelvinBot May 31, 2026
4d7b979
Refactor useFrozenPreSelection to identify rows by keyForList
MelvinBot May 31, 2026
fe9d789
Refactor useFrozenPreSelection to sections-in / sections-out shape
MelvinBot May 31, 2026
2a77fa5
Simplify useFrozenPreSelection to sections + initialSelectedValues + …
MelvinBot May 31, 2026
a3c25bd
Use Set for frozenKeys + captured; drop redundant liveByKey/seen
MelvinBot May 31, 2026
a2b945b
Simplify frozenKeys capture to new Set(initialSelectedValues)
MelvinBot May 31, 2026
bf634e9
Collapse frozen-row collection and section filtering into single pass
MelvinBot May 31, 2026
0ebd9c7
Add shouldRenderPinned to useFrozenPreSelection
MelvinBot May 31, 2026
a7dc46e
Add tests covering shouldRenderPinned and util edge cases
MelvinBot May 31, 2026
7882376
Fix: pass currentUserAccountID to doesPersonalDetailMatchSearchTerm i…
MelvinBot May 31, 2026
5de35d3
Add optional getKey to useFrozenPreSelection, default to keyForList
MelvinBot May 31, 2026
38fbf7a
Dedupe extraSelectedOptions by accountID and login
MelvinBot May 31, 2026
246dd61
Remove (you) suffix from current user row
MelvinBot May 31, 2026
cfa6292
Revert unrelated test additions to SelectionListOrderUtilsTest
MelvinBot May 31, 2026
b8dc256
Revert "Remove (you) suffix from current user row"
MelvinBot May 31, 2026
8d837f7
Update comment wording for pinned rows in lazy-loaded list
MelvinBot May 31, 2026
5b43009
Simplify code comments for readability
MelvinBot May 31, 2026
22d1ef8
Reuse getKey in applyChanges to remove duplicate identifier logic
MelvinBot Jun 1, 2026
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
53 changes: 25 additions & 28 deletions src/components/Search/SearchFiltersChatsSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import React, {useEffect, useState} from 'react';
import {usePersonalDetails} from '@components/OnyxListItemProvider';
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 useFilteredOptions from '@hooks/useFilteredOptions';
import useFrozenPreSelection from '@hooks/useFrozenPreSelection';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import usePrivateIsArchivedMap from '@hooks/usePrivateIsArchivedMap';
Expand All @@ -14,9 +16,9 @@ import useScreenWrapperTransitionStatus from '@hooks/useScreenWrapperTransitionS
import useSortedActions from '@hooks/useSortedActions';
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
import {createOptionFromReport, filterAndOrderOptions, formatSectionsFromSearchTerm, getAlternateText, getSearchOptions} from '@libs/OptionsListUtils';
import {createOptionFromReport, filterAndOrderOptions, filterReports, getAlternateText, getSearchOptions} from '@libs/OptionsListUtils';
import type {Option} from '@libs/OptionsListUtils';
import type {OptionWithKey, SelectionListSections} from '@libs/OptionsListUtils/types';
import type {OptionWithKey, SearchOptionData} from '@libs/OptionsListUtils/types';
import type {OptionData} from '@libs/ReportUtils';
import Navigation from '@navigation/Navigation';
import {searchInServer} from '@userActions/Report';
Expand Down Expand Up @@ -105,35 +107,27 @@ function SearchFiltersChatsSelector({initialReportIDs, onFiltersUpdate, isScreen
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT,
});

const sections: SelectionListSections = [];
const selectedReportIDsSet = new Set(selectedReportIDs);
// Mark selected rows in place so the checkmark moves with the toggle without reordering the list.
const recentReportsWithSelection = chatOptions.recentReports.map((report) => (selectedReportIDsSet.has(report.reportID) ? getSelectedOptionData(report) : report));

// Selected reports that don't show up in Recents — surface them but respect the search term.
const visibleReportIDsSet = new Set(chatOptions.recentReports.map((report) => report.reportID));
const reportIDsMatchingSearch = cleanSearchTerm === '' ? null : new Set(filterReports(selectedOptions as SearchOptionData[], [cleanSearchTerm]).map((report) => report.reportID));
const matchesSearchTerm = (report: OptionData) => reportIDsMatchingSearch === null || reportIDsMatchingSearch.has(report.reportID);
const extraSelectedReports = selectedOptions.filter((report) => !visibleReportIDsSet.has(report.reportID) && matchesSearchTerm(report));

const baseSections: Array<Section<OptionData>> = [];
if (!isLoading) {
const formattedResults = formatSectionsFromSearchTerm(
cleanSearchTerm,
selectedOptions,
chatOptions.recentReports,
chatOptions.personalDetails,
privateIsArchivedMap,
currentUserAccountID,
allPolicies,
personalDetails,
false,
undefined,
reportAttributesDerived,
);

sections.push(formattedResults.section);

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

sections.push({
data: reportsFiltered,
sectionIndex: 1,
});
if (extraSelectedReports.length > 0) {
baseSections.push({data: extraSelectedReports, sectionIndex: 1});
}
baseSections.push({data: recentReportsWithSelection, sectionIndex: 2});
}
const noResultsFound = didScreenTransitionEnd && sections.at(0)?.data.length === 0 && sections.at(1)?.data.length === 0;

const sections = useFrozenPreSelection<OptionData>(baseSections, {initialSelectedValues: initialReportIDs, canCapture: !isLoading});

const noResultsFound = didScreenTransitionEnd && !isLoading && sections.every((section) => section.data.length === 0);
const headerMessage = noResultsFound ? translate('common.noResultsFound') : undefined;

useEffect(() => {
Expand Down Expand Up @@ -191,6 +185,9 @@ function SearchFiltersChatsSelector({initialReportIDs, onFiltersUpdate, isScreen
footerContent={footerContent}
canSelectMultiple
shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()}
shouldUpdateFocusedIndex
shouldPreventAutoScrollOnSelect
shouldClearInputOnSelect={false}
textInputOptions={textInputOptions}
isLoadingNewOptions={isLoadingNewOptions}
shouldShowLoadingPlaceholder={shouldShowLoadingPlaceholder}
Expand Down
Loading
Loading