From 230cbc9878a086c78ba3664d47ef0da489b1aefc Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Thu, 16 Apr 2026 14:32:36 +0200 Subject: [PATCH 1/7] refactor: add ChatMessageContent --- .../inbox/report/PureReportActionItem.tsx | 338 ++------------- src/pages/inbox/report/ReportActionItem.tsx | 33 +- .../actionContents/ChatMessageContent.tsx | 409 ++++++++++++++++++ tests/ui/ClearReportActionErrorsUITest.tsx | 3 - tests/ui/PureReportActionItemTest.tsx | 66 --- 5 files changed, 438 insertions(+), 411 deletions(-) create mode 100644 src/pages/inbox/report/actionContents/ChatMessageContent.tsx diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index 5cd0f49fe7df..1516d24b03ad 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -5,15 +5,13 @@ import mapValues from 'lodash/mapValues'; import React, {memo, use, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; import type {GestureResponderEvent, TextInput} from 'react-native'; import {InteractionManager, Keyboard, View} from 'react-native'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type {Emoji} from '@assets/emojis/types'; import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; -import {AttachmentContext} from '@components/AttachmentContext'; import Button from '@components/Button'; import DisplayNames from '@components/DisplayNames'; import Hoverable from '@components/Hoverable'; -import MentionReportContext from '@components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/MentionReportContext'; import Icon from '@components/Icon'; import InlineSystemMessage from '@components/InlineSystemMessage'; import KYCWall from '@components/KYCWall'; @@ -23,8 +21,6 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import PressableWithSecondaryInteraction from '@components/PressableWithSecondaryInteraction'; import ReportActionItemEmojiReactions from '@components/Reactions/ReportActionItemEmojiReactions'; import RenderHTML from '@components/RenderHTML'; -import type {ActionableItem} from '@components/ReportActionItem/ActionableItemButtons'; -import ActionableItemButtons from '@components/ReportActionItem/ActionableItemButtons'; import ChronosOOOListActions from '@components/ReportActionItem/ChronosOOOListActions'; import CreatedReportForUnapprovedTransactionsAction from '@components/ReportActionItem/CreatedReportForUnapprovedTransactionsAction'; import CreateHarvestReportAction from '@components/ReportActionItem/CreateHarvestReportAction'; @@ -44,14 +40,12 @@ import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from '@compo import Text from '@components/Text'; import TextLink from '@components/TextLink'; import UnreadActionIndicator from '@components/UnreadActionIndicator'; -import useActivePolicy from '@hooks/useActivePolicy'; import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useEnvironment from '@hooks/useEnvironment'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import usePreferredPolicy from '@hooks/usePreferredPolicy'; import usePrevious from '@hooks/usePrevious'; import useReportIsArchived from '@hooks/useReportIsArchived'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -59,7 +53,6 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {cleanUpMoneyRequest} from '@libs/actions/IOU/DeleteMoneyRequest'; -import {resolveSuggestedFollowup} from '@libs/actions/Report/SuggestedFollowup'; import {isPersonalCardBrokenConnection} from '@libs/CardUtils'; import {isChronosOOOListAction} from '@libs/ChronosUtils'; import ControlSelection from '@libs/ControlSelection'; @@ -75,7 +68,6 @@ import type {ReportsSplitNavigatorParamList} from '@libs/Navigation/types'; import Permissions from '@libs/Permissions'; import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; import {isPolicyAdmin} from '@libs/PolicyUtils'; -import {containsActionableFollowUps, parseFollowupsFromHtml} from '@libs/ReportActionFollowupUtils'; import { extractLinksFromMessageHtml, getCardConnectionBrokenMessage, @@ -93,18 +85,14 @@ import { getSettlementAccountLockedMessage, getTravelUpdateMessage, getWhisperedTo, - isActionableAddPaymentCard, isActionableCardFraudAlert, isActionableJoinRequest, isActionableMentionInviteToSubmitExpenseConfirmWhisper, isActionableMentionWhisper, isActionableReportMentionWhisper, - isActionableTrackExpense, isActionOfType, isCardBrokenConnectionAction, isCardIssuedAction, - isConciergeCategoryOptions, - isConciergeDescriptionOptions, isCreatedTaskReportAction, isDeletedAction, isDeletedParentAction as isDeletedParentActionUtils, @@ -115,8 +103,6 @@ import { isReimbursementDeQueuedOrCanceledAction, isReimbursementQueuedAction, isRenamedAction, - isResolvedConciergeCategoryOptions, - isResolvedConciergeDescriptionOptions, isSplitBillAction as isSplitBillActionReportActionsUtils, isTaskAction, isTrackExpenseAction as isTrackExpenseActionReportActionsUtils, @@ -124,15 +110,13 @@ import { isWhisperActionTargetedToOthers, useTableReportViewActionRenderConditionals, } from '@libs/ReportActionsUtils'; -import type {CreateDraftTransactionParams, MissingPaymentMethod} from '@libs/ReportUtils'; +import type {MissingPaymentMethod} from '@libs/ReportUtils'; import { canWriteInReport, - chatIncludesConcierge, getChatListItemReportName, getDisplayNamesWithTooltips, getMovedActionMessage, getWhisperDisplayNames, - isArchivedNonExpenseReport, isChatThread, isCompletedTaskReport, isExpenseReport, @@ -141,16 +125,14 @@ import { shouldDisplayThreadReplies as shouldDisplayThreadRepliesUtils, } from '@libs/ReportUtils'; import SelectionScraper from '@libs/SelectionScraper'; -import shouldRenderAddPaymentCard from '@libs/shouldRenderAppPaymentCard'; import {ReactionListContext} from '@pages/inbox/ReportScreenContext'; import AttachmentModalContext from '@pages/media/AttachmentModalScreen/AttachmentModalContext'; import variables from '@styles/variables'; import {openPersonalBankAccountSetupView} from '@userActions/BankAccounts'; import type {IgnoreDirection} from '@userActions/ClearReportActionErrors'; import {hideEmojiPicker, isActive} from '@userActions/EmojiPickerAction'; -import {createTransactionThreadReport, expandURLPreview, resolveConciergeCategoryOptions, resolveConciergeDescriptionOptions} from '@userActions/Report'; +import {createTransactionThreadReport, expandURLPreview} from '@userActions/Report'; import {isAnonymousUser, signOutAndRedirectToSignIn} from '@userActions/Session'; -import {isBlockedFromConcierge} from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -159,6 +141,7 @@ import type * as OnyxTypes from '@src/types/onyx'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import {isEmptyObject, isEmptyValueObject} from '@src/types/utils/EmptyObject'; import ApprovalFlowContent, {isApprovalFlowAction} from './actionContents/ApprovalFlowContent'; +import ChatMessageContent from './actionContents/ChatMessageContent'; import ConfirmWhisperContent from './actionContents/ConfirmWhisperContent'; import FraudAlertContent from './actionContents/FraudAlertContent'; import JoinRequestContent from './actionContents/JoinRequestContent'; @@ -176,8 +159,6 @@ import ReportActionItemBasicMessage from './ReportActionItemBasicMessage'; import ReportActionItemContentCreated from './ReportActionItemContentCreated'; import ReportActionItemDraft from './ReportActionItemDraft'; import ReportActionItemGrouped from './ReportActionItemGrouped'; -import ReportActionItemMessage from './ReportActionItemMessage'; -import ReportActionItemMessageEdit from './ReportActionItemMessageEdit'; import ReportActionItemMessageWithExplain from './ReportActionItemMessageWithExplain'; import ReportActionItemSingle from './ReportActionItemSingle'; import ReportActionItemThread from './ReportActionItemThread'; @@ -193,9 +174,6 @@ type PureReportActionItemProps = { /** Beta features list */ betas: OnyxEntry; - /** All transaction draft IDs */ - draftTransactionIDs: string[] | undefined; - /** Report for this action */ report: OnyxEntry; @@ -277,9 +255,6 @@ type PureReportActionItemProps = { /** Personal details list */ personalDetails?: OnyxTypes.PersonalDetailsList; - /** Whether or not the user is blocked from concierge */ - blockedFromConcierge?: OnyxTypes.BlockedFromConcierge; - /** ID of the original report from which the given reportAction is first created */ originalReportID?: string; @@ -309,9 +284,6 @@ type PureReportActionItemProps = { ignoreSkinToneOnCompare: boolean | undefined, ) => void; - /** Function to create a draft transaction and navigate to participant selector */ - createDraftTransactionAndNavigateToParticipantSelector?: (params: CreateDraftTransactionParams) => void; - /** Function to resolve actionable report mention whisper */ resolveActionableReportMentionWhisper?: ( report: OnyxEntry, @@ -358,9 +330,6 @@ type PureReportActionItemProps = { keys?: string[], ) => void; - /** Function to dismiss the actionable whisper for tracking expenses */ - dismissTrackExpenseActionableWhisper?: (reportID: string | undefined, reportAction: OnyxEntry) => void; - /** User payment card ID */ userBillingFundID?: number; @@ -376,9 +345,6 @@ type PureReportActionItemProps = { /** Current user's account id */ currentUserAccountID: number; - /** Current user's email */ - currentUserEmail: string | undefined; - /** The bank account list */ bankAccountList?: OnyxTypes.BankAccountList | undefined; @@ -390,9 +356,6 @@ type PureReportActionItemProps = { /** Report metadata for the report */ reportMetadata?: OnyxEntry; - - /** The billing grace end period's shared NVP collection */ - userBillingGracePeriodEnds: OnyxCollection; }; // This is equivalent to returning a negative boolean in normal functions, but we can keep the element return type @@ -405,7 +368,6 @@ function PureReportActionItem({ personalPolicyID, introSelected, betas, - draftTransactionIDs, action, report, policy, @@ -433,14 +395,12 @@ function PureReportActionItem({ isUserValidated, parentReport, personalDetails, - blockedFromConcierge, originalReportID = '-1', originalReport, deleteReportActionDraft = () => {}, isArchivedRoom, isChronosReport, toggleEmojiReaction = () => {}, - createDraftTransactionAndNavigateToParticipantSelector = () => {}, resolveActionableReportMentionWhisper = () => {}, resolveActionableMentionWhisper = () => {}, isClosedExpenseReportWithNoExpenses, @@ -451,24 +411,19 @@ function PureReportActionItem({ getTransactionsWithReceipts = () => [], clearError = () => {}, clearAllRelatedReportActionErrors = () => {}, - dismissTrackExpenseActionableWhisper = () => {}, userBillingFundID, shouldShowBorder, shouldHighlight = false, isTryNewDotNVPDismissed = false, currentUserAccountID, - currentUserEmail, bankAccountList, reportNameValuePairsOrigin, reportNameValuePairsOriginalID, reportMetadata, - userBillingGracePeriodEnds, }: PureReportActionItemProps) { const isConciergeGreeting = action.reportActionID === CONST.CONCIERGE_GREETING_ACTION_ID; const shouldDisplayContextMenuValue = shouldDisplayContextMenu && !isConciergeGreeting; - const [amountOwed] = useOnyx(ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED); - const [ownerBillingGracePeriodEnd] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const {transitionActionSheetState} = ActionSheetAwareScrollView.useActionSheetAwareScrollViewActions(); const {translate, formatPhoneNumber, localeCompare, formatTravelDate, datetimeToCalendarTime} = useLocalize(); @@ -482,8 +437,6 @@ function PureReportActionItem({ const [isContextMenuActive, setIsContextMenuActive] = useState(() => isActiveReportAction(action.reportActionID)); const [isEmojiPickerActive, setIsEmojiPickerActive] = useState(); const [isPaymentMethodPopoverActive, setIsPaymentMethodPopoverActive] = useState(); - const {isRestrictedToPreferredPolicy, preferredPolicyID} = usePreferredPolicy(); - const activePolicy = useActivePolicy(); const shouldRenderViewBasedOnAction = useTableReportViewActionRenderConditionals(action); const [isHidden, setIsHidden] = useState(false); const [moderationDecision, setModerationDecision] = useState(CONST.MODERATION.MODERATOR_DECISION_APPROVED); @@ -504,8 +457,6 @@ function PureReportActionItem({ const [childReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(action.childReportID)}`); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(report?.chatReportID)}`); - const trackExpenseTransactionID = isActionableTrackExpense(action) ? getOriginalMessage(action)?.transactionID : undefined; - const [trackExpenseTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(trackExpenseTransactionID)}`); const highlightedBackgroundColorIfNeeded = useMemo( // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing @@ -776,194 +727,6 @@ function PureReportActionItem({ [toggleContextMenuFromActiveReportAction, handleShowContextMenu], ); - const attachmentContextValue = useMemo(() => { - if (isOnSearch) { - return {type: CONST.ATTACHMENT_TYPE.SEARCH, currentSearchHash}; - } - return {reportID, type: CONST.ATTACHMENT_TYPE.REPORT}; - }, [reportID, isOnSearch, currentSearchHash]); - - const mentionReportContextValue = useMemo(() => ({currentReportID: report?.reportID, exactlyMatch: true}), [report?.reportID]); - const actionableItemButtons: ActionableItem[] = useMemo(() => { - if (isActionableAddPaymentCard(action) && userBillingFundID === undefined && shouldRenderAddPaymentCard()) { - return [ - { - text: 'subscription.cardSection.addCardButton', - key: `${action.reportActionID}-actionableAddPaymentCard-submit`, - onPress: () => { - Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD); - }, - isPrimary: true, - }, - ]; - } - - const reportActionReport = originalReport ?? report; - if (isConciergeCategoryOptions(action)) { - const options = getOriginalMessage(action)?.options; - if (!options) { - return []; - } - - if (isResolvedConciergeCategoryOptions(action)) { - return []; - } - - if (!reportActionReport) { - return []; - } - - return options.map((option, i) => ({ - text: `${i + 1} - ${option}`, - key: `${action.reportActionID}-conciergeCategoryOptions-${option}`, - onPress: () => { - resolveConciergeCategoryOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); - }, - })); - } - - if (isConciergeDescriptionOptions(action)) { - const options = getOriginalMessage(action)?.options; - if (!options) { - return []; - } - - if (isResolvedConciergeDescriptionOptions(action)) { - return []; - } - - if (!reportActionReport) { - return []; - } - - return options.map((option, i) => ({ - text: `${i + 1} - ${option}`, - key: `${action.reportActionID}-conciergeDescriptionOptions-${option}`, - onPress: () => { - resolveConciergeDescriptionOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); - }, - })); - } - const messageHtml = getReportActionMessage(action)?.html; - if (messageHtml && reportActionReport) { - const followups = parseFollowupsFromHtml(messageHtml); - if (followups && followups.length > 0) { - return followups.map((followup) => ({ - text: followup.text, - shouldUseLocalization: false, - key: `${action.reportActionID}-followup-${followup.text}`, - onPress: () => { - resolveSuggestedFollowup(reportActionReport, reportID, action, followup, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID, currentUserEmail); - }, - })); - } - } - - if (isActionableTrackExpense(action)) { - const reportActionReportID = originalReportID ?? reportID; - const options = [ - { - text: 'actionableMentionTrackExpense.submit', - key: `${action.reportActionID}-actionableMentionTrackExpense-submit`, - onPress: () => { - createDraftTransactionAndNavigateToParticipantSelector({ - reportID: reportActionReportID, - actionName: CONST.IOU.ACTION.SUBMIT, - reportActionID: action.reportActionID, - introSelected, - draftTransactionIDs, - activePolicy, - userBillingGracePeriodEnds, - amountOwed, - ownerBillingGracePeriodEnd, - isRestrictedToPreferredPolicy, - preferredPolicyID, - transaction: trackExpenseTransaction, - currentUserAccountID: personalDetail.accountID, - currentUserEmail: personalDetail.email ?? '', - }); - }, - }, - ]; - - if (Permissions.canUseTrackFlows()) { - options.push( - { - text: 'actionableMentionTrackExpense.categorize', - key: `${action.reportActionID}-actionableMentionTrackExpense-categorize`, - onPress: () => { - createDraftTransactionAndNavigateToParticipantSelector({ - reportID: reportActionReportID, - actionName: CONST.IOU.ACTION.CATEGORIZE, - reportActionID: action.reportActionID, - introSelected, - draftTransactionIDs, - activePolicy, - userBillingGracePeriodEnds, - amountOwed, - ownerBillingGracePeriodEnd, - transaction: trackExpenseTransaction, - currentUserAccountID: personalDetail.accountID, - currentUserEmail: personalDetail.email ?? '', - }); - }, - }, - { - text: 'actionableMentionTrackExpense.share', - key: `${action.reportActionID}-actionableMentionTrackExpense-share`, - onPress: () => { - createDraftTransactionAndNavigateToParticipantSelector({ - reportID: reportActionReportID, - actionName: CONST.IOU.ACTION.SHARE, - reportActionID: action.reportActionID, - introSelected, - draftTransactionIDs, - activePolicy, - userBillingGracePeriodEnds, - amountOwed, - ownerBillingGracePeriodEnd, - transaction: trackExpenseTransaction, - currentUserAccountID: personalDetail.accountID, - currentUserEmail: personalDetail.email ?? '', - }); - }, - }, - ); - } - options.push({ - text: 'actionableMentionTrackExpense.nothing', - key: `${action.reportActionID}-actionableMentionTrackExpense-nothing`, - onPress: () => { - dismissTrackExpenseActionableWhisper(reportActionReportID, action); - }, - }); - return options; - } - - return []; - }, [ - action, - userBillingFundID, - originalReportID, - reportID, - currentUserAccountID, - currentUserEmail, - personalDetail.timezone, - createDraftTransactionAndNavigateToParticipantSelector, - isRestrictedToPreferredPolicy, - preferredPolicyID, - dismissTrackExpenseActionableWhisper, - introSelected, - draftTransactionIDs, - activePolicy, - report, - originalReport, - userBillingGracePeriodEnds, - amountOwed, - ownerBillingGracePeriodEnd, - trackExpenseTransaction, - ]); - /** * Get the content of ReportActionItem * @param hovered whether the ReportActionItem is hovered @@ -1349,74 +1112,29 @@ function PureReportActionItem({ ); } else { - const hasBeenFlagged = - ![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === moderationDecision) && !isPendingRemove(action); - - const isConciergeOptions = isConciergeCategoryOptions(action) || isConciergeDescriptionOptions(action); - const actionContainsFollowUps = containsActionableFollowUps(action); - const isPhrasalConciergeOptions = isConciergeOptions || actionContainsFollowUps; - const actionableButtonsNoLines = isPhrasalConciergeOptions ? 3 : 1; - children = ( - - - - - {draftMessage === undefined ? ( - - - {hasBeenFlagged && ( - - )} - {actionableItemButtons.length > 0 && ( - - )} - - ) : ( - - )} - - - - + ); } const numberOfThreadReplies = action.childVisibleActionCount ?? 0; @@ -1778,7 +1496,6 @@ export default memo(PureReportActionItem, (prevProps, nextProps) => { deepEqual(prevProps.personalDetails, nextProps.personalDetails) && deepEqual(prevProps.introSelected, nextProps.introSelected) && deepEqual(prevProps.betas, nextProps.betas) && - deepEqual(prevProps.blockedFromConcierge, nextProps.blockedFromConcierge) && prevProps.originalReportID === nextProps.originalReportID && deepEqual(prevProps.originalReport?.participants, nextProps.originalReport?.participants) && prevProps.isArchivedRoom === nextProps.isArchivedRoom && @@ -1794,7 +1511,6 @@ export default memo(PureReportActionItem, (prevProps, nextProps) => { deepEqual(prevProps.cardList, nextProps.cardList) && prevProps.reportNameValuePairsOrigin === nextProps.reportNameValuePairsOrigin && prevProps.reportNameValuePairsOriginalID === nextProps.reportNameValuePairsOriginalID && - prevProps.reportMetadata?.pendingExpenseAction === nextProps.reportMetadata?.pendingExpenseAction && - deepEqual(prevProps.draftTransactionIDs, nextProps.draftTransactionIDs) + prevProps.reportMetadata?.pendingExpenseAction === nextProps.reportMetadata?.pendingExpenseAction ); }); diff --git a/src/pages/inbox/report/ReportActionItem.tsx b/src/pages/inbox/report/ReportActionItem.tsx index 114e4491d4e7..45c2c7148cd7 100644 --- a/src/pages/inbox/report/ReportActionItem.tsx +++ b/src/pages/inbox/report/ReportActionItem.tsx @@ -1,7 +1,5 @@ -import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; import React, {useCallback} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; -import {useBlockedFromConcierge} from '@components/OnyxListItemProvider'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -14,7 +12,6 @@ import {getForReportAction, getMovedReportID} from '@libs/ModifiedExpenseMessage import {getIOUReportIDFromReportActionPreview, getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import { chatIncludesChronosWithID, - createDraftTransactionAndNavigateToParticipantSelector, getIndicatedMissingPaymentMethod, getReimbursementDeQueuedOrCanceledActionMessage, getTransactionsWithReceipts, @@ -24,13 +21,7 @@ import { isCurrentUserTheOnlyParticipant, } from '@libs/ReportUtils'; import {clearAllRelatedReportActionErrors} from '@userActions/ClearReportActionErrors'; -import { - deleteReportActionDraft, - dismissTrackExpenseActionableWhisper, - resolveActionableMentionWhisper, - resolveActionableReportMentionWhisper, - toggleEmojiReaction, -} from '@userActions/Report'; +import {deleteReportActionDraft, resolveActionableMentionWhisper, resolveActionableReportMentionWhisper, toggleEmojiReaction} from '@userActions/Report'; import {clearError} from '@userActions/Transaction'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -38,18 +29,7 @@ import type {PersonalDetailsList, ReportAction, ReportActionReactions, Transacti import type {PureReportActionItemProps} from './PureReportActionItem'; import PureReportActionItem from './PureReportActionItem'; -type ReportActionItemProps = Omit< - PureReportActionItemProps, - | 'taskReport' - | 'linkedReport' - | 'iouReportOfLinkedReport' - | 'currentUserAccountID' - | 'currentUserEmail' - | 'personalPolicyID' - | 'draftTransactionIDs' - | 'userBillingGracePeriodEnds' - | 'betas' -> & { +type ReportActionItemProps = Omit & { /** Whether to show the draft message or not */ shouldShowDraftMessage?: boolean; @@ -103,7 +83,6 @@ function ReportActionItem({ const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyIDForTags}`); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [betas] = useOnyx(ONYXKEYS.BETAS); - const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); const [reportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`); const [originalReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${originalReportID}`); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getIOUReportIDFromReportActionPreview(action)}`); @@ -122,7 +101,6 @@ function ReportActionItem({ const [cardList] = useOnyx(ONYXKEYS.CARD_LIST); const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID); - const [userBillingGracePeriodEnds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END); const transactionsOnIOUReport = useReportTransactions(iouReport?.reportID); const transactionID = isMoneyRequestAction(action) && getOriginalMessage(action)?.IOUTransactionID; @@ -135,7 +113,6 @@ function ReportActionItem({ const [linkedTransactionRouteError] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {selector: getLinkedTransactionRouteError}); - const blockedFromConcierge = useBlockedFromConcierge(); const targetReport = isChatThread(report) ? parentReport : report; const missingPaymentMethod = getIndicatedMissingPaymentMethod(userWalletTierName, targetReport?.reportID, action, bankAccountList); @@ -145,13 +122,11 @@ function ReportActionItem({ {...props} introSelected={introSelected} betas={betas} - draftTransactionIDs={draftTransactionIDs} personalPolicyID={personalPolicyID} action={action} report={report} policy={policy} currentUserAccountID={currentUserAccountID} - currentUserEmail={currentUserEmail} draftMessage={draftMessage} iouReport={iouReport} taskReport={taskReport} @@ -163,14 +138,12 @@ function ReportActionItem({ isUserValidated={isUserValidated} parentReport={parentReport} personalDetails={personalDetails} - blockedFromConcierge={blockedFromConcierge} originalReportID={originalReportID} originalReport={originalReport} deleteReportActionDraft={deleteReportActionDraft} isArchivedRoom={isArchivedNonExpenseReport(originalReport, isOriginalReportArchived)} isChronosReport={chatIncludesChronosWithID(originalReportID)} toggleEmojiReaction={toggleEmojiReaction} - createDraftTransactionAndNavigateToParticipantSelector={createDraftTransactionAndNavigateToParticipantSelector} resolveActionableReportMentionWhisper={resolveActionableReportMentionWhisper} resolveActionableMentionWhisper={resolveActionableMentionWhisper} isClosedExpenseReportWithNoExpenses={isClosedExpenseReportWithNoExpenses(iouReport, transactionsOnIOUReport)} @@ -193,12 +166,10 @@ function ReportActionItem({ getTransactionsWithReceipts={getTransactionsWithReceipts} clearError={clearError} clearAllRelatedReportActionErrors={clearAllRelatedReportActionErrors} - dismissTrackExpenseActionableWhisper={dismissTrackExpenseActionableWhisper} userBillingFundID={userBillingFundID} isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} bankAccountList={bankAccountList} reportMetadata={reportMetadata} - userBillingGracePeriodEnds={userBillingGracePeriodEnds} /> ); } diff --git a/src/pages/inbox/report/actionContents/ChatMessageContent.tsx b/src/pages/inbox/report/actionContents/ChatMessageContent.tsx new file mode 100644 index 000000000000..6e1fd21b1306 --- /dev/null +++ b/src/pages/inbox/report/actionContents/ChatMessageContent.tsx @@ -0,0 +1,409 @@ +import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; +import React, {useMemo} from 'react'; +import type {TextInput} from 'react-native'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import {AttachmentContext} from '@components/AttachmentContext'; +import Button from '@components/Button'; +import MentionReportContext from '@components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/MentionReportContext'; +import {useBlockedFromConcierge} from '@components/OnyxListItemProvider'; +import type {ActionableItem} from '@components/ReportActionItem/ActionableItemButtons'; +import ActionableItemButtons from '@components/ReportActionItem/ActionableItemButtons'; +import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from '@components/ShowContextMenuContext'; +import Text from '@components/Text'; +import useActivePolicy from '@hooks/useActivePolicy'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useLocalize from '@hooks/useLocalize'; +import useOnyx from '@hooks/useOnyx'; +import usePreferredPolicy from '@hooks/usePreferredPolicy'; +import useReportIsArchived from '@hooks/useReportIsArchived'; +import useThemeStyles from '@hooks/useThemeStyles'; +import {resolveSuggestedFollowup} from '@libs/actions/Report/SuggestedFollowup'; +import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; +import Navigation from '@libs/Navigation/Navigation'; +import Permissions from '@libs/Permissions'; +import {containsActionableFollowUps, parseFollowupsFromHtml} from '@libs/ReportActionFollowupUtils'; +import { + getOriginalMessage, + getReportActionMessage, + isActionableAddPaymentCard, + isActionableMentionInviteToSubmitExpenseConfirmWhisper, + isActionableTrackExpense, + isConciergeCategoryOptions, + isConciergeDescriptionOptions, + isPendingRemove, + isResolvedConciergeCategoryOptions, + isResolvedConciergeDescriptionOptions, +} from '@libs/ReportActionsUtils'; +import {chatIncludesConcierge, createDraftTransactionAndNavigateToParticipantSelector, isArchivedNonExpenseReport} from '@libs/ReportUtils'; +import shouldRenderAddPaymentCard from '@libs/shouldRenderAppPaymentCard'; +import type {ContextMenuAnchor} from '@pages/inbox/report/ContextMenu/ReportActionContextMenu'; +import ReportActionItemMessage from '@pages/inbox/report/ReportActionItemMessage'; +import ReportActionItemMessageEdit from '@pages/inbox/report/ReportActionItemMessageEdit'; +import {dismissTrackExpenseActionableWhisper, resolveActionableMentionConfirmWhisper, resolveConciergeCategoryOptions, resolveConciergeDescriptionOptions} from '@userActions/Report'; +import {isBlockedFromConcierge} from '@userActions/User'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type * as OnyxTypes from '@src/types/onyx'; + +type ChatMessageContentProps = { + action: OnyxTypes.ReportAction; + report: OnyxEntry; + originalReport: OnyxEntry; + reportID: string | undefined; + originalReportID: string; + displayAsGroup: boolean; + draftMessage: string | undefined; + index: number; + isHidden: boolean; + moderationDecision: OnyxTypes.DecisionName; + updateHiddenState: (isHiddenValue: boolean) => void; + isArchivedRoom?: boolean; + composerTextInputRef: React.RefObject; + isOnSearch: boolean; + currentSearchHash: number | undefined; + contextMenuStateValue: { + anchor: ContextMenuAnchor | null; + report: OnyxEntry; + isReportArchived: boolean; + action: OnyxTypes.ReportAction; + transactionThreadReport?: OnyxEntry; + isDisabled: boolean; + shouldDisplayContextMenu: boolean; + originalReportID: string | undefined; + }; + contextMenuActionsValue: { + checkIfContextMenuActive: () => void; + onShowContextMenu: (callback: () => void) => void; + }; + userBillingFundID: number | undefined; + introSelected: OnyxEntry; + currentUserAccountID: number; +}; + +function ChatMessageContent({ + action, + report, + originalReport, + reportID, + originalReportID, + displayAsGroup, + draftMessage, + index, + isHidden, + moderationDecision, + updateHiddenState, + isArchivedRoom, + composerTextInputRef, + isOnSearch, + currentSearchHash, + contextMenuStateValue, + contextMenuActionsValue, + userBillingFundID, + introSelected, + currentUserAccountID, +}: ChatMessageContentProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + const personalDetail = useCurrentUserPersonalDetails(); + const {isRestrictedToPreferredPolicy, preferredPolicyID} = usePreferredPolicy(); + const activePolicy = useActivePolicy(); + const isOriginalReportArchived = useReportIsArchived(originalReportID); + + const blockedFromConcierge = useBlockedFromConcierge(); + const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); + const [userBillingGracePeriodEnds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END); + const [amountOwed] = useOnyx(ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED); + const [ownerBillingGracePeriodEnd] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END); + const trackExpenseTransactionID = isActionableTrackExpense(action) ? getOriginalMessage(action)?.transactionID : undefined; + const [trackExpenseTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(trackExpenseTransactionID)}`); + + const mentionReportContextValue = useMemo(() => ({currentReportID: report?.reportID, exactlyMatch: true}), [report?.reportID]); + + const attachmentContextValue = useMemo(() => { + if (isOnSearch) { + return {type: CONST.ATTACHMENT_TYPE.SEARCH, currentSearchHash}; + } + return {reportID, type: CONST.ATTACHMENT_TYPE.REPORT}; + }, [reportID, isOnSearch, currentSearchHash]); + + const actionableItemButtons: ActionableItem[] = useMemo(() => { + if (isActionableAddPaymentCard(action) && userBillingFundID === undefined && shouldRenderAddPaymentCard()) { + return [ + { + text: 'subscription.cardSection.addCardButton', + key: `${action.reportActionID}-actionableAddPaymentCard-submit`, + onPress: () => { + Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD); + }, + isPrimary: true, + }, + ]; + } + + const reportActionReport = originalReport ?? report; + if (isConciergeCategoryOptions(action)) { + const options = getOriginalMessage(action)?.options; + if (!options) { + return []; + } + + if (isResolvedConciergeCategoryOptions(action)) { + return []; + } + + if (!reportActionReport) { + return []; + } + + return options.map((option, i) => ({ + text: `${i + 1} - ${option}`, + key: `${action.reportActionID}-conciergeCategoryOptions-${option}`, + onPress: () => { + resolveConciergeCategoryOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); + }, + })); + } + + if (isConciergeDescriptionOptions(action)) { + const options = getOriginalMessage(action)?.options; + if (!options) { + return []; + } + + if (isResolvedConciergeDescriptionOptions(action)) { + return []; + } + + if (!reportActionReport) { + return []; + } + + return options.map((option, i) => ({ + text: `${i + 1} - ${option}`, + key: `${action.reportActionID}-conciergeDescriptionOptions-${option}`, + onPress: () => { + resolveConciergeDescriptionOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); + }, + })); + } + const messageHtml = getReportActionMessage(action)?.html; + if (messageHtml && reportActionReport) { + const followups = parseFollowupsFromHtml(messageHtml); + if (followups && followups.length > 0) { + return followups.map((followup) => ({ + text: followup.text, + shouldUseLocalization: false, + key: `${action.reportActionID}-followup-${followup.text}`, + onPress: () => { + resolveSuggestedFollowup( + reportActionReport, + reportID, + action, + followup, + personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, + currentUserAccountID, + personalDetail.email, + ); + }, + })); + } + } + + if (isActionableTrackExpense(action)) { + const reportActionReportID = originalReportID ?? reportID; + const options = [ + { + text: 'actionableMentionTrackExpense.submit', + key: `${action.reportActionID}-actionableMentionTrackExpense-submit`, + onPress: () => { + createDraftTransactionAndNavigateToParticipantSelector({ + reportID: reportActionReportID, + actionName: CONST.IOU.ACTION.SUBMIT, + reportActionID: action.reportActionID, + introSelected, + draftTransactionIDs, + activePolicy, + userBillingGracePeriodEnds, + amountOwed, + ownerBillingGracePeriodEnd, + isRestrictedToPreferredPolicy, + preferredPolicyID, + transaction: trackExpenseTransaction, + currentUserAccountID: personalDetail.accountID, + currentUserEmail: personalDetail.email ?? '', + }); + }, + }, + ]; + + if (Permissions.canUseTrackFlows()) { + options.push( + { + text: 'actionableMentionTrackExpense.categorize', + key: `${action.reportActionID}-actionableMentionTrackExpense-categorize`, + onPress: () => { + createDraftTransactionAndNavigateToParticipantSelector({ + reportID: reportActionReportID, + actionName: CONST.IOU.ACTION.CATEGORIZE, + reportActionID: action.reportActionID, + introSelected, + draftTransactionIDs, + activePolicy, + userBillingGracePeriodEnds, + amountOwed, + ownerBillingGracePeriodEnd, + transaction: trackExpenseTransaction, + currentUserAccountID: personalDetail.accountID, + currentUserEmail: personalDetail.email ?? '', + }); + }, + }, + { + text: 'actionableMentionTrackExpense.share', + key: `${action.reportActionID}-actionableMentionTrackExpense-share`, + onPress: () => { + createDraftTransactionAndNavigateToParticipantSelector({ + reportID: reportActionReportID, + actionName: CONST.IOU.ACTION.SHARE, + reportActionID: action.reportActionID, + introSelected, + draftTransactionIDs, + activePolicy, + userBillingGracePeriodEnds, + amountOwed, + ownerBillingGracePeriodEnd, + transaction: trackExpenseTransaction, + currentUserAccountID: personalDetail.accountID, + currentUserEmail: personalDetail.email ?? '', + }); + }, + }, + ); + } + options.push({ + text: 'actionableMentionTrackExpense.nothing', + key: `${action.reportActionID}-actionableMentionTrackExpense-nothing`, + onPress: () => { + dismissTrackExpenseActionableWhisper(reportActionReportID, action); + }, + }); + return options; + } + + if (isActionableMentionInviteToSubmitExpenseConfirmWhisper(action)) { + return [ + { + text: 'common.buttonConfirm', + key: `${action.reportActionID}-actionableReportMentionConfirmWhisper-${CONST.REPORT.ACTIONABLE_MENTION_INVITE_TO_SUBMIT_EXPENSE_CONFIRM_WHISPER.DONE}`, + onPress: () => + resolveActionableMentionConfirmWhisper( + reportActionReport, + action, + CONST.REPORT.ACTIONABLE_MENTION_INVITE_TO_SUBMIT_EXPENSE_CONFIRM_WHISPER.DONE, + isOriginalReportArchived, + ), + isPrimary: true, + }, + ]; + } + + return []; + }, [ + action, + userBillingFundID, + originalReportID, + reportID, + currentUserAccountID, + personalDetail.timezone, + personalDetail.accountID, + personalDetail.email, + isRestrictedToPreferredPolicy, + preferredPolicyID, + isOriginalReportArchived, + introSelected, + draftTransactionIDs, + activePolicy, + report, + originalReport, + userBillingGracePeriodEnds, + amountOwed, + ownerBillingGracePeriodEnd, + trackExpenseTransaction, + ]); + + const hasBeenFlagged = + ![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === moderationDecision) && !isPendingRemove(action); + + const isConciergeOptions = isConciergeCategoryOptions(action) || isConciergeDescriptionOptions(action); + const actionContainsFollowUps = containsActionableFollowUps(action); + const isPhrasalConciergeOptions = isConciergeOptions || actionContainsFollowUps; + const actionableButtonsNoLines = isPhrasalConciergeOptions ? 3 : 1; + + return ( + + + + + {draftMessage === undefined ? ( + + + {hasBeenFlagged && ( + + )} + {actionableItemButtons.length > 0 && ( + + )} + + ) : ( + + )} + + + + + ); +} + +ChatMessageContent.displayName = 'ChatMessageContent'; + +export default ChatMessageContent; diff --git a/tests/ui/ClearReportActionErrorsUITest.tsx b/tests/ui/ClearReportActionErrorsUITest.tsx index 252f21f379f0..c60e78b67f85 100644 --- a/tests/ui/ClearReportActionErrorsUITest.tsx +++ b/tests/ui/ClearReportActionErrorsUITest.tsx @@ -93,7 +93,6 @@ describe('ClearReportActionErrors UI', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} clearAllRelatedReportActionErrors={clearErrorFn} originalReportID={originalReportID} /> diff --git a/tests/ui/PureReportActionItemTest.tsx b/tests/ui/PureReportActionItemTest.tsx index b3704eb3607f..b444d68af211 100644 --- a/tests/ui/PureReportActionItemTest.tsx +++ b/tests/ui/PureReportActionItemTest.tsx @@ -100,7 +100,6 @@ describe('PureReportActionItem', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -394,7 +391,6 @@ describe('PureReportActionItem', () => { { reportMetadata={reportMetadata} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -453,7 +447,6 @@ describe('PureReportActionItem', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -514,7 +505,6 @@ describe('PureReportActionItem', () => { { reportMetadata={reportMetadata} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -569,7 +557,6 @@ describe('PureReportActionItem', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -648,7 +633,6 @@ describe('PureReportActionItem', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -713,7 +695,6 @@ describe('PureReportActionItem', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -798,7 +777,6 @@ describe('PureReportActionItem', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} modifiedExpenseMessage={modifiedExpenseMessage} - userBillingGracePeriodEnds={undefined} /> @@ -1093,7 +1069,6 @@ describe('PureReportActionItem', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} reimbursementDeQueuedOrCanceledActionMessage="Payment canceled" /> @@ -1281,7 +1254,6 @@ describe('PureReportActionItem', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -1381,7 +1351,6 @@ describe('PureReportActionItem', () => { { currentUserAccountID={ACTOR_ACCOUNT_ID} isClosedExpenseReportWithNoExpenses betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -1417,7 +1384,6 @@ describe('PureReportActionItem', () => { { currentUserAccountID={ACTOR_ACCOUNT_ID} missingPaymentMethod="bankAccount" betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -1455,7 +1419,6 @@ describe('PureReportActionItem', () => { { currentUserAccountID={ACTOR_ACCOUNT_ID} missingPaymentMethod="wallet" betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -1496,7 +1457,6 @@ describe('PureReportActionItem', () => { { // eslint-disable-next-line @typescript-eslint/naming-convention bankAccountList={{12345: {accountData: {accountNumber: '000098765'}} as never}} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -1538,7 +1496,6 @@ describe('PureReportActionItem', () => { { // eslint-disable-next-line @typescript-eslint/naming-convention bankAccountList={{12345: {accountData: {accountNumber: '000098765'}} as never}} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -1583,7 +1538,6 @@ describe('PureReportActionItem', () => { { // eslint-disable-next-line @typescript-eslint/naming-convention bankAccountList={{55555: {accountData: {accountNumber: '000012345'}} as never}} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -1629,7 +1581,6 @@ describe('PureReportActionItem', () => { { // eslint-disable-next-line @typescript-eslint/naming-convention bankAccountList={{77777: {accountData: {accountNumber: '000067890'}} as never}} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -1726,7 +1675,6 @@ describe('PureReportActionItem', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -1772,7 +1718,6 @@ describe('PureReportActionItem', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -2301,7 +2244,6 @@ describe('PureReportActionItem', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> @@ -2360,7 +2300,6 @@ describe('PureReportActionItem', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} reportNameValuePairsOrigin="harvest" reportNameValuePairsOriginalID="origReport123" /> @@ -2417,7 +2354,6 @@ describe('PureReportActionItem', () => { { iouReportOfLinkedReport={undefined} currentUserAccountID={ACTOR_ACCOUNT_ID} betas={undefined} - draftTransactionIDs={[]} - userBillingGracePeriodEnds={undefined} /> From 4940516c3f87bcb88265b394570b6249a3f2975e Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Thu, 16 Apr 2026 14:59:18 +0200 Subject: [PATCH 2/7] add isOriginalReportArchived prop --- src/pages/inbox/report/PureReportActionItem.tsx | 5 +++++ src/pages/inbox/report/ReportActionItem.tsx | 1 + src/pages/inbox/report/actionContents/ChatMessageContent.tsx | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index 1516d24b03ad..05723dfda147 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -270,6 +270,9 @@ type PureReportActionItemProps = { /** Whether the room is a chronos report */ isChronosReport?: boolean; + /** Whether the original report is archived */ + isOriginalReportArchived?: boolean; + /** All cards */ cardList?: OnyxTypes.CardList; @@ -400,6 +403,7 @@ function PureReportActionItem({ deleteReportActionDraft = () => {}, isArchivedRoom, isChronosReport, + isOriginalReportArchived, toggleEmojiReaction = () => {}, resolveActionableReportMentionWhisper = () => {}, resolveActionableMentionWhisper = () => {}, @@ -1126,6 +1130,7 @@ function PureReportActionItem({ moderationDecision={moderationDecision} updateHiddenState={updateHiddenState} isArchivedRoom={isArchivedRoom} + isOriginalReportArchived={!!isOriginalReportArchived} composerTextInputRef={composerTextInputRef} isOnSearch={isOnSearch} currentSearchHash={currentSearchHash} diff --git a/src/pages/inbox/report/ReportActionItem.tsx b/src/pages/inbox/report/ReportActionItem.tsx index 45c2c7148cd7..a427bbc3ee26 100644 --- a/src/pages/inbox/report/ReportActionItem.tsx +++ b/src/pages/inbox/report/ReportActionItem.tsx @@ -143,6 +143,7 @@ function ReportActionItem({ deleteReportActionDraft={deleteReportActionDraft} isArchivedRoom={isArchivedNonExpenseReport(originalReport, isOriginalReportArchived)} isChronosReport={chatIncludesChronosWithID(originalReportID)} + isOriginalReportArchived={isOriginalReportArchived} toggleEmojiReaction={toggleEmojiReaction} resolveActionableReportMentionWhisper={resolveActionableReportMentionWhisper} resolveActionableMentionWhisper={resolveActionableMentionWhisper} diff --git a/src/pages/inbox/report/actionContents/ChatMessageContent.tsx b/src/pages/inbox/report/actionContents/ChatMessageContent.tsx index 6e1fd21b1306..cb778515e022 100644 --- a/src/pages/inbox/report/actionContents/ChatMessageContent.tsx +++ b/src/pages/inbox/report/actionContents/ChatMessageContent.tsx @@ -16,7 +16,6 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePreferredPolicy from '@hooks/usePreferredPolicy'; -import useReportIsArchived from '@hooks/useReportIsArchived'; import useThemeStyles from '@hooks/useThemeStyles'; import {resolveSuggestedFollowup} from '@libs/actions/Report/SuggestedFollowup'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; @@ -60,6 +59,7 @@ type ChatMessageContentProps = { moderationDecision: OnyxTypes.DecisionName; updateHiddenState: (isHiddenValue: boolean) => void; isArchivedRoom?: boolean; + isOriginalReportArchived: boolean; composerTextInputRef: React.RefObject; isOnSearch: boolean; currentSearchHash: number | undefined; @@ -95,6 +95,7 @@ function ChatMessageContent({ moderationDecision, updateHiddenState, isArchivedRoom, + isOriginalReportArchived, composerTextInputRef, isOnSearch, currentSearchHash, @@ -109,7 +110,6 @@ function ChatMessageContent({ const personalDetail = useCurrentUserPersonalDetails(); const {isRestrictedToPreferredPolicy, preferredPolicyID} = usePreferredPolicy(); const activePolicy = useActivePolicy(); - const isOriginalReportArchived = useReportIsArchived(originalReportID); const blockedFromConcierge = useBlockedFromConcierge(); const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); From 01a6bf1071ad4f286e7b4ce46b34bd92cf5239a0 Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Thu, 16 Apr 2026 15:27:23 +0200 Subject: [PATCH 3/7] add ChatActionableButtons --- .../actionContents/ChatActionableButtons.tsx | 279 +++++++++++++++++ .../actionContents/ChatMessageContent.tsx | 281 ++---------------- 2 files changed, 304 insertions(+), 256 deletions(-) create mode 100644 src/pages/inbox/report/actionContents/ChatActionableButtons.tsx diff --git a/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx b/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx new file mode 100644 index 000000000000..b3f940b089d0 --- /dev/null +++ b/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx @@ -0,0 +1,279 @@ +import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; +import React from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; +import type {ActionableItem} from '@components/ReportActionItem/ActionableItemButtons'; +import ActionableItemButtons from '@components/ReportActionItem/ActionableItemButtons'; +import useActivePolicy from '@hooks/useActivePolicy'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useOnyx from '@hooks/useOnyx'; +import usePreferredPolicy from '@hooks/usePreferredPolicy'; +import useThemeStyles from '@hooks/useThemeStyles'; +import {resolveSuggestedFollowup} from '@libs/actions/Report/SuggestedFollowup'; +import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; +import Navigation from '@libs/Navigation/Navigation'; +import Permissions from '@libs/Permissions'; +import {containsActionableFollowUps, parseFollowupsFromHtml} from '@libs/ReportActionFollowupUtils'; +import { + getOriginalMessage, + getReportActionMessage, + isActionableAddPaymentCard, + isActionableMentionInviteToSubmitExpenseConfirmWhisper, + isActionableTrackExpense, + isConciergeCategoryOptions, + isConciergeDescriptionOptions, + isResolvedConciergeCategoryOptions, + isResolvedConciergeDescriptionOptions, +} from '@libs/ReportActionsUtils'; +import {createDraftTransactionAndNavigateToParticipantSelector} from '@libs/ReportUtils'; +import shouldRenderAddPaymentCard from '@libs/shouldRenderAppPaymentCard'; +import {dismissTrackExpenseActionableWhisper, resolveActionableMentionConfirmWhisper, resolveConciergeCategoryOptions, resolveConciergeDescriptionOptions} from '@userActions/Report'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type * as OnyxTypes from '@src/types/onyx'; + +type ChatActionableButtonsProps = { + action: OnyxTypes.ReportAction; + report: OnyxEntry; + originalReport: OnyxEntry; + reportID: string | undefined; + originalReportID: string; + userBillingFundID: number | undefined; + introSelected: OnyxEntry; + currentUserAccountID: number; + isOriginalReportArchived: boolean; +}; + +function ChatActionableButtons({ + action, + report, + originalReport, + reportID, + originalReportID, + userBillingFundID, + introSelected, + currentUserAccountID, + isOriginalReportArchived, +}: ChatActionableButtonsProps) { + const styles = useThemeStyles(); + const personalDetail = useCurrentUserPersonalDetails(); + const {isRestrictedToPreferredPolicy, preferredPolicyID} = usePreferredPolicy(); + const activePolicy = useActivePolicy(); + + const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); + const [userBillingGracePeriodEnds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END); + const [amountOwed] = useOnyx(ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED); + const [ownerBillingGracePeriodEnd] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END); + const trackExpenseTransactionID = isActionableTrackExpense(action) ? getOriginalMessage(action)?.transactionID : undefined; + const [trackExpenseTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(trackExpenseTransactionID)}`); + + const actionableItemButtons = ((): ActionableItem[] => { + if (isActionableAddPaymentCard(action) && userBillingFundID === undefined && shouldRenderAddPaymentCard()) { + return [ + { + text: 'subscription.cardSection.addCardButton', + key: `${action.reportActionID}-actionableAddPaymentCard-submit`, + onPress: () => { + Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD); + }, + isPrimary: true, + }, + ]; + } + + const reportActionReport = originalReport ?? report; + if (isConciergeCategoryOptions(action)) { + const options = getOriginalMessage(action)?.options; + if (!options) { + return []; + } + + if (isResolvedConciergeCategoryOptions(action)) { + return []; + } + + if (!reportActionReport) { + return []; + } + + return options.map((option, i) => ({ + text: `${i + 1} - ${option}`, + key: `${action.reportActionID}-conciergeCategoryOptions-${option}`, + onPress: () => { + resolveConciergeCategoryOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); + }, + })); + } + + if (isConciergeDescriptionOptions(action)) { + const options = getOriginalMessage(action)?.options; + if (!options) { + return []; + } + + if (isResolvedConciergeDescriptionOptions(action)) { + return []; + } + + if (!reportActionReport) { + return []; + } + + return options.map((option, i) => ({ + text: `${i + 1} - ${option}`, + key: `${action.reportActionID}-conciergeDescriptionOptions-${option}`, + onPress: () => { + resolveConciergeDescriptionOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); + }, + })); + } + const messageHtml = getReportActionMessage(action)?.html; + if (messageHtml && reportActionReport) { + const followups = parseFollowupsFromHtml(messageHtml); + if (followups && followups.length > 0) { + return followups.map((followup) => ({ + text: followup.text, + shouldUseLocalization: false, + key: `${action.reportActionID}-followup-${followup.text}`, + onPress: () => { + resolveSuggestedFollowup( + reportActionReport, + reportID, + action, + followup, + personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, + currentUserAccountID, + personalDetail.email, + ); + }, + })); + } + } + + if (isActionableTrackExpense(action)) { + const reportActionReportID = originalReportID ?? reportID; + const options = [ + { + text: 'actionableMentionTrackExpense.submit', + key: `${action.reportActionID}-actionableMentionTrackExpense-submit`, + onPress: () => { + createDraftTransactionAndNavigateToParticipantSelector({ + reportID: reportActionReportID, + actionName: CONST.IOU.ACTION.SUBMIT, + reportActionID: action.reportActionID, + introSelected, + draftTransactionIDs, + activePolicy, + userBillingGracePeriodEnds, + amountOwed, + ownerBillingGracePeriodEnd, + isRestrictedToPreferredPolicy, + preferredPolicyID, + transaction: trackExpenseTransaction, + currentUserAccountID: personalDetail.accountID, + currentUserEmail: personalDetail.email ?? '', + }); + }, + }, + ]; + + if (Permissions.canUseTrackFlows()) { + options.push( + { + text: 'actionableMentionTrackExpense.categorize', + key: `${action.reportActionID}-actionableMentionTrackExpense-categorize`, + onPress: () => { + createDraftTransactionAndNavigateToParticipantSelector({ + reportID: reportActionReportID, + actionName: CONST.IOU.ACTION.CATEGORIZE, + reportActionID: action.reportActionID, + introSelected, + draftTransactionIDs, + activePolicy, + userBillingGracePeriodEnds, + amountOwed, + ownerBillingGracePeriodEnd, + transaction: trackExpenseTransaction, + currentUserAccountID: personalDetail.accountID, + currentUserEmail: personalDetail.email ?? '', + }); + }, + }, + { + text: 'actionableMentionTrackExpense.share', + key: `${action.reportActionID}-actionableMentionTrackExpense-share`, + onPress: () => { + createDraftTransactionAndNavigateToParticipantSelector({ + reportID: reportActionReportID, + actionName: CONST.IOU.ACTION.SHARE, + reportActionID: action.reportActionID, + introSelected, + draftTransactionIDs, + activePolicy, + userBillingGracePeriodEnds, + amountOwed, + ownerBillingGracePeriodEnd, + transaction: trackExpenseTransaction, + currentUserAccountID: personalDetail.accountID, + currentUserEmail: personalDetail.email ?? '', + }); + }, + }, + ); + } + options.push({ + text: 'actionableMentionTrackExpense.nothing', + key: `${action.reportActionID}-actionableMentionTrackExpense-nothing`, + onPress: () => { + dismissTrackExpenseActionableWhisper(reportActionReportID, action); + }, + }); + return options; + } + + if (isActionableMentionInviteToSubmitExpenseConfirmWhisper(action)) { + return [ + { + text: 'common.buttonConfirm', + key: `${action.reportActionID}-actionableReportMentionConfirmWhisper-${CONST.REPORT.ACTIONABLE_MENTION_INVITE_TO_SUBMIT_EXPENSE_CONFIRM_WHISPER.DONE}`, + onPress: () => + resolveActionableMentionConfirmWhisper( + reportActionReport, + action, + CONST.REPORT.ACTIONABLE_MENTION_INVITE_TO_SUBMIT_EXPENSE_CONFIRM_WHISPER.DONE, + isOriginalReportArchived, + ), + isPrimary: true, + }, + ]; + } + + return []; + })(); + + if (actionableItemButtons.length === 0) { + return null; + } + + const isConciergeOptions = isConciergeCategoryOptions(action) || isConciergeDescriptionOptions(action); + const actionContainsFollowUps = containsActionableFollowUps(action); + const isPhrasalConciergeOptions = isConciergeOptions || actionContainsFollowUps; + const actionableButtonsNoLines = isPhrasalConciergeOptions ? 3 : 1; + + return ( + + ); +} + +ChatActionableButtons.displayName = 'ChatActionableButtons'; + +export default ChatActionableButtons; diff --git a/src/pages/inbox/report/actionContents/ChatMessageContent.tsx b/src/pages/inbox/report/actionContents/ChatMessageContent.tsx index cb778515e022..f512e771a61c 100644 --- a/src/pages/inbox/report/actionContents/ChatMessageContent.tsx +++ b/src/pages/inbox/report/actionContents/ChatMessageContent.tsx @@ -1,5 +1,4 @@ -import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; -import React, {useMemo} from 'react'; +import React from 'react'; import type {TextInput} from 'react-native'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -7,23 +6,12 @@ import {AttachmentContext} from '@components/AttachmentContext'; import Button from '@components/Button'; import MentionReportContext from '@components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/MentionReportContext'; import {useBlockedFromConcierge} from '@components/OnyxListItemProvider'; -import type {ActionableItem} from '@components/ReportActionItem/ActionableItemButtons'; -import ActionableItemButtons from '@components/ReportActionItem/ActionableItemButtons'; import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; -import useActivePolicy from '@hooks/useActivePolicy'; -import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; -import useOnyx from '@hooks/useOnyx'; -import usePreferredPolicy from '@hooks/usePreferredPolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import {resolveSuggestedFollowup} from '@libs/actions/Report/SuggestedFollowup'; -import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; -import Navigation from '@libs/Navigation/Navigation'; -import Permissions from '@libs/Permissions'; -import {containsActionableFollowUps, parseFollowupsFromHtml} from '@libs/ReportActionFollowupUtils'; +import {parseFollowupsFromHtml} from '@libs/ReportActionFollowupUtils'; import { - getOriginalMessage, getReportActionMessage, isActionableAddPaymentCard, isActionableMentionInviteToSubmitExpenseConfirmWhisper, @@ -31,20 +19,15 @@ import { isConciergeCategoryOptions, isConciergeDescriptionOptions, isPendingRemove, - isResolvedConciergeCategoryOptions, - isResolvedConciergeDescriptionOptions, } from '@libs/ReportActionsUtils'; -import {chatIncludesConcierge, createDraftTransactionAndNavigateToParticipantSelector, isArchivedNonExpenseReport} from '@libs/ReportUtils'; -import shouldRenderAddPaymentCard from '@libs/shouldRenderAppPaymentCard'; +import {chatIncludesConcierge, isArchivedNonExpenseReport} from '@libs/ReportUtils'; import type {ContextMenuAnchor} from '@pages/inbox/report/ContextMenu/ReportActionContextMenu'; import ReportActionItemMessage from '@pages/inbox/report/ReportActionItemMessage'; import ReportActionItemMessageEdit from '@pages/inbox/report/ReportActionItemMessageEdit'; -import {dismissTrackExpenseActionableWhisper, resolveActionableMentionConfirmWhisper, resolveConciergeCategoryOptions, resolveConciergeDescriptionOptions} from '@userActions/Report'; import {isBlockedFromConcierge} from '@userActions/User'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; +import ChatActionableButtons from './ChatActionableButtons'; type ChatMessageContentProps = { action: OnyxTypes.ReportAction; @@ -107,239 +90,24 @@ function ChatMessageContent({ }: ChatMessageContentProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); - const personalDetail = useCurrentUserPersonalDetails(); - const {isRestrictedToPreferredPolicy, preferredPolicyID} = usePreferredPolicy(); - const activePolicy = useActivePolicy(); const blockedFromConcierge = useBlockedFromConcierge(); - const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); - const [userBillingGracePeriodEnds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END); - const [amountOwed] = useOnyx(ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED); - const [ownerBillingGracePeriodEnd] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END); - const trackExpenseTransactionID = isActionableTrackExpense(action) ? getOriginalMessage(action)?.transactionID : undefined; - const [trackExpenseTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(trackExpenseTransactionID)}`); - const mentionReportContextValue = useMemo(() => ({currentReportID: report?.reportID, exactlyMatch: true}), [report?.reportID]); + const mentionReportContextValue = {currentReportID: report?.reportID, exactlyMatch: true}; - const attachmentContextValue = useMemo(() => { - if (isOnSearch) { - return {type: CONST.ATTACHMENT_TYPE.SEARCH, currentSearchHash}; - } - return {reportID, type: CONST.ATTACHMENT_TYPE.REPORT}; - }, [reportID, isOnSearch, currentSearchHash]); - - const actionableItemButtons: ActionableItem[] = useMemo(() => { - if (isActionableAddPaymentCard(action) && userBillingFundID === undefined && shouldRenderAddPaymentCard()) { - return [ - { - text: 'subscription.cardSection.addCardButton', - key: `${action.reportActionID}-actionableAddPaymentCard-submit`, - onPress: () => { - Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD); - }, - isPrimary: true, - }, - ]; - } - - const reportActionReport = originalReport ?? report; - if (isConciergeCategoryOptions(action)) { - const options = getOriginalMessage(action)?.options; - if (!options) { - return []; - } - - if (isResolvedConciergeCategoryOptions(action)) { - return []; - } - - if (!reportActionReport) { - return []; - } - - return options.map((option, i) => ({ - text: `${i + 1} - ${option}`, - key: `${action.reportActionID}-conciergeCategoryOptions-${option}`, - onPress: () => { - resolveConciergeCategoryOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); - }, - })); - } - - if (isConciergeDescriptionOptions(action)) { - const options = getOriginalMessage(action)?.options; - if (!options) { - return []; - } - - if (isResolvedConciergeDescriptionOptions(action)) { - return []; - } - - if (!reportActionReport) { - return []; - } - - return options.map((option, i) => ({ - text: `${i + 1} - ${option}`, - key: `${action.reportActionID}-conciergeDescriptionOptions-${option}`, - onPress: () => { - resolveConciergeDescriptionOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); - }, - })); - } - const messageHtml = getReportActionMessage(action)?.html; - if (messageHtml && reportActionReport) { - const followups = parseFollowupsFromHtml(messageHtml); - if (followups && followups.length > 0) { - return followups.map((followup) => ({ - text: followup.text, - shouldUseLocalization: false, - key: `${action.reportActionID}-followup-${followup.text}`, - onPress: () => { - resolveSuggestedFollowup( - reportActionReport, - reportID, - action, - followup, - personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, - currentUserAccountID, - personalDetail.email, - ); - }, - })); - } - } - - if (isActionableTrackExpense(action)) { - const reportActionReportID = originalReportID ?? reportID; - const options = [ - { - text: 'actionableMentionTrackExpense.submit', - key: `${action.reportActionID}-actionableMentionTrackExpense-submit`, - onPress: () => { - createDraftTransactionAndNavigateToParticipantSelector({ - reportID: reportActionReportID, - actionName: CONST.IOU.ACTION.SUBMIT, - reportActionID: action.reportActionID, - introSelected, - draftTransactionIDs, - activePolicy, - userBillingGracePeriodEnds, - amountOwed, - ownerBillingGracePeriodEnd, - isRestrictedToPreferredPolicy, - preferredPolicyID, - transaction: trackExpenseTransaction, - currentUserAccountID: personalDetail.accountID, - currentUserEmail: personalDetail.email ?? '', - }); - }, - }, - ]; - - if (Permissions.canUseTrackFlows()) { - options.push( - { - text: 'actionableMentionTrackExpense.categorize', - key: `${action.reportActionID}-actionableMentionTrackExpense-categorize`, - onPress: () => { - createDraftTransactionAndNavigateToParticipantSelector({ - reportID: reportActionReportID, - actionName: CONST.IOU.ACTION.CATEGORIZE, - reportActionID: action.reportActionID, - introSelected, - draftTransactionIDs, - activePolicy, - userBillingGracePeriodEnds, - amountOwed, - ownerBillingGracePeriodEnd, - transaction: trackExpenseTransaction, - currentUserAccountID: personalDetail.accountID, - currentUserEmail: personalDetail.email ?? '', - }); - }, - }, - { - text: 'actionableMentionTrackExpense.share', - key: `${action.reportActionID}-actionableMentionTrackExpense-share`, - onPress: () => { - createDraftTransactionAndNavigateToParticipantSelector({ - reportID: reportActionReportID, - actionName: CONST.IOU.ACTION.SHARE, - reportActionID: action.reportActionID, - introSelected, - draftTransactionIDs, - activePolicy, - userBillingGracePeriodEnds, - amountOwed, - ownerBillingGracePeriodEnd, - transaction: trackExpenseTransaction, - currentUserAccountID: personalDetail.accountID, - currentUserEmail: personalDetail.email ?? '', - }); - }, - }, - ); - } - options.push({ - text: 'actionableMentionTrackExpense.nothing', - key: `${action.reportActionID}-actionableMentionTrackExpense-nothing`, - onPress: () => { - dismissTrackExpenseActionableWhisper(reportActionReportID, action); - }, - }); - return options; - } - - if (isActionableMentionInviteToSubmitExpenseConfirmWhisper(action)) { - return [ - { - text: 'common.buttonConfirm', - key: `${action.reportActionID}-actionableReportMentionConfirmWhisper-${CONST.REPORT.ACTIONABLE_MENTION_INVITE_TO_SUBMIT_EXPENSE_CONFIRM_WHISPER.DONE}`, - onPress: () => - resolveActionableMentionConfirmWhisper( - reportActionReport, - action, - CONST.REPORT.ACTIONABLE_MENTION_INVITE_TO_SUBMIT_EXPENSE_CONFIRM_WHISPER.DONE, - isOriginalReportArchived, - ), - isPrimary: true, - }, - ]; - } - - return []; - }, [ - action, - userBillingFundID, - originalReportID, - reportID, - currentUserAccountID, - personalDetail.timezone, - personalDetail.accountID, - personalDetail.email, - isRestrictedToPreferredPolicy, - preferredPolicyID, - isOriginalReportArchived, - introSelected, - draftTransactionIDs, - activePolicy, - report, - originalReport, - userBillingGracePeriodEnds, - amountOwed, - ownerBillingGracePeriodEnd, - trackExpenseTransaction, - ]); + const attachmentContextValue = isOnSearch ? {type: CONST.ATTACHMENT_TYPE.SEARCH, currentSearchHash} : {reportID, type: CONST.ATTACHMENT_TYPE.REPORT}; const hasBeenFlagged = ![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === moderationDecision) && !isPendingRemove(action); - const isConciergeOptions = isConciergeCategoryOptions(action) || isConciergeDescriptionOptions(action); - const actionContainsFollowUps = containsActionableFollowUps(action); - const isPhrasalConciergeOptions = isConciergeOptions || actionContainsFollowUps; - const actionableButtonsNoLines = isPhrasalConciergeOptions ? 3 : 1; + const messageHtml = getReportActionMessage(action)?.html; + const mayHaveActionableButtons = + isActionableAddPaymentCard(action) || + isConciergeCategoryOptions(action) || + isConciergeDescriptionOptions(action) || + isActionableTrackExpense(action) || + isActionableMentionInviteToSubmitExpenseConfirmWhisper(action) || + !!(messageHtml && parseFollowupsFromHtml(messageHtml)?.length); return ( @@ -369,16 +137,17 @@ function ChatMessageContent({ )} - {actionableItemButtons.length > 0 && ( - )} From f797941b8f705687ed10f7d3c4ba30a18be617de Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Thu, 16 Apr 2026 15:39:35 +0200 Subject: [PATCH 4/7] clean up code from isActionableMentionInviteToSubmitExpenseConfirmWhisper --- .../inbox/report/PureReportActionItem.tsx | 5 --- src/pages/inbox/report/ReportActionItem.tsx | 1 - .../actionContents/ChatActionableButtons.tsx | 33 ++----------------- .../actionContents/ChatMessageContent.tsx | 5 --- 4 files changed, 2 insertions(+), 42 deletions(-) diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index 05723dfda147..1516d24b03ad 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -270,9 +270,6 @@ type PureReportActionItemProps = { /** Whether the room is a chronos report */ isChronosReport?: boolean; - /** Whether the original report is archived */ - isOriginalReportArchived?: boolean; - /** All cards */ cardList?: OnyxTypes.CardList; @@ -403,7 +400,6 @@ function PureReportActionItem({ deleteReportActionDraft = () => {}, isArchivedRoom, isChronosReport, - isOriginalReportArchived, toggleEmojiReaction = () => {}, resolveActionableReportMentionWhisper = () => {}, resolveActionableMentionWhisper = () => {}, @@ -1130,7 +1126,6 @@ function PureReportActionItem({ moderationDecision={moderationDecision} updateHiddenState={updateHiddenState} isArchivedRoom={isArchivedRoom} - isOriginalReportArchived={!!isOriginalReportArchived} composerTextInputRef={composerTextInputRef} isOnSearch={isOnSearch} currentSearchHash={currentSearchHash} diff --git a/src/pages/inbox/report/ReportActionItem.tsx b/src/pages/inbox/report/ReportActionItem.tsx index a427bbc3ee26..45c2c7148cd7 100644 --- a/src/pages/inbox/report/ReportActionItem.tsx +++ b/src/pages/inbox/report/ReportActionItem.tsx @@ -143,7 +143,6 @@ function ReportActionItem({ deleteReportActionDraft={deleteReportActionDraft} isArchivedRoom={isArchivedNonExpenseReport(originalReport, isOriginalReportArchived)} isChronosReport={chatIncludesChronosWithID(originalReportID)} - isOriginalReportArchived={isOriginalReportArchived} toggleEmojiReaction={toggleEmojiReaction} resolveActionableReportMentionWhisper={resolveActionableReportMentionWhisper} resolveActionableMentionWhisper={resolveActionableMentionWhisper} diff --git a/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx b/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx index b3f940b089d0..875546f687f8 100644 --- a/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx +++ b/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx @@ -17,7 +17,6 @@ import { getOriginalMessage, getReportActionMessage, isActionableAddPaymentCard, - isActionableMentionInviteToSubmitExpenseConfirmWhisper, isActionableTrackExpense, isConciergeCategoryOptions, isConciergeDescriptionOptions, @@ -26,7 +25,7 @@ import { } from '@libs/ReportActionsUtils'; import {createDraftTransactionAndNavigateToParticipantSelector} from '@libs/ReportUtils'; import shouldRenderAddPaymentCard from '@libs/shouldRenderAppPaymentCard'; -import {dismissTrackExpenseActionableWhisper, resolveActionableMentionConfirmWhisper, resolveConciergeCategoryOptions, resolveConciergeDescriptionOptions} from '@userActions/Report'; +import {dismissTrackExpenseActionableWhisper, resolveConciergeCategoryOptions, resolveConciergeDescriptionOptions} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -41,20 +40,9 @@ type ChatActionableButtonsProps = { userBillingFundID: number | undefined; introSelected: OnyxEntry; currentUserAccountID: number; - isOriginalReportArchived: boolean; }; -function ChatActionableButtons({ - action, - report, - originalReport, - reportID, - originalReportID, - userBillingFundID, - introSelected, - currentUserAccountID, - isOriginalReportArchived, -}: ChatActionableButtonsProps) { +function ChatActionableButtons({action, report, originalReport, reportID, originalReportID, userBillingFundID, introSelected, currentUserAccountID}: ChatActionableButtonsProps) { const styles = useThemeStyles(); const personalDetail = useCurrentUserPersonalDetails(); const {isRestrictedToPreferredPolicy, preferredPolicyID} = usePreferredPolicy(); @@ -231,23 +219,6 @@ function ChatActionableButtons({ return options; } - if (isActionableMentionInviteToSubmitExpenseConfirmWhisper(action)) { - return [ - { - text: 'common.buttonConfirm', - key: `${action.reportActionID}-actionableReportMentionConfirmWhisper-${CONST.REPORT.ACTIONABLE_MENTION_INVITE_TO_SUBMIT_EXPENSE_CONFIRM_WHISPER.DONE}`, - onPress: () => - resolveActionableMentionConfirmWhisper( - reportActionReport, - action, - CONST.REPORT.ACTIONABLE_MENTION_INVITE_TO_SUBMIT_EXPENSE_CONFIRM_WHISPER.DONE, - isOriginalReportArchived, - ), - isPrimary: true, - }, - ]; - } - return []; })(); diff --git a/src/pages/inbox/report/actionContents/ChatMessageContent.tsx b/src/pages/inbox/report/actionContents/ChatMessageContent.tsx index f512e771a61c..c462e4c60c32 100644 --- a/src/pages/inbox/report/actionContents/ChatMessageContent.tsx +++ b/src/pages/inbox/report/actionContents/ChatMessageContent.tsx @@ -14,7 +14,6 @@ import {parseFollowupsFromHtml} from '@libs/ReportActionFollowupUtils'; import { getReportActionMessage, isActionableAddPaymentCard, - isActionableMentionInviteToSubmitExpenseConfirmWhisper, isActionableTrackExpense, isConciergeCategoryOptions, isConciergeDescriptionOptions, @@ -42,7 +41,6 @@ type ChatMessageContentProps = { moderationDecision: OnyxTypes.DecisionName; updateHiddenState: (isHiddenValue: boolean) => void; isArchivedRoom?: boolean; - isOriginalReportArchived: boolean; composerTextInputRef: React.RefObject; isOnSearch: boolean; currentSearchHash: number | undefined; @@ -78,7 +76,6 @@ function ChatMessageContent({ moderationDecision, updateHiddenState, isArchivedRoom, - isOriginalReportArchived, composerTextInputRef, isOnSearch, currentSearchHash, @@ -106,7 +103,6 @@ function ChatMessageContent({ isConciergeCategoryOptions(action) || isConciergeDescriptionOptions(action) || isActionableTrackExpense(action) || - isActionableMentionInviteToSubmitExpenseConfirmWhisper(action) || !!(messageHtml && parseFollowupsFromHtml(messageHtml)?.length); return ( @@ -147,7 +143,6 @@ function ChatMessageContent({ userBillingFundID={userBillingFundID} introSelected={introSelected} currentUserAccountID={currentUserAccountID} - isOriginalReportArchived={isOriginalReportArchived} /> )} From befe17c68b57d65dbc6106632ac526b9e9cc042a Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Fri, 17 Apr 2026 08:49:18 +0200 Subject: [PATCH 5/7] add tests, clean up props --- .../inbox/report/PureReportActionItem.tsx | 1 - .../actionContents/ChatActionableButtons.tsx | 23 +++++++++--- .../actionContents/ChatMessageContent.tsx | 3 -- tests/ui/PureReportActionItemTest.tsx | 35 +++++++++++++++++++ 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index 1516d24b03ad..c7bb3bf70713 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -1133,7 +1133,6 @@ function PureReportActionItem({ contextMenuActionsValue={contextMenuActionsValue} userBillingFundID={userBillingFundID} introSelected={introSelected} - currentUserAccountID={currentUserAccountID} /> ); } diff --git a/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx b/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx index 875546f687f8..29d92371cd29 100644 --- a/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx +++ b/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx @@ -39,10 +39,9 @@ type ChatActionableButtonsProps = { originalReportID: string; userBillingFundID: number | undefined; introSelected: OnyxEntry; - currentUserAccountID: number; }; -function ChatActionableButtons({action, report, originalReport, reportID, originalReportID, userBillingFundID, introSelected, currentUserAccountID}: ChatActionableButtonsProps) { +function ChatActionableButtons({action, report, originalReport, reportID, originalReportID, userBillingFundID, introSelected}: ChatActionableButtonsProps) { const styles = useThemeStyles(); const personalDetail = useCurrentUserPersonalDetails(); const {isRestrictedToPreferredPolicy, preferredPolicyID} = usePreferredPolicy(); @@ -88,7 +87,14 @@ function ChatActionableButtons({action, report, originalReport, reportID, origin text: `${i + 1} - ${option}`, key: `${action.reportActionID}-conciergeCategoryOptions-${option}`, onPress: () => { - resolveConciergeCategoryOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); + resolveConciergeCategoryOptions( + reportActionReport, + reportID, + action.reportActionID, + option, + personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, + personalDetail.accountID, + ); }, })); } @@ -111,7 +117,14 @@ function ChatActionableButtons({action, report, originalReport, reportID, origin text: `${i + 1} - ${option}`, key: `${action.reportActionID}-conciergeDescriptionOptions-${option}`, onPress: () => { - resolveConciergeDescriptionOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); + resolveConciergeDescriptionOptions( + reportActionReport, + reportID, + action.reportActionID, + option, + personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, + personalDetail.accountID, + ); }, })); } @@ -130,7 +143,7 @@ function ChatActionableButtons({action, report, originalReport, reportID, origin action, followup, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, - currentUserAccountID, + personalDetail.accountID, personalDetail.email, ); }, diff --git a/src/pages/inbox/report/actionContents/ChatMessageContent.tsx b/src/pages/inbox/report/actionContents/ChatMessageContent.tsx index c462e4c60c32..7ba2bcc2bcfd 100644 --- a/src/pages/inbox/report/actionContents/ChatMessageContent.tsx +++ b/src/pages/inbox/report/actionContents/ChatMessageContent.tsx @@ -60,7 +60,6 @@ type ChatMessageContentProps = { }; userBillingFundID: number | undefined; introSelected: OnyxEntry; - currentUserAccountID: number; }; function ChatMessageContent({ @@ -83,7 +82,6 @@ function ChatMessageContent({ contextMenuActionsValue, userBillingFundID, introSelected, - currentUserAccountID, }: ChatMessageContentProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -142,7 +140,6 @@ function ChatMessageContent({ originalReportID={originalReportID} userBillingFundID={userBillingFundID} introSelected={introSelected} - currentUserAccountID={currentUserAccountID} /> )} diff --git a/tests/ui/PureReportActionItemTest.tsx b/tests/ui/PureReportActionItemTest.tsx index b444d68af211..a229615bbfd3 100644 --- a/tests/ui/PureReportActionItemTest.tsx +++ b/tests/ui/PureReportActionItemTest.tsx @@ -2378,4 +2378,39 @@ describe('PureReportActionItem', () => { expect(screen.getByText(translateLocal('travel.tripSummary'))).toBeOnTheScreen(); }); }); + + describe('ChatMessageContent moderation and actionable buttons', () => { + it('flagged message shows "Reveal message" button when moderation decision is hidden', async () => { + const action = createReportAction(CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, {}); + action.message = [{type: 'COMMENT', html: 'bad message', text: 'bad message', moderationDecision: {decision: CONST.MODERATION.MODERATOR_DECISION_HIDDEN}}]; + renderItemWithAction(action); + await waitForBatchedUpdatesWithAct(); + + expect(screen.getByText('Reveal message')).toBeOnTheScreen(); + }); + + it('clicking "Reveal message" toggles to "Hide message"', async () => { + const action = createReportAction(CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, {}); + action.message = [{type: 'COMMENT', html: 'bad message', text: 'bad message', moderationDecision: {decision: CONST.MODERATION.MODERATOR_DECISION_HIDDEN}}]; + renderItemWithAction(action); + await waitForBatchedUpdatesWithAct(); + + fireEvent.press(screen.getByText('Reveal message')); + await waitForBatchedUpdatesWithAct(); + + expect(screen.getByText('Hide message')).toBeOnTheScreen(); + }); + + it('ACTIONABLETRACKEXPENSEWHISPER renders track expense buttons', async () => { + const action = createReportAction(CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_TRACK_EXPENSE_WHISPER, { + transactionID: 'tx123', + }); + action.message = [{type: 'COMMENT', html: 'Track this expense', text: 'Track this expense'}]; + renderItemWithAction(action); + await waitForBatchedUpdatesWithAct(); + + expect(screen.getByText(translateLocal('actionableMentionTrackExpense.submit' as TranslationPaths))).toBeOnTheScreen(); + expect(screen.getByText(translateLocal('actionableMentionTrackExpense.nothing' as TranslationPaths))).toBeOnTheScreen(); + }); + }); }); From b8b56d2d0425e6e23ee1b48f3131ec986fb9631c Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Fri, 17 Apr 2026 12:14:26 +0200 Subject: [PATCH 6/7] fix spell issue --- tests/ui/PureReportActionItemTest.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/PureReportActionItemTest.tsx b/tests/ui/PureReportActionItemTest.tsx index a229615bbfd3..94b90f345954 100644 --- a/tests/ui/PureReportActionItemTest.tsx +++ b/tests/ui/PureReportActionItemTest.tsx @@ -2401,7 +2401,7 @@ describe('PureReportActionItem', () => { expect(screen.getByText('Hide message')).toBeOnTheScreen(); }); - it('ACTIONABLETRACKEXPENSEWHISPER renders track expense buttons', async () => { + it('actionable track expense whisper renders track expense buttons', async () => { const action = createReportAction(CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_TRACK_EXPENSE_WHISPER, { transactionID: 'tx123', }); From 1d52ef5b5f4d1b1027692588eb484a7636644035 Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Fri, 17 Apr 2026 12:56:30 +0200 Subject: [PATCH 7/7] make actionableItemButtons DRY, add prepareTrackExpenseButton --- .../actionContents/ChatActionableButtons.tsx | 96 ++++++------------- 1 file changed, 31 insertions(+), 65 deletions(-) diff --git a/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx b/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx index 29d92371cd29..37fbe01aae63 100644 --- a/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx +++ b/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx @@ -23,6 +23,7 @@ import { isResolvedConciergeCategoryOptions, isResolvedConciergeDescriptionOptions, } from '@libs/ReportActionsUtils'; +import type {CreateDraftTransactionParams} from '@libs/ReportUtils'; import {createDraftTransactionAndNavigateToParticipantSelector} from '@libs/ReportUtils'; import shouldRenderAddPaymentCard from '@libs/shouldRenderAppPaymentCard'; import {dismissTrackExpenseActionableWhisper, resolveConciergeCategoryOptions, resolveConciergeDescriptionOptions} from '@userActions/Report'; @@ -153,74 +154,39 @@ function ChatActionableButtons({action, report, originalReport, reportID, origin if (isActionableTrackExpense(action)) { const reportActionReportID = originalReportID ?? reportID; - const options = [ - { - text: 'actionableMentionTrackExpense.submit', - key: `${action.reportActionID}-actionableMentionTrackExpense-submit`, - onPress: () => { - createDraftTransactionAndNavigateToParticipantSelector({ - reportID: reportActionReportID, - actionName: CONST.IOU.ACTION.SUBMIT, - reportActionID: action.reportActionID, - introSelected, - draftTransactionIDs, - activePolicy, - userBillingGracePeriodEnds, - amountOwed, - ownerBillingGracePeriodEnd, - isRestrictedToPreferredPolicy, - preferredPolicyID, - transaction: trackExpenseTransaction, - currentUserAccountID: personalDetail.accountID, - currentUserEmail: personalDetail.email ?? '', - }); - }, + const baseDraftTransactionParams = { + reportID: reportActionReportID, + reportActionID: action.reportActionID, + introSelected, + draftTransactionIDs, + activePolicy, + userBillingGracePeriodEnds, + amountOwed, + ownerBillingGracePeriodEnd, + transaction: trackExpenseTransaction, + currentUserAccountID: personalDetail.accountID, + currentUserEmail: personalDetail.email ?? '', + }; + const TRACK_EXPENSE_ACTIONS = { + submit: CONST.IOU.ACTION.SUBMIT, + categorize: CONST.IOU.ACTION.CATEGORIZE, + share: CONST.IOU.ACTION.SHARE, + } as const; + const prepareTrackExpenseButton = (actionKey: keyof typeof TRACK_EXPENSE_ACTIONS, extraParams?: Partial) => ({ + text: `actionableMentionTrackExpense.${actionKey}`, + key: `${action.reportActionID}-actionableMentionTrackExpense-${actionKey}`, + onPress: () => { + createDraftTransactionAndNavigateToParticipantSelector({ + ...baseDraftTransactionParams, + ...extraParams, + actionName: TRACK_EXPENSE_ACTIONS[actionKey], + }); }, - ]; + }); + const options = [prepareTrackExpenseButton('submit', {isRestrictedToPreferredPolicy, preferredPolicyID})]; if (Permissions.canUseTrackFlows()) { - options.push( - { - text: 'actionableMentionTrackExpense.categorize', - key: `${action.reportActionID}-actionableMentionTrackExpense-categorize`, - onPress: () => { - createDraftTransactionAndNavigateToParticipantSelector({ - reportID: reportActionReportID, - actionName: CONST.IOU.ACTION.CATEGORIZE, - reportActionID: action.reportActionID, - introSelected, - draftTransactionIDs, - activePolicy, - userBillingGracePeriodEnds, - amountOwed, - ownerBillingGracePeriodEnd, - transaction: trackExpenseTransaction, - currentUserAccountID: personalDetail.accountID, - currentUserEmail: personalDetail.email ?? '', - }); - }, - }, - { - text: 'actionableMentionTrackExpense.share', - key: `${action.reportActionID}-actionableMentionTrackExpense-share`, - onPress: () => { - createDraftTransactionAndNavigateToParticipantSelector({ - reportID: reportActionReportID, - actionName: CONST.IOU.ACTION.SHARE, - reportActionID: action.reportActionID, - introSelected, - draftTransactionIDs, - activePolicy, - userBillingGracePeriodEnds, - amountOwed, - ownerBillingGracePeriodEnd, - transaction: trackExpenseTransaction, - currentUserAccountID: personalDetail.accountID, - currentUserEmail: personalDetail.email ?? '', - }); - }, - }, - ); + options.push(prepareTrackExpenseButton('categorize'), prepareTrackExpenseButton('share')); } options.push({ text: 'actionableMentionTrackExpense.nothing',