Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b09211b
chore: draft sol
lorretheboy Nov 16, 2025
dba9e12
merge main
lorretheboy Nov 19, 2025
ec6d33e
fix: 74518
lorretheboy Nov 19, 2025
4705a3e
fix: 74519
lorretheboy Nov 19, 2025
45f9f54
fix: 74520
lorretheboy Nov 19, 2025
579f47e
fix: 74521
lorretheboy Nov 19, 2025
cf54e2f
fix: 74534
lorretheboy Nov 19, 2025
9d4ceca
fix: 74537
lorretheboy Nov 19, 2025
e772673
fix: 74539
lorretheboy Nov 19, 2025
d397250
chore: merge main
lorretheboy Nov 23, 2025
4218daf
fix: select issue
lorretheboy Nov 23, 2025
0d2f7f3
fix: 74538
lorretheboy Nov 23, 2025
40f8481
fix: 74538
lorretheboy Nov 23, 2025
113ff85
fix: lint
lorretheboy Nov 23, 2025
20b55fb
fix: conflicts
lorretheboy Dec 3, 2025
7ff612e
fix: eslint
lorretheboy Dec 3, 2025
10d9b51
fix: err message
lorretheboy Dec 3, 2025
89f1fd1
fix: selection issue
lorretheboy Dec 3, 2025
638743f
fix: test
lorretheboy Dec 3, 2025
d82ed03
fix: type err
lorretheboy Dec 3, 2025
f2ed3c1
merge main
lorretheboy Dec 8, 2025
6dc296d
fix: resolve some comments
lorretheboy Dec 9, 2025
fab1efd
fix: resolved conflicts
lorretheboy Dec 9, 2025
004fcb6
fix: update export basic data err msg for empty reports
lorretheboy Dec 9, 2025
9f6e722
fix: prural form
lorretheboy Dec 9, 2025
45880d3
fix: conflicts
lorretheboy Dec 13, 2025
4cecb8e
fix: prural delete msg
lorretheboy Dec 13, 2025
2483f72
fix: conflicts
lorretheboy Dec 18, 2025
79b2322
fix: delete
lorretheboy Dec 18, 2025
5b316fb
fix: conflicts
lorretheboy Dec 22, 2025
56178b0
fix: conflicts
lorretheboy Jan 1, 2026
89c47b2
fix: types
lorretheboy Jan 1, 2026
eb49764
fix: linter
lorretheboy Jan 1, 2026
d8eef1e
fix: ppline
lorretheboy Jan 4, 2026
68ff8b9
fix: conflicts
lorretheboy Jan 5, 2026
99a7eb6
fix: types
lorretheboy Jan 5, 2026
c1f4366
fix: bulkDeleteReports
lorretheboy Jan 5, 2026
9696ac3
fix: conflicts
lorretheboy Jan 11, 2026
578c693
fix: comments
lorretheboy Jan 11, 2026
7325bac
fix: import issue
lorretheboy Jan 11, 2026
35cd604
fix: conflicts
lorretheboy Jan 14, 2026
8a34ddb
fix: tests
lorretheboy Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1401,8 +1401,8 @@ function MoneyReportHeader({
}

const result = await showConfirmModal({
title: translate('iou.deleteReport'),
prompt: translate('iou.deleteReportConfirmation'),
title: translate('iou.deleteReport', {count: transactionCount}),
prompt: translate('iou.deleteReportConfirmation', {count: transactionCount}),
confirmText: translate('common.delete'),
cancelText: translate('common.cancel'),
danger: true,
Expand Down
10 changes: 9 additions & 1 deletion src/components/Search/SearchContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,15 @@ function SearchContextProvider({children}: ChildrenProps) {

if (data.length && data.every(isTransactionReportGroupListItemType)) {
selectedReports = data
.filter((item) => isMoneyRequestReport(item) && item.transactions.length > 0 && item.transactions.every(({keyForList}) => selectedTransactions[keyForList]?.isSelected))
.filter((item) => {
if (!isMoneyRequestReport(item)) {
return false;
}
if (item.transactions.length === 0) {
return !!item.keyForList && selectedTransactions[item.keyForList]?.isSelected;
}
return item.transactions.every(({keyForList}) => selectedTransactions[keyForList]?.isSelected);
})
.map(({reportID, action = CONST.SEARCH.ACTION_TYPES.VIEW, total = CONST.DEFAULT_NUMBER_ID, policyID, allActions = [action], currency, chatReportID}) => ({
reportID,
action,
Expand Down
70 changes: 54 additions & 16 deletions src/components/Search/SearchList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import {turnOnMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
import navigationRef from '@libs/Navigation/navigationRef';
import {getTableMinWidth} from '@libs/SearchUIUtils';
import {getTableMinWidth, isTransactionReportGroupListItemType} from '@libs/SearchUIUtils';
import variables from '@styles/variables';
import type {TransactionPreviewData} from '@userActions/Search';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -196,16 +196,51 @@ function SearchList({
}
return data;
}, [data, groupBy, type]);
const flattenedItemsWithoutPendingDelete = useMemo(() => flattenedItems.filter((t) => t?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE), [flattenedItems]);
const emptyReports = useMemo(() => {
if (type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT && isTransactionGroupListItemArray(data)) {
return data.filter((item) => item.transactions.length === 0);
}
return [];
}, [data, type]);

const selectedItemsLength = useMemo(() => {
return flattenedItemsWithoutPendingDelete.reduce((acc, item) => {
if (item.keyForList && selectedTransactions[item.keyForList]?.isSelected) {
return acc + 1;
}
return acc;
const selectedTransactionsCount = flattenedItems.reduce((acc, item) => {
const isTransactionSelected = !!(item?.keyForList && selectedTransactions[item.keyForList]?.isSelected);
return acc + (isTransactionSelected ? 1 : 0);
}, 0);
}, [flattenedItemsWithoutPendingDelete, selectedTransactions]);

if (type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT && isTransactionGroupListItemArray(data)) {
const selectedEmptyReports = emptyReports.reduce((acc, item) => {
const isEmptyReportSelected = !!(item.keyForList && selectedTransactions[item.keyForList]?.isSelected);
return acc + (isEmptyReportSelected ? 1 : 0);
}, 0);

return selectedEmptyReports + selectedTransactionsCount;
}

return selectedTransactionsCount;
}, [flattenedItems, type, data, emptyReports, selectedTransactions]);

const totalItems = useMemo(() => {
if (type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT && isTransactionGroupListItemArray(data)) {
const selectableEmptyReports = emptyReports.filter((item) => item.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
const selectableTransactions = flattenedItems.filter((item) => {
if ('pendingAction' in item) {
return item.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
}
return true;
});
return selectableEmptyReports.length + selectableTransactions.length;
}

const selectableTransactions = flattenedItems.filter((item) => {
if ('pendingAction' in item) {
return item.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
}
return true;
});
return selectableTransactions.length;
}, [data, type, flattenedItems, emptyReports]);

const itemsWithSelection = useMemo(() => {
return data.map((item) => {
Expand All @@ -216,10 +251,16 @@ function SearchList({
if (!canSelectMultiple) {
itemWithSelection = {...item, isSelected: false};
} else {
const hasAnySelected = item.transactions.some((t) => t.keyForList && selectedTransactions[t.keyForList]?.isSelected);
const isEmptyReportSelected =
item.transactions.length === 0 && isTransactionReportGroupListItemType(item) && !!(item.keyForList && selectedTransactions[item.keyForList]?.isSelected);

const hasAnySelected = item.transactions.some((t) => t.keyForList && selectedTransactions[t.keyForList]?.isSelected) || isEmptyReportSelected;

if (!hasAnySelected) {
itemWithSelection = {...item, isSelected: false};
} else if (isEmptyReportSelected) {
isSelected = true;
itemWithSelection = {...item, isSelected};
} else {
let allNonDeletedSelected = true;
let hasNonDeletedTransactions = false;
Expand Down Expand Up @@ -312,10 +353,7 @@ function SearchList({
if (shouldPreventLongPressRow || !isSmallScreenWidth || item?.isDisabled || item?.isDisabledCheckbox) {
return;
}
// disable long press for empty expense reports
if ('transactions' in item && item.transactions.length === 0 && !groupBy) {
return;
}

if (isMobileSelectionModeEnabled) {
onCheckboxPress(item, itemTransactions);
return;
Expand All @@ -324,7 +362,7 @@ function SearchList({
setLongPressedItemTransactions(itemTransactions);
setIsModalVisible(true);
},
[groupBy, route.key, shouldPreventLongPressRow, isSmallScreenWidth, isMobileSelectionModeEnabled, onCheckboxPress],
[route.key, shouldPreventLongPressRow, isSmallScreenWidth, isMobileSelectionModeEnabled, onCheckboxPress],
);

const turnOnSelectionMode = useCallback(() => {
Expand Down Expand Up @@ -453,7 +491,7 @@ function SearchList({

const tableHeaderVisible = canSelectMultiple || !!SearchTableHeader;
const selectAllButtonVisible = canSelectMultiple && !SearchTableHeader;
const isSelectAllChecked = selectedItemsLength > 0 && selectedItemsLength === flattenedItemsWithoutPendingDelete.length;
const isSelectAllChecked = selectedItemsLength > 0 && selectedItemsLength === totalItems;

const content = (
<View style={[styles.flex1, !isKeyboardShown && safeAreaPaddingBottomStyle, containerStyle]}>
Expand All @@ -463,7 +501,7 @@ function SearchList({
<Checkbox
accessibilityLabel={translate('workspace.people.selectAll')}
isChecked={isSelectAllChecked}
isIndeterminate={selectedItemsLength > 0 && selectedItemsLength !== flattenedItemsWithoutPendingDelete.length}
isIndeterminate={selectedItemsLength > 0 && selectedItemsLength !== totalItems}
onPress={() => {
onAllCheckboxPress();
}}
Expand Down
Loading
Loading