From a3809cb35bf62b01bfd28bcaec0c329c778c935e Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 25 Apr 2024 13:21:03 +0700 Subject: [PATCH 1/7] Allow searching new user in chat finder page --- src/libs/OptionsListUtils.ts | 49 ++++++++++++++++++++++++++++-- src/pages/ChatFinderPage/index.tsx | 4 +-- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index e9f0fc9dc451..ba163758d318 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -2239,9 +2239,10 @@ function getFirstKeyForList(data?: Option[] | null) { /** * Filters options based on the search input value */ -function filterOptions(options: Options, searchInputValue: string): Options { +function filterOptions(options: Options, searchInputValue: string, betas: Beta[]): Options { const searchValue = getSearchValueForPhoneOrEmail(searchInputValue); const searchTerms = searchValue ? searchValue.split(' ') : []; + const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchInputValue))); // The regex below is used to remove dots only from the local part of the user email (local-part@domain) // so that we can match emails that have dots without explicitly writing the dots (e.g: fistlast@domain will match first.last@domain) @@ -2309,10 +2310,54 @@ function filterOptions(options: Options, searchInputValue: string): Options { const recentReports = matchResults.recentReports.concat(matchResults.personalDetails); + let userToInvite: ReportUtils.OptionData | null = null; + + /** + * We create a new user option if the following conditions are satisfied: + * - there's no match recent report and personal detail option + * - The searchValue is a valid email or phone number + * - The searchValue isn't the current personal detail login + * - We can use chronos or the search value is not the chronos email + */ + if ( + searchValue && + recentReports.length === 0 && + !isCurrentUser({login: searchValue} as PersonalDetails) && + ((Str.isValidEmail(searchValue) && !Str.isDomainEmail(searchValue) && !Str.endsWith(searchValue, CONST.SMS.DOMAIN)) || + (parsedPhoneNumber.possible && Str.isValidE164Phone(LoginUtils.getPhoneNumberWithoutSpecialChars(parsedPhoneNumber.number?.input ?? '')))) && + (searchValue !== CONST.EMAIL.CHRONOS || Permissions.canUseChronos(betas)) + ) { + // Generates an optimistic account ID for new users not yet saved in Onyx + const optimisticAccountID = UserUtils.generateAccountID(searchValue); + const personalDetailsExtended = { + ...allPersonalDetails, + [optimisticAccountID]: { + accountID: optimisticAccountID, + login: searchValue, + }, + }; + userToInvite = createOption([optimisticAccountID], personalDetailsExtended, null, {}); + userToInvite.isOptimisticAccount = true; + 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 + userToInvite.alternateText = userToInvite.alternateText || searchValue; + + // If user doesn't exist, use a fallback avatar + userToInvite.icons = [ + { + source: FallbackAvatar, + name: searchValue, + type: CONST.ICON_TYPE_AVATAR, + }, + ]; + } + return { personalDetails: [], recentReports: orderOptions(recentReports, searchValue), - userToInvite: null, + userToInvite, currentUserOption: null, categoryOptions: [], tagOptions: [], diff --git a/src/pages/ChatFinderPage/index.tsx b/src/pages/ChatFinderPage/index.tsx index a7516163c40b..3645e61d9be6 100644 --- a/src/pages/ChatFinderPage/index.tsx +++ b/src/pages/ChatFinderPage/index.tsx @@ -102,11 +102,11 @@ function ChatFinderPage({betas, isSearchingForReports, navigation}: ChatFinderPa } const newOptions = OptionsListUtils.filterOptions(searchOptions, debouncedSearchValue); - const header = OptionsListUtils.getHeaderMessage(newOptions.recentReports.length > 0, false, debouncedSearchValue); + const header = OptionsListUtils.getHeaderMessage(newOptions.recentReports.length + Number(!!newOptions.userToInvite) > 0, false, debouncedSearchValue); return { recentReports: newOptions.recentReports, personalDetails: newOptions.personalDetails, - userToInvite: null, + userToInvite: newOptions.userToInvite, headerMessage: header, }; }, [debouncedSearchValue, searchOptions]); From ddac1ef5ac3cee453349311cbab3027cd304f117 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 25 Apr 2024 15:37:55 +0700 Subject: [PATCH 2/7] fix lint --- src/libs/OptionsListUtils.ts | 4 ++-- src/pages/ChatFinderPage/index.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index ba163758d318..79625768b29b 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -2239,7 +2239,7 @@ function getFirstKeyForList(data?: Option[] | null) { /** * Filters options based on the search input value */ -function filterOptions(options: Options, searchInputValue: string, betas: Beta[]): Options { +function filterOptions(options: Options, searchInputValue: string, betas: OnyxEntry = []): Options { const searchValue = getSearchValueForPhoneOrEmail(searchInputValue); const searchTerms = searchValue ? searchValue.split(' ') : []; const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchInputValue))); @@ -2315,7 +2315,7 @@ function filterOptions(options: Options, searchInputValue: string, betas: Beta[] /** * We create a new user option if the following conditions are satisfied: * - there's no match recent report and personal detail option - * - The searchValue is a valid email or phone number + * - The searchValue is a valid email or phone number * - The searchValue isn't the current personal detail login * - We can use chronos or the search value is not the chronos email */ diff --git a/src/pages/ChatFinderPage/index.tsx b/src/pages/ChatFinderPage/index.tsx index 3645e61d9be6..9a15fce6718a 100644 --- a/src/pages/ChatFinderPage/index.tsx +++ b/src/pages/ChatFinderPage/index.tsx @@ -101,7 +101,7 @@ function ChatFinderPage({betas, isSearchingForReports, navigation}: ChatFinderPa }; } - const newOptions = OptionsListUtils.filterOptions(searchOptions, debouncedSearchValue); + const newOptions = OptionsListUtils.filterOptions(searchOptions, debouncedSearchValue, betas); const header = OptionsListUtils.getHeaderMessage(newOptions.recentReports.length + Number(!!newOptions.userToInvite) > 0, false, debouncedSearchValue); return { recentReports: newOptions.recentReports, @@ -109,7 +109,7 @@ function ChatFinderPage({betas, isSearchingForReports, navigation}: ChatFinderPa userToInvite: newOptions.userToInvite, headerMessage: header, }; - }, [debouncedSearchValue, searchOptions]); + }, [debouncedSearchValue, searchOptions, betas]); const {recentReports, personalDetails: localPersonalDetails, userToInvite, headerMessage} = debouncedSearchValue.trim() !== '' ? filteredOptions : searchOptions; From 2b37da288775a1dc06698add3a07a3716e273fb3 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 26 Apr 2024 17:18:22 +0700 Subject: [PATCH 3/7] dry create user to invite option --- src/libs/OptionsListUtils.ts | 147 ++++++++++++++++++----------------- 1 file changed, 77 insertions(+), 70 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index cb1ecb7cba69..56863282da14 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -166,6 +166,16 @@ type GetOptionsConfig = { transactionViolations?: OnyxCollection; }; +type GetUserToInviteConfig = { + searchValue: string; + excludeUnknownUsers?: boolean; + optionsToExclude?: Array>; + selectedOptions?: Array>; + betas: OnyxEntry; + reportActions?: ReportActions; + showChatPreviewLine?: boolean; +}; + type MemberForList = { text: string; alternateText: string; @@ -1521,6 +1531,60 @@ function orderOptions(options: ReportUtils.OptionData[], searchValue: string | u ); } +function getUserToInviteOption({ + searchValue, + excludeUnknownUsers = false, + optionsToExclude = [], + selectedOptions = [], + betas, + reportActions = {}, + showChatPreviewLine = false, +}: GetUserToInviteConfig): ReportUtils.OptionData | null { + let userToInvite: ReportUtils.OptionData | null = null; + const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchValue))); + + if ( + searchValue && + !isCurrentUser({login: searchValue} as PersonalDetails) && + selectedOptions.every((option) => 'login' in option && option.login !== searchValue) && + ((Str.isValidEmail(searchValue) && !Str.isDomainEmail(searchValue) && !Str.endsWith(searchValue, CONST.SMS.DOMAIN)) || + (parsedPhoneNumber.possible && Str.isValidE164Phone(LoginUtils.getPhoneNumberWithoutSpecialChars(parsedPhoneNumber.number?.input ?? '')))) && + !optionsToExclude.find((optionToExclude) => 'login' in optionToExclude && optionToExclude.login === PhoneNumber.addSMSDomainIfPhoneNumber(searchValue).toLowerCase()) && + (searchValue !== CONST.EMAIL.CHRONOS || Permissions.canUseChronos(betas)) && + !excludeUnknownUsers + ) { + // Generates an optimistic account ID for new users not yet saved in Onyx + const optimisticAccountID = UserUtils.generateAccountID(searchValue); + const personalDetailsExtended = { + ...allPersonalDetails, + [optimisticAccountID]: { + accountID: optimisticAccountID, + login: searchValue, + }, + }; + userToInvite = createOption([optimisticAccountID], personalDetailsExtended, null, reportActions, { + showChatPreviewLine, + }); + userToInvite.isOptimisticAccount = true; + 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 + userToInvite.alternateText = userToInvite.alternateText || searchValue; + + // If user doesn't exist, use a fallback avatar + userToInvite.icons = [ + { + source: FallbackAvatar, + name: searchValue, + type: CONST.ICON_TYPE_AVATAR, + }, + ]; + } + + return userToInvite; +} + /** * filter options based on specific conditions */ @@ -1825,44 +1889,16 @@ function getOptions( .concat(recentReportOptions) .find((option) => option.login === PhoneNumber.addSMSDomainIfPhoneNumber(searchValue ?? '').toLowerCase() || option.login === searchValue?.toLowerCase()); - if ( - searchValue && - (noOptions || noOptionsMatchExactly) && - !isCurrentUser({login: searchValue} as PersonalDetails) && - selectedOptions.every((option) => 'login' in option && option.login !== searchValue) && - ((Str.isValidEmail(searchValue) && !Str.isDomainEmail(searchValue) && !Str.endsWith(searchValue, CONST.SMS.DOMAIN)) || - (parsedPhoneNumber.possible && Str.isValidE164Phone(LoginUtils.getPhoneNumberWithoutSpecialChars(parsedPhoneNumber.number?.input ?? '')))) && - !optionsToExclude.find((optionToExclude) => 'login' in optionToExclude && optionToExclude.login === PhoneNumber.addSMSDomainIfPhoneNumber(searchValue).toLowerCase()) && - (searchValue !== CONST.EMAIL.CHRONOS || Permissions.canUseChronos(betas)) && - !excludeUnknownUsers - ) { - // Generates an optimistic account ID for new users not yet saved in Onyx - const optimisticAccountID = UserUtils.generateAccountID(searchValue); - const personalDetailsExtended = { - ...allPersonalDetails, - [optimisticAccountID]: { - accountID: optimisticAccountID, - login: searchValue, - }, - }; - userToInvite = createOption([optimisticAccountID], personalDetailsExtended, null, reportActions, { + if (noOptions || noOptionsMatchExactly) { + userToInvite = getUserToInviteOption({ + searchValue, + excludeUnknownUsers, + optionsToExclude, + selectedOptions, + betas, + reportActions, showChatPreviewLine, }); - userToInvite.isOptimisticAccount = true; - 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 - userToInvite.alternateText = userToInvite.alternateText || searchValue; - - // If user doesn't exist, use a fallback avatar - userToInvite.icons = [ - { - source: FallbackAvatar, - name: searchValue, - type: CONST.ICON_TYPE_AVATAR, - }, - ]; } // If we are prioritizing 1:1 chats in search, do it only once we started searching @@ -2226,7 +2262,6 @@ function getFirstKeyForList(data?: Option[] | null) { function filterOptions(options: Options, searchInputValue: string, betas: OnyxEntry = []): Options { const searchValue = getSearchValueForPhoneOrEmail(searchInputValue); const searchTerms = searchValue ? searchValue.split(' ') : []; - const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchInputValue))); // The regex below is used to remove dots only from the local part of the user email (local-part@domain) // so that we can match emails that have dots without explicitly writing the dots (e.g: fistlast@domain will match first.last@domain) @@ -2303,39 +2338,11 @@ function filterOptions(options: Options, searchInputValue: string, betas: OnyxEn * - The searchValue isn't the current personal detail login * - We can use chronos or the search value is not the chronos email */ - if ( - searchValue && - recentReports.length === 0 && - !isCurrentUser({login: searchValue} as PersonalDetails) && - ((Str.isValidEmail(searchValue) && !Str.isDomainEmail(searchValue) && !Str.endsWith(searchValue, CONST.SMS.DOMAIN)) || - (parsedPhoneNumber.possible && Str.isValidE164Phone(LoginUtils.getPhoneNumberWithoutSpecialChars(parsedPhoneNumber.number?.input ?? '')))) && - (searchValue !== CONST.EMAIL.CHRONOS || Permissions.canUseChronos(betas)) - ) { - // Generates an optimistic account ID for new users not yet saved in Onyx - const optimisticAccountID = UserUtils.generateAccountID(searchValue); - const personalDetailsExtended = { - ...allPersonalDetails, - [optimisticAccountID]: { - accountID: optimisticAccountID, - login: searchValue, - }, - }; - userToInvite = createOption([optimisticAccountID], personalDetailsExtended, null, {}); - userToInvite.isOptimisticAccount = true; - 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 - userToInvite.alternateText = userToInvite.alternateText || searchValue; - - // If user doesn't exist, use a fallback avatar - userToInvite.icons = [ - { - source: FallbackAvatar, - name: searchValue, - type: CONST.ICON_TYPE_AVATAR, - }, - ]; + if (recentReports.length === 0) { + userToInvite = getUserToInviteOption({ + searchValue, + betas, + }); } return { From d9f71f110affa44de4eda93715b49c394fbdeebd Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 30 Apr 2024 21:48:36 +0700 Subject: [PATCH 4/7] fix ts check --- src/libs/OptionsListUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index f946ee0428d6..e687e9005763 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1580,7 +1580,7 @@ function getUserToInviteOption({ // If user doesn't exist, use a fallback avatar userToInvite.icons = [ { - source: FallbackAvatar, + source: UserUtils.getAvatar('', optimisticAccountID), name: searchValue, type: CONST.ICON_TYPE_AVATAR, }, From 73fe334bf6b68a6d67b3c72f002ce7c3582b740a Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 30 Apr 2024 23:52:50 +0700 Subject: [PATCH 5/7] add shouldGetOptionToInvite param --- src/libs/OptionsListUtils.ts | 53 +++++++++++++++++------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index e687e9005763..adf8cc12e2f9 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -173,6 +173,7 @@ type GetUserToInviteConfig = { betas: OnyxEntry; reportActions?: ReportActions; showChatPreviewLine?: boolean; + shouldGetOptionToInvite: boolean; }; type MemberForList = { @@ -1544,12 +1545,21 @@ function getUserToInviteOption({ betas, reportActions = {}, showChatPreviewLine = false, + shouldGetOptionToInvite, }: GetUserToInviteConfig): ReportUtils.OptionData | null { let userToInvite: ReportUtils.OptionData | null = null; const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchValue))); + /** + * We create a new user option if the following conditions are satisfied: + * - there's no match recent report and personal detail option + * - The searchValue is a valid email or phone number + * - The searchValue isn't the current personal detail login + * - We can use chronos or the search value is not the chronos email + */ if ( searchValue && + shouldGetOptionToInvite && !isCurrentUser({login: searchValue} as PersonalDetails) && selectedOptions.every((option) => 'login' in option && option.login !== searchValue) && ((Str.isValidEmail(searchValue) && !Str.isDomainEmail(searchValue) && !Str.endsWith(searchValue, CONST.SMS.DOMAIN)) || @@ -1888,23 +1898,21 @@ function getOptions( currentUserOption = undefined; } - let userToInvite: ReportUtils.OptionData | null = null; const noOptions = recentReportOptions.length + personalDetailsOptions.length === 0 && !currentUserOption; const noOptionsMatchExactly = !personalDetailsOptions .concat(recentReportOptions) .find((option) => option.login === PhoneNumber.addSMSDomainIfPhoneNumber(searchValue ?? '').toLowerCase() || option.login === searchValue?.toLowerCase()); - if (noOptions || noOptionsMatchExactly) { - userToInvite = getUserToInviteOption({ - searchValue, - excludeUnknownUsers, - optionsToExclude, - selectedOptions, - betas, - reportActions, - showChatPreviewLine, - }); - } + const userToInvite = getUserToInviteOption({ + searchValue, + excludeUnknownUsers, + optionsToExclude, + selectedOptions, + betas, + reportActions, + showChatPreviewLine, + shouldGetOptionToInvite: noOptions || noOptionsMatchExactly, + }); // If we are prioritizing 1:1 chats in search, do it only once we started searching if (sortByReportTypeInSearch && searchValue !== '') { @@ -2338,22 +2346,11 @@ function filterOptions(options: Options, searchInputValue: string, betas: OnyxEn }, options); const recentReports = matchResults.recentReports.concat(matchResults.personalDetails); - - let userToInvite: ReportUtils.OptionData | null = null; - - /** - * We create a new user option if the following conditions are satisfied: - * - there's no match recent report and personal detail option - * - The searchValue is a valid email or phone number - * - The searchValue isn't the current personal detail login - * - We can use chronos or the search value is not the chronos email - */ - if (recentReports.length === 0) { - userToInvite = getUserToInviteOption({ - searchValue, - betas, - }); - } + const userToInvite = getUserToInviteOption({ + searchValue, + betas, + shouldGetOptionToInvite: recentReports.length === 0, + }); return { personalDetails: [], From 2bd5401888026852afd6b0df352350e9f0de8f65 Mon Sep 17 00:00:00 2001 From: nkdengineer <161821005+nkdengineer@users.noreply.github.com> Date: Thu, 2 May 2024 12:29:27 +0700 Subject: [PATCH 6/7] Update src/libs/OptionsListUtils.ts Co-authored-by: Marc Glasser --- src/libs/OptionsListUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index adf8cc12e2f9..9f9ef78e1a26 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1552,7 +1552,7 @@ function getUserToInviteOption({ /** * We create a new user option if the following conditions are satisfied: - * - there's no match recent report and personal detail option + * - There's no matching recent report and personal detail option * - The searchValue is a valid email or phone number * - The searchValue isn't the current personal detail login * - We can use chronos or the search value is not the chronos email From 23960ac998b8ce53a073c1e3825b54f3764472fe Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 2 May 2024 12:56:58 +0700 Subject: [PATCH 7/7] refactor getUserToInviteOption function --- src/libs/OptionsListUtils.ts | 130 +++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 61 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 4ea15a1615b1..3bebd5a0813a 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -173,7 +173,6 @@ type GetUserToInviteConfig = { betas: OnyxEntry; reportActions?: ReportActions; showChatPreviewLine?: boolean; - shouldGetOptionToInvite: boolean; }; type MemberForList = { @@ -1547,6 +1546,13 @@ function orderOptions(options: ReportUtils.OptionData[], searchValue: string | u ); } +/** + * We create a new user option if the following conditions are satisfied: + * - There's no matching recent report and personal detail option + * - The searchValue is a valid email or phone number + * - The searchValue isn't the current personal detail login + * - We can use chronos or the search value is not the chronos email + */ function getUserToInviteOption({ searchValue, excludeUnknownUsers = false, @@ -1555,58 +1561,56 @@ function getUserToInviteOption({ betas, reportActions = {}, showChatPreviewLine = false, - shouldGetOptionToInvite, }: GetUserToInviteConfig): ReportUtils.OptionData | null { - let userToInvite: ReportUtils.OptionData | null = null; const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchValue))); + const isCurrentUserLogin = isCurrentUser({login: searchValue} as PersonalDetails); + const isInSelectedOption = selectedOptions.some((option) => 'login' in option && option.login === searchValue); + const isValidEmail = Str.isValidEmail(searchValue) && !Str.isDomainEmail(searchValue) && !Str.endsWith(searchValue, CONST.SMS.DOMAIN); + const isValidPhoneNumber = parsedPhoneNumber.possible && Str.isValidE164Phone(LoginUtils.getPhoneNumberWithoutSpecialChars(parsedPhoneNumber.number?.input ?? '')); + const isInOptionToExclude = + optionsToExclude.findIndex((optionToExclude) => 'login' in optionToExclude && optionToExclude.login === PhoneNumber.addSMSDomainIfPhoneNumber(searchValue).toLowerCase()) !== -1; + const isChronosEmail = searchValue === CONST.EMAIL.CHRONOS; - /** - * We create a new user option if the following conditions are satisfied: - * - There's no matching recent report and personal detail option - * - The searchValue is a valid email or phone number - * - The searchValue isn't the current personal detail login - * - We can use chronos or the search value is not the chronos email - */ if ( - searchValue && - shouldGetOptionToInvite && - !isCurrentUser({login: searchValue} as PersonalDetails) && - selectedOptions.every((option) => 'login' in option && option.login !== searchValue) && - ((Str.isValidEmail(searchValue) && !Str.isDomainEmail(searchValue) && !Str.endsWith(searchValue, CONST.SMS.DOMAIN)) || - (parsedPhoneNumber.possible && Str.isValidE164Phone(LoginUtils.getPhoneNumberWithoutSpecialChars(parsedPhoneNumber.number?.input ?? '')))) && - !optionsToExclude.find((optionToExclude) => 'login' in optionToExclude && optionToExclude.login === PhoneNumber.addSMSDomainIfPhoneNumber(searchValue).toLowerCase()) && - (searchValue !== CONST.EMAIL.CHRONOS || Permissions.canUseChronos(betas)) && - !excludeUnknownUsers + !searchValue || + isCurrentUserLogin || + isInSelectedOption || + (!isValidEmail && !isValidPhoneNumber) || + isInOptionToExclude || + (isChronosEmail && !Permissions.canUseChronos(betas)) || + excludeUnknownUsers ) { - // Generates an optimistic account ID for new users not yet saved in Onyx - const optimisticAccountID = UserUtils.generateAccountID(searchValue); - const personalDetailsExtended = { - ...allPersonalDetails, - [optimisticAccountID]: { - accountID: optimisticAccountID, - login: searchValue, - }, - }; - userToInvite = createOption([optimisticAccountID], personalDetailsExtended, null, reportActions, { - showChatPreviewLine, - }); - userToInvite.isOptimisticAccount = true; - 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 - userToInvite.alternateText = userToInvite.alternateText || searchValue; - - // If user doesn't exist, use a fallback avatar - userToInvite.icons = [ - { - source: UserUtils.getAvatar('', optimisticAccountID), - name: searchValue, - type: CONST.ICON_TYPE_AVATAR, - }, - ]; + return null; } + // Generates an optimistic account ID for new users not yet saved in Onyx + const optimisticAccountID = UserUtils.generateAccountID(searchValue); + const personalDetailsExtended = { + ...allPersonalDetails, + [optimisticAccountID]: { + accountID: optimisticAccountID, + login: searchValue, + }, + }; + const userToInvite = createOption([optimisticAccountID], personalDetailsExtended, null, reportActions, { + showChatPreviewLine, + }); + userToInvite.isOptimisticAccount = true; + 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 + userToInvite.alternateText = userToInvite.alternateText || searchValue; + + // If user doesn't exist, use a fallback avatar + userToInvite.icons = [ + { + source: UserUtils.getAvatar('', optimisticAccountID), + name: searchValue, + type: CONST.ICON_TYPE_AVATAR, + }, + ]; + return userToInvite; } @@ -1923,16 +1927,18 @@ function getOptions( .concat(recentReportOptions) .find((option) => option.login === PhoneNumber.addSMSDomainIfPhoneNumber(searchValue ?? '').toLowerCase() || option.login === searchValue?.toLowerCase()); - const userToInvite = getUserToInviteOption({ - searchValue, - excludeUnknownUsers, - optionsToExclude, - selectedOptions, - betas, - reportActions, - showChatPreviewLine, - shouldGetOptionToInvite: noOptions || noOptionsMatchExactly, - }); + const userToInvite = + noOptions || noOptionsMatchExactly + ? getUserToInviteOption({ + searchValue, + excludeUnknownUsers, + optionsToExclude, + selectedOptions, + betas, + reportActions, + showChatPreviewLine, + }) + : null; // If we are prioritizing 1:1 chats in search, do it only once we started searching if (sortByReportTypeInSearch && searchValue !== '') { @@ -2361,11 +2367,13 @@ function filterOptions(options: Options, searchInputValue: string, betas: OnyxEn }, options); const recentReports = matchResults.recentReports.concat(matchResults.personalDetails); - const userToInvite = getUserToInviteOption({ - searchValue, - betas, - shouldGetOptionToInvite: recentReports.length === 0, - }); + const userToInvite = + recentReports.length === 0 + ? getUserToInviteOption({ + searchValue, + betas, + }) + : null; return { personalDetails: [],