Skip to content

Commit 332d296

Browse files
authored
Merge pull request Expensify#68561 from Expensify/youssef_auto_hide_columns_2
Auto show/hide columns depending on available data
2 parents 5f6e6ee + 8e1e8d1 commit 332d296

26 files changed

Lines changed: 621 additions & 367 deletions

src/CONST/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,7 @@ const CONST = {
13141314
RECEIPT: 'receipt',
13151315
DATE: 'date',
13161316
MERCHANT: 'merchant',
1317+
DESCRIPTION: 'description',
13171318
FROM: 'from',
13181319
TO: 'to',
13191320
CATEGORY: 'category',
@@ -6436,6 +6437,7 @@ const CONST = {
64366437
TITLE: 'title',
64376438
ASSIGNEE: 'assignee',
64386439
IN: 'in',
6440+
COMMENTS: 'comments',
64396441
CARD: 'card',
64406442
WITHDRAWAL_ID: 'withdrawalID',
64416443
},
@@ -6581,6 +6583,9 @@ const CONST = {
65816583
RECONCILIATION: 'reconciliation',
65826584
},
65836585
GROUP_PREFIX: 'group_',
6586+
ANIMATION: {
6587+
FADE_DURATION: 200,
6588+
},
65846589
},
65856590

65866591
EXPENSE: {

src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,18 @@
1-
import React, {useCallback} from 'react';
1+
import React, {useCallback, useMemo} from 'react';
22
import {View} from 'react-native';
3-
import type {SortOrder, TableColumnSize} from '@components/Search/types';
3+
import type {SearchColumnType, SortOrder, TableColumnSize} from '@components/Search/types';
4+
import {expenseHeaders} from '@components/SelectionList/SearchTableHeader';
45
import SortableTableHeader from '@components/SelectionList/SortableTableHeader';
56
import type {SortableColumnName} from '@components/SelectionList/types';
67
import useThemeStyles from '@hooks/useThemeStyles';
78
import CONST from '@src/CONST';
89
import type {TranslationPaths} from '@src/languages/types';
910

1011
type ColumnConfig = {
11-
columnName: SortableColumnName;
12+
columnName: SearchColumnType;
1213
translationKey: TranslationPaths | undefined;
1314
isColumnSortable?: boolean;
14-
};
15-
16-
const shouldShowColumnConfig: Record<SortableColumnName, (isIOUReport: boolean) => boolean> = {
17-
[CONST.SEARCH.TABLE_COLUMNS.RECEIPT]: () => true,
18-
[CONST.SEARCH.TABLE_COLUMNS.TYPE]: () => true,
19-
[CONST.SEARCH.TABLE_COLUMNS.DATE]: () => true,
20-
[CONST.SEARCH.TABLE_COLUMNS.MERCHANT]: () => true,
21-
[CONST.SEARCH.TABLE_COLUMNS.CATEGORY]: (isIOUReport) => !isIOUReport,
22-
[CONST.SEARCH.TABLE_COLUMNS.TAG]: (isIOUReport) => !isIOUReport,
23-
[CONST.REPORT.TRANSACTION_LIST.COLUMNS.COMMENTS]: () => true,
24-
[CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT]: () => true,
25-
[CONST.SEARCH.TABLE_COLUMNS.IN]: () => false,
26-
[CONST.SEARCH.TABLE_COLUMNS.FROM]: () => false,
27-
[CONST.SEARCH.TABLE_COLUMNS.TO]: () => false,
28-
[CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION]: () => false,
29-
[CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT]: () => false,
30-
[CONST.SEARCH.TABLE_COLUMNS.ACTION]: () => false,
31-
[CONST.SEARCH.TABLE_COLUMNS.TITLE]: () => false,
32-
[CONST.SEARCH.TABLE_COLUMNS.ASSIGNEE]: () => false,
33-
[CONST.SEARCH.TABLE_COLUMNS.CARD]: () => false,
34-
[CONST.SEARCH.TABLE_COLUMNS.WITHDRAWAL_ID]: () => false,
15+
canBeMissing?: boolean;
3516
};
3617

3718
const columnConfig: ColumnConfig[] = [
@@ -52,14 +33,22 @@ const columnConfig: ColumnConfig[] = [
5233
{
5334
columnName: CONST.SEARCH.TABLE_COLUMNS.MERCHANT,
5435
translationKey: 'common.merchant',
36+
canBeMissing: true,
37+
},
38+
{
39+
columnName: CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION,
40+
translationKey: 'common.description',
41+
canBeMissing: true,
5542
},
5643
{
5744
columnName: CONST.SEARCH.TABLE_COLUMNS.CATEGORY,
5845
translationKey: 'common.category',
46+
canBeMissing: true,
5947
},
6048
{
6149
columnName: CONST.SEARCH.TABLE_COLUMNS.TAG,
6250
translationKey: 'common.tag',
51+
canBeMissing: true,
6352
},
6453
{
6554
columnName: CONST.REPORT.TRANSACTION_LIST.COLUMNS.COMMENTS,
@@ -80,27 +69,30 @@ type SearchTableHeaderProps = {
8069
amountColumnSize: TableColumnSize;
8170
taxAmountColumnSize: TableColumnSize;
8271
shouldShowSorting: boolean;
83-
isIOUReport: boolean;
72+
columns: SearchColumnType[];
8473
};
8574

86-
function MoneyRequestReportTableHeader({sortBy, sortOrder, onSortPress, dateColumnSize, shouldShowSorting, isIOUReport, amountColumnSize, taxAmountColumnSize}: SearchTableHeaderProps) {
75+
function MoneyRequestReportTableHeader({sortBy, sortOrder, onSortPress, dateColumnSize, shouldShowSorting, columns, amountColumnSize, taxAmountColumnSize}: SearchTableHeaderProps) {
8776
const styles = useThemeStyles();
8877

8978
const shouldShowColumn = useCallback(
90-
(columnName: SortableColumnName) => {
91-
const shouldShowFun = shouldShowColumnConfig[columnName];
92-
if (!shouldShowFun) {
93-
return false;
94-
}
95-
return shouldShowFun(isIOUReport);
79+
(columnName: SearchColumnType) => {
80+
return columns.includes(columnName);
9681
},
97-
[isIOUReport],
82+
[columns],
9883
);
84+
85+
const areAllOptionalColumnsHidden = useMemo(() => {
86+
const canBeMissingColumns = expenseHeaders.filter((header) => header.canBeMissing).map((header) => header.columnName);
87+
return canBeMissingColumns.every((column) => !columns.includes(column));
88+
}, [columns]);
89+
9990
return (
10091
<View style={[styles.dFlex, styles.flex5]}>
10192
<SortableTableHeader
10293
columns={columnConfig}
10394
shouldShowColumn={shouldShowColumn}
95+
areAllOptionalColumnsHidden={areAllOptionalColumnsHidden}
10496
dateColumnSize={dateColumnSize}
10597
amountColumnSize={amountColumnSize}
10698
taxAmountColumnSize={taxAmountColumnSize}

src/components/MoneyRequestReportView/MoneyRequestReportTransactionItem.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import React, {useEffect, useRef} from 'react';
1+
import React, {useEffect, useMemo, useRef} from 'react';
22
import type {View} from 'react-native';
33
import {getButtonRole} from '@components/Button/utils';
44
import OfflineWithFeedback from '@components/OfflineWithFeedback';
55
import {PressableWithFeedback} from '@components/Pressable';
6-
import type {TableColumnSize} from '@components/Search/types';
6+
import type {SearchColumnType, TableColumnSize} from '@components/Search/types';
7+
import {expenseHeaders} from '@components/SelectionList/SearchTableHeader';
78
import TransactionItemRow from '@components/TransactionItemRow';
89
import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle';
910
import useLocalize from '@hooks/useLocalize';
@@ -18,17 +19,6 @@ import CONST from '@src/CONST';
1819
import type {Report} from '@src/types/onyx';
1920
import type {TransactionWithOptionalHighlight} from './MoneyRequestReportTransactionList';
2021

21-
const allReportColumns = [
22-
CONST.REPORT.TRANSACTION_LIST.COLUMNS.RECEIPT,
23-
CONST.REPORT.TRANSACTION_LIST.COLUMNS.TYPE,
24-
CONST.REPORT.TRANSACTION_LIST.COLUMNS.DATE,
25-
CONST.REPORT.TRANSACTION_LIST.COLUMNS.MERCHANT,
26-
CONST.REPORT.TRANSACTION_LIST.COLUMNS.CATEGORY,
27-
CONST.REPORT.TRANSACTION_LIST.COLUMNS.TAG,
28-
CONST.REPORT.TRANSACTION_LIST.COLUMNS.COMMENTS,
29-
CONST.REPORT.TRANSACTION_LIST.COLUMNS.TOTAL_AMOUNT,
30-
];
31-
3222
type MoneyRequestReportTransactionItemProps = {
3323
/** The transaction that is being displayed */
3424
transaction: TransactionWithOptionalHighlight;
@@ -60,6 +50,9 @@ type MoneyRequestReportTransactionItemProps = {
6050
/** The size of the tax amount column */
6151
taxAmountColumnSize: TableColumnSize;
6252

53+
/** Columns to show */
54+
columns: SearchColumnType[];
55+
6356
/** Callback function that scrolls to this transaction in case it is newly added */
6457
scrollToNewTransaction?: (offset: number) => void;
6558
};
@@ -72,6 +65,7 @@ function MoneyRequestReportTransactionItem({
7265
isSelected,
7366
handleOnPress,
7467
handleLongPress,
68+
columns,
7569
dateColumnSize,
7670
amountColumnSize,
7771
taxAmountColumnSize,
@@ -104,6 +98,11 @@ function MoneyRequestReportTransactionItem({
10498
backgroundColor: theme.highlightBG,
10599
});
106100

101+
const areAllOptionalColumnsHidden = useMemo(() => {
102+
const canBeMissingColumns = expenseHeaders.filter((header) => header.canBeMissing).map((header) => header.columnName);
103+
return canBeMissingColumns.every((column) => !columns.includes(column));
104+
}, [columns]);
105+
107106
return (
108107
<OfflineWithFeedback pendingAction={pendingAction}>
109108
<PressableWithFeedback
@@ -138,7 +137,8 @@ function MoneyRequestReportTransactionItem({
138137
shouldUseNarrowLayout={shouldUseNarrowLayout || isMediumScreenWidth}
139138
shouldShowCheckbox={!!isSelectionModeEnabled || !isSmallScreenWidth}
140139
onCheckboxPress={toggleTransaction}
141-
columns={allReportColumns}
140+
columns={columns}
141+
areAllOptionalColumnsHidden={areAllOptionalColumnsHidden}
142142
isDisabled={isPendingDelete}
143143
style={[styles.p3]}
144144
/>

src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import Checkbox from '@components/Checkbox';
77
import * as Expensicons from '@components/Icon/Expensicons';
88
import MenuItem from '@components/MenuItem';
99
import Modal from '@components/Modal';
10+
import {useSession} from '@components/OnyxListItemProvider';
1011
import {useSearchContext} from '@components/Search/SearchContext';
11-
import type {SortOrder} from '@components/Search/types';
12+
import type {SearchColumnType, SortOrder} from '@components/Search/types';
1213
import Text from '@components/Text';
1314
import useCopySelectionHelper from '@hooks/useCopySelectionHelper';
1415
import useLocalize from '@hooks/useLocalize';
@@ -22,8 +23,8 @@ import {convertToDisplayString} from '@libs/CurrencyUtils';
2223
import {getThreadReportIDsForTransactions} from '@libs/MoneyRequestReportUtils';
2324
import {navigationRef} from '@libs/Navigation/Navigation';
2425
import {getIOUActionForTransactionID} from '@libs/ReportActionsUtils';
25-
import {getMoneyRequestSpendBreakdown, isIOUReport} from '@libs/ReportUtils';
26-
import {compareValues, isTransactionAmountTooLong, isTransactionTaxAmountTooLong} from '@libs/SearchUIUtils';
26+
import {getMoneyRequestSpendBreakdown} from '@libs/ReportUtils';
27+
import {compareValues, getColumnsToShow, isTransactionAmountTooLong, isTransactionTaxAmountTooLong} from '@libs/SearchUIUtils';
2728
import {getTransactionPendingAction, isTransactionPendingDelete} from '@libs/TransactionUtils';
2829
import shouldShowTransactionYear from '@libs/TransactionUtils/shouldShowTransactionYear';
2930
import Navigation from '@navigation/Navigation';
@@ -110,6 +111,7 @@ function MoneyRequestReportTransactionList({
110111
const formattedCompanySpendAmount = convertToDisplayString(nonReimbursableSpend, report?.currency);
111112
const shouldShowBreakdown = !!nonReimbursableSpend && !!reimbursableSpend;
112113
const transactionsWithoutPendingDelete = useMemo(() => transactions.filter((t) => !isTransactionPendingDelete(t)), [transactions]);
114+
const session = useSession();
113115

114116
const hasPendingAction = useMemo(() => {
115117
return transactions.some(getTransactionPendingAction);
@@ -160,6 +162,11 @@ function MoneyRequestReportTransactionList({
160162
}));
161163
}, [newTransactions, sortBy, sortOrder, transactions, localeCompare]);
162164

165+
const columnsToShow = useMemo(() => {
166+
const columns = getColumnsToShow(session?.accountID, transactions, true);
167+
return (Object.keys(columns) as SearchColumnType[]).filter((column) => columns[column]);
168+
}, [transactions, session?.accountID]);
169+
163170
const navigateToTransaction = useCallback(
164171
(activeTransactionID: string) => {
165172
const iouAction = getIOUActionForTransactionID(reportActions, activeTransactionID);
@@ -262,6 +269,7 @@ function MoneyRequestReportTransactionList({
262269
shouldShowSorting
263270
sortBy={sortBy}
264271
sortOrder={sortOrder}
272+
columns={columnsToShow}
265273
dateColumnSize={dateColumnSize}
266274
amountColumnSize={amountColumnSize}
267275
taxAmountColumnSize={taxAmountColumnSize}
@@ -272,7 +280,6 @@ function MoneyRequestReportTransactionList({
272280

273281
setSortConfig((prevState) => ({...prevState, sortBy: selectedSortBy, sortOrder: selectedSortOrder}));
274282
}}
275-
isIOUReport={isIOUReport(report)}
276283
/>
277284
)}
278285
</View>
@@ -283,6 +290,7 @@ function MoneyRequestReportTransactionList({
283290
<MoneyRequestReportTransactionItem
284291
key={transaction.transactionID}
285292
transaction={transaction}
293+
columns={columnsToShow}
286294
report={report}
287295
isSelectionModeEnabled={isMobileSelectionModeEnabled}
288296
toggleTransaction={toggleTransaction}

src/components/Search/SearchList/BaseSearchList/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const AnimatedFlashListComponent = Animated.createAnimatedComponent(FlashList<Se
1414

1515
function BaseSearchList({
1616
data,
17+
columns,
1718
renderItem,
1819
onSelectRow,
1920
keyExtractor,
@@ -103,7 +104,7 @@ function BaseSearchList({
103104
return () => removeKeyDownPressListener(setHasKeyBeenPressed);
104105
}, [setHasKeyBeenPressed]);
105106

106-
const extraData = useMemo(() => [focusedIndex, isFocused], [focusedIndex, isFocused]);
107+
const extraData = useMemo(() => [focusedIndex, isFocused, columns], [focusedIndex, isFocused, columns]);
107108

108109
return (
109110
<AnimatedFlashListComponent

src/components/Search/SearchList/BaseSearchList/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {FlashList, FlashListProps} from '@shopify/flash-list';
22
import type {ForwardedRef} from 'react';
33
import type {NativeSyntheticEvent} from 'react-native';
4+
import type {SearchColumnType} from '@components/Search/types';
45
import type {ExtendedTargetedEvent, SearchListItem} from '@components/SelectionList/types';
56

67
type BaseSearchListProps = Pick<
@@ -26,6 +27,9 @@ type BaseSearchListProps = Pick<
2627
/** The function to render each item in the list */
2728
renderItem: (item: SearchListItem, isItemFocused: boolean, onFocus?: (event: NativeSyntheticEvent<ExtendedTargetedEvent>) => void) => React.JSX.Element;
2829

30+
/** The columns that might change to trigger re-render via extraData */
31+
columns: SearchColumnType[];
32+
2933
/** The length of the flattened items in the list */
3034
flattenedItemsLength: number;
3135

src/components/Search/SearchList/index.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {usePersonalDetails} from '@components/OnyxListItemProvider';
1313
import {PressableWithFeedback} from '@components/Pressable';
1414
import {createItemHeightCalculator} from '@components/Search/itemHeightCalculator';
1515
import ITEM_HEIGHTS from '@components/Search/itemHeights';
16-
import type {SearchQueryJSON} from '@components/Search/types';
16+
import type {SearchColumnType, SearchQueryJSON} from '@components/Search/types';
1717
import type ChatListItem from '@components/SelectionList/ChatListItem';
1818
import type TaskListItem from '@components/SelectionList/Search/TaskListItem';
1919
import type TransactionGroupListItem from '@components/SelectionList/Search/TransactionGroupListItem';
@@ -79,6 +79,9 @@ type SearchListProps = Pick<FlashListProps<SearchListItem>, 'onScroll' | 'conten
7979
/** The search query */
8080
queryJSON: SearchQueryJSON;
8181

82+
/** Columns to show */
83+
columns: SearchColumnType[];
84+
8285
/** Whether the screen is focused */
8386
isFocused: boolean;
8487

@@ -96,6 +99,8 @@ type SearchListProps = Pick<FlashListProps<SearchListItem>, 'onScroll' | 'conten
9699

97100
/** Whether mobile selection mode is enabled */
98101
isMobileSelectionModeEnabled: boolean;
102+
103+
areAllOptionalColumnsHidden: boolean;
99104
};
100105

101106
const keyExtractor = (item: SearchListItem, index: number) => item.keyForList ?? `${index}`;
@@ -126,12 +131,14 @@ function SearchList(
126131
shouldPreventDefaultFocusOnSelectRow,
127132
shouldPreventLongPressRow,
128133
queryJSON,
134+
columns,
129135
isFocused,
130136
onViewableItemsChanged,
131137
onLayout,
132138
shouldAnimate,
133139
estimatedItemSize = ITEM_HEIGHTS.NARROW_WITHOUT_DRAWER.STANDARD,
134140
isMobileSelectionModeEnabled,
141+
areAllOptionalColumnsHidden,
135142
}: SearchListProps,
136143
ref: ForwardedRef<SearchListHandle>,
137144
) {
@@ -262,6 +269,8 @@ function SearchList(
262269
item={item}
263270
shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow}
264271
queryJSONHash={hash}
272+
columns={columns}
273+
areAllOptionalColumnsHidden={areAllOptionalColumnsHidden}
265274
policies={policies}
266275
isDisabled={isDisabled}
267276
allReports={allReports}
@@ -283,6 +292,7 @@ function SearchList(
283292
ListItem,
284293
onSelectRow,
285294
handleLongPressRow,
295+
columns,
286296
onCheckboxPress,
287297
canSelectMultiple,
288298
shouldPreventDefaultFocusOnSelectRow,
@@ -294,6 +304,7 @@ function SearchList(
294304
isUserValidated,
295305
personalDetails,
296306
userBillingFundID,
307+
areAllOptionalColumnsHidden,
297308
],
298309
);
299310

@@ -387,6 +398,7 @@ function SearchList(
387398
onScroll={onScroll}
388399
showsVerticalScrollIndicator={false}
389400
ref={listRef}
401+
columns={columns}
390402
scrollToIndex={scrollToIndex}
391403
isFocused={isFocused}
392404
flattenedItemsLength={flattenedItems.length}

0 commit comments

Comments
 (0)