diff --git a/src/hooks/useSearchSelector.base.ts b/src/hooks/useSearchSelector.base.ts index 7196fa0a9ea5..cd87176342b2 100644 --- a/src/hooks/useSearchSelector.base.ts +++ b/src/hooks/useSearchSelector.base.ts @@ -271,6 +271,7 @@ function useSearchSelectorBase({ searchString: computedSearchTerm, includeUserToInvite, includeCurrentUser, + shouldAcceptName: true, }); default: return getEmptyOptions(); diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index 7a3e40a13a5a..a161671fead7 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -1687,7 +1687,10 @@ function getUserToInviteOption({ const isValidPhoneNumber = parsedPhoneNumber.possible && Str.isValidE164Phone(getPhoneNumberWithoutSpecialChars(parsedPhoneNumber.number?.input ?? '')); const isInOptionToExclude = loginsToExclude[addSMSDomainIfPhoneNumber(searchValue).toLowerCase()]; - if (isCurrentUserLogin || isInSelectedOption || (!isValidEmail && !isValidPhoneNumber && !shouldAcceptName) || isInOptionToExclude) { + // Angle brackets are not valid characters for user names + const hasInvalidCharacters = shouldAcceptName && (searchValue.includes('<') || searchValue.includes('>')); + + if (isCurrentUserLogin || isInSelectedOption || (!isValidEmail && !isValidPhoneNumber && !shouldAcceptName) || isInOptionToExclude || hasInvalidCharacters) { return null; } @@ -1704,7 +1707,7 @@ function getUserToInviteOption({ showChatPreviewLine, }); userToInvite.isOptimisticAccount = true; - userToInvite.login = isValidEmail || isValidPhoneNumber ? searchValue : ''; + userToInvite.login = searchValue; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing userToInvite.text = userToInvite.text || searchValue; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing @@ -2125,6 +2128,7 @@ function getValidOptions( maxElements, includeUserToInvite = false, maxRecentReportElements = undefined, + shouldAcceptName = false, ...config }: GetOptionsConfig = {}, countryCode: number = CONST.DEFAULT_COUNTRY_CODE, @@ -2311,6 +2315,7 @@ function getValidOptions( countryCode, { excludeLogins: loginsToExclude, + shouldAcceptName, }, ); } @@ -2444,7 +2449,7 @@ function getFilteredRecentAttendees(personalDetails: OnyxEntry !attendees.find(({email, displayName}) => (attendee.email ? email === attendee.email : displayName === attendee.displayName))) .map((attendee) => ({ ...attendee, - login: attendee.email ?? attendee.displayName, + login: attendee.email ? attendee.email : attendee.displayName, ...getPersonalDetailByEmail(attendee.email), })) .map((attendee) => getParticipantsOption(attendee, personalDetails)); diff --git a/src/libs/OptionsListUtils/types.ts b/src/libs/OptionsListUtils/types.ts index 814cc47f115e..603399bf9d84 100644 --- a/src/libs/OptionsListUtils/types.ts +++ b/src/libs/OptionsListUtils/types.ts @@ -190,6 +190,7 @@ type GetOptionsConfig = { maxElements?: number; maxRecentReportElements?: number; includeUserToInvite?: boolean; + shouldAcceptName?: boolean; } & GetValidReportsConfig; type GetUserToInviteConfig = { diff --git a/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx b/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx index 36c8b99b1f8a..8b626bb99e5c 100644 --- a/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx +++ b/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx @@ -79,7 +79,7 @@ function MoneyRequestAttendeeSelector({attendees = [], onFinish, onAttendeesAdde ...attendee, reportID: CONST.DEFAULT_NUMBER_ID.toString(), selected: true, - login: attendee.email, + login: attendee.email ? attendee.email : attendee.displayName, ...getPersonalDetailByEmail(attendee.email), })), [attendees], @@ -204,13 +204,7 @@ function MoneyRequestAttendeeSelector({attendees = [], onFinish, onAttendeesAdde const cleanSearchTerm = searchTerm.trim().toLowerCase(); const formatResults = formatSectionsFromSearchTerm( cleanSearchTerm, - attendees.map((attendee) => ({ - ...attendee, - reportID: CONST.DEFAULT_NUMBER_ID.toString(), - selected: true, - login: attendee.email, - ...getPersonalDetailByEmail(attendee.email), - })), + initialSelectedOptions, orderedAvailableOptions.recentReports, orderedAvailableOptions.personalDetails, personalDetails, @@ -272,6 +266,7 @@ function MoneyRequestAttendeeSelector({attendees = [], onFinish, onAttendeesAdde areOptionsInitialized, didScreenTransitionEnd, searchTerm, + initialSelectedOptions, attendees, orderedAvailableOptions.recentReports, orderedAvailableOptions.personalDetails, diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 283a8a23b589..418fda95bcb1 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -27,6 +27,7 @@ import { getPersonalDetailSearchTerms, getSearchOptions, getSearchValueForPhoneOrEmail, + getUserToInviteOption, getValidOptions, optionsOrderBy, orderOptions, @@ -2962,4 +2963,24 @@ describe('OptionsListUtils', () => { expect(searchTerms2.includes(displayName)).toBe(true); }); }); + + describe('getUserToInviteOption', () => { + it('should not return userToInvite for plain text name when shouldAcceptName is false', () => { + const result = getUserToInviteOption({ + searchValue: 'Jeff Amazon', + loginList: {}, + }); + expect(result).toBeNull(); + }); + + it('should return userToInvite for plain text name when shouldAcceptName is true', () => { + const result = getUserToInviteOption({ + searchValue: 'Jeff Amazon', + shouldAcceptName: true, + loginList: {}, + }); + expect(result).not.toBeNull(); + expect(result?.login).toBe('Jeff Amazon'); + }); + }); });