Skip to content

Commit 4d90adb

Browse files
authored
Merge pull request Expensify#67309 from callstack-internal/feat/66149-ReportActionItem-optimization
perf: move useOnyx from ReportActionItem to list component
2 parents 1dfb9cb + 047b762 commit 4d90adb

13 files changed

Lines changed: 310 additions & 47 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 [userWalletTierName] = useOnyx(ONYXKEYS.USER_WALLET, {selector: (wallet) => wallet?.tierName, 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+
userWalletTierName={userWalletTierName}
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+
userWalletTierName,
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';
@@ -186,6 +187,12 @@ function SearchList(
186187

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

190+
const personalDetails = usePersonalDetails();
191+
192+
const [userWalletTierName] = useOnyx(ONYXKEYS.USER_WALLET, {selector: (wallet) => wallet?.tierName, canBeMissing: false});
193+
const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => account?.validated, canBeMissing: true});
194+
const [userBillingFundID] = useOnyx(ONYXKEYS.NVP_BILLING_FUND_ID, {canBeMissing: true});
195+
189196
const handleLongPressRow = useCallback(
190197
(item: SearchListItem) => {
191198
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
@@ -348,15 +355,19 @@ function SearchList(
348355
isDisabled={isDisabled}
349356
allReports={allReports}
350357
groupBy={groupBy}
358+
userWalletTierName={userWalletTierName}
359+
isUserValidated={isUserValidated}
360+
personalDetails={personalDetails}
361+
userBillingFundID={userBillingFundID}
351362
/>
352363
);
353364
},
354365
[
355366
ListItem,
356367
canSelectMultiple,
357368
focusedIndex,
358-
handleLongPressRow,
359369
itemsToHighlight,
370+
handleLongPressRow,
360371
onCheckboxPress,
361372
onSelectRow,
362373
policies,
@@ -366,6 +377,10 @@ function SearchList(
366377
setFocusedIndex,
367378
shouldPreventDefaultFocusOnSelectRow,
368379
allReports,
380+
userWalletTierName,
381+
isUserValidated,
382+
personalDetails,
383+
userBillingFundID,
369384
],
370385
);
371386

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} 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+
userWalletTierName?: string | undefined;
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+
userWalletTierName,
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+
userWalletTierName={userWalletTierName}
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+
userWalletTierName,
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+
userWalletTierName={userWalletTierName}
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} 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';
@@ -488,6 +488,18 @@ type ChatListItemProps<TItem extends ListItem> = ListItemProps<TItem> & {
488488

489489
/** The report data */
490490
report?: Report;
491+
492+
/** The user wallet tierName */
493+
userWalletTierName: string | undefined;
494+
495+
/** Whether the user is validated */
496+
isUserValidated: boolean | undefined;
497+
498+
/** Personal details list */
499+
personalDetails: OnyxEntry<PersonalDetailsList>;
500+
501+
/** User billing fund ID */
502+
userBillingFundID: number | undefined;
491503
};
492504

493505
type ValidListItem =

src/libs/ReportUtils.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ import type {
5252
Transaction,
5353
TransactionViolation,
5454
TransactionViolations,
55-
UserWallet,
5655
} from '@src/types/onyx';
5756
import type {Attendee, Participant} from '@src/types/onyx/IOU';
5857
import type {SelectedParticipant} from '@src/types/onyx/NewGroupChatDraft';
@@ -9705,14 +9704,14 @@ function isAllowedToSubmitDraftExpenseReport(report: OnyxEntry<Report>): boolean
97059704
/**
97069705
* What missing payment method does this report action indicate, if any?
97079706
*/
9708-
function getIndicatedMissingPaymentMethod(userWallet: OnyxEntry<UserWallet>, reportId: string | undefined, reportAction: ReportAction): MissingPaymentMethod | undefined {
9707+
function getIndicatedMissingPaymentMethod(userWalletTierName: string | undefined, reportId: string | undefined, reportAction: ReportAction): MissingPaymentMethod | undefined {
97099708
const isSubmitterOfUnsettledReport = reportId && isCurrentUserSubmitter(getReport(reportId, allReports)) && !isSettled(reportId);
97109709
if (!reportId || !isSubmitterOfUnsettledReport || !isReimbursementQueuedAction(reportAction)) {
97119710
return undefined;
97129711
}
97139712
const paymentType = getOriginalMessage(reportAction)?.paymentType;
97149713
if (paymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY) {
9715-
return isEmpty(userWallet) || userWallet.tierName === CONST.WALLET.TIER_NAME.SILVER ? 'wallet' : undefined;
9714+
return !userWalletTierName || userWalletTierName === CONST.WALLET.TIER_NAME.SILVER ? 'wallet' : undefined;
97169715
}
97179716

97189717
return !hasCreditBankAccount() ? 'bankAccount' : undefined;
@@ -9721,11 +9720,11 @@ function getIndicatedMissingPaymentMethod(userWallet: OnyxEntry<UserWallet>, rep
97219720
/**
97229721
* Checks if report chat contains missing payment method
97239722
*/
9724-
function hasMissingPaymentMethod(userWallet: OnyxEntry<UserWallet>, iouReportID: string | undefined): boolean {
9723+
function hasMissingPaymentMethod(userWalletTierName: string | undefined, iouReportID: string | undefined): boolean {
97259724
const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReportID}`] ?? {};
97269725
return Object.values(reportActions)
97279726
.filter(Boolean)
9728-
.some((action) => getIndicatedMissingPaymentMethod(userWallet, iouReportID, action) !== undefined);
9727+
.some((action) => getIndicatedMissingPaymentMethod(userWalletTierName, iouReportID, action) !== undefined);
97299728
}
97309729

97319730
/**

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 [userWalletTierName] = useOnyx(ONYXKEYS.USER_WALLET, {selector: (wallet) => wallet?.tierName, canBeMissing: false});
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+
userWalletTierName={userWalletTierName}
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 [userWalletTierName] = useOnyx(ONYXKEYS.USER_WALLET, {selector: (wallet) => wallet?.tierName, canBeMissing: false});
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+
userWalletTierName={userWalletTierName}
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 [userWalletTierName] = useOnyx(ONYXKEYS.USER_WALLET, {selector: (wallet) => wallet?.tierName, canBeMissing: false});
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+
userWalletTierName={userWalletTierName}
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)