diff --git a/.storybook/webpack.config.ts b/.storybook/webpack.config.ts index 1c22608160cf..fc94d7e07bea 100644 --- a/.storybook/webpack.config.ts +++ b/.storybook/webpack.config.ts @@ -58,6 +58,7 @@ const webpackConfig = ({config}: {config: Configuration}) => { 'react-native$': 'react-native-web', '@react-native-community/netinfo': path.resolve(__dirname, '../__mocks__/@react-native-community/netinfo.ts'), '@react-navigation/native': path.resolve(__dirname, '../__mocks__/@react-navigation/native'), + '@libs/TransactionPreviewUtils': path.resolve(__dirname, '../src/libs/__mocks__/TransactionPreviewUtils.ts'), ...custom.resolve.alias, }; diff --git a/src/components/ReportActionItem/MoneyRequestAction.tsx b/src/components/ReportActionItem/MoneyRequestAction.tsx index b41b90354ef9..02b29b4fe9ae 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.tsx +++ b/src/components/ReportActionItem/MoneyRequestAction.tsx @@ -116,6 +116,10 @@ function MoneyRequestAction({ } return ${translate(message)}`} />; } + + // NOTE: this part of code is needed here if we want to replace MoneyRequestPreview with TransactionPreview + // const renderCondition = lodashIsEmpty(iouReport) && !(isSplitBillAction || isTrackExpenseAction); + // return renderCondition ? null : ( return ( >(() => getTransactionDetails(transaction) ?? {}, [transaction]); + const managerID = iouReport?.managerID ?? CONST.DEFAULT_NUMBER_ID; + const ownerAccountID = iouReport?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID; + const isReportAPolicyExpenseChat = isPolicyExpenseChat(chatReport); + const {amount: requestAmount, comment: requestComment, merchant, tag, category, currency: requestCurrency} = transactionDetails; + + const transactionPreviewUtilsArguments = useMemo( + () => ({ + iouReport, + transaction, + translate, + action, + isBillSplit, + violations, + transactionDetails, + }), + [action, iouReport, isBillSplit, transaction, transactionDetails, translate, violations], + ); + + const conditionals = useMemo( + () => + createTransactionPreviewConditionals({ + ...transactionPreviewUtilsArguments, + areThereDuplicates, + isReportAPolicyExpenseChat, + }), + [areThereDuplicates, transactionPreviewUtilsArguments, isReportAPolicyExpenseChat], + ); + + const { + shouldShowRBR, + shouldShowMerchant, + shouldShowSplitShare, + shouldShowTag, + shouldShowCategory, + shouldShowSkeleton, + shouldShowDescription, + shouldShowKeepButton, + shouldDisableOnPress, + } = conditionals; + + const firstViolation = violations.at(0); + const isIOUActionType = isMoneyRequestAction(action); + const canEdit = isIOUActionType && canEditMoneyRequest(action, transaction); + const violationMessage = firstViolation ? ViolationsUtils.getViolationTranslation(firstViolation, translate, canEdit) : undefined; + + const previewText = useMemo( + () => + getTransactionPreviewTextAndTranslationPaths({ + ...transactionPreviewUtilsArguments, + shouldShowRBR, + violationMessage, + }), + [transactionPreviewUtilsArguments, shouldShowRBR, violationMessage], + ); + const getTranslatedText = (item: TranslationPathOrText) => (item.translationPath ? translate(item.translationPath) : item.text ?? ''); + + const previewHeaderText = previewText.previewHeaderText.reduce((text, currentKey) => { + return `${text}${getTranslatedText(currentKey)}`; + }, ''); + + 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, iouReport, personalDetails, (transaction && transaction.amount) ?? 0); + const {from, to} = iouData ?? {from: null, to: null}; + const isDeleted = action?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + const shouldShowCategoryOrTag = shouldShowCategory || shouldShowTag; + const shouldShowMerchantOrDescription = shouldShowDescription || shouldShowMerchant; + const shouldShowIOUHeader = !!from && !!to; + const description = truncate(StringUtils.lineBreaksToSpaces(requestComment), {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); + const requestMerchant = truncate(merchant, {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); + const hasReceipt = hasReceiptTransactionUtils(transaction); + const isApproved = isReportApproved({report: iouReport}); + const isIOUSettled = isSettled(iouReport?.reportID); + const isSettlementOrApprovalPartial = !!iouReport?.pendingFields?.partial; + const isScanning = hasReceipt && isReceiptBeingScanned(transaction); + 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); + if (isReportAPolicyExpenseChat && isBillSplit) { + sortedParticipantAvatars.push(getWorkspaceIcon(chatReport)); + } + + // If available, retrieve the split share from the splits object of the transaction, if not, display an even share. + const splitShare = useMemo( + () => + shouldShowSplitShare + ? transaction?.comment?.splits?.find((split) => split.accountID === sessionAccountID)?.amount ?? + calculateAmount(isReportAPolicyExpenseChat ? 1 : participantAccountIDs.length - 1, requestAmount ?? 0, requestCurrency ?? '', action.actorAccountID === sessionAccountID) + : 0, + [ + shouldShowSplitShare, + isReportAPolicyExpenseChat, + action.actorAccountID, + participantAccountIDs.length, + transaction?.comment?.splits, + requestAmount, + requestCurrency, + sessionAccountID, + ], + ); + + const transactionContent = ( + + offlineWithFeedbackOnClose} + errorRowStyles={[styles.mbn1]} + needsOffscreenAlphaCompositing + pendingAction={action?.pendingAction} + shouldDisableStrikeThrough={isDeleted} + shouldDisableOpacity={isDeleted} + shouldHideOnDelete={shouldHideOnDelete} + > + + {!isDeleted && ( + + )} + {shouldShowSkeleton ? ( + + ) : ( + + + + {shouldShowIOUHeader && ( + + + + )} + + {previewHeaderText} + {isBillSplit && ( + + + + )} + {!isBillSplit && !shouldShowMerchantOrDescription && ( + + {displayAmount} + + )} + + + + + {shouldShowMerchantOrDescription && ( + + {merchantOrDescription} + + )} + {(shouldShowMerchant || shouldShowDescription || isBillSplit) && ( + + {displayAmount} + + )} + + + + {!!splitShare && ( + + {translate('iou.yourSplit', {amount: convertToDisplayString(splitShare, requestCurrency)})} + + )} + + + {shouldShowCategoryOrTag && ( + + {shouldShowCategory && ( + + + + {category} + + + )} + {shouldShowTag && !!tag && ( + + + + {getCleanedTagName(tag)} + + + )} + + )} + + {!isIOUSettled && shouldShowRBR && ( + + + + {RBRMessage} + + + )} + + + )} + + + + ); + + 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, + containerStyles, + themeStyles, + shouldDisableOnPress && styles.cursorDefault, + (isIOUSettled || isApproved) && isSettlementOrApprovalPartial && styles.offlineFeedback.pending, + ]} + > + {transactionContent} + {isReviewDuplicateTransactionPage && !isIOUSettled && !isApproved && shouldShowKeepButton && ( +