Skip to content

Commit fef79e8

Browse files
authored
Merge pull request Expensify#66927 from callstack-internal/feat/66148-improve-ReportActionItem-performance
2 parents 2b1a9fd + 1690c83 commit fef79e8

12 files changed

Lines changed: 287 additions & 38 deletions

src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import ConfirmModal from '@components/ConfirmModal';
1212
import DecisionModal from '@components/DecisionModal';
1313
import FlatList from '@components/FlatList';
1414
import {AUTOSCROLL_TO_TOP_THRESHOLD} from '@components/InvertedFlatList/BaseInvertedFlatList';
15+
import {usePersonalDetails} from '@components/OnyxListItemProvider';
1516
import {PressableWithFeedback} from '@components/Pressable';
1617
import {useSearchContext} from '@components/Search/SearchContext';
1718
import Text from '@components/Text';
@@ -44,7 +45,7 @@ import {
4445
shouldReportActionBeVisible,
4546
wasMessageReceivedWhileOffline,
4647
} from '@libs/ReportActionsUtils';
47-
import {canUserPerformWriteAction, chatIncludesChronosWithID, getReportLastVisibleActionCreated, isUnread} from '@libs/ReportUtils';
48+
import {canUserPerformWriteAction, chatIncludesChronosWithID, getOriginalReportID, getReportLastVisibleActionCreated, isUnread} from '@libs/ReportUtils';
4849
import markOpenReportEnd from '@libs/Telemetry/markOpenReportEnd';
4950
import {isTransactionPendingDelete} from '@libs/TransactionUtils';
5051
import Visibility from '@libs/Visibility';
@@ -141,6 +142,13 @@ function MoneyRequestReportActionsList({
141142
selector: (parentReportActions) => getParentReportAction(parentReportActions, report?.parentReportActionID),
142143
});
143144

145+
const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET, {canBeMissing: false});
146+
const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => account?.validated, canBeMissing: true});
147+
const [userBillingFundID] = useOnyx(ONYXKEYS.NVP_BILLING_FUND_ID, {canBeMissing: true});
148+
const personalDetails = usePersonalDetails();
149+
const [emojiReactions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}`, {canBeMissing: true});
150+
const [draftMessage] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}`, {canBeMissing: true});
151+
144152
const transactionsWithoutPendingDelete = useMemo(() => transactions.filter((t) => !isTransactionPendingDelete(t)), [transactions]);
145153
const mostRecentIOUReportActionID = useMemo(() => getMostRecentIOURequestActionID(reportActions), [reportActions]);
146154
const transactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, reportActions ?? [], false, reportTransactionIDs);
@@ -471,6 +479,12 @@ function MoneyRequestReportActionsList({
471479
!isConsecutiveChronosAutomaticTimerAction(visibleReportActions, index, chatIncludesChronosWithID(reportAction?.reportID)) &&
472480
hasNextActionMadeBySameActor(visibleReportActions, index);
473481

482+
const actionEmojiReactions = emojiReactions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportAction.reportActionID}`];
483+
const originalReportID = getOriginalReportID(report.reportID, reportAction);
484+
const reportDraftMessages = draftMessage?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${originalReportID}`];
485+
const matchingDraftMessage = reportDraftMessages?.[reportAction.reportActionID];
486+
const matchingDraftMessageString = typeof matchingDraftMessage === 'string' ? matchingDraftMessage : matchingDraftMessage?.message;
487+
474488
return (
475489
<ReportActionsListItemRenderer
476490
allReports={allReports}
@@ -489,6 +503,12 @@ function MoneyRequestReportActionsList({
489503
isFirstVisibleReportAction={firstVisibleReportActionID === reportAction.reportActionID}
490504
shouldHideThreadDividerLine
491505
linkedReportActionID={linkedReportActionID}
506+
userWallet={userWallet}
507+
isUserValidated={isUserValidated}
508+
personalDetails={personalDetails}
509+
userBillingFundID={userBillingFundID}
510+
emojiReactions={actionEmojiReactions}
511+
draftMessage={matchingDraftMessageString}
492512
/>
493513
);
494514
},
@@ -504,6 +524,12 @@ function MoneyRequestReportActionsList({
504524
linkedReportActionID,
505525
allReports,
506526
policies,
527+
userWallet,
528+
isUserValidated,
529+
personalDetails,
530+
userBillingFundID,
531+
emojiReactions,
532+
draftMessage,
507533
],
508534
);
509535

src/components/Search/SearchList.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Checkbox from '@components/Checkbox';
1010
import * as Expensicons from '@components/Icon/Expensicons';
1111
import MenuItem from '@components/MenuItem';
1212
import Modal from '@components/Modal';
13+
import {usePersonalDetails} from '@components/OnyxListItemProvider';
1314
import {PressableWithFeedback} from '@components/Pressable';
1415
import type ChatListItem from '@components/SelectionList/ChatListItem';
1516
import type TaskListItem from '@components/SelectionList/Search/TaskListItem';
@@ -174,6 +175,12 @@ function SearchList(
174175

175176
const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false});
176177

