Skip to content

Commit 088ed06

Browse files
authored
Merge pull request Expensify#81036 from lorretheboy/feat/70591
Can't select an empty report from the Reports > Reports page
2 parents cf38f88 + f7b6666 commit 088ed06

27 files changed

Lines changed: 1216 additions & 208 deletions

src/CONST/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8069,6 +8069,7 @@ const CONST = {
80698069
SEARCH_BUTTON: 'Search-SearchButton',
80708070
USER_SELECTION_CHECKBOX: 'Search-UserSelectionCheckbox',
80718071
TRANSACTION_GROUP_LIST_ITEM: 'Search-TransactionGroupListItem',
8072+
SELECT_ALL_BUTTON: 'Search-SelectAllButton',
80728073
},
80738074
REPORT: {
80748075
FLOATING_MESSAGE_COUNTER: 'Report-FloatingMessageCounter',

src/components/MoneyReportHeader.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,8 +1495,8 @@ function MoneyReportHeader({
14951495
}
14961496

14971497
const result = await showConfirmModal({
1498-
title: translate('iou.deleteReport'),
1499-
prompt: translate('iou.deleteReportConfirmation'),
1498+
title: translate('iou.deleteReport', {count: 1}),
1499+
prompt: translate('iou.deleteReportConfirmation', {count: 1}),
15001500
confirmText: translate('common.delete'),
15011501
cancelText: translate('common.cancel'),
15021502
danger: true,
@@ -1510,7 +1510,7 @@ function MoneyReportHeader({
15101510
Navigation.goBack(backToRoute);
15111511
// eslint-disable-next-line @typescript-eslint/no-deprecated
15121512
InteractionManager.runAfterInteractions(() => {
1513-
deleteAppReport(moneyRequestReport?.reportID, email ?? '', accountID, reportTransactions, allTransactionViolations, bankAccountList);
1513+
deleteAppReport(moneyRequestReport?.reportID, email ?? '', accountID, reportTransactions, allTransactionViolations, bankAccountList, currentSearchHash);
15141514
});
15151515
});
15161516
},

src/components/Search/SearchContext.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,15 @@ function SearchContextProvider({children}: ChildrenProps) {
153153

154154
if (data.length && data.every(isTransactionReportGroupListItemType)) {
155155
selectedReports = data
156-
.filter((item) => isMoneyRequestReport(item) && item.transactions.length > 0 && item.transactions.every(({keyForList}) => selectedTransactions[keyForList]?.isSelected))
156+
.filter((item) => {
157+
if (!isMoneyRequestReport(item)) {
158+
return false;
159+
}
160+
if (item.transactions.length === 0) {
161+
return !!item.keyForList && selectedTransactions[item.keyForList]?.isSelected;
162+
}
163+
return item.transactions.every(({keyForList}) => selectedTransactions[keyForList]?.isSelected);
164+
})
157165
.map(({reportID, action = CONST.SEARCH.ACTION_TYPES.VIEW, total = CONST.DEFAULT_NUMBER_ID, policyID, allActions = [action], currency, chatReportID}) => ({
158166
reportID,
159167
action,

src/components/Search/SearchList/index.tsx

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
4848
import {turnOnMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
4949
import DateUtils from '@libs/DateUtils';
5050
import navigationRef from '@libs/Navigation/navigationRef';
51-
import {getTableMinWidth} from '@libs/SearchUIUtils';
51+
import {getTableMinWidth, isTransactionReportGroupListItemType} from '@libs/SearchUIUtils';
5252
import variables from '@styles/variables';
5353
import type {TransactionPreviewData} from '@userActions/Search';
5454
import CONST from '@src/CONST';
@@ -239,16 +239,51 @@ function SearchList({
239239
}
240240
return data;
241241
}, [data, groupBy, type]);
242-
const flattenedItemsWithoutPendingDelete = useMemo(() => flattenedItems.filter((t) => t?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE), [flattenedItems]);
242+
const emptyReports = useMemo(() => {
243+
if (type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT && isTransactionGroupListItemArray(data)) {
244+
return data.filter((item) => item.transactions.length === 0);
245+
}
246+
return [];
247+
}, [data, type]);
243248

244249
const selectedItemsLength = useMemo(() => {
245-
return flattenedItemsWithoutPendingDelete.reduce((acc, item) => {
246-
if (item.keyForList && selectedTransactions[item.keyForList]?.isSelected) {
247-
return acc + 1;
248-
}
249-
return acc;
250+
const selectedTransactionsCount = flattenedItems.reduce((acc, item) => {
251+
const isTransactionSelected = !!(item?.keyForList && selectedTransactions[item.keyForList]?.isSelected);
252+
return acc + (isTransactionSelected ? 1 : 0);
250253
}, 0);
251-
}, [flattenedItemsWithoutPendingDelete, selectedTransactions]);
254+
255+
if (type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT && isTransactionGroupListItemArray(data)) {
256+
const selectedEmptyReports = emptyReports.reduce((acc, item) => {
257+
const isEmptyReportSelected = !!(item.keyForList && selectedTransactions[item.keyForList]?.isSelected);
258+
return acc + (isEmptyReportSelected ? 1 : 0);
259+
}, 0);
260+
261+
return selectedEmptyReports + selectedTransactionsCount;
262+
}
263+
264+
return selectedTransactionsCount;
265+
}, [flattenedItems, type, data, emptyReports, selectedTransactions]);
266+
267+
const totalItems = useMemo(() => {
268+
if (type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT && isTransactionGroupListItemArray(data)) {
269+
const selectableEmptyReports = emptyReports.filter((item) => item.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
270+
const selectableTransactions = flattenedItems.filter((item) => {
271+
if ('pendingAction' in item) {
272+
return item.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
273+
}
274+
return true;
275+
});
276+
return selectableEmptyReports.length + selectableTransactions.length;
277+
}
278+
279+
const selectableTransactions = flattenedItems.filter((item) => {
280+
if ('pendingAction' in item) {
281+
return item.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
282+
}
283+
return true;
284+
});
285+
return selectableTransactions.length;
286+
}, [data, type, flattenedItems, emptyReports]);
252287

253288
const itemsWithSelection = useMemo(() => {
254289
return data.map((item) => {
@@ -259,10 +294,16 @@ function SearchList({
259294
if (!canSelectMultiple) {
260295
itemWithSelection = {...item, isSelected: false};
261296
} else {
262-
const hasAnySelected = item.transactions.some((t) => t.keyForList && selectedTransactions[t.keyForList]?.isSelected);
297+
const isEmptyReportSelected =
298+
item.transactions.length === 0 && isTransactionReportGroupListItemType(item) && !!(item.keyForList && selectedTransactions[item.keyForList]?.isSelected);
299+
300+
const hasAnySelected = item.transactions.some((t) => t.keyForList && selectedTransactions[t.keyForList]?.isSelected) || isEmptyReportSelected;
263301

264302
if (!hasAnySelected) {
265303
itemWithSelection = {...item, isSelected: false};
304+
} else if (isEmptyReportSelected) {
305+
isSelected = true;
306+
itemWithSelection = {...item, isSelected};
266307
} else {
267308
let allNonDeletedSelected = true;
268309
let hasNonDeletedTransactions = false;
@@ -351,13 +392,10 @@ function SearchList({
351392
}
352393

353394
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
354-
if (shouldPreventLongPressRow || !isSmallScreenWidth || item?.isDisabled || item?.isDisabledCheckbox) {
355-
return;
356-
}
357-
// disable long press for empty expense reports
358-
if ('transactions' in item && item.transactions.length === 0 && !groupBy) {
395+
if (shouldPreventLongPressRow || !isSmallScreenWidth || item?.isDisabled || item?.isDisabledCheckbox || item.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) {
359396
return;
360397
}
398+
361399
if (isMobileSelectionModeEnabled) {
362400
onCheckboxPress(item, itemTransactions);
363401
return;
@@ -366,7 +404,7 @@ function SearchList({
366404
setLongPressedItemTransactions(itemTransactions);
367405
setIsModalVisible(true);
368406
},
369-
[groupBy, route.key, shouldPreventLongPressRow, isSmallScreenWidth, isMobileSelectionModeEnabled, onCheckboxPress],
407+
[route.key, shouldPreventLongPressRow, isSmallScreenWidth, isMobileSelectionModeEnabled, onCheckboxPress],
370408
);
371409

372410
const turnOnSelectionMode = useCallback(() => {
@@ -493,7 +531,7 @@ function SearchList({
493531

494532
const tableHeaderVisible = canSelectMultiple || !!SearchTableHeader;
495533
const selectAllButtonVisible = canSelectMultiple && !SearchTableHeader;
496-
const isSelectAllChecked = selectedItemsLength > 0 && selectedItemsLength === flattenedItemsWithoutPendingDelete.length && hasLoadedAllTransactions;
534+
const isSelectAllChecked = selectedItemsLength > 0 && selectedItemsLength === totalItems && hasLoadedAllTransactions;
497535

498536
const content = (
499537
<View style={[styles.flex1, !isKeyboardShown && safeAreaPaddingBottomStyle, containerStyle]}>
@@ -503,11 +541,11 @@ function SearchList({
503541
<Checkbox
504542
accessibilityLabel={translate('workspace.people.selectAll')}
505543
isChecked={isSelectAllChecked}
506-
isIndeterminate={selectedItemsLength > 0 && (selectedItemsLength !== flattenedItemsWithoutPendingDelete.length || !hasLoadedAllTransactions)}
544+
isIndeterminate={selectedItemsLength > 0 && (selectedItemsLength !== totalItems || !hasLoadedAllTransactions)}
507545
onPress={() => {
508546
onAllCheckboxPress();
509547
}}
510-
disabled={flattenedItems.length === 0}
548+
disabled={totalItems === 0}
511549
/>
512550
)}
513551

@@ -520,6 +558,7 @@ function SearchList({
520558
accessibilityLabel={translate('workspace.people.selectAll')}
521559
role="button"
522560
accessibilityState={{checked: isSelectAllChecked}}
561+
sentryLabel={CONST.SENTRY_LABEL.SEARCH.SELECT_ALL_BUTTON}
523562
dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
524563
>
525564
<Text style={[styles.textMicroSupporting, styles.ph3]}>{translate('workspace.people.selectAll')}</Text>

0 commit comments

Comments
 (0)