diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index 002c6ff1c527..6c475bcfb8c4 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -1,6 +1,6 @@ import React, {useCallback} from 'react'; import {InteractionManager, View} from 'react-native'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import HeaderGap from '@components/HeaderGap'; import MoneyReportHeader from '@components/MoneyReportHeader'; @@ -14,9 +14,10 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {removeFailedReport} from '@libs/actions/Report'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Log from '@libs/Log'; +import {selectAllTransactionsForReport} from '@libs/MoneyRequestReportUtils'; import navigationRef from '@libs/Navigation/navigationRef'; -import {getIOUActionForTransactionID, getOneTransactionThreadReportID, isDeletedParentAction, isMoneyRequestAction} from '@libs/ReportActionsUtils'; -import {canEditReportAction, getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils'; +import {getOneTransactionThreadReportID, isMoneyRequestAction} from '@libs/ReportActionsUtils'; +import {canEditReportAction, getReportOfflinePendingActionAndErrors, isReportTransactionThread} from '@libs/ReportUtils'; import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; import Navigation from '@navigation/Navigation'; import ReportActionsView from '@pages/home/report/ReportActionsView'; @@ -82,20 +83,6 @@ function getParentReportAction(parentReportActions: OnyxEntry, reportID: string | undefined, reportActions: OnyxTypes.ReportAction[]) { - if (!reportID) { - return []; - } - - return Object.values(transactions ?? {}).filter((transaction): transaction is OnyxTypes.Transaction => { - if (!transaction) { - return false; - } - const action = getIOUActionForTransactionID(reportActions, transaction.transactionID); - return transaction.reportID === reportID && !isDeletedParentAction(action); - }); -} - function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayReportFooter, backToRoute}: MoneyRequestReportViewProps) { const styles = useThemeStyles(); const {isOffline} = useNetwork(); @@ -110,10 +97,9 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline); const [transactions = []] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { - selector: (allTransactions): OnyxTypes.Transaction[] => selectTransactionsForReportID(allTransactions, reportID, reportActions), + selector: (allTransactions): OnyxTypes.Transaction[] => selectAllTransactionsForReport(allTransactions, reportID, reportActions), canBeMissing: true, }); - const shouldUseSingleTransactionView = transactions.length === 1; const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(report?.parentReportID)}`, { canEvict: false, @@ -161,6 +147,10 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe ); } + // Special case handling a report that is a transaction thread + // If true we will use standard `ReportActionsView` to display report data, anything else is handled via `MoneyRequestReportActionsList` + const isTransactionThreadView = isReportTransactionThread(report); + return ( - {shouldUseSingleTransactionView ? ( - // This component originally lives in ReportScreen, it is used here to handle the case when the report has a single transaction. Any other case will be handled by MoneyRequestReportActionsList + {isTransactionThreadView ? ( ; function MoneyRequestReportPreviewContent({ iouReportID, - policyID, chatReportID, action, containerStyles, @@ -105,12 +104,13 @@ function MoneyRequestReportPreviewContent({ invoiceReceiverPersonalDetail, lastTransactionViolations, isDelegateAccessRestricted, - renderItem, - getCurrentWidth, + renderTransactionItem, + onLayout, reportPreviewStyles, shouldDisplayContextMenu = true, isInvoice, shouldShowBorder = false, + onPress, }: MoneyRequestReportPreviewContentProps) { const lastTransaction = transactions?.at(0); const transactionIDList = transactions?.map((reportTransaction) => reportTransaction.transactionID) ?? []; @@ -405,7 +405,7 @@ function MoneyRequestReportPreviewContent({ ); } - return renderItem(itemInfo); + return renderTransactionItem(itemInfo); }; // The button should expand up to transaction width @@ -440,10 +440,10 @@ function MoneyRequestReportPreviewContent({ > {}} + onPress={onPress} onPressIn={() => canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} onLongPress={(event) => { @@ -580,7 +580,6 @@ function MoneyRequestReportPreviewContent({ onAnimationFinish={stopAnimation} formattedAmount={getTotalAmountForIOUReportPreviewButton(iouReport, policy, buttonType)} currency={iouReport?.currency} - policyID={policyID} chatReportID={chatReportID} iouReport={iouReport} wrapperStyle={buttonMaxWidth} @@ -625,7 +624,7 @@ function MoneyRequestReportPreviewContent({ text={translate('common.review', { amount: shouldShowSettlementButton ? getTotalAmountForIOUReportPreviewButton(iouReport, policy, buttonType) : '', })} - onPress={() => {}} + onPress={onPress} style={buttonMaxWidth} /> )} @@ -647,7 +646,6 @@ function MoneyRequestReportPreviewContent({ isNoDelegateAccessMenuVisible={isNoDelegateAccessMenuVisible} onClose={() => setIsNoDelegateAccessMenuVisible(false)} /> - {isHoldMenuVisible && !!iouReport && !!requestType && ( { + if (!iouReportID || contextMenuRef.current?.isContextMenuOpening) { + return; + } + + Performance.markStart(CONST.TIMING.OPEN_REPORT_FROM_PREVIEW); + Timing.start(CONST.TIMING.OPEN_REPORT_FROM_PREVIEW); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(iouReportID)); + }, [iouReportID]); + const renderItem: ListRenderItem = ({item}) => ( {}} - wrapperStyle={reportPreviewStyles.transactionPreviewStyle} - containerStyles={[styles.h100, containerStyles]} + containerStyles={[styles.h100, reportPreviewStyles.transactionPreviewStyle, containerStyles]} + transactionPreviewWidth={reportPreviewStyles.transactionPreviewStyle.width} transactionID={item.transactionID} reportPreviewAction={action} /> @@ -79,6 +93,11 @@ function MoneyRequestReportPreview({ return ( { + renderTransactionItem={renderItem} + onLayout={(e: LayoutChangeEvent) => { setCurrentWidth(e.nativeEvent.layout.width ?? 255); }} reportPreviewStyles={reportPreviewStyles} shouldDisplayContextMenu={shouldDisplayContextMenu} isInvoice={isInvoice} + onPress={openReportFromPreview} shouldShowBorder={shouldShowBorder} /> ); diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts b/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts index 76c1582d4fec..4a429e5e8861 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts @@ -73,6 +73,18 @@ type MoneyRequestReportPreviewContentOnyxProps = { }; type MoneyRequestReportPreviewContentProps = MoneyRequestReportPreviewContentOnyxProps & - MoneyRequestReportPreviewProps & {renderItem: ListRenderItem; getCurrentWidth: (e: LayoutChangeEvent) => void; reportPreviewStyles: MoneyRequestReportPreviewStyleType}; + Omit & { + /** Extra styles passed used by MoneyRequestReportPreviewContent */ + reportPreviewStyles: MoneyRequestReportPreviewStyleType; + + /** Callback passed to onLayout */ + onLayout: (e: LayoutChangeEvent) => void; + + /** Callback to render a transaction preview item */ + renderTransactionItem: ListRenderItem; + + /** Callback called when the whole preview is pressed */ + onPress: () => void; + }; export type {MoneyRequestReportPreviewContentProps, MoneyRequestReportPreviewProps, MoneyRequestReportPreviewStyleType}; diff --git a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx index 32df23f475ea..e386d3bc014b 100644 --- a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx +++ b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx @@ -2,12 +2,10 @@ import lodashSortBy from 'lodash/sortBy'; import truncate from 'lodash/truncate'; import React, {useMemo} from 'react'; import {View} from 'react-native'; -import Button from '@components/Button'; import Icon from '@components/Icon'; import {DotIndicator, Folder, Tag} from '@components/Icon/Expensicons'; import MultipleAvatars from '@components/MultipleAvatars'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import ReportActionItemImages from '@components/ReportActionItem/ReportActionItemImages'; import UserInfoCellsWithArrow from '@components/SelectionList/Search/UserInfoCellsWithArrow'; import Text from '@components/Text'; @@ -15,9 +13,7 @@ import TransactionPreviewSkeletonView from '@components/TransactionPreviewSkelet import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import ControlSelection from '@libs/ControlSelection'; import {convertToDisplayString} from '@libs/CurrencyUtils'; -import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import {calculateAmount} from '@libs/IOUUtils'; import {getAvatarsForAccountIDs} from '@libs/OptionsListUtils'; import {getCleanedTagName} from '@libs/PolicyUtils'; @@ -32,7 +28,6 @@ import {hasReceipt as hasReceiptTransactionUtils, isReceiptBeingScanned} from '@ import ViolationsUtils from '@libs/Violations/ViolationsUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; -import SCREENS from '@src/SCREENS'; import type {TransactionPreviewContentProps} from './types'; function TransactionPreviewContent({ @@ -44,17 +39,13 @@ function TransactionPreviewContent({ iouReport, transaction, violations, - showContextMenu, offlineWithFeedbackOnClose, - navigateToReviewFields, - onPreviewPressed, containerStyles, - wrapperStyle, + transactionPreviewWidth, isBillSplit, areThereDuplicates, sessionAccountID, walletTermsErrors, - routeName, reportPreviewAction, shouldHideOnDelete = true, }: TransactionPreviewContentProps) { @@ -62,50 +53,35 @@ function TransactionPreviewContent({ const styles = useThemeStyles(); const {translate} = useLocalize(); - const themeStyles = { - backgroundColor: theme.cardBG, - }; - const transactionDetails = useMemo>(() => getTransactionDetails(transaction) ?? {}, [transaction]); const managerID = iouReport?.managerID ?? reportPreviewAction?.childManagerAccountID ?? CONST.DEFAULT_NUMBER_ID; const ownerAccountID = iouReport?.ownerAccountID ?? reportPreviewAction?.childOwnerAccountID ?? CONST.DEFAULT_NUMBER_ID; const isReportAPolicyExpenseChat = isPolicyExpenseChat(chatReport); const {amount: requestAmount, comment: requestComment, merchant, tag, category, currency: requestCurrency} = transactionDetails; - const transactionPreviewUtilsArguments = useMemo( + const transactionPreviewCommonArguments = useMemo( () => ({ iouReport, transaction, - translate, action, isBillSplit, violations, transactionDetails, }), - [action, iouReport, isBillSplit, transaction, transactionDetails, translate, violations], + [action, iouReport, isBillSplit, transaction, transactionDetails, violations], ); const conditionals = useMemo( () => createTransactionPreviewConditionals({ - ...transactionPreviewUtilsArguments, + ...transactionPreviewCommonArguments, areThereDuplicates, isReportAPolicyExpenseChat, }), - [areThereDuplicates, transactionPreviewUtilsArguments, isReportAPolicyExpenseChat], + [areThereDuplicates, transactionPreviewCommonArguments, isReportAPolicyExpenseChat], ); - const { - shouldShowRBR, - shouldShowMerchant, - shouldShowSplitShare, - shouldShowTag, - shouldShowCategory, - shouldShowSkeleton, - shouldShowDescription, - shouldShowKeepButton, - shouldDisableOnPress, - } = conditionals; + const {shouldShowRBR, shouldShowMerchant, shouldShowSplitShare, shouldShowTag, shouldShowCategory, shouldShowSkeleton, shouldShowDescription} = conditionals; const firstViolation = violations.at(0); const isIOUActionType = isMoneyRequestAction(action); @@ -115,11 +91,11 @@ function TransactionPreviewContent({ const previewText = useMemo( () => getTransactionPreviewTextAndTranslationPaths({ - ...transactionPreviewUtilsArguments, + ...transactionPreviewCommonArguments, shouldShowRBR, violationMessage, }), - [transactionPreviewUtilsArguments, shouldShowRBR, violationMessage], + [transactionPreviewCommonArguments, shouldShowRBR, violationMessage], ); const getTranslatedText = (item: TranslationPathOrText) => (item.translationPath ? translate(item.translationPath) : item.text ?? ''); @@ -129,7 +105,6 @@ function TransactionPreviewContent({ const RBRMessage = getTranslatedText(previewText.RBRMessage); const displayAmountText = getTranslatedText(previewText.displayAmountText); - const showCashOrCard = getTranslatedText(previewText.showCashOrCard); const displayDeleteAmountText = getTranslatedText(previewText.displayDeleteAmountText); const iouData = getIOUData(managerID, ownerAccountID, personalDetails); @@ -148,7 +123,6 @@ function TransactionPreviewContent({ const displayAmount = isDeleted ? displayDeleteAmountText : displayAmountText; const receiptImages = [{...getThumbnailAndImageURIs(transaction), transaction}]; const merchantOrDescription = shouldShowMerchant ? requestMerchant : description || ''; - const isReviewDuplicateTransactionPage = routeName === SCREENS.TRANSACTION_DUPLICATE.REVIEW; const participantAccountIDs = isMoneyRequestAction(action) && isBillSplit ? getOriginalMessage(action)?.participantAccountIDs ?? [] : [managerID, ownerAccountID]; const participantAvatars = getAvatarsForAccountIDs(participantAccountIDs, personalDetails ?? {}); const sortedParticipantAvatars = lodashSortBy(participantAvatars, (avatar) => avatar.id); @@ -179,8 +153,10 @@ function TransactionPreviewContent({ const previewTextViewGap = (shouldShowCategoryOrTag || !shouldWrapDisplayAmount) && styles.gap2; const previewTextMargin = shouldShowIOUHeader && shouldShowMerchantOrDescription && !isBillSplit && !shouldShowCategoryOrTag && styles.mbn1; - const transactionContent = ( - + const transactionWrapperStyles = [styles.border, styles.moneyRequestPreviewBox, (isIOUSettled || isApproved) && isSettlementOrApprovalPartial && styles.offlineFeedback.pending]; + + return ( + offlineWithFeedbackOnClose} @@ -191,12 +167,7 @@ function TransactionPreviewContent({ shouldDisableOpacity={isDeleted} shouldHideOnDelete={shouldHideOnDelete} > - + {!isDeleted && ( )} {shouldShowSkeleton ? ( - + ) : ( @@ -356,39 +327,6 @@ function TransactionPreviewContent({ ); - - if (!onPreviewPressed) { - return transactionContent; - } - - return ( - canUseTouchScreen() && ControlSelection.block()} - onPressOut={() => ControlSelection.unblock()} - onLongPress={showContextMenu} - shouldUseHapticsOnLongPress - accessibilityLabel={isBillSplit ? translate('iou.split') : showCashOrCard} - accessibilityHint={convertToDisplayString(requestAmount, requestCurrency)} - style={[ - styles.moneyRequestPreviewBox, - wrapperStyle, - themeStyles, - shouldDisableOnPress && styles.cursorDefault, - (isIOUSettled || isApproved) && isSettlementOrApprovalPartial && styles.offlineFeedback.pending, - ]} - > - {transactionContent} - {isReviewDuplicateTransactionPage && !isIOUSettled && !isApproved && shouldShowKeepButton && ( -