From ded66a7c08444389e60d584edc707917cb3f38cd Mon Sep 17 00:00:00 2001
From: Bernhard Owen Josephus <bernhard.josephus@gmail.com>
Date: Mon, 28 Jul 2025 00:26:24 +0800
Subject: [PATCH 1/9] optimistically mark transaction as deleted and filter it
 out

---
 src/libs/SearchUIUtils.ts       |  2 +-
 src/libs/actions/Search.ts      | 23 +++++++++++++++++++++--
 src/pages/Search/SearchPage.tsx |  2 +-
 3 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts
index 14fe4733f01e..237d8ce84ec4 100644
--- a/src/libs/SearchUIUtils.ts
+++ b/src/libs/SearchUIUtils.ts
@@ -1473,7 +1473,7 @@ function getSortedReportActionData(data: ReportActionListItemType[]) {
  * Checks if the search results contain any data, useful for determining if the search results are empty.
  */
 function isSearchResultsEmpty(searchResults: SearchResults) {
-    return !Object.keys(searchResults?.data).some((key) => key.startsWith(ONYXKEYS.COLLECTION.TRANSACTION));
+    return !Object.values(searchResults?.data).some((data) => 'transactionID' in data && (data as SearchTransaction)?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
 }
 
 /**
diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts
index d2b27aff7621..00bac95ce2e3 100644
--- a/src/libs/actions/Search.ts
+++ b/src/libs/actions/Search.ts
@@ -441,8 +441,27 @@ function unholdMoneyRequestOnSearch(hash: number, transactionIDList: string[]) {
 }
 
 function deleteMoneyRequestOnSearch(hash: number, transactionIDList: string[]) {
-    const {optimisticData, finallyData} = getOnyxLoadingData(hash);
-    API.write(WRITE_COMMANDS.DELETE_MONEY_REQUEST_ON_SEARCH, {hash, transactionIDList}, {optimisticData, finallyData});
+    const {optimisticData: loadingOptimisticData, finallyData} = getOnyxLoadingData(hash);
+    const optimisticData: OnyxUpdate[] = [
+        ...loadingOptimisticData,
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
+            value: { 
+                data: Object.fromEntries(transactionIDList.map((transactionID) => [`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}])) as Partial<SearchTransaction>
+            },
+        },
+    ]
+    const failureData: OnyxUpdate[] = [
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
+            value: { 
+                data: Object.fromEntries(transactionIDList.map((transactionID) => [`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {pendingAction: null}])) as Partial<SearchTransaction>
+            },
+        },
+    ]
+    API.write(WRITE_COMMANDS.DELETE_MONEY_REQUEST_ON_SEARCH, {hash, transactionIDList}, {optimisticData, failureData, finallyData});
 }
 
 type Params = Record<string, ExportSearchItemsToCSVParams>;
diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx
index 3f748195e886..a42b3875e030 100644
--- a/src/pages/Search/SearchPage.tsx
+++ b/src/pages/Search/SearchPage.tsx
@@ -381,11 +381,11 @@ function SearchPage({route}: SearchPageProps) {
         }
 
         setIsDeleteExpensesConfirmModalVisible(false);
-        deleteMoneyRequestOnSearch(hash, selectedTransactionsKeys);
 
         // Translations copy for delete modal depends on amount of selected items,
         // We need to wait for modal to fully disappear before clearing them to avoid translation flicker between singular vs plural
         InteractionManager.runAfterInteractions(() => {
+            deleteMoneyRequestOnSearch(hash, selectedTransactionsKeys);
             clearSelectedTransactions();
         });
     };

From 679792528a6499c23b22820f836e995b4d533fc9 Mon Sep 17 00:00:00 2001
From: Bernhard Owen Josephus <bernhard.josephus@gmail.com>
Date: Mon, 28 Jul 2025 00:37:09 +0800
Subject: [PATCH 2/9] prettier

---
 src/libs/actions/Search.ts | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts
index 00bac95ce2e3..7250d5f8e100 100644
--- a/src/libs/actions/Search.ts
+++ b/src/libs/actions/Search.ts
@@ -447,20 +447,24 @@ function deleteMoneyRequestOnSearch(hash: number, transactionIDList: string[]) {
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
-            value: { 
-                data: Object.fromEntries(transactionIDList.map((transactionID) => [`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}])) as Partial<SearchTransaction>
+            value: {
+                data: Object.fromEntries(
+                    transactionIDList.map((transactionID) => [`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}]),
+                ) as Partial<SearchTransaction>,
             },
         },
-    ]
+    ];
     const failureData: OnyxUpdate[] = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
-            value: { 
-                data: Object.fromEntries(transactionIDList.map((transactionID) => [`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {pendingAction: null}])) as Partial<SearchTransaction>
+            value: {
+                data: Object.fromEntries(
+                    transactionIDList.map((transactionID) => [`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {pendingAction: null}]),
+                ) as Partial<SearchTransaction>,
             },
         },
-    ]
+    ];
     API.write(WRITE_COMMANDS.DELETE_MONEY_REQUEST_ON_SEARCH, {hash, transactionIDList}, {optimisticData, failureData, finallyData});
 }
 

From 9836943a29c878a85d32159855307a6696d32bb4 Mon Sep 17 00:00:00 2001
From: Bernhard Owen Josephus <bernhard.josephus@gmail.com>
Date: Wed, 20 Aug 2025 14:28:21 +0700
Subject: [PATCH 3/9] refactor

---
 src/libs/SearchUIUtils.ts | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts
index 133ad43cc83c..48ae96e01b09 100644
--- a/src/libs/SearchUIUtils.ts
+++ b/src/libs/SearchUIUtils.ts
@@ -1670,7 +1670,11 @@ function getSortedReportActionData(data: ReportActionListItemType[], localeCompa
  * Checks if the search results contain any data, useful for determining if the search results are empty.
  */
 function isSearchResultsEmpty(searchResults: SearchResults) {
-    return !Object.values(searchResults?.data).some((data) => 'transactionID' in data && (data as SearchTransaction)?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
+    return !Object.keys(searchResults?.data).some(
+        (key) =>
+            key.startsWith(ONYXKEYS.COLLECTION.TRANSACTION) &&
+            (searchResults?.data[key as keyof typeof searchResults.data] as SearchTransaction)?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
+    );
 }
 
 /**

From 618f4cd85102aa5e5216e5ee65c6ac4e8ca4618d Mon Sep 17 00:00:00 2001
From: Bernhard Owen Josephus <bernhard.josephus@gmail.com>
Date: Wed, 20 Aug 2025 14:28:28 +0700
Subject: [PATCH 4/9] add test

---
 tests/unit/Search/SearchUIUtilsTest.ts | 62 +++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts
index 08d44333a777..ebef8a745b38 100644
--- a/tests/unit/Search/SearchUIUtilsTest.ts
+++ b/tests/unit/Search/SearchUIUtilsTest.ts
@@ -1,4 +1,5 @@
 import Onyx from 'react-native-onyx';
+import createRandomTransaction from 'tests/utils/collections/transaction';
 import ChatListItem from '@components/SelectionList/ChatListItem';
 import TransactionGroupListItem from '@components/SelectionList/Search/TransactionGroupListItem';
 import TransactionListItem from '@components/SelectionList/Search/TransactionListItem';
@@ -15,7 +16,7 @@ import IntlStore from '@src/languages/IntlStore';
 import * as SearchUIUtils from '@src/libs/SearchUIUtils';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type * as OnyxTypes from '@src/types/onyx';
-import type {SearchDataTypes} from '@src/types/onyx/SearchResults';
+import type {SearchDataTypes, SearchTransaction} from '@src/types/onyx/SearchResults';
 import {formatPhoneNumber, localeCompare} from '../../utils/TestHelper';
 import waitForBatchedUpdates from '../../utils/waitForBatchedUpdates';
 
@@ -1691,6 +1692,65 @@ describe('SearchUIUtils', () => {
         });
     });
 
+    describe('Test isSearchResultsEmpty', () => {
+        it('should return true when all transactions have delete pending action', () => {
+            const results: OnyxTypes.SearchResults = {
+                data: {
+                    personalDetailsList: {},
+                    transactions_1: {
+                        accountID: 2074551,
+                        amount: 0,
+                        canDelete: false,
+                        canHold: true,
+                        canUnhold: false,
+                        category: 'Employee Meals Remote (Fringe Benefit)',
+                        action: 'approve',
+                        comment: {
+                            comment: '',
+                        },
+                        created: '2025-05-26',
+                        currency: 'USD',
+                        hasEReceipt: false,
+                        isFromOneTransactionReport: true,
+                        managerID: adminAccountID,
+                        merchant: '(none)',
+                        modifiedAmount: -1000,
+                        modifiedCreated: '2025-05-22',
+                        modifiedCurrency: 'USD',
+                        modifiedMerchant: 'Costco Wholesale',
+                        parentTransactionID: '',
+                        policyID: '137DA25D273F2423',
+                        receipt: {
+                            source: 'https://www.expensify.com/receipts/fake.jpg',
+                            state: CONST.IOU.RECEIPT_STATE.SCAN_COMPLETE,
+                        },
+                        reportID: '6523565988285061',
+                        reportType: 'expense',
+                        tag: '',
+                        transactionID: '1805965960759424086',
+                        transactionThreadReportID: '4139222832581831',
+                        transactionType: 'cash',
+                        pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
+                    },
+                },
+                search: {
+                    type: 'expense',
+                    status: CONST.SEARCH.STATUS.EXPENSE.ALL,
+                    offset: 0,
+                    hasMoreResults: false,
+                    hasResults: true,
+                    isLoading: false,
+                    columnsToShow: {
+                        shouldShowCategoryColumn: true,
+                        shouldShowTagColumn: true,
+                        shouldShowTaxColumn: true,
+                    },
+                },
+            };
+            expect(SearchUIUtils.isSearchResultsEmpty(results)).toBe(true);
+        });
+    });
+
     test('Should show `View` to overlimit approver', () => {
         Onyx.merge(ONYXKEYS.SESSION, {accountID: overlimitApproverAccountID});
         searchResults.data[`policy_${policyID}`].role = CONST.POLICY.ROLE.USER;

From ee976da14413451aeead334d6d464fc46d3fe027 Mon Sep 17 00:00:00 2001
From: Bernhard Owen Josephus <bernhard.josephus@gmail.com>
Date: Thu, 21 Aug 2025 13:17:53 +0700
Subject: [PATCH 5/9] lint

---
 tests/unit/Search/SearchUIUtilsTest.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts
index ebef8a745b38..c86eedc57fa3 100644
--- a/tests/unit/Search/SearchUIUtilsTest.ts
+++ b/tests/unit/Search/SearchUIUtilsTest.ts
@@ -1,5 +1,4 @@
 import Onyx from 'react-native-onyx';
-import createRandomTransaction from 'tests/utils/collections/transaction';
 import ChatListItem from '@components/SelectionList/ChatListItem';
 import TransactionGroupListItem from '@components/SelectionList/Search/TransactionGroupListItem';
 import TransactionListItem from '@components/SelectionList/Search/TransactionListItem';
@@ -16,7 +15,7 @@ import IntlStore from '@src/languages/IntlStore';
 import * as SearchUIUtils from '@src/libs/SearchUIUtils';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type * as OnyxTypes from '@src/types/onyx';
-import type {SearchDataTypes, SearchTransaction} from '@src/types/onyx/SearchResults';
+import type {SearchDataTypes} from '@src/types/onyx/SearchResults';
 import {formatPhoneNumber, localeCompare} from '../../utils/TestHelper';
 import waitForBatchedUpdates from '../../utils/waitForBatchedUpdates';
 
@@ -1697,6 +1696,7 @@ describe('SearchUIUtils', () => {
             const results: OnyxTypes.SearchResults = {
                 data: {
                     personalDetailsList: {},
+                    // eslint-disable-next-line @typescript-eslint/naming-convention
                     transactions_1: {
                         accountID: 2074551,
                         amount: 0,

From 9f2b69324b0b0c47aa059a9c162ae6bba06a7098 Mon Sep 17 00:00:00 2001
From: Bernhard Owen Josephus <bernhard.josephus@gmail.com>
Date: Fri, 22 Aug 2025 14:37:48 +0700
Subject: [PATCH 6/9] fix test

---
 tests/unit/Search/SearchUIUtilsTest.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts
index f873d597a983..7e0b7f108cf4 100644
--- a/tests/unit/Search/SearchUIUtilsTest.ts
+++ b/tests/unit/Search/SearchUIUtilsTest.ts
@@ -1841,6 +1841,7 @@ describe('SearchUIUtils', () => {
                         canUnhold: false,
                         category: 'Employee Meals Remote (Fringe Benefit)',
                         action: 'approve',
+                        allActions: ['approve'],
                         comment: {
                             comment: '',
                         },

From d4398018d3dae48b1a4c2cc04843c974287d2c4b Mon Sep 17 00:00:00 2001
From: Bernhard Owen Josephus <bernhard.josephus@gmail.com>
Date: Fri, 22 Aug 2025 14:40:13 +0700
Subject: [PATCH 7/9] fix pending delete report or transaction is not hidden

---
 .../Search/TransactionGroupListItem.tsx       | 49 +++++++++++--------
 1 file changed, 28 insertions(+), 21 deletions(-)

diff --git a/src/components/SelectionList/Search/TransactionGroupListItem.tsx b/src/components/SelectionList/Search/TransactionGroupListItem.tsx
index 9db2f6da979f..46db56d2bc2c 100644
--- a/src/components/SelectionList/Search/TransactionGroupListItem.tsx
+++ b/src/components/SelectionList/Search/TransactionGroupListItem.tsx
@@ -170,8 +170,13 @@ function TransactionGroupListItem<TItem extends ListItem>({
 
     useSyncFocus(pressableRef, !!isFocused, shouldSyncFocus);
 
+    const pendingAction =
+        (item.pendingAction ?? groupItem.transactions.every((transaction) => transaction.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE))
+            ? CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE
+            : undefined;
+
     return (
-        <OfflineWithFeedback pendingAction={item.pendingAction}>
+        <OfflineWithFeedback pendingAction={pendingAction}>
             <PressableWithFeedback
                 ref={pressableRef}
                 onLongPress={onLongPress}
@@ -204,26 +209,28 @@ function TransactionGroupListItem<TItem extends ListItem>({
                         </View>
                     ) : (
                         groupItem.transactions.map((transaction) => (
-                            <TransactionItemRow
-                                key={transaction.transactionID}
-                                report={transaction.report}
-                                transactionItem={transaction}
-                                isSelected={!!transaction.isSelected}
-                                dateColumnSize={dateColumnSize}
-                                amountColumnSize={amountColumnSize}
-                                taxAmountColumnSize={taxAmountColumnSize}
-                                shouldShowTooltip={showTooltip}
-                                shouldUseNarrowLayout={!isLargeScreenWidth}
-                                shouldShowCheckbox={!!canSelectMultiple}
-                                onCheckboxPress={() => onCheckboxPress?.(transaction as unknown as TItem)}
-                                columns={columns}
-                                onButtonPress={() => {
-                                    openReportInRHP(transaction);
-                                }}
-                                style={[styles.noBorderRadius, shouldUseNarrowLayout ? [styles.p3, styles.pt2] : [styles.ph3, styles.pv1Half]]}
-                                isReportItemChild
-                                isInSingleTransactionReport={groupItem.transactions.length === 1}
-                            />
+                            <OfflineWithFeedback pendingAction={transaction.pendingAction}>
+                                <TransactionItemRow
+                                    key={transaction.transactionID}
+                                    report={transaction.report}
+                                    transactionItem={transaction}
+                                    isSelected={!!transaction.isSelected}
+                                    dateColumnSize={dateColumnSize}
+                                    amountColumnSize={amountColumnSize}
+                                    taxAmountColumnSize={taxAmountColumnSize}
+                                    shouldShowTooltip={showTooltip}
+                                    shouldUseNarrowLayout={!isLargeScreenWidth}
+                                    shouldShowCheckbox={!!canSelectMultiple}
+                                    onCheckboxPress={() => onCheckboxPress?.(transaction as unknown as TItem)}
+                                    columns={columns}
+                                    onButtonPress={() => {
+                                        openReportInRHP(transaction);
+                                    }}
+                                    style={[styles.noBorderRadius, shouldUseNarrowLayout ? [styles.p3, styles.pt2] : [styles.ph3, styles.pv1Half]]}
+                                    isReportItemChild
+                                    isInSingleTransactionReport={groupItem.transactions.length === 1}
+                                />
+                            </OfflineWithFeedback>
                         ))
                     )}
                 </View>

From f59f6e45ea824864a7e288ae52af41e1132f8900 Mon Sep 17 00:00:00 2001
From: Bernhard Owen Josephus <bernhard.josephus@gmail.com>
Date: Sun, 24 Aug 2025 22:24:15 +0700
Subject: [PATCH 8/9] rename

---
 tests/unit/Search/SearchUIUtilsTest.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts
index 7e0b7f108cf4..cca50306667a 100644
--- a/tests/unit/Search/SearchUIUtilsTest.ts
+++ b/tests/unit/Search/SearchUIUtilsTest.ts
@@ -1833,7 +1833,7 @@ describe('SearchUIUtils', () => {
                 data: {
                     personalDetailsList: {},
                     // eslint-disable-next-line @typescript-eslint/naming-convention
-                    transactions_1: {
+                    transactions_1805965960759424086: {
                         accountID: 2074551,
                         amount: 0,
                         canDelete: false,

From 0ce4d8dd957ca071298032d5cf83fe4e1fb7283c Mon Sep 17 00:00:00 2001
From: Bernhard Owen Josephus <bernhard.josephus@gmail.com>
Date: Sun, 24 Aug 2025 22:25:32 +0700
Subject: [PATCH 9/9] lint

---
 .../SelectionList/Search/TransactionGroupListItem.tsx       | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/components/SelectionList/Search/TransactionGroupListItem.tsx b/src/components/SelectionList/Search/TransactionGroupListItem.tsx
index 46db56d2bc2c..13629002e70a 100644
--- a/src/components/SelectionList/Search/TransactionGroupListItem.tsx
+++ b/src/components/SelectionList/Search/TransactionGroupListItem.tsx
@@ -209,9 +209,11 @@ function TransactionGroupListItem<TItem extends ListItem>({
                         </View>
                     ) : (
                         groupItem.transactions.map((transaction) => (
-                            <OfflineWithFeedback pendingAction={transaction.pendingAction}>
+                            <OfflineWithFeedback
+                                key={transaction.transactionID}
+                                pendingAction={transaction.pendingAction}
+                            >
                                 <TransactionItemRow
-                                    key={transaction.transactionID}
                                     report={transaction.report}
                                     transactionItem={transaction}
                                     isSelected={!!transaction.isSelected}
