Skip to content

Commit

Permalink
Merge pull request #50426 from c3024/move-deduplicating-logic-of-pers…
Browse files Browse the repository at this point in the history
…onal-details-to-filteredOptions

Move the logic of excluding contacts of DMs already included in reports to `filterOptions`
  • Loading branch information
MonilBhavsar authored Oct 18, 2024
2 parents 1e81aa8 + 0519359 commit 0ec8b26
Show file tree
Hide file tree
Showing 11 changed files with 306 additions and 399 deletions.
18 changes: 6 additions & 12 deletions src/components/CategoryPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,14 @@ function CategoryPicker({selectedCategory, policyID, onSubmit}: CategoryPickerPr
const [sections, headerMessage, shouldShowTextInput] = useMemo(() => {
const categories = policyCategories ?? policyCategoriesDraft ?? {};
const validPolicyRecentlyUsedCategories = policyRecentlyUsedCategories?.filter?.((p) => !isEmptyObject(p));
const {categoryOptions} = OptionsListUtils.getFilteredOptions(
[],
[],
[],
debouncedSearchValue,
const {categoryOptions} = OptionsListUtils.getFilteredOptions({
searchValue: debouncedSearchValue,
selectedOptions,
[],
false,
false,
true,
includeP2P: false,
includeCategories: true,
categories,
validPolicyRecentlyUsedCategories,
false,
);
recentlyUsedCategories: validPolicyRecentlyUsedCategories,
});

const categoryData = categoryOptions?.at(0)?.data ?? [];
const header = OptionsListUtils.getHeaderMessageForNonUserList(categoryData.length > 0, debouncedSearchValue);
Expand Down
27 changes: 6 additions & 21 deletions src/components/Search/SearchFiltersParticipantsSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,28 +57,13 @@ function SearchFiltersParticipantsSelector({initialAccountIDs, onFiltersUpdate}:
return defaultListOptions;
}

return OptionsListUtils.getFilteredOptions(
options.reports,
options.personalDetails,
undefined,
'',
return OptionsListUtils.getFilteredOptions({
reports: options.reports,
personalDetails: options.personalDetails,
selectedOptions,
CONST.EXPENSIFY_EMAILS,
false,
true,
false,
{},
[],
false,
{},
[],
true,
false,
false,
0,
undefined,
false,
);
excludeLogins: CONST.EXPENSIFY_EMAILS,
maxRecentReportsToShow: 0,
});
}, [areOptionsInitialized, options.personalDetails, options.reports, selectedOptions]);

const chatOptions = useMemo(() => {
Expand Down
39 changes: 17 additions & 22 deletions src/components/TagPicker/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, {useMemo, useState} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import SelectionList from '@components/SelectionList';
import RadioListItem from '@components/SelectionList/RadioListItem';
import useLocalize from '@hooks/useLocalize';
Expand All @@ -10,7 +9,7 @@ import * as PolicyUtils from '@libs/PolicyUtils';
import type * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {PolicyTag, PolicyTagLists, PolicyTags, RecentlyUsedTags} from '@src/types/onyx';
import type {PolicyTag, PolicyTags} from '@src/types/onyx';
import type {PendingAction} from '@src/types/onyx/OnyxCommon';

type SelectedTagOption = {
Expand All @@ -21,15 +20,7 @@ type SelectedTagOption = {
pendingAction?: PendingAction;
};

type TagPickerOnyxProps = {
/** Collection of tag list on a policy */
policyTags: OnyxEntry<PolicyTagLists>;

/** List of recently used tags */
policyRecentlyUsedTags: OnyxEntry<RecentlyUsedTags>;
};

type TagPickerProps = TagPickerOnyxProps & {
type TagPickerProps = {
/** The policyID we are getting tags for */
// It's used in withOnyx HOC.
// eslint-disable-next-line react/no-unused-prop-types
Expand All @@ -51,7 +42,9 @@ type TagPickerProps = TagPickerOnyxProps & {
tagListIndex: number;
};

function TagPicker({selectedTag, tagListName, policyTags, tagListIndex, policyRecentlyUsedTags, shouldShowDisabledAndSelectedOption = false, onSubmit}: TagPickerProps) {
function TagPicker({selectedTag, tagListName, policyID, tagListIndex, shouldShowDisabledAndSelectedOption = false, onSubmit}: TagPickerProps) {
const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`);
const [policyRecentlyUsedTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${policyID}`);
const styles = useThemeStyles();
const {translate} = useLocalize();
const [searchValue, setSearchValue] = useState('');
Expand Down Expand Up @@ -87,7 +80,16 @@ function TagPicker({selectedTag, tagListName, policyTags, tagListIndex, policyRe
}, [selectedOptions, policyTagList, shouldShowDisabledAndSelectedOption]);

const sections = useMemo(
() => OptionsListUtils.getFilteredOptions([], [], [], searchValue, selectedOptions, [], false, false, false, {}, [], true, enabledTags, policyRecentlyUsedTagsList, false).tagOptions,
() =>
OptionsListUtils.getFilteredOptions({
searchValue,
selectedOptions,
includeP2P: false,
includeTags: true,
tags: enabledTags,
recentlyUsedTags: policyRecentlyUsedTagsList,
canInviteUser: false,
}).tagOptions,
[searchValue, enabledTags, selectedOptions, policyRecentlyUsedTagsList],
);

Expand All @@ -113,13 +115,6 @@ function TagPicker({selectedTag, tagListName, policyTags, tagListIndex, policyRe

TagPicker.displayName = 'TagPicker';

export default withOnyx<TagPickerProps, TagPickerOnyxProps>({
policyTags: {
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`,
},
policyRecentlyUsedTags: {
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${policyID}`,
},
})(TagPicker);
export default TagPicker;

export type {SelectedTagOption};
120 changes: 84 additions & 36 deletions src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1977,11 +1977,6 @@ function getOptions(
}
}
}

// Add this login to the exclude list so it won't appear when we process the personal details
if (reportOption.login) {
optionsToExclude.push({login: reportOption.login});
}
}
}

Expand Down Expand Up @@ -2114,34 +2109,75 @@ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: OnyxEn
/**
* Build the options for the New Group view
*/
function getFilteredOptions(
reports: Array<SearchOption<Report>> = [],
personalDetails: Array<SearchOption<PersonalDetails>> = [],
betas: OnyxEntry<Beta[]> = [],
searchValue = '',
selectedOptions: Array<Partial<ReportUtils.OptionData>> = [],
excludeLogins: string[] = [],
includeOwnedWorkspaceChats = false,
includeP2P = true,
includeCategories = false,
categories: PolicyCategories = {},
recentlyUsedCategories: string[] = [],
includeTags = false,
tags: PolicyTags | Array<PolicyTag | SelectedTagOption> = {},
recentlyUsedTags: string[] = [],
canInviteUser = true,
includeSelectedOptions = false,
includeTaxRates = false,
maxRecentReportsToShow: number = CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW,
taxRates: TaxRatesWithDefault = {} as TaxRatesWithDefault,
includeSelfDM = false,
includePolicyReportFieldOptions = false,
policyReportFieldOptions: string[] = [],
recentlyUsedPolicyReportFieldOptions: string[] = [],
includeInvoiceRooms = false,
action: IOUAction | undefined = undefined,
sortByReportTypeInSearch = false,
) {
type FilteredOptionsParams = {
reports?: Array<SearchOption<Report>>;
personalDetails?: Array<SearchOption<PersonalDetails>>;
betas?: OnyxEntry<Beta[]>;
searchValue?: string;
selectedOptions?: Array<Partial<ReportUtils.OptionData>>;
excludeLogins?: string[];
includeOwnedWorkspaceChats?: boolean;
includeP2P?: boolean;
includeCategories?: boolean;
categories?: PolicyCategories;
recentlyUsedCategories?: string[];
includeTags?: boolean;
tags?: PolicyTags | Array<PolicyTag | SelectedTagOption>;
recentlyUsedTags?: string[];
canInviteUser?: boolean;
includeSelectedOptions?: boolean;
includeTaxRates?: boolean;
taxRates?: TaxRatesWithDefault;
maxRecentReportsToShow?: number;
includeSelfDM?: boolean;
includePolicyReportFieldOptions?: boolean;
policyReportFieldOptions?: string[];
recentlyUsedPolicyReportFieldOptions?: string[];
includeInvoiceRooms?: boolean;
action?: IOUAction;
sortByReportTypeInSearch?: boolean;
};

// It is not recommended to pass a search value to getFilteredOptions when passing reports and personalDetails.
// If a search value is passed, the search value should be passed to filterOptions.
// When it is necessary to pass a search value when passing reports and personalDetails, follow these steps:
// 1. Use getFilteredOptions with reports and personalDetails only, without the search value.
// 2. Pass the returned options from getFilteredOptions to filterOptions along with the search value.
// The above constraints are enforced with TypeScript.

type FilteredOptionsParamsWithDefaultSearchValue = Omit<FilteredOptionsParams, 'searchValue'> & {searchValue?: ''};

type FilteredOptionsParamsWithoutOptions = Omit<FilteredOptionsParams, 'reports' | 'personalDetails'> & {reports?: []; personalDetails?: []};

function getFilteredOptions(params: FilteredOptionsParamsWithDefaultSearchValue | FilteredOptionsParamsWithoutOptions) {
const {
reports = [],
personalDetails = [],
betas = [],
searchValue = '',
selectedOptions = [],
excludeLogins = [],
includeOwnedWorkspaceChats = false,
includeP2P = true,
includeCategories = false,
categories = {},
recentlyUsedCategories = [],
includeTags = false,
tags = {},
recentlyUsedTags = [],
canInviteUser = true,
includeSelectedOptions = false,
includeTaxRates = false,
maxRecentReportsToShow = CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW,
taxRates = {} as TaxRatesWithDefault,
includeSelfDM = false,
includePolicyReportFieldOptions = false,
policyReportFieldOptions = [],
recentlyUsedPolicyReportFieldOptions = [],
includeInvoiceRooms = false,
action,
sortByReportTypeInSearch = false,
} = params;
return getOptions(
{reports, personalDetails},
{
Expand Down Expand Up @@ -2421,8 +2457,19 @@ function filterOptions(options: Options, searchInputValue: string, config?: Filt
preferPolicyExpenseChat = false,
preferRecentExpenseReports = false,
} = config ?? {};
// Remove the personal details for the DMs that are already in the recent reports so that we don't show duplicates
function filteredPersonalDetailsOfRecentReports(recentReports: ReportUtils.OptionData[], personalDetails: ReportUtils.OptionData[]) {
const excludedLogins = new Set(recentReports.map((report) => report.login));
return personalDetails.filter((personalDetail) => !excludedLogins.has(personalDetail.login));
}
if (searchInputValue.trim() === '' && maxRecentReportsToShow > 0) {
return {...options, recentReports: options.recentReports.slice(0, maxRecentReportsToShow)};
const recentReports = options.recentReports.slice(0, maxRecentReportsToShow);
const personalDetails = filteredPersonalDetailsOfRecentReports(recentReports, options.personalDetails);
return {
...options,
recentReports,
personalDetails,
};
}

const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchInputValue)));
Expand Down Expand Up @@ -2464,7 +2511,6 @@ function filterOptions(options: Options, searchInputValue: string, config?: Filt
const currentUserOptionSearchText = items.currentUserOption ? uniqFast(getCurrentUserSearchTerms(items.currentUserOption)).join(' ') : '';

const currentUserOption = isSearchStringMatch(term, currentUserOptionSearchText) ? items.currentUserOption : null;

return {
recentReports: recentReports ?? [],
personalDetails: personalDetails ?? [],
Expand All @@ -2479,6 +2525,7 @@ function filterOptions(options: Options, searchInputValue: string, config?: Filt
let {recentReports, personalDetails} = matchResults;

if (sortByReportTypeInSearch) {
personalDetails = filteredPersonalDetailsOfRecentReports(recentReports, personalDetails);
recentReports = recentReports.concat(personalDetails);
personalDetails = [];
recentReports = orderOptions(recentReports, searchValue);
Expand All @@ -2489,9 +2536,10 @@ function filterOptions(options: Options, searchInputValue: string, config?: Filt
if (maxRecentReportsToShow > 0 && recentReports.length > maxRecentReportsToShow) {
recentReports.splice(maxRecentReportsToShow);
}
const filteredPersonalDetails = filteredPersonalDetailsOfRecentReports(recentReports, personalDetails);

return {
personalDetails,
personalDetails: filteredPersonalDetails,
recentReports: orderOptions(recentReports, searchValue, {preferChatroomsOverThreads, preferPolicyExpenseChat, preferRecentExpenseReports}),
userToInvite,
currentUserOption: matchResults.currentUserOption,
Expand Down
55 changes: 15 additions & 40 deletions src/pages/EditReportFieldDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, {useCallback, useMemo} from 'react';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import SelectionList from '@components/SelectionList';
Expand All @@ -11,9 +10,7 @@ import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import localeCompare from '@libs/LocaleCompare';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {RecentlyUsedReportFields} from '@src/types/onyx';

type EditReportFieldDropdownPageComponentProps = {
/** Value of the policy report field */
Expand All @@ -33,13 +30,10 @@ type EditReportFieldDropdownPageComponentProps = {
onSubmit: (form: Record<string, string>) => void;
};

type EditReportFieldDropdownPageOnyxProps = {
recentlyUsedReportFields: OnyxEntry<RecentlyUsedReportFields>;
};

type EditReportFieldDropdownPageProps = EditReportFieldDropdownPageComponentProps & EditReportFieldDropdownPageOnyxProps;
type EditReportFieldDropdownPageProps = EditReportFieldDropdownPageComponentProps;

function EditReportFieldDropdownPage({onSubmit, fieldKey, fieldValue, fieldOptions, recentlyUsedReportFields}: EditReportFieldDropdownPageProps) {
function EditReportFieldDropdownPage({onSubmit, fieldKey, fieldValue, fieldOptions}: EditReportFieldDropdownPageProps) {
const [recentlyUsedReportFields] = useOnyx(ONYXKEYS.RECENTLY_USED_REPORT_FIELDS);
const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState('');
const theme = useTheme();
const {translate} = useLocalize();
Expand All @@ -64,37 +58,22 @@ function EditReportFieldDropdownPage({onSubmit, fieldKey, fieldValue, fieldOptio
const [sections, headerMessage] = useMemo(() => {
const validFieldOptions = fieldOptions?.filter((option) => !!option)?.sort(localeCompare);

const {policyReportFieldOptions} = OptionsListUtils.getFilteredOptions(
[],
[],
[],
debouncedSearchValue,
[
const {policyReportFieldOptions} = OptionsListUtils.getFilteredOptions({
searchValue: debouncedSearchValue,
selectedOptions: [
{
keyForList: fieldValue,
searchText: fieldValue,
text: fieldValue,
},
],
[],
false,
false,
false,
{},
[],
false,
{},
[],
false,
false,
undefined,
CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW,
undefined,
undefined,
true,
validFieldOptions,
recentlyUsedOptions,
);

includeP2P: false,
canInviteUser: false,
includePolicyReportFieldOptions: true,
policyReportFieldOptions: validFieldOptions,
recentlyUsedPolicyReportFieldOptions: recentlyUsedOptions,
});

const policyReportFieldData = policyReportFieldOptions?.[0]?.data ?? [];
const header = OptionsListUtils.getHeaderMessageForNonUserList(policyReportFieldData.length > 0, debouncedSearchValue);
Expand All @@ -121,8 +100,4 @@ function EditReportFieldDropdownPage({onSubmit, fieldKey, fieldValue, fieldOptio

EditReportFieldDropdownPage.displayName = 'EditReportFieldDropdownPage';

export default withOnyx<EditReportFieldDropdownPageProps, EditReportFieldDropdownPageOnyxProps>({
recentlyUsedReportFields: {
key: () => ONYXKEYS.RECENTLY_USED_REPORT_FIELDS,
},
})(EditReportFieldDropdownPage);
export default EditReportFieldDropdownPage;
Loading

0 comments on commit 0ec8b26

Please sign in to comment.