Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 6 additions & 190 deletions src/components/LHNOptionsList/LHNOptionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {FlashList} from '@shopify/flash-list';
import type {ReactElement} from 'react';
import React, {memo, useCallback, useContext, useEffect, useMemo, useRef} from 'react';
import {StyleSheet, View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import type {BlockingViewProps} from '@components/BlockingViews/BlockingView';
import BlockingView from '@components/BlockingViews/BlockingView';
import Icon from '@components/Icon';
Expand All @@ -15,7 +14,6 @@ import TextBlock from '@components/TextBlock';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses';
import useReportAttributes from '@hooks/useReportAttributes';
Expand All @@ -25,24 +23,11 @@ import useScrollEventEmitter from '@hooks/useScrollEventEmitter';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import getPlatform from '@libs/getPlatform';
import Log from '@libs/Log';
import {getMovedReportID} from '@libs/ModifiedExpenseMessage';
import {getIOUReportIDOfLastAction, getLastMessageTextForReport} from '@libs/OptionsListUtils';
import {
getOneTransactionThreadReportID,
getOriginalMessage,
getSortedReportActions,
getSortedReportActionsForDisplay,
isInviteOrRemovedAction,
isMoneyRequestAction,
shouldReportActionBeVisibleAsLastAction,
} from '@libs/ReportActionsUtils';
import {canUserPerformWriteAction as canUserPerformWriteActionUtil} from '@libs/ReportUtils';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import NAVIGATORS from '@src/NAVIGATORS';
import ONYXKEYS from '@src/ONYXKEYS';
import type {PersonalDetails, Report, ReportAction} from '@src/types/onyx';
import type {Report} from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import OptionRowLHNData from './OptionRowLHNData';
import OptionRowRendererComponent from './OptionRowRendererComponent';
Expand All @@ -53,28 +38,21 @@ const keyExtractor = (item: Report) => `report_${item.reportID}`;

function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optionMode, shouldDisableFocusOptions = false, onFirstItemRendered = () => {}}: LHNOptionsListProps) {
const {saveScrollOffset, getScrollOffset, saveScrollIndex, getScrollIndex} = useContext(ScrollOffsetContext);
const {isOffline} = useNetwork();
const flashListRef = useRef<FlashListRef<Report>>(null);
const route = useRoute();
const isScreenFocused = useIsFocused();
const expensifyIcons = useMemoizedLazyExpensifyIcons(['MagnifyingGlass']);

const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false});
const reportAttributes = useReportAttributes();
const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {canBeMissing: true});
const [reportMetadataCollection] = useOnyx(ONYXKEYS.COLLECTION.REPORT_METADATA, {canBeMissing: true});
const [reportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, {canBeMissing: false});
const [policy] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: false});
const {policyForMovingExpensesID} = usePolicyForMovingExpenses();
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true});
const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: false});
const [draftComments] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT, {canBeMissing: false});
const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: false});
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: true});
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true});
const [onboarding] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {canBeMissing: true});
const [isFullscreenVisible] = useOnyx(ONYXKEYS.FULLSCREEN_VISIBILITY, {canBeMissing: true});
const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails();
const {policyForMovingExpensesID} = usePolicyForMovingExpenses();

const theme = useTheme();
const styles = useThemeStyles();
Expand Down Expand Up @@ -174,121 +152,19 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio
*/
const renderItem = useCallback(
({item, index}: RenderItemProps): ReactElement => {
const reportID = item.reportID;
const itemParentReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${item.parentReportID}`];
const itemReportNameValuePairs = reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`];
const chatReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${item.chatReportID}`];
const itemReportActions = reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`];
const itemOneTransactionThreadReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${getOneTransactionThreadReportID(item, chatReport, itemReportActions, isOffline)}`];
const itemParentReportActions = reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${item?.parentReportID}`];
const itemParentReportAction = item?.parentReportActionID ? itemParentReportActions?.[item?.parentReportActionID] : undefined;
const itemReportAttributes = reportAttributes?.[reportID];

