diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/MentionReportContext.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/MentionReportContext.tsx new file mode 100644 index 000000000000..9fe1088c9809 --- /dev/null +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/MentionReportContext.tsx @@ -0,0 +1,11 @@ +import {createContext} from 'react'; + +type MentionReportContextProps = { + currentReportID: string; +}; + +const MentionReportContext = createContext({ + currentReportID: '', +}); + +export default MentionReportContext; diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx similarity index 93% rename from src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx rename to src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx index 66e297e50734..e4351ebe961a 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx @@ -1,5 +1,5 @@ import isEmpty from 'lodash/isEmpty'; -import React, {useMemo} from 'react'; +import React, {useContext, useMemo} from 'react'; import type {TextStyle} from 'react-native'; import {StyleSheet} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; @@ -16,6 +16,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Report} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import MentionReportContext from './MentionReportContext'; type MentionReportOnyxProps = { /** All reports shared with the user */ @@ -56,10 +57,12 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, reports, ...defa const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const htmlAttributeReportID = tnode.attributes.reportid; + const {currentReportID: currentReportIDContext} = useContext(MentionReportContext); const currentReportID = useCurrentReportID(); + const currentReportIDValue = currentReportIDContext ?? currentReportID?.currentReportID; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const [currentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${currentReportID?.currentReportID || -1}`); + const [currentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${currentReportIDValue || -1}`); // When we invite someone to a room they don't have the policy object, but we still want them to be able to see and click on report mentions, so we only check if the policyID in the report is from a workspace const isGroupPolicyReport = useMemo(() => currentReport && !isEmptyObject(currentReport) && !!currentReport.policyID && currentReport.policyID !== CONST.POLICY.ID_FAKE, [currentReport]); @@ -71,7 +74,7 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, reports, ...defa const {reportID, mentionDisplayText} = mentionDetails; const navigationRoute = reportID ? ROUTES.REPORT_WITH_ID.getRoute(reportID) : undefined; - const isCurrentRoomMention = reportID === currentReportID?.currentReportID; + const isCurrentRoomMention = reportID === currentReportIDValue; const flattenStyle = StyleSheet.flatten(style as TextStyle); const {color, ...styleWithoutColor} = flattenStyle; diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 3993633da58d..55bb451b0763 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -273,6 +273,9 @@ type MenuItemBaseProps = { /** Whether should render error text as HTML or as Text */ shouldRenderErrorAsHTML?: boolean; + /** List of markdown rules that will be ignored */ + excludedMarkdownRules?: string[]; + /** Should check anonymous user in onPress function */ shouldCheckActionAllowedOnPress?: boolean; @@ -409,6 +412,7 @@ function MenuItem( shouldParseHelperText = false, shouldRenderHintAsHTML = false, shouldRenderErrorAsHTML = false, + excludedMarkdownRules = [], shouldCheckActionAllowedOnPress = true, onSecondaryInteraction, titleWithTooltips, @@ -464,8 +468,8 @@ function MenuItem( if (!title || !shouldParseTitle) { return ''; } - return Parser.replace(title, {shouldEscapeText}); - }, [title, shouldParseTitle, shouldEscapeText]); + return Parser.replace(title, {shouldEscapeText, disabledRules: excludedMarkdownRules}); + }, [title, shouldParseTitle, shouldEscapeText, excludedMarkdownRules]); const helperHtml = useMemo(() => { if (!helperText || !shouldParseHelperText) { diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 623348a4c7a4..ff18b48d5901 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -28,6 +28,7 @@ import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; import type {Unit} from '@src/types/onyx/Policy'; import ConfirmedRoute from './ConfirmedRoute'; +import MentionReportContext from './HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/MentionReportContext'; import MenuItem from './MenuItem'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; import PDFThumbnail from './PDFThumbnail'; @@ -266,6 +267,8 @@ function MoneyRequestConfirmationListFooter({ const resolvedThumbnail = isLocalFile ? receiptThumbnail : tryResolveUrlFromApiRoot(receiptThumbnail ?? ''); const resolvedReceiptImage = isLocalFile ? receiptImage : tryResolveUrlFromApiRoot(receiptImage ?? ''); + const mentionReportContextValue = useMemo(() => ({currentReportID: reportID}), [reportID]); + // An intermediate structure that helps us classify the fields as "primary" and "supplementary". // The primary fields are always shown to the user, while an extra action is needed to reveal the supplementary ones. const classifiedFields = [ @@ -296,23 +299,26 @@ function MoneyRequestConfirmationListFooter({ }, { item: ( - { - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams(), reportActionID), - ); - }} - style={[styles.moneyRequestMenuItem]} - titleStyle={styles.flex1} - disabled={didConfirm} - interactive={!isReadOnly} - numberOfLinesTitle={2} - /> + + { + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams(), reportActionID), + ); + }} + style={[styles.moneyRequestMenuItem]} + titleStyle={styles.flex1} + disabled={didConfirm} + interactive={!isReadOnly} + numberOfLinesTitle={2} + /> + ), shouldShow: true, isSupplementary: false, diff --git a/src/components/TextInput/BaseTextInput/index.native.tsx b/src/components/TextInput/BaseTextInput/index.native.tsx index 59f205da023f..a03e9dbb9aa2 100644 --- a/src/components/TextInput/BaseTextInput/index.native.tsx +++ b/src/components/TextInput/BaseTextInput/index.native.tsx @@ -61,6 +61,7 @@ function BaseTextInput( prefixCharacter = '', inputID, isMarkdownEnabled = false, + excludedMarkdownStyles = [], shouldShowClearButton = false, prefixContainerStyle = [], prefixStyle = [], @@ -74,7 +75,7 @@ function BaseTextInput( const inputProps = {shouldSaveDraft: false, shouldUseDefaultValue: false, ...props}; const theme = useTheme(); const styles = useThemeStyles(); - const markdownStyle = useMarkdownStyle(); + const markdownStyle = useMarkdownStyle(undefined, excludedMarkdownStyles); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); diff --git a/src/components/TextInput/BaseTextInput/index.tsx b/src/components/TextInput/BaseTextInput/index.tsx index 685d54d86765..ff1a186d86b8 100644 --- a/src/components/TextInput/BaseTextInput/index.tsx +++ b/src/components/TextInput/BaseTextInput/index.tsx @@ -63,6 +63,7 @@ function BaseTextInput( prefixCharacter = '', inputID, isMarkdownEnabled = false, + excludedMarkdownStyles = [], shouldShowClearButton = false, prefixContainerStyle = [], prefixStyle = [], @@ -75,7 +76,7 @@ function BaseTextInput( const theme = useTheme(); const styles = useThemeStyles(); - const markdownStyle = useMarkdownStyle(); + const markdownStyle = useMarkdownStyle(undefined, excludedMarkdownStyles); const {hasError = false} = inputProps; const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); diff --git a/src/components/TextInput/BaseTextInput/types.ts b/src/components/TextInput/BaseTextInput/types.ts index 80325e0a21f3..cb66876d56d0 100644 --- a/src/components/TextInput/BaseTextInput/types.ts +++ b/src/components/TextInput/BaseTextInput/types.ts @@ -1,3 +1,4 @@ +import type {MarkdownStyle} from '@expensify/react-native-live-markdown'; import type {GestureResponderEvent, StyleProp, TextInputProps, TextStyle, ViewStyle} from 'react-native'; import type {AnimatedTextInputRef} from '@components/RNTextInput'; import type IconAsset from '@src/types/utils/IconAsset'; @@ -112,6 +113,9 @@ type CustomBaseTextInputProps = { /** Should live markdown be enabled. Changes RNTextInput component to RNMarkdownTextInput */ isMarkdownEnabled?: boolean; + /** List of markdowns that won't be styled as a markdown */ + excludedMarkdownStyles?: Array; + /** Whether the clear button should be displayed */ shouldShowClearButton?: boolean; diff --git a/src/pages/iou/request/step/IOURequestStepDescription.tsx b/src/pages/iou/request/step/IOURequestStepDescription.tsx index 993bf580f038..a4c6ce577798 100644 --- a/src/pages/iou/request/step/IOURequestStepDescription.tsx +++ b/src/pages/iou/request/step/IOURequestStepDescription.tsx @@ -149,6 +149,8 @@ function IOURequestStepDescription({ const canEditSplitBill = isSplitBill && reportAction && session?.accountID === reportAction.actorAccountID && TransactionUtils.areRequiredFieldsEmpty(transaction); // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = isEditing && (isSplitBill ? !canEditSplitBill : !ReportActionsUtils.isMoneyRequestAction(reportAction) || !ReportUtils.canEditMoneyRequest(reportAction)); + const isReportInGroupPolicy = !!report?.policyID && report.policyID !== CONST.POLICY.ID_FAKE; + return (