Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
30 changes: 21 additions & 9 deletions src/components/Search/FilterDropdowns/UserSelectPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus';
import memoize from '@libs/memoize';
import type {Option, Section} from '@libs/OptionsListUtils';
import {filterAndOrderOptions, getValidOptions} from '@libs/OptionsListUtils';
import type {OptionData} from '@libs/ReportUtils';
Expand All @@ -22,6 +23,8 @@ function getSelectedOptionData(option: Option) {
return {...option, reportID: `${option.reportID}`, selected: true};
}

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

type UserSelectPopupProps = {
/** The currently selected users */
value: string[];
Expand Down Expand Up @@ -63,28 +66,29 @@ function UserSelectPopup({value, closeOverlay, onChange}: UserSelectPopupProps)

const cleanSearchTerm = searchTerm.trim().toLowerCase();

// Get a list of all options/personal details and filter them by the current search term
const listData = useMemo(() => {
const optionsList = getValidOptions(
const optionsList = useMemo(() => {
return memoizedGetValidOptions(
{
reports: options.reports,
personalDetails: options.personalDetails,
},
{
selectedOptions,
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT,
includeSelectedOptions: true,
includeCurrentUser: true,
},
);
}, [options.reports, options.personalDetails]);

const {personalDetails: filteredOptionsList, recentReports} = filterAndOrderOptions(optionsList, cleanSearchTerm, {
const filteredOptions = useMemo(() => {
return filterAndOrderOptions(optionsList, cleanSearchTerm, {
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT,
maxRecentReportsToShow: CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW,
canInviteUser: false,
});
}, [optionsList, cleanSearchTerm]);

const personalDetailList = filteredOptionsList
const listData = useMemo(() => {
const personalDetailList = filteredOptions.personalDetails
.map((participant) => ({
...participant,
isSelected: selectedOptions.some((selectedOption) => selectedOption.accountID === participant.accountID),
Expand All @@ -100,8 +104,16 @@ function UserSelectPopup({value, closeOverlay, onChange}: UserSelectPopupProps)
return 0;
});

return [...(personalDetailList ?? []), ...(recentReports ?? [])];
}, [cleanSearchTerm, options.personalDetails, options.reports, selectedOptions, accountID]);
const recentReportsList = filteredOptions.recentReports.map((report) => {
const isSelected = selectedOptions.some((selectedOption) => selectedOption.reportID === report.reportID);
return {
...report,
isSelected,
};
});

return [...personalDetailList, ...recentReportsList];
}, [filteredOptions, selectedOptions, accountID]);

const {sections, headerMessage} = useMemo(() => {
const newSections: Section[] = [
Expand Down
18 changes: 12 additions & 6 deletions src/components/Search/SearchFiltersParticipantsSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useScreenWrapperTransitionStatus from '@hooks/useScreenWrapperTransitionStatus';
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
import {filterAndOrderOptions, formatSectionsFromSearchTerm, getValidOptions} from '@libs/OptionsListUtils';
import memoize from '@libs/memoize';
import {filterAndOrderOptions, filterSelectedOptions, formatSectionsFromSearchTerm, getValidOptions} from '@libs/OptionsListUtils';
import type {Option, Section} from '@libs/OptionsListUtils';
import type {OptionData} from '@libs/ReportUtils';
import {getDisplayNameForParticipant} from '@libs/ReportUtils';
Expand All @@ -25,6 +26,8 @@ const defaultListOptions = {
headerMessage: '',
};

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

function getSelectedOptionData(option: Option): OptionData {
// eslint-disable-next-line rulesdir/no-default-id-values
return {...option, selected: true, reportID: option.reportID ?? '-1'};
Expand Down Expand Up @@ -53,26 +56,29 @@ function SearchFiltersParticipantsSelector({initialAccountIDs, onFiltersUpdate}:
return defaultListOptions;
}

return getValidOptions(
return memoizedGetValidOptions(
{
reports: options.reports,
personalDetails: options.personalDetails,
},
{
selectedOptions,
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT,
},
);
}, [areOptionsInitialized, options.personalDetails, options.reports, selectedOptions]);
}, [areOptionsInitialized, options.personalDetails, options.reports]);

const unselectedOptions = useMemo(() => {
return filterSelectedOptions(defaultOptions, new Set(selectedOptions.map((option) => option.accountID)));
}, [defaultOptions, selectedOptions]);

const chatOptions = useMemo(() => {
return filterAndOrderOptions(defaultOptions, cleanSearchTerm, {
return filterAndOrderOptions(unselectedOptions, cleanSearchTerm, {
selectedOptions,
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT,
maxRecentReportsToShow: CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW,
canInviteUser: false,
});
}, [defaultOptions, cleanSearchTerm, selectedOptions]);
}, [unselectedOptions, cleanSearchTerm, selectedOptions]);

const {sections, headerMessage} = useMemo(() => {
const newSections: Section[] = [];
Expand Down
16 changes: 16 additions & 0 deletions src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@
*/
let currentUserLogin: string | undefined;
let currentUserAccountID: number | undefined;
Onyx.connect({

Check warning on line 330 in src/libs/OptionsListUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.SESSION,
callback: (value) => {
currentUserLogin = value?.email;
Expand All @@ -336,19 +336,19 @@
});

let loginList: OnyxEntry<Login>;
Onyx.connect({

Check warning on line 339 in src/libs/OptionsListUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.LOGIN_LIST,
callback: (value) => (loginList = isEmptyObject(value) ? {} : value),
});

let allPersonalDetails: OnyxEntry<PersonalDetailsList>;
Onyx.connect({

Check warning on line 345 in src/libs/OptionsListUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => (allPersonalDetails = isEmptyObject(value) ? {} : value),
});

const policies: OnyxCollection<Policy> = {};
Onyx.connect({

Check warning on line 351 in src/libs/OptionsListUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY,
callback: (policy, key) => {
if (!policy || !key || !policy.name) {
Expand All @@ -360,14 +360,14 @@
});

let allPolicies: OnyxCollection<Policy> = {};
Onyx.connect({

Check warning on line 363 in src/libs/OptionsListUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY,
waitForCollectionCallback: true,
callback: (val) => (allPolicies = val),
});

let allReports: OnyxCollection<Report>;
Onyx.connect({

Check warning on line 370 in src/libs/OptionsListUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -376,7 +376,7 @@
});

let allReportNameValuePairs: OnyxCollection<ReportNameValuePairs>;
Onyx.connect({

Check warning on line 379 in src/libs/OptionsListUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -388,7 +388,7 @@
const allSortedReportActions: Record<string, ReportAction[]> = {};
let allReportActions: OnyxCollection<ReportActions>;
const lastVisibleReportActions: ReportActions = {};
Onyx.connect({

Check warning on line 391 in src/libs/OptionsListUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
waitForCollectionCallback: true,
callback: (actions) => {
Expand Down Expand Up @@ -447,13 +447,13 @@
});

let activePolicyID: OnyxEntry<string>;
Onyx.connect({

Check warning on line 450 in src/libs/OptionsListUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.NVP_ACTIVE_POLICY_ID,
callback: (value) => (activePolicyID = value),
});

let nvpDismissedProductTraining: OnyxEntry<DismissedProductTraining>;
Onyx.connect({

Check warning on line 456 in src/libs/OptionsListUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING,
callback: (value) => (nvpDismissedProductTraining = value),
});
Expand Down Expand Up @@ -2572,6 +2572,21 @@
};
}

/**
* Filter out selected options from personal details and recent reports
* @param options - The options to filter
* @param selectedOptions - The selected options to filter out.
* @returns The filtered options
*/
function filterSelectedOptions(options: Options, selectedOptions: Set<number | undefined>): Options {
const filteredOptions = {
Comment thread
roryabraham marked this conversation as resolved.
...options,
personalDetails: options.personalDetails.filter(({accountID}) => !selectedOptions.has(accountID)),
recentReports: options.recentReports.filter(({accountID}) => !selectedOptions.has(accountID)),
};
return filteredOptions;
}

function sortAlphabetically<T extends Partial<Record<TKey, string | undefined>>, TKey extends keyof T>(items: T[], key: TKey): T[] {
return items.sort((a, b) => localeCompare(a[key]?.toLowerCase() ?? '', b[key]?.toLowerCase() ?? ''));
}
Expand Down Expand Up @@ -2681,6 +2696,7 @@
shallowOptionsListCompare,
optionsOrderBy,
recentReportComparator,
filterSelectedOptions,
};

export type {Section, SectionBase, MemberForList, Options, OptionList, SearchOption, Option, OptionTree, ReportAndPersonalDetailOptions};
15 changes: 10 additions & 5 deletions src/pages/NewChatPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ import useThemeStyles from '@hooks/useThemeStyles';
import {navigateToAndOpenReport, searchInServer, setGroupDraft} from '@libs/actions/Report';
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
import Log from '@libs/Log';
import memoize from '@libs/memoize';
import Navigation from '@libs/Navigation/Navigation';
import type {Option, Section} from '@libs/OptionsListUtils';
import {
filterAndOrderOptions,
filterSelectedOptions,
formatSectionsFromSearchTerm,
getFirstKeyForList,
getHeaderMessage,
Expand All @@ -49,6 +51,8 @@ type SelectedOption = ListItem &
reportID?: string;
};

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

function useOptions() {
const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
const [selectedOptions, setSelectedOptions] = useState<SelectedOption[]>([]);
Expand All @@ -61,28 +65,29 @@ function useOptions() {
});

const defaultOptions = useMemo(() => {
const filteredOptions = getValidOptions(
const filteredOptions = memoizedGetValidOptions(
{
reports: listOptions.reports ?? [],
personalDetails: listOptions.personalDetails ?? [],
},
{
betas: betas ?? [],
selectedOptions,
includeSelfDM: true,
},
);
return filteredOptions;
}, [betas, listOptions.personalDetails, listOptions.reports, selectedOptions]);
}, [betas, listOptions.personalDetails, listOptions.reports]);

const unselectedOptions = useMemo(() => filterSelectedOptions(defaultOptions, new Set(selectedOptions.map(({accountID}) => accountID))), [defaultOptions, selectedOptions]);

const options = useMemo(() => {
const filteredOptions = filterAndOrderOptions(defaultOptions, debouncedSearchTerm, {
const filteredOptions = filterAndOrderOptions(unselectedOptions, debouncedSearchTerm, {
selectedOptions,
maxRecentReportsToShow: CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW,
});

return filteredOptions;
}, [debouncedSearchTerm, defaultOptions, selectedOptions]);
}, [debouncedSearchTerm, unselectedOptions, selectedOptions]);
const cleanSearchTerm = useMemo(() => debouncedSearchTerm.trim().toLowerCase(), [debouncedSearchTerm]);
const headerMessage = useMemo(() => {
return getHeaderMessage(
Expand Down
5 changes: 4 additions & 1 deletion src/pages/iou/request/MoneyRequestAccountantSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import useScreenWrapperTransitionStatus from '@hooks/useScreenWrapperTransitionStatus';
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
import memoize from '@libs/memoize';
import type {Section} from '@libs/OptionsListUtils';
import {
filterAndOrderOptions,
Expand All @@ -30,6 +31,8 @@ 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'});

type MoneyRequestAccountantSelectorProps = {
/** Callback to request parent modal to go to next step */
onFinish: (value?: string) => void;
Expand Down Expand Up @@ -66,7 +69,7 @@ function MoneyRequestAccountantSelector({onFinish, onAccountantSelected, iouType
getEmptyOptions();
}

const optionList = getValidOptions(
const optionList = memoizedGetValidOptions(
{
reports: options.reports,
personalDetails: options.personalDetails,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {canUseTouchScreen} from '@libs/DeviceCapabilities';
import getPlatform from '@libs/getPlatform';
import goToSettings from '@libs/goToSettings';
import {isMovingTransactionFromTrackExpense} from '@libs/IOUUtils';
import memoize from '@libs/memoize';
import Navigation from '@libs/Navigation/Navigation';
import type {Option, SearchOption, Section} from '@libs/OptionsListUtils';
import {
Expand Down Expand Up @@ -63,6 +64,8 @@ import type {Participant} from '@src/types/onyx/IOU';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import ImportContactButton from './ImportContactButton';

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

type MoneyRequestParticipantsSelectorProps = {
/** Callback to request parent modal to go to next step, which should be split */
onFinish?: (value?: string) => void;
Expand Down Expand Up @@ -168,7 +171,7 @@ function MoneyRequestParticipantsSelector(
};
}

const optionList = getValidOptions(
const optionList = memoizedGetValidOptions(
{
reports: options.reports,
personalDetails: options.personalDetails.concat(contacts),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useThemeStyles from '@hooks/useThemeStyles';
import {searchInServer} from '@libs/actions/Report';
import memoize from '@libs/memoize';
import Navigation from '@libs/Navigation/Navigation';
import {filterAndOrderOptions, getHeaderMessage, getValidOptions} from '@libs/OptionsListUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Participant} from '@src/types/onyx/IOU';

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

function useOptions() {
const betas = useBetas();
const [isLoading, setIsLoading] = useState(true);
Expand All @@ -39,7 +42,7 @@ function useOptions() {
);

const defaultOptions = useMemo(() => {
const {recentReports, personalDetails, userToInvite, currentUserOption} = getValidOptions(
const {recentReports, personalDetails, userToInvite, currentUserOption} = memoizedGetValidOptions(
{
reports: optionsList.reports,
personalDetails: optionsList.personalDetails,
Expand Down
5 changes: 4 additions & 1 deletion src/pages/tasks/TaskAssigneeSelectorModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {searchInServer} from '@libs/actions/Report';
import {canModifyTask, editTaskAssignee, setAssigneeValue} from '@libs/actions/Task';
import {READ_COMMANDS} from '@libs/API/types';
import HttpUtils from '@libs/HttpUtils';
import memoize from '@libs/memoize';
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types';
import {filterAndOrderOptions, getHeaderMessage, getValidOptions, isCurrentUser} from '@libs/OptionsListUtils';
Expand All @@ -34,14 +35,16 @@ import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {Report} from '@src/types/onyx';

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

function useOptions() {
const betas = useBetas();
const [isLoading, setIsLoading] = useState(true);
const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState('');
const {options: optionsList, areOptionsInitialized} = useOptionsList();

const defaultOptions = useMemo(() => {
const {recentReports, personalDetails, userToInvite, currentUserOption} = getValidOptions(
const {recentReports, personalDetails, userToInvite, currentUserOption} = memoizedGetValidOptions(
{
reports: optionsList.reports,
personalDetails: optionsList.personalDetails,
Expand Down
Loading