let invoiceReceiverPolicyID = '-1';
if (item?.invoiceReceiver && 'policyID' in item.invoiceReceiver) {
invoiceReceiverPolicyID = item.invoiceReceiver.policyID;
}
if (itemParentReport?.invoiceReceiver && 'policyID' in itemParentReport.invoiceReceiver) {
invoiceReceiverPolicyID = itemParentReport.invoiceReceiver.policyID;
}
const itemInvoiceReceiverPolicy = policy?.[`${ONYXKEYS.COLLECTION.POLICY}${invoiceReceiverPolicyID}`];

const iouReportIDOfLastAction = getIOUReportIDOfLastAction(item);
const itemIouReportReportActions = iouReportIDOfLastAction ? reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReportIDOfLastAction}`] : undefined;

const itemPolicy = policy?.[`${ONYXKEYS.COLLECTION.POLICY}${item?.policyID}`];
const transactionID = isMoneyRequestAction(itemParentReportAction)
? (getOriginalMessage(itemParentReportAction)?.IOUTransactionID ?? CONST.DEFAULT_NUMBER_ID)
: CONST.DEFAULT_NUMBER_ID;
const itemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`];
const hasDraftComment =
!!draftComments?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] &&
!draftComments?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`]?.match(CONST.REGEX.EMPTY_COMMENT);

const isReportArchived = !!itemReportNameValuePairs?.private_isArchived;
const canUserPerformWrite = canUserPerformWriteActionUtil(item, isReportArchived);
const sortedReportActions = getSortedReportActionsForDisplay(itemReportActions, canUserPerformWrite);
const lastReportAction = sortedReportActions.at(0);

// Get the transaction for the last report action
const lastReportActionTransactionID = isMoneyRequestAction(lastReportAction)
? (getOriginalMessage(lastReportAction)?.IOUTransactionID ?? CONST.DEFAULT_NUMBER_ID)
: CONST.DEFAULT_NUMBER_ID;
const lastReportActionTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${lastReportActionTransactionID}`];

// SidebarUtils.getOptionData in OptionRowLHNData does not get re-evaluated when the linked task report changes, so we have the lastMessageTextFromReport evaluation logic here
let lastActorDetails: Partial<PersonalDetails> | null = item?.lastActorAccountID && personalDetails?.[item.lastActorAccountID] ? personalDetails[item.lastActorAccountID] : null;
if (!lastActorDetails && lastReportAction) {
const lastActorDisplayName = lastReportAction?.person?.[0]?.text;
lastActorDetails = lastActorDisplayName
? {
displayName: lastActorDisplayName,
accountID: item?.lastActorAccountID,
}
: null;
}
const movedFromReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${getMovedReportID(lastReportAction, CONST.REPORT.MOVE_TYPE.FROM)}`];
const movedToReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${getMovedReportID(lastReportAction, CONST.REPORT.MOVE_TYPE.TO)}`];
const itemReportMetadata = reportMetadataCollection?.[`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`];
const lastMessageTextFromReport = getLastMessageTextForReport({
translate,
report: item,
lastActorDetails,
movedFromReport,
movedToReport,
policy: itemPolicy,
isReportArchived: !!itemReportNameValuePairs?.private_isArchived,
policyForMovingExpensesID,
reportMetadata: itemReportMetadata,
reportAttributesDerived: reportAttributes,
});

const shouldShowRBRorGBRTooltip = firstReportIDWithGBRorRBR === reportID;

let lastAction: ReportAction | undefined;
if (!itemReportActions || !item) {
lastAction = undefined;
} else {
const canUserPerformWriteAction = canUserPerformWriteActionUtil(item, isReportArchived);
const actionsArray = getSortedReportActions(Object.values(itemReportActions));
const reportActionsForDisplay = actionsArray.filter(
(reportAction) => shouldReportActionBeVisibleAsLastAction(reportAction, canUserPerformWriteAction) && reportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED,
);
lastAction = reportActionsForDisplay.at(-1);
}

