From 9bf59e79b2ac68eceedc272dedf94df73f5ec90a Mon Sep 17 00:00:00 2001
From: FitseTLT <engfitsum@gmail.com>
Date: Wed, 8 Jan 2025 19:11:12 +0300
Subject: [PATCH 1/7] update group chat creation

---
 src/pages/NewChatPage.tsx | 59 ++++++++++++++++++++++-----------------
 1 file changed, 34 insertions(+), 25 deletions(-)

diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx
index 65aa253ff09d..536fc39974f4 100755
--- a/src/pages/NewChatPage.tsx
+++ b/src/pages/NewChatPage.tsx
@@ -186,17 +186,45 @@ function NewChatPage() {
 
         return [sectionsList, firstKey];
     }, [debouncedSearchTerm, selectedOptions, recentReports, personalDetails, translate, userToInvite]);
+    /**
+     * Removes a selected option from list if already selected. If not already selected add this option to the list.
+     * @param  option
+     */
+    const toggleOption = useMemo(
+        () => (option: ListItem & Partial<OptionData>) => {
+            const isOptionInList = !!option.isSelected;
+
+            let newSelectedOptions;
+
+            if (isOptionInList) {
+                newSelectedOptions = reject(selectedOptions, (selectedOption) => selectedOption.login === option.login);
+            } else {
+                newSelectedOptions = [...selectedOptions, {...option, isSelected: true, selected: true, reportID: option.reportID ?? '-1'}];
+            }
+
+            selectionListRef?.current?.clearInputAfterSelect?.();
+
+            setSelectedOptions(newSelectedOptions);
+        },
+        [selectedOptions, setSelectedOptions],
+    );
 
     /**
-     * Creates a new 1:1 chat with the option and the current user,
+     * If there are selected options already then it will toggle the option otherwise
+     * creates a new 1:1 chat with the option and the current user,
      * or navigates to the existing chat if one with those participants already exists.
      */
-    const createChat = useCallback(
+    const selectOption = useCallback(
         (option?: OptionsListUtils.Option) => {
             if (option?.isSelfDM) {
                 Navigation.dismissModal(option.reportID);
                 return;
             }
+            if (selectedOptions.length && option) {
+                toggleOption(option);
+                return;
+            }
+
             let login = '';
 
             if (option?.login) {
@@ -210,7 +238,7 @@ function NewChatPage() {
             }
             Report.navigateToAndOpenReport([login]);
         },
-        [selectedOptions],
+        [selectedOptions, toggleOption],
     );
 
     const itemRightSideComponent = useCallback(
@@ -218,25 +246,6 @@ function NewChatPage() {
             if (!!item.isSelfDM || (item.login && excludedGroupEmails.includes(item.login))) {
                 return null;
             }
-            /**
-             * Removes a selected option from list if already selected. If not already selected add this option to the list.
-             * @param  option
-             */
-            function toggleOption(option: ListItem & Partial<OptionData>) {
-                const isOptionInList = !!option.isSelected;
-
-                let newSelectedOptions;
-
-                if (isOptionInList) {
-                    newSelectedOptions = reject(selectedOptions, (selectedOption) => selectedOption.login === option.login);
-                } else {
-                    newSelectedOptions = [...selectedOptions, {...option, isSelected: true, selected: true, reportID: option.reportID ?? '-1'}];
-                }
-
-                selectionListRef?.current?.clearInputAfterSelect?.();
-
-                setSelectedOptions(newSelectedOptions);
-            }
 
             if (item.isSelected) {
                 return (
@@ -265,7 +274,7 @@ function NewChatPage() {
                 />
             );
         },
-        [selectedOptions, setSelectedOptions, styles.alignItemsCenter, styles.buttonDefaultHovered, styles.flexRow, styles.ml0, styles.ml5, styles.optionSelectCircle, styles.pl2, translate],
+        [toggleOption, styles.alignItemsCenter, styles.buttonDefaultHovered, styles.flexRow, styles.ml0, styles.ml5, styles.optionSelectCircle, styles.pl2, translate],
     );
 
     const createGroup = useCallback(() => {
@@ -329,9 +338,9 @@ function NewChatPage() {
                     onChangeText={setSearchTerm}
                     textInputLabel={translate('selectionList.nameEmailOrPhoneNumber')}
                     headerMessage={headerMessage}
-                    onSelectRow={createChat}
+                    onSelectRow={selectOption}
                     shouldSingleExecuteRowSelect
-                    onConfirm={(e, option) => (selectedOptions.length > 0 ? createGroup() : createChat(option))}
+                    onConfirm={(e, option) => (selectedOptions.length > 0 ? createGroup() : selectOption(option))}
                     rightHandSideComponent={itemRightSideComponent}
                     footerContent={footerContent}
                     showLoadingPlaceholder={!areOptionsInitialized}

From 75d700d6da28498409d73d3a8c773aca7bf766ad Mon Sep 17 00:00:00 2001
From: FitseTLT <engfitsum@gmail.com>
Date: Wed, 8 Jan 2025 19:12:27 +0300
Subject: [PATCH 2/7] filter out self dm on group creation flow

---
 src/pages/NewChatPage.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx
index 536fc39974f4..ee823804a1dc 100755
--- a/src/pages/NewChatPage.tsx
+++ b/src/pages/NewChatPage.tsx
@@ -157,7 +157,7 @@ function NewChatPage() {
 
         sectionsList.push({
             title: translate('common.recents'),
-            data: recentReports,
+            data: selectedOptions.length ? recentReports.filter((option) => !option.isSelfDM) : recentReports,
             shouldShow: !isEmpty(recentReports),
         });
         if (!firstKey) {

From 79b053443371c8fdd6b7010e31509c154918c0da Mon Sep 17 00:00:00 2001
From: FitseTLT <engfitsum@gmail.com>
Date: Thu, 9 Jan 2025 23:55:06 +0300
Subject: [PATCH 3/7] fix lint

---
 src/libs/ReportUtils.ts   | 2 +-
 src/pages/NewChatPage.tsx | 7 +++++--
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 8a5ae8b1d102..ca0d6d3a8523 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -531,7 +531,7 @@ type OptionData = {
     icons?: Icon[];
     iouReportAmount?: number;
     displayName?: string;
-} & Report;
+} & Omit<Report, 'reportID'>;
 
 type OnyxDataTaskAssigneeChat = {
     optimisticData: OnyxUpdate[];
diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx
index ee823804a1dc..9feb16f4ab4e 100755
--- a/src/pages/NewChatPage.tsx
+++ b/src/pages/NewChatPage.tsx
@@ -199,7 +199,7 @@ function NewChatPage() {
             if (isOptionInList) {
                 newSelectedOptions = reject(selectedOptions, (selectedOption) => selectedOption.login === option.login);
             } else {
-                newSelectedOptions = [...selectedOptions, {...option, isSelected: true, selected: true, reportID: option.reportID ?? '-1'}];
+                newSelectedOptions = [...selectedOptions, {...option, isSelected: true, selected: true, reportID: option.reportID}];
             }
 
             selectionListRef?.current?.clearInputAfterSelect?.();
@@ -281,7 +281,10 @@ function NewChatPage() {
         if (!personalData || !personalData.login || !personalData.accountID) {
             return;
         }
-        const selectedParticipants: SelectedParticipant[] = selectedOptions.map((option: OptionData) => ({login: option.login ?? '', accountID: option.accountID ?? -1}));
+        const selectedParticipants: SelectedParticipant[] = selectedOptions.map((option: OptionData) => ({
+            login: option.login ?? '',
+            accountID: option.accountID ?? CONST.DEFAULT_NUMBER_ID,
+        }));
         const logins = [...selectedParticipants, {login: personalData.login, accountID: personalData.accountID}];
         Report.setGroupDraft({participants: logins});
         Navigation.navigate(ROUTES.NEW_CHAT_CONFIRM);

From 6df9e9b2ad631826014b4c947a9bdabcb35003e2 Mon Sep 17 00:00:00 2001
From: FitseTLT <engfitsum@gmail.com>
Date: Fri, 10 Jan 2025 15:55:02 +0300
Subject: [PATCH 4/7] change to useCallback

---
 src/pages/NewChatPage.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx
index 8d8da79bfba5..aa6b167c30f2 100755
--- a/src/pages/NewChatPage.tsx
+++ b/src/pages/NewChatPage.tsx
@@ -191,8 +191,8 @@ function NewChatPage() {
      * Removes a selected option from list if already selected. If not already selected add this option to the list.
      * @param  option
      */
-    const toggleOption = useMemo(
-        () => (option: ListItem & Partial<OptionData>) => {
+    const toggleOption = useCallback(
+        (option: ListItem & Partial<OptionData>) => {
             const isOptionInList = !!option.isSelected;
 
             let newSelectedOptions;

From ef09dba9f13b9b989afc381c84f2719c29300919 Mon Sep 17 00:00:00 2001
From: FitseTLT <engfitsum@gmail.com>
Date: Fri, 10 Jan 2025 16:03:35 +0300
Subject: [PATCH 5/7] fix ts error

---
 src/libs/ReportUtils.ts   | 2 +-
 src/pages/NewChatPage.tsx | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 77c235ea2dff..7494901cf786 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -531,7 +531,7 @@ type OptionData = {
     icons?: Icon[];
     iouReportAmount?: number;
     displayName?: string;
-} & Omit<Report, 'reportID'>;
+} & Report;
 
 type OnyxDataTaskAssigneeChat = {
     optimisticData: OnyxUpdate[];
diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx
index aa6b167c30f2..34965a568521 100755
--- a/src/pages/NewChatPage.tsx
+++ b/src/pages/NewChatPage.tsx
@@ -200,7 +200,7 @@ function NewChatPage() {
             if (isOptionInList) {
                 newSelectedOptions = reject(selectedOptions, (selectedOption) => selectedOption.login === option.login);
             } else {
-                newSelectedOptions = [...selectedOptions, {...option, isSelected: true, selected: true, reportID: option.reportID}];
+                newSelectedOptions = [...selectedOptions, {...option, isSelected: true, selected: true, reportID: option.reportID ?? `${CONST.DEFAULT_NUMBER_ID}`}];
             }
 
             selectionListRef?.current?.clearInputAfterSelect?.();

From e5de3adafbda0a259579ce8012737f4b7a6c29e0 Mon Sep 17 00:00:00 2001
From: FitseTLT <engfitsum@gmail.com>
Date: Tue, 14 Jan 2025 15:34:33 +0300
Subject: [PATCH 6/7] fix lint

---
 src/pages/NewChatPage.tsx | 51 +++++++++++++++++++++++----------------
 1 file changed, 30 insertions(+), 21 deletions(-)

diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx
index 4b8f7a7d5989..ebb9ef81d457 100755
--- a/src/pages/NewChatPage.tsx
+++ b/src/pages/NewChatPage.tsx
@@ -25,13 +25,22 @@ import useSafeAreaInsets from '@hooks/useSafeAreaInsets';
 import useScreenWrapperTranstionStatus from '@hooks/useScreenWrapperTransitionStatus';
 import useStyledSafeAreaInsets from '@hooks/useStyledSafeAreaInsets';
 import useThemeStyles from '@hooks/useThemeStyles';
-import * as DeviceCapabilities from '@libs/DeviceCapabilities';
+import {navigateToAndOpenReport, searchInServer, setGroupDraft} from '@libs/actions/Report';
+import {canUseTouchScreen} from '@libs/DeviceCapabilities';
 import Log from '@libs/Log';
 import Navigation from '@libs/Navigation/Navigation';
-import * as OptionsListUtils from '@libs/OptionsListUtils';
+import type {Option, Section} from '@libs/OptionsListUtils';
+import {
+    filterAndOrderOptions,
+    formatSectionsFromSearchTerm,
+    getFirstKeyForList,
+    getHeaderMessage,
+    getPersonalDetailSearchTerms,
+    getUserToInviteOption,
+    getValidOptions,
+} from '@libs/OptionsListUtils';
 import type {OptionData} from '@libs/ReportUtils';
 import variables from '@styles/variables';
-import * as Report from '@userActions/Report';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
@@ -51,7 +60,7 @@ function useOptions() {
     });
 
     const defaultOptions = useMemo(() => {
-        const filteredOptions = OptionsListUtils.getValidOptions(
+        const filteredOptions = getValidOptions(
             {
                 reports: listOptions.reports ?? [],
                 personalDetails: listOptions.personalDetails ?? [],
@@ -66,7 +75,7 @@ function useOptions() {
     }, [betas, listOptions.personalDetails, listOptions.reports, selectedOptions]);
 
     const options = useMemo(() => {
-        const filteredOptions = OptionsListUtils.filterAndOrderOptions(defaultOptions, debouncedSearchTerm, {
+        const filteredOptions = filterAndOrderOptions(defaultOptions, debouncedSearchTerm, {
             selectedOptions,
             maxRecentReportsToShow: CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW,
         });
@@ -75,11 +84,11 @@ function useOptions() {
     }, [debouncedSearchTerm, defaultOptions, selectedOptions]);
     const cleanSearchTerm = useMemo(() => debouncedSearchTerm.trim().toLowerCase(), [debouncedSearchTerm]);
     const headerMessage = useMemo(() => {
-        return OptionsListUtils.getHeaderMessage(
+        return getHeaderMessage(
             options.personalDetails.length + options.recentReports.length !== 0,
             !!options.userToInvite,
             debouncedSearchTerm.trim(),
-            selectedOptions.some((participant) => OptionsListUtils.getPersonalDetailSearchTerms(participant).join(' ').toLowerCase?.().includes(cleanSearchTerm)),
+            selectedOptions.some((participant) => getPersonalDetailSearchTerms(participant).join(' ').toLowerCase?.().includes(cleanSearchTerm)),
         );
     }, [cleanSearchTerm, debouncedSearchTerm, options.personalDetails.length, options.recentReports.length, options.userToInvite, selectedOptions]);
 
@@ -88,7 +97,7 @@ function useOptions() {
             return;
         }
 
-        Report.searchInServer(debouncedSearchTerm);
+        searchInServer(debouncedSearchTerm);
     }, [debouncedSearchTerm]);
 
     useEffect(() => {
@@ -102,7 +111,7 @@ function useOptions() {
             }
             let participantOption: OptionData | undefined | null = listOptions.personalDetails.find((option) => option.accountID === participant.accountID);
             if (!participantOption) {
-                participantOption = OptionsListUtils.getUserToInviteOption({
+                participantOption = getUserToInviteOption({
                     searchValue: participant?.login,
                 });
             }
@@ -146,14 +155,14 @@ function NewChatPage() {
         useOptions();
 
     const [sections, firstKeyForList] = useMemo(() => {
-        const sectionsList: OptionsListUtils.Section[] = [];
+        const sectionsList: Section[] = [];
         let firstKey = '';
 
-        const formatResults = OptionsListUtils.formatSectionsFromSearchTerm(debouncedSearchTerm, selectedOptions, recentReports, personalDetails);
+        const formatResults = formatSectionsFromSearchTerm(debouncedSearchTerm, selectedOptions, recentReports, personalDetails);
         sectionsList.push(formatResults.section);
 
         if (!firstKey) {
-            firstKey = OptionsListUtils.getFirstKeyForList(formatResults.section.data);
+            firstKey = getFirstKeyForList(formatResults.section.data);
         }
 
         sectionsList.push({
@@ -162,7 +171,7 @@ function NewChatPage() {
             shouldShow: !isEmpty(recentReports),
         });
         if (!firstKey) {
-            firstKey = OptionsListUtils.getFirstKeyForList(recentReports);
+            firstKey = getFirstKeyForList(recentReports);
         }
 
         sectionsList.push({
@@ -171,7 +180,7 @@ function NewChatPage() {
             shouldShow: !isEmpty(personalDetails),
         });
         if (!firstKey) {
-            firstKey = OptionsListUtils.getFirstKeyForList(personalDetails);
+            firstKey = getFirstKeyForList(personalDetails);
         }
 
         if (userToInvite) {
@@ -181,7 +190,7 @@ function NewChatPage() {
                 shouldShow: true,
             });
             if (!firstKey) {
-                firstKey = OptionsListUtils.getFirstKeyForList([userToInvite]);
+                firstKey = getFirstKeyForList([userToInvite]);
             }
         }
 
@@ -218,7 +227,7 @@ function NewChatPage() {
      * or navigates to the existing chat if one with those participants already exists.
      */
     const selectOption = useCallback(
-        (option?: OptionsListUtils.Option) => {
+        (option?: Option) => {
             if (option?.isSelfDM) {
                 Navigation.dismissModal(option.reportID);
                 return;
@@ -239,13 +248,13 @@ function NewChatPage() {
                 Log.warn('Tried to create chat with empty login');
                 return;
             }
-            Report.navigateToAndOpenReport([login]);
+            navigateToAndOpenReport([login]);
         },
         [selectedOptions, toggleOption],
     );
 
     const itemRightSideComponent = useCallback(
-        (item: ListItem & OptionsListUtils.Option, isFocused?: boolean) => {
+        (item: ListItem & Option, isFocused?: boolean) => {
             if (!!item.isSelfDM || (item.login && excludedGroupEmails.includes(item.login))) {
                 return null;
             }
@@ -289,7 +298,7 @@ function NewChatPage() {
             accountID: option.accountID ?? CONST.DEFAULT_NUMBER_ID,
         }));
         const logins = [...selectedParticipants, {login: personalData.login, accountID: personalData.accountID}];
-        Report.setGroupDraft({participants: logins});
+        setGroupDraft({participants: logins});
         Keyboard.dismiss();
         Navigation.navigate(ROUTES.NEW_CHAT_CONFIRM);
     }, [selectedOptions, personalData]);
@@ -336,7 +345,7 @@ function NewChatPage() {
                 // This is because when wrapping whole screen the screen was freezing when changing Tabs.
                 keyboardVerticalOffset={variables.contentHeaderHeight + top + variables.tabSelectorButtonHeight + variables.tabSelectorButtonPadding}
             >
-                <SelectionList<OptionsListUtils.Option & ListItem>
+                <SelectionList<Option & ListItem>
                     ref={selectionListRef}
                     ListItem={UserListItem}
                     sections={areOptionsInitialized ? sections : CONST.EMPTY_ARRAY}
@@ -351,7 +360,7 @@ function NewChatPage() {
                     rightHandSideComponent={itemRightSideComponent}
                     footerContent={footerContent}
                     showLoadingPlaceholder={!areOptionsInitialized}
-                    shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
+                    shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()}
                     isLoadingNewOptions={!!isSearchingForReports}
                     initiallyFocusedOptionKey={firstKeyForList}
                     shouldTextInputInterceptSwipe

From 444384d93232fe715dcbc8dc8de3a9da84dffc4b Mon Sep 17 00:00:00 2001
From: FitseTLT <engfitsum@gmail.com>
Date: Mon, 20 Jan 2025 14:49:43 +0300
Subject: [PATCH 7/7] minor update

---
 src/pages/NewChatPage.tsx | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx
index ebb9ef81d457..e2df8ceda5c9 100755
--- a/src/pages/NewChatPage.tsx
+++ b/src/pages/NewChatPage.tsx
@@ -196,9 +196,9 @@ function NewChatPage() {
 
         return [sectionsList, firstKey];
     }, [debouncedSearchTerm, selectedOptions, recentReports, personalDetails, translate, userToInvite]);
+
     /**
      * Removes a selected option from list if already selected. If not already selected add this option to the list.
-     * @param  option
      */
     const toggleOption = useCallback(
         (option: ListItem & Partial<OptionData>) => {
@@ -213,7 +213,6 @@ function NewChatPage() {
             }
 
             selectionListRef?.current?.clearInputAfterSelect?.();
-
             selectionListRef.current?.focusTextInput();
             selectionListRef?.current?.scrollToIndex(Math.max(newSelectedOptions.length - 1, 0), true);
             setSelectedOptions(newSelectedOptions);
