From fb70f342b5ca2dafbc65f9d78f8f35c88461ba8d Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 2 Apr 2024 15:28:49 -1000 Subject: [PATCH] Merge pull request #39466 from Expensify/aldo_support-markdown-for-description-preview Support markdown for description preview (cherry picked from commit bce472451a3bfcd3158aaf5a8694e16382ea5dfb) --- src/components/MenuItem.tsx | 25 ++++++------- .../ReportActionItem/ReportPreview.tsx | 37 +++++++++++++------ 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 110256ba166b..46a0766c1057 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -1,7 +1,7 @@ import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import type {ImageContentFit} from 'expo-image'; import type {ForwardedRef, ReactNode} from 'react'; -import React, {forwardRef, useContext, useEffect, useMemo, useRef, useState} from 'react'; +import React, {forwardRef, useContext, useMemo} from 'react'; import type {GestureResponderEvent, StyleProp, TextStyle, ViewStyle} from 'react-native'; import {View} from 'react-native'; import type {AnimatedStyle} from 'react-native-reanimated'; @@ -325,8 +325,6 @@ function MenuItem( const StyleUtils = useStyleUtils(); const combinedStyle = [style, styles.popoverMenuItem]; const {isSmallScreenWidth} = useWindowDimensions(); - const [html, setHtml] = useState(''); - const titleRef = useRef(''); const {isExecuting, singleExecution, waitForNavigate} = useContext(MenuItemGroupContext) ?? {}; const isDeleted = style && Array.isArray(style) ? style.includes(styles.offlineFeedback.deleted) : false; @@ -354,26 +352,25 @@ function MenuItem( isDeleted ? styles.offlineFeedback.deleted : {}, ]); - useEffect(() => { - if (!title || (titleRef.current.length && titleRef.current === title) || !shouldParseTitle) { - return; + const html = useMemo(() => { + if (!title || !shouldParseTitle) { + return ''; } const parser = new ExpensiMark(); - setHtml(parser.replace(title)); - titleRef.current = title; + return parser.replace(title); }, [title, shouldParseTitle]); - const getProcessedTitle = useMemo(() => { - let processedTitle = ''; + const processedTitle = useMemo(() => { + let titleToWrap = ''; if (shouldRenderAsHTML) { - processedTitle = title ? convertToLTR(title) : ''; + titleToWrap = title ? convertToLTR(title) : ''; } if (shouldParseTitle) { - processedTitle = html; + titleToWrap = html; } - return processedTitle ? `${processedTitle}` : ''; + return titleToWrap ? `${titleToWrap}` : ''; }, [title, shouldRenderAsHTML, shouldParseTitle, html]); const hasPressableRightComponent = iconRight || (shouldShowRightComponent && rightComponent); @@ -536,7 +533,7 @@ function MenuItem( {!!title && (shouldRenderAsHTML || (shouldParseTitle && !!html.length)) && ( - + )} {!shouldRenderAsHTML && !shouldParseTitle && !!title && ( diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 2581359b4543..efe0b71b1012 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -1,3 +1,4 @@ +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import React, {useMemo} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; @@ -8,6 +9,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; +import RenderHTML from '@components/RenderHTML'; import SettlementButton from '@components/SettlementButton'; import {showContextMenuForReport} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; @@ -145,15 +147,6 @@ function ReportPreview({ if (TransactionUtils.isPartialMerchant(formattedMerchant ?? '')) { formattedMerchant = null; } - const previewSubtitle = - // Formatted merchant can be an empty string - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - (formattedMerchant ?? formattedDescription) || - translate('iou.requestCount', { - count: numberOfRequests - numberOfScanningReceipts - numberOfPendingRequests, - scanningReceipts: numberOfScanningReceipts, - pendingReceipts: numberOfPendingRequests, - }); const shouldShowSubmitButton = isOpenExpenseReport && reimbursableSpend !== 0; const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(iouReport); @@ -234,6 +227,24 @@ function ReportPreview({ numberOfRequests === 1 && (!!formattedMerchant || !!formattedDescription) && !(hasOnlyTransactionsWithPendingRoutes && !totalDisplaySpend); const shouldShowSubtitle = !isScanning && (shouldShowSingleRequestMerchantOrDescription || numberOfRequests > 1); + const {isSupportTextHtml, supportText} = useMemo(() => { + if (formattedMerchant) { + return {isSupportTextHtml: false, supportText: formattedMerchant}; + } + if (formattedDescription ?? moneyRequestComment) { + const parsedSubtitle = new ExpensiMark().replace(formattedDescription ?? moneyRequestComment); + return {isSupportTextHtml: !!parsedSubtitle, supportText: parsedSubtitle ? `${parsedSubtitle}` : ''}; + } + return { + isSupportTextHtml: false, + supportText: translate('iou.requestCount', { + count: numberOfRequests - numberOfScanningReceipts - numberOfPendingRequests, + scanningReceipts: numberOfScanningReceipts, + pendingReceipts: numberOfPendingRequests, + }), + }; + }, [formattedMerchant, formattedDescription, moneyRequestComment, translate, numberOfRequests, numberOfScanningReceipts, numberOfPendingRequests]); + return ( - {shouldShowSubtitle && ( + {shouldShowSubtitle && supportText && ( - {previewSubtitle || moneyRequestComment} + {isSupportTextHtml ? ( + + ) : ( + {supportText} + )} )}