let lastActionReport: OnyxEntry<Report> | undefined;
if (isInviteOrRemovedAction(lastAction)) {
const lastActionOriginalMessage = lastAction?.actionName ? getOriginalMessage(lastAction) : null;
lastActionReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${lastActionOriginalMessage?.reportID}`];
}

return (
<OptionRowLHNData
reportID={reportID}
reportID={item.reportID}
fullReport={item}
reportAttributes={itemReportAttributes}
reportAttributesDerived={reportAttributes}
oneTransactionThreadReport={itemOneTransactionThreadReport}
reportNameValuePairs={itemReportNameValuePairs}
reportActions={itemReportActions}
parentReportAction={itemParentReportAction}
iouReportReportActions={itemIouReportReportActions}
policy={itemPolicy}
invoiceReceiverPolicy={itemInvoiceReceiverPolicy}
personalDetails={personalDetails ?? {}}
transaction={itemTransaction}
lastReportActionTransaction={lastReportActionTransaction}
receiptTransactions={transactions}
viewMode={optionMode}
isOptionFocused={!shouldDisableFocusOptions}
lastMessageTextFromReport={lastMessageTextFromReport}
onSelectRow={onSelectRow}
preferredLocale={preferredLocale}
hasDraftComment={hasDraftComment}
transactionViolations={transactionViolations}
onLayout={onLayoutItem}
shouldShowRBRorGBRTooltip={shouldShowRBRorGBRTooltip}
activePolicyID={activePolicyID}
onboardingPurpose={introSelected?.choice}
onboarding={onboarding}
Expand All @@ -298,25 +174,17 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio
localeCompare={localeCompare}
translate={translate}
testID={index}
isReportArchived={isReportArchived}
lastAction={lastAction}
lastActionReport={lastActionReport}
policyForMovingExpensesID={policyForMovingExpensesID}
firstReportIDWithGBRorRBR={firstReportIDWithGBRorRBR}
currentUserAccountID={currentUserAccountID}
/>
);
},
[
reports,
reportNameValuePairs,
reportActions,
reportMetadataCollection,
isOffline,
policyForMovingExpensesID,
reportAttributes,
policy,
transactions,
draftComments,
personalDetails,
policyForMovingExpensesID,
firstReportIDWithGBRorRBR,
optionMode,
shouldDisableFocusOptions,
Expand All @@ -336,43 +204,6 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio
],
);

const extraData = useMemo(
() => [
reportActions,
reports,
reportAttributes,
reportNameValuePairs,
transactionViolations,
policy,
personalDetails,
data.length,
draftComments,
optionMode,
preferredLocale,
transactions,
isOffline,
isScreenFocused,
isReportsSplitNavigatorLast,
],
[
reportActions,
reports,
reportAttributes,
reportNameValuePairs,
transactionViolations,
policy,
personalDetails,
data.length,
draftComments,
optionMode,
preferredLocale,
transactions,
isOffline,
isScreenFocused,
isReportsSplitNavigatorLast,
],
);

const previousOptionMode = usePrevious(optionMode);

useEffect(() => {
Expand Down Expand Up @@ -420,20 +251,6 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio
});
}, [getScrollOffset, route, isWeb]);

// eslint-disable-next-line rulesdir/prefer-early-return
useEffect(() => {
if (shouldShowEmptyLHN) {
Log.info('Woohoo! All caught up. Was rendered', false, {
reportsCount: Object.keys(reports ?? {}).length,
reportActionsCount: Object.keys(reportActions ?? {}).length,
policyCount: Object.keys(policy ?? {}).length,
personalDetailsCount: Object.keys(personalDetails ?? {}).length,
route,
reportsIDsFromUseReportsCount: data.length,
});
}
}, [data.length, shouldShowEmptyLHN, route, reports, reportActions, policy, personalDetails]);

return (
<View style={[style ?? styles.flex1, shouldShowEmptyLHN ? styles.emptyLHNWrapper : undefined]}>
{shouldShowEmptyLHN ? (
Expand All @@ -455,7 +272,6 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio
testID="lhn-options-list"
keyExtractor={keyExtractor}
renderItem={renderItem}
extraData={extraData}
showsVerticalScrollIndicator={false}
onLayout={onLayout}
onScroll={onScroll}
Expand Down
Loading
Loading