diff --git a/src/components/Search/SearchList/ListItem/ActionCell/DeferredActionCell.tsx b/src/components/Search/SearchList/ListItem/ActionCell/DeferredActionCell.tsx
new file mode 100644
index 000000000000..13a85cde7fc1
--- /dev/null
+++ b/src/components/Search/SearchList/ListItem/ActionCell/DeferredActionCell.tsx
@@ -0,0 +1,39 @@
+import React, {useDeferredValue} from 'react';
+import Button from '@components/Button';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import CONST from '@src/CONST';
+import ActionCell from '.';
+import type {ActionCellProps} from '.';
+import actionTranslationsMap from './actionTranslationsMap';
+
+function DeferredActionCell(actionCellProps: ActionCellProps) {
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+ const shouldRender = useDeferredValue(true, false);
+
+ if (!shouldRender) {
+ const action = actionCellProps.action ?? CONST.SEARCH.ACTION_TYPES.VIEW;
+ const shouldUseViewAction = action === CONST.SEARCH.ACTION_TYPES.VIEW || action === CONST.SEARCH.ACTION_TYPES.PAID || action === CONST.SEARCH.ACTION_TYPES.DONE;
+ const isSuccess = !shouldUseViewAction && action !== CONST.SEARCH.ACTION_TYPES.UNDELETE;
+ const text = shouldUseViewAction ? translate(actionTranslationsMap[CONST.SEARCH.ACTION_TYPES.VIEW]) : translate(actionTranslationsMap[action]);
+
+ return (
+
+ );
+ }
+
+ // Deferred wrapper intentionally forwards all props to the underlying component
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ return ;
+}
+
+export default DeferredActionCell;
diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx
index c99154f4badf..dc485f3cfbfb 100644
--- a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx
+++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx
@@ -309,46 +309,22 @@ function ExpenseReportListItem({
isSelected={!!reportItem.isSelected}
/>
)}
- {!isLargeScreenWidth && (
-
-
-
- )}
- {isLargeScreenWidth && (
-
- )}
+
{getDescription}
)}
diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/ExpenseReportListItemAvatar.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/ExpenseReportListItemAvatar.tsx
new file mode 100644
index 000000000000..7de5b5bc0792
--- /dev/null
+++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/ExpenseReportListItemAvatar.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import {View} from 'react-native';
+import SearchReportAvatar from '@components/ReportActionAvatars/SearchReportAvatar';
+import type {ExpenseReportListItemType} from '@components/Search/SearchList/ListItem/types';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import CONST from '@src/CONST';
+
+type ExpenseReportListItemAvatarProps = {
+ item: ExpenseReportListItemType;
+ showTooltip: boolean;
+ isHovered?: boolean;
+ isFocused?: boolean;
+ isLargeScreenWidth?: boolean;
+};
+
+function ExpenseReportListItemAvatar({item, showTooltip, isHovered = false, isFocused = false, isLargeScreenWidth = false}: ExpenseReportListItemAvatarProps) {
+ const StyleUtils = useStyleUtils();
+ const styles = useThemeStyles();
+ const theme = useTheme();
+
+ const finalAvatarBorderColor =
+ StyleUtils.getItemBackgroundColorStyle(!!item.isSelected, isFocused || isHovered, !!item.isDisabled, theme.activeComponentBG, theme.hoverComponentBG)?.backgroundColor ??
+ theme.highlightBG;
+
+ return (
+
+
+
+ );
+}
+
+export default ExpenseReportListItemAvatar;
diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/ExpenseReportListItemRowNarrow.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/ExpenseReportListItemRowNarrow.tsx
new file mode 100644
index 000000000000..d0b5d19cefbf
--- /dev/null
+++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/ExpenseReportListItemRowNarrow.tsx
@@ -0,0 +1,70 @@
+import React from 'react';
+import {View} from 'react-native';
+import Checkbox from '@components/Checkbox';
+import Text from '@components/Text';
+import {useCurrencyListActions} from '@hooks/useCurrencyList';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import DateUtils from '@libs/DateUtils';
+import CONST from '@src/CONST';
+import type {ExpenseReportListItemRowNarrowProps} from './types';
+
+function ExpenseReportListItemRowNarrow({item, onCheckboxPress = () => {}, canSelectMultiple, isSelectAllChecked, isIndeterminate, isDisabledCheckbox}: ExpenseReportListItemRowNarrowProps) {
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+ const {convertToDisplayString} = useCurrencyListActions();
+
+ const currency = item.currency ?? CONST.CURRENCY.USD;
+ const {totalDisplaySpend = 0, isAllScanning: isScanning = false} = item;
+
+ const filteredTransactions = item.transactions?.filter((t) => t.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
+ const expenseCount = (filteredTransactions?.length ? filteredTransactions.length : undefined) ?? item.transactionCount ?? 0;
+ const expenseCountText = translate('iou.expenseCount', {count: expenseCount});
+ const formattedDate = DateUtils.formatWithUTCTimeZone(
+ item.created ?? '',
+ DateUtils.doesDateBelongToAPastYear(item.created ?? '') ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT,
+ );
+
+ const amountText = isScanning ? translate('iou.receiptStatusTitle') : convertToDisplayString(totalDisplaySpend, currency);
+ const groupAccessibilityLabel = [item.reportName, amountText, formattedDate, expenseCountText].filter(Boolean).join(', ');
+
+ return (
+
+ {!!canSelectMultiple && (
+
+ )}
+
+
+
+ {item.reportName ?? ''}
+
+ {amountText}
+
+
+ {formattedDate}
+ {expenseCountText}
+
+
+
+ );
+}
+
+export default ExpenseReportListItemRowNarrow;
diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/ExpenseReportListItemRowWide.tsx
similarity index 62%
rename from src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx
rename to src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/ExpenseReportListItemRowWide.tsx
index 5b96a7bc579e..7151ebab9782 100644
--- a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx
+++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/ExpenseReportListItemRowWide.tsx
@@ -1,52 +1,26 @@
import React, {Fragment} from 'react';
import {View} from 'react-native';
-import type {StyleProp, ViewStyle} from 'react-native';
import Checkbox from '@components/Checkbox';
import Icon from '@components/Icon';
-import SearchReportAvatar from '@components/ReportActionAvatars/SearchReportAvatar';
-import type {SearchColumnType} from '@components/Search/types';
-import Text from '@components/Text';
-import {useCurrencyListActions} from '@hooks/useCurrencyList';
+import DeferredActionCell from '@components/Search/SearchList/ListItem/ActionCell/DeferredActionCell';
+import DateCell from '@components/Search/SearchList/ListItem/DateCell';
+import ExportedIconCell from '@components/Search/SearchList/ListItem/ExportedIconCell';
+import StatusCell from '@components/Search/SearchList/ListItem/StatusCell';
+import TextCell from '@components/Search/SearchList/ListItem/TextCell';
+import TotalCell from '@components/Search/SearchList/ListItem/TotalCell';
+import UserInfoCell from '@components/Search/SearchList/ListItem/UserInfoCell';
+import WorkspaceCell from '@components/Search/SearchList/ListItem/WorkspaceCell';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
-import useLocalize from '@hooks/useLocalize';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
-import DateUtils from '@libs/DateUtils';
import getBase62ReportID from '@libs/getBase62ReportID';
import variables from '@styles/variables';
import CONST from '@src/CONST';
-import type {ReportAction} from '@src/types/onyx';
-import ActionCell from './ActionCell';
-import DateCell from './DateCell';
-import ExportedIconCell from './ExportedIconCell';
-import StatusCell from './StatusCell';
-import TextCell from './TextCell';
-import TotalCell from './TotalCell';
-import type {ExpenseReportListItemType} from './types';
-import UserInfoCell from './UserInfoCell';
-import WorkspaceCell from './WorkspaceCell';
+import ExpenseReportListItemAvatar from './ExpenseReportListItemAvatar';
+import type {ExpenseReportListItemRowWideProps} from './types';
-type ExpenseReportListItemRowProps = {
- item: ExpenseReportListItemType;
- reportActions?: ReportAction[];
- showTooltip: boolean;
- canSelectMultiple?: boolean;
- isActionLoading?: boolean;
- onButtonPress?: () => void;
- onCheckboxPress?: () => void;
- containerStyle?: StyleProp;
- isSelectAllChecked?: boolean;
- isIndeterminate?: boolean;
- isDisabledCheckbox?: boolean;
- isHovered?: boolean;
- isFocused?: boolean;
- isPendingDelete?: boolean;
- columns?: SearchColumnType[];
- isLargeScreenWidth?: boolean;
-};
-
-function ExpenseReportListItemRow({
+function ExpenseReportListItemRowWide({
item,
reportActions,
onCheckboxPress = () => {},
@@ -62,36 +36,24 @@ function ExpenseReportListItemRow({
isHovered = false,
isFocused = false,
isPendingDelete = false,
- isLargeScreenWidth = false,
-}: ExpenseReportListItemRowProps) {
+}: ExpenseReportListItemRowWideProps) {
const StyleUtils = useStyleUtils();
const styles = useThemeStyles();
const theme = useTheme();
- const {translate} = useLocalize();
- const {convertToDisplayString} = useCurrencyListActions();
const expensifyIcons = useMemoizedLazyExpensifyIcons(['ArrowRight']);
const currency = item.currency ?? CONST.CURRENCY.USD;
const {totalDisplaySpend = 0, nonReimbursableSpend = 0, reimbursableSpend = 0, isAllScanning: isScanning = false} = item;
- // Calculate the correct border color for avatars based on hover and focus states
- const finalAvatarBorderColor =
- StyleUtils.getItemBackgroundColorStyle(!!item.isSelected, !!isFocused || !!isHovered, !!item.isDisabled, theme.activeComponentBG, theme.hoverComponentBG)?.backgroundColor ??
- theme.highlightBG;
-
const columnComponents = {
[CONST.SEARCH.TABLE_COLUMNS.AVATAR]: (
-
-
-
+
),
[CONST.SEARCH.TABLE_COLUMNS.DATE]: (
@@ -143,7 +105,7 @@ function ExpenseReportListItemRow({
),
@@ -154,7 +116,7 @@ function ExpenseReportListItemRow({
accountID={item.from.accountID}
avatar={item.from.avatar}
displayName={item.formattedFrom ?? ''}
- isLargeScreenWidth={isLargeScreenWidth}
+ isLargeScreenWidth
/>
)}
@@ -166,7 +128,7 @@ function ExpenseReportListItemRow({
accountID={item.to.accountID}
avatar={item.to.avatar}
displayName={item.formattedTo ?? ''}
- isLargeScreenWidth={isLargeScreenWidth}
+ isLargeScreenWidth
/>
)}
@@ -226,7 +188,7 @@ function ExpenseReportListItemRow({
),
[CONST.SEARCH.TABLE_COLUMNS.ACTION]: (
- t.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
- const expenseCount = (filteredTransactions?.length ? filteredTransactions.length : undefined) ?? item.transactionCount ?? 0;
- const expenseCountText = translate('iou.expenseCount', {count: expenseCount});
- const formattedDate = DateUtils.formatWithUTCTimeZone(
- item.created ?? '',
- DateUtils.doesDateBelongToAPastYear(item.created ?? '') ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT,
- );
-
- const amountText = isScanning ? translate('iou.receiptStatusTitle') : convertToDisplayString(totalDisplaySpend, currency);
- const groupAccessibilityLabel = [item.reportName, amountText, formattedDate, expenseCountText].filter(Boolean).join(', ');
- return (
-
- {!!canSelectMultiple && (
-
- )}
-
-
-
- {item.reportName ?? ''}
-
-
- {isScanning ? translate('iou.receiptStatusTitle') : convertToDisplayString(totalDisplaySpend, currency)}
-
-
-
- {formattedDate}
- {expenseCountText}
-
-
-
- );
- }
-
return (
@@ -333,4 +243,4 @@ function ExpenseReportListItemRow({
);
}
-export default ExpenseReportListItemRow;
+export default ExpenseReportListItemRowWide;
diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/index.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/index.tsx
new file mode 100644
index 000000000000..e4adf93772cb
--- /dev/null
+++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/index.tsx
@@ -0,0 +1,44 @@
+import React from 'react';
+import useResponsiveLayout from '@hooks/useResponsiveLayout';
+import ExpenseReportListItemRowNarrow from './ExpenseReportListItemRowNarrow';
+import ExpenseReportListItemRowWide from './ExpenseReportListItemRowWide';
+import type {ExpenseReportListItemRowProps} from './types';
+
+function ExpenseReportListItemRow(props: ExpenseReportListItemRowProps) {
+ const {isLargeScreenWidth} = useResponsiveLayout();
+
+ if (isLargeScreenWidth) {
+ return (
+
+ );
+ }
+ return (
+
+ );
+}
+
+export default ExpenseReportListItemRow;
+export type {ExpenseReportListItemRowProps, ExpenseReportListItemRowNarrowProps, ExpenseReportListItemRowWideProps} from './types';
diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/types.ts b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/types.ts
new file mode 100644
index 000000000000..6a405152939a
--- /dev/null
+++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/types.ts
@@ -0,0 +1,29 @@
+import type {StyleProp, ViewStyle} from 'react-native';
+import type {ExpenseReportListItemType} from '@components/Search/SearchList/ListItem/types';
+import type {SearchColumnType} from '@components/Search/types';
+import type {ReportAction} from '@src/types/onyx';
+
+type ExpenseReportListItemRowNarrowProps = {
+ item: ExpenseReportListItemType;
+ canSelectMultiple?: boolean;
+ onCheckboxPress?: () => void;
+ isSelectAllChecked?: boolean;
+ isIndeterminate?: boolean;
+ isDisabledCheckbox?: boolean;
+};
+
+type ExpenseReportListItemRowWideProps = ExpenseReportListItemRowNarrowProps & {
+ reportActions?: ReportAction[];
+ showTooltip: boolean;
+ isActionLoading?: boolean;
+ onButtonPress?: () => void;
+ containerStyle?: StyleProp;
+ isHovered?: boolean;
+ isFocused?: boolean;
+ isPendingDelete?: boolean;
+ columns?: SearchColumnType[];
+};
+
+type ExpenseReportListItemRowProps = ExpenseReportListItemRowWideProps;
+
+export type {ExpenseReportListItemRowProps, ExpenseReportListItemRowNarrowProps, ExpenseReportListItemRowWideProps};