178+
const personalDetails = usePersonalDetails();
179+
180+
const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET, {canBeMissing: false});
181+
const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => account?.validated, canBeMissing: true});
182+
const [userBillingFundID] = useOnyx(ONYXKEYS.NVP_BILLING_FUND_ID, {canBeMissing: true});
183+
177184
const handleLongPressRow = useCallback(
178185
(item: SearchListItem) => {
179186
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
@@ -342,15 +349,19 @@ function SearchList(
342349
isDisabled={isDisabled}
343350
allReports={allReports}
344351
groupBy={groupBy}
352+
userWallet={userWallet}
353+
isUserValidated={isUserValidated}
354+
personalDetails={personalDetails}
355+
userBillingFundID={userBillingFundID}
345356
/>
346357
);
347358
},
348359
[
349360
ListItem,
350361
canSelectMultiple,
351362
focusedIndex,
352-
handleLongPressRow,
353363
itemsToHighlight,
364+
handleLongPressRow,
354365
onCheckboxPress,
355366
onSelectRow,
356367
policies,
@@ -359,6 +370,10 @@ function SearchList(
359370
setFocusedIndex,
360371
shouldPreventDefaultFocusOnSelectRow,
361372
allReports,
373+
userWallet,
374+
isUserValidated,
375+
personalDetails,
376+
userBillingFundID,
362377
],
363378
);
364379

src/components/SelectionList/BaseSelectionListItemRenderer.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import React from 'react';
22
import type {NativeSyntheticEvent, StyleProp, TextStyle, ViewStyle} from 'react-native';
3+
import type {OnyxEntry} from 'react-native-onyx';
34
import type useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager';
45
import type useSingleExecution from '@hooks/useSingleExecution';
56
import {isMobileChrome} from '@libs/Browser';
67
import {isTransactionGroupListItemType} from '@libs/SearchUIUtils';
8+
import type {PersonalDetailsList, UserWallet} from '@src/types/onyx';
79
import type {BaseListItemProps, ExtendedTargetedEvent, ListItem, SelectionListProps} from './types';
810

911
type BaseSelectionListItemRendererProps<TItem extends ListItem> = Omit<BaseListItemProps<TItem>, 'onSelectRow'> &
@@ -15,6 +17,10 @@ type BaseSelectionListItemRendererProps<TItem extends ListItem> = Omit<BaseListI
1517
singleExecution: ReturnType<typeof useSingleExecution>['singleExecution'];
1618
titleStyles?: StyleProp<TextStyle>;
1719
titleContainerStyles?: StyleProp<ViewStyle>;
20+
userWallet?: OnyxEntry<UserWallet>;
21+
isUserValidated?: boolean | undefined;
22+
personalDetails?: OnyxEntry<PersonalDetailsList>;
23+
userBillingFundID?: number | undefined;
1824
};
1925

2026
function BaseSelectionListItemRenderer<TItem extends ListItem>({
@@ -45,6 +51,10 @@ function BaseSelectionListItemRenderer<TItem extends ListItem>({
4551
titleContainerStyles,
4652
shouldUseDefaultRightHandSideCheckmark,
4753
canShowProductTrainingTooltip = true,
54+
userWallet,
55+
isUserValidated,
56+
personalDetails,
57+
userBillingFundID,
4858
}: BaseSelectionListItemRendererProps<TItem>) {
4959
const handleOnCheckboxPress = () => {
5060
if (isTransactionGroupListItemType(item)) {
@@ -96,6 +106,10 @@ function BaseSelectionListItemRenderer<TItem extends ListItem>({
96106
titleContainerStyles={titleContainerStyles}
97107
shouldUseDefaultRightHandSideCheckmark={shouldUseDefaultRightHandSideCheckmark}
98108
canShowProductTrainingTooltip={canShowProductTrainingTooltip}
109+
userWallet={userWallet}
110+
isUserValidated={isUserValidated}
111+
personalDetails={personalDetails}
112+
userBillingFundID={userBillingFundID}
99113
/>
100114
{item.footerContent && item.footerContent}
101115
</>

src/components/SelectionList/ChatListItem.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ function ChatListItem<TItem extends ListItem>({
2323
shouldSyncFocus,
2424
policies,
2525
allReports,
26+
userWallet,
27+
isUserValidated,
28+
personalDetails,
29+
userBillingFundID,
2630
}: ChatListItemProps<TItem>) {
2731
const reportActionItem = item as unknown as ReportActionListItemType;
2832
const reportID = Number(reportActionItem?.reportID ?? CONST.DEFAULT_NUMBER_ID);
@@ -93,6 +97,10 @@ function ChatListItem<TItem extends ListItem>({
9397
}
9498
policies={policies}
9599
shouldShowBorder
100+
userWallet={userWallet}
101+
isUserValidated={isUserValidated}
102+
personalDetails={personalDetails}
103+
userBillingFundID={userBillingFundID}
96104
/>
97105
</BaseListItem>
98106
);

src/components/SelectionList/types.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type {
1212
TextStyle,
1313
ViewStyle,
1414
} from 'react-native';
15-
import type {OnyxCollection} from 'react-native-onyx';
15+
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
1616
import type {AnimatedStyle} from 'react-native-reanimated';
1717
import type {SearchRouterItem} from '@components/Search/SearchAutocompleteList';
1818
import type {SearchColumnType, SearchGroupBy} from '@components/Search/types';
@@ -22,7 +22,7 @@ import type SpendCategorySelectorListItem from '@pages/workspace/categories/Spen
2222
// eslint-disable-next-line no-restricted-imports
2323
import type CursorStyles from '@styles/utils/cursor/types';
2424
import type CONST from '@src/CONST';
25-
import type {Policy, Report, TransactionViolation} from '@src/types/onyx';
25+
import type {PersonalDetailsList, Policy, Report, TransactionViolation, UserWallet} from '@src/types/onyx';
2626
import type {Attendee, SplitExpense} from '@src/types/onyx/IOU';
2727
import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon';
2828
import type {SearchCard, SearchPersonalDetails, SearchReport, SearchReportAction, SearchTask, SearchTransaction} from '@src/types/onyx/SearchResults';
@@ -477,6 +477,18 @@ type ChatListItemProps<TItem extends ListItem> = ListItemProps<TItem> & {
477477

478478
/** The report data */
479479
report?: Report;
480+
481+
/** The user wallet */
482+
userWallet: OnyxEntry<UserWallet>;
483+
484+
/** Whether the user is validated */
485+
isUserValidated: boolean | undefined;
486+
487+
/** Personal details list */
488+
personalDetails: OnyxEntry<PersonalDetailsList>;
489+
490+
/** User billing fund ID */
491+
userBillingFundID: number | undefined;
480492
};
481493

482494
type ValidListItem =

src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ function DebugReportActionCreatePage({
5050
const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: false});
5151
const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false});
5252
const [personalDetailsList] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false});
53+
const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET, {canBeMissing: true});
54+
const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => account?.validated, canBeMissing: true});
5355
const [draftReportAction, setDraftReportAction] = useState<string>(() => getInitialReportAction(reportID, session, personalDetailsList));
56+
const [userBillingFundID] = useOnyx(ONYXKEYS.NVP_BILLING_FUND_ID, {canBeMissing: true});
57+
5458
const [error, setError] = useState<string>();
5559

5660
const createReportAction = useCallback(() => {
@@ -120,6 +124,10 @@ function DebugReportActionCreatePage({
120124
index={0}
121125
isFirstVisibleReportAction={false}
122126
shouldDisplayContextMenu={false}
127+
userWallet={userWallet}
128+
isUserValidated={isUserValidated}
129+
personalDetails={personalDetailsList}
130+
userBillingFundID={userBillingFundID}
123131
/>
124132
) : (
125133
<Text>{translate('debug.nothingToPreview')}</Text>

src/pages/Debug/ReportAction/DebugReportActionPreview.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import type {OnyxEntry} from 'react-native-onyx';
3+
import {usePersonalDetails} from '@components/OnyxListItemProvider';
34
import ScrollView from '@components/ScrollView';
45
import useOnyx from '@hooks/useOnyx';
56
import ReportActionItem from '@pages/home/report/ReportActionItem';
@@ -17,6 +18,10 @@ type DebugReportActionPreviewProps = {
1718
function DebugReportActionPreview({reportAction, reportID}: DebugReportActionPreviewProps) {
1819
const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false});
1920
const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: false});
21+
const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET, {canBeMissing: true});
22+
const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => account?.validated, canBeMissing: true});
23+
const personalDetails = usePersonalDetails();
24+
const [userBillingFundID] = useOnyx(ONYXKEYS.NVP_BILLING_FUND_ID, {canBeMissing: true});
2025
const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`];
2126

2227
return (
@@ -34,6 +39,10 @@ function DebugReportActionPreview({reportAction, reportID}: DebugReportActionPre
3439
index={0}
3540
isFirstVisibleReportAction={false}
3641
shouldDisplayContextMenu={false}
42+
userWallet={userWallet}
43+
isUserValidated={isUserValidated}
44+
personalDetails={personalDetails}
45+
userBillingFundID={userBillingFundID}
3746
/>
3847
</ScrollView>
3948
);

src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import React from 'react';
22
import {View} from 'react-native';
33
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
4+
import {usePersonalDetails} from '@components/OnyxListItemProvider';
45
import useOnyx from '@hooks/useOnyx';
56
import useThemeStyles from '@hooks/useThemeStyles';
67
import {getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils';
8+
import {getOriginalReportID} from '@libs/ReportUtils';
79
import ReportActionItem from '@pages/home/report/ReportActionItem';
810
import CONST from '@src/CONST';
911
import ONYXKEYS from '@src/ONYXKEYS';
@@ -19,6 +21,10 @@ type DuplicateTransactionItemProps = {
1921

2022
function DuplicateTransactionItem({transaction, index, allReports, policies}: DuplicateTransactionItemProps) {
2123
const styles = useThemeStyles();
24+
const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET, {canBeMissing: true});
25+
const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => account?.validated, canBeMissing: true});
26+
const personalDetails = usePersonalDetails();
27+
const [userBillingFundID] = useOnyx(ONYXKEYS.NVP_BILLING_FUND_ID, {canBeMissing: true});
2228
const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`];
2329
const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`, {canBeMissing: false});
2430

@@ -28,10 +34,28 @@ function DuplicateTransactionItem({transaction, index, allReports, policies}: Du
2834
return IOUTransactionID === transaction?.transactionID;
2935
});
3036

37+
const originalReportID = getOriginalReportID(report?.reportID, action);
38+
39+
const [draftMessage] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${originalReportID}`, {
40+
canBeMissing: true,
41+
});
42+
43+
const [emojiReactions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${action?.reportActionID}`, {
44+
canBeMissing: true,
45+
});
46+
47+
const [linkedTransactionRouteError] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${isMoneyRequestAction(action) && getOriginalMessage(action)?.IOUTransactionID}`, {
48+
canBeMissing: true,
49+
selector: (transactionItem) => transactionItem?.errorFields?.route ?? null,
50+
});
51+
3152
if (!action || !report) {
3253
return null;
3354
}
3455

56+
const reportDraftMessage = draftMessage?.[action.reportActionID];
57+
const matchingDraftMessage = typeof reportDraftMessage === 'string' ? reportDraftMessage : reportDraftMessage?.message;
58+
3559
return (
3660
<View style={styles.pb2}>
3761
<ReportActionItem
@@ -47,6 +71,13 @@ function DuplicateTransactionItem({transaction, index, allReports, policies}: Du
4771
isMostRecentIOUReportAction={false}
4872
isFirstVisibleReportAction={false}
4973
shouldDisplayContextMenu={false}
74+
userWallet={userWallet}
75+
isUserValidated={isUserValidated}
76+
personalDetails={personalDetails}
77+
draftMessage={matchingDraftMessage}
78+
emojiReactions={emojiReactions}
79+
linkedTransactionRouteError={linkedTransactionRouteError}
80+
userBillingFundID={userBillingFundID}
5081
/>
5182
</View>
5283
);

0 commit comments

Comments
 (0)