From 188bae84a659dbdca87499dd42fe424a9cbb4121 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 18 Feb 2026 13:41:54 -0700 Subject: [PATCH 1/7] Left reference up one level --- src/libs/actions/ReportActions.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index 739e87d7625c..c1659a7bd067 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -26,9 +26,7 @@ Onyx.connect({ }, }); -function clearReportActionErrors(reportID: string, reportAction: ReportAction, keys?: string[]) { - const originalReportID = getOriginalReportID(reportID, reportAction, undefined); - +function clearReportActionErrors(reportID: string, reportAction: ReportAction, originalReportID: string | undefined, keys?: string[]) { if (!reportAction?.reportActionID) { return; } @@ -86,7 +84,8 @@ function clearAllRelatedReportActionErrors(reportID: string | undefined, reportA return; } - clearReportActionErrors(reportID, reportAction, keys); + const originalReportID = getOriginalReportID(reportID, reportAction, undefined); + clearReportActionErrors(reportID, reportAction, originalReportID, keys); const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (report?.parentReportID && report?.parentReportActionID && ignore !== 'parent') { From e31ea59bf26a2a851ccd3c3b063a2c18f6d27eeb Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 18 Feb 2026 14:41:56 -0700 Subject: [PATCH 2/7] Lift up originalReportID higher --- .../MoneyRequestReceiptView.tsx | 10 +++- src/libs/actions/IOU/index.ts | 5 +- src/libs/actions/ReportActions.ts | 10 ++-- .../inbox/report/PureReportActionItem.tsx | 59 ++++++++----------- 4 files changed, 41 insertions(+), 43 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx index 7210c76e0d38..9cdaa53d9ff5 100644 --- a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx +++ b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx @@ -27,6 +27,7 @@ import { canEditMoneyRequest, canUserPerformWriteAction as canUserPerformWriteActionReportUtils, getCreationReportErrors, + getOriginalReportID, isInvoiceReport, isPaidGroupPolicy, isTrackExpenseReportNew, @@ -51,6 +52,7 @@ import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {TransactionPendingFieldsKey} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import useOriginalReportID from '@hooks/useOriginalReportID'; import ReportActionItemImage from './ReportActionItemImage'; type MoneyRequestReceiptViewProps = { @@ -113,6 +115,7 @@ function MoneyRequestReceiptView({ const [isLoading, setIsLoading] = useState(true); const parentReportAction = report?.parentReportActionID ? parentReportActions?.[report.parentReportActionID] : undefined; + const originalReportID = useOriginalReportID(report?.reportID, parentReportAction); const {iouReport, chatReport: chatIOUReport, isChatIOUReportArchived} = useGetIOUReportFromReportAction(parentReportAction); const isTrackExpense = !mergeTransactionID && isTrackExpenseReportNew(report, parentReport, parentReportAction); const moneyRequestReport = parentReport; @@ -267,16 +270,17 @@ function MoneyRequestReceiptView({ return; } if (parentReportAction) { - cleanUpMoneyRequest(transaction?.transactionID ?? linkedTransactionID, parentReportAction, report.reportID, iouReport, chatIOUReport, isChatIOUReportArchived, true); + cleanUpMoneyRequest(transaction?.transactionID ?? linkedTransactionID, parentReportAction, report.reportID, iouReport, chatIOUReport, isChatIOUReportArchived, true, originalReportID); return; } } + if (!transaction?.transactionID) { if (!linkedTransactionID) { return; } clearError(linkedTransactionID); - clearAllRelatedReportActionErrors(report.reportID, parentReportAction); + clearAllRelatedReportActionErrors(report.reportID, parentReportAction, originalReportID); return; } if (!isEmptyObject(transactionAndReportActionErrors)) { @@ -284,7 +288,7 @@ function MoneyRequestReceiptView({ } if (!isEmptyObject(errorsWithoutReportCreation)) { clearError(transaction.transactionID); - clearAllRelatedReportActionErrors(report.reportID, parentReportAction); + clearAllRelatedReportActionErrors(report.reportID, parentReportAction, originalReportID); } if (!isEmptyObject(reportCreationError)) { if (isInNarrowPaneModal) { diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index a2aeb57857fb..2363ae27f269 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -8843,6 +8843,7 @@ function cleanUpMoneyRequest( chatReport: OnyxEntry, isChatIOUReportArchived: boolean | undefined, isSingleTransactionView = false, + originalReportID: string | undefined, ) { const {shouldDeleteTransactionThread, shouldDeleteIOUReport, updatedReportAction, updatedIOUReport, updatedReportPreviewAction, transactionThreadID, reportPreviewAction} = prepareToCleanUpMoneyRequest(transactionID, reportAction, iouReport, chatReport, isChatIOUReportArchived, false); @@ -8999,7 +9000,7 @@ function cleanUpMoneyRequest( } if (!shouldDeleteIOUReport) { - clearAllRelatedReportActionErrors(reportID, reportAction); + clearAllRelatedReportActionErrors(reportID, reportAction, originalReportID); } // First, update the reportActions to ensure related actions are not displayed. @@ -9008,7 +9009,7 @@ function cleanUpMoneyRequest( // eslint-disable-next-line @typescript-eslint/no-deprecated InteractionManager.runAfterInteractions(() => { if (shouldDeleteIOUReport) { - clearAllRelatedReportActionErrors(reportID, reportAction); + clearAllRelatedReportActionErrors(reportID, reportAction, originalReportID); } // After navigation, update the remaining data. Onyx.update(onyxUpdates); diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index c1659a7bd067..b210596356f8 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -78,28 +78,30 @@ function clearReportActionErrors(reportID: string, reportAction: ReportAction, o ignore: `undefined` means we want to check both parent and children report actions ignore: `parent` or `child` means we want to ignore checking parent or child report actions because they've been previously checked */ -function clearAllRelatedReportActionErrors(reportID: string | undefined, reportAction: ReportAction | null | undefined, ignore?: IgnoreDirection, keys?: string[]) { +function clearAllRelatedReportActionErrors(reportID: string | undefined, reportAction: ReportAction | null | undefined, originalReportID: string | undefined, ignore?: IgnoreDirection, keys?: string[]) { const errorKeys = keys ?? Object.keys(reportAction?.errors ?? {}); if (!reportAction || errorKeys.length === 0 || !reportID) { return; } - const originalReportID = getOriginalReportID(reportID, reportAction, undefined); clearReportActionErrors(reportID, reportAction, originalReportID, keys); const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (report?.parentReportID && report?.parentReportActionID && ignore !== 'parent') { const parentReportAction = getReportAction(report.parentReportID, report.parentReportActionID); const parentErrorKeys = Object.keys(parentReportAction?.errors ?? {}).filter((err) => errorKeys.includes(err)); + const parentReportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`] ?? {}; + const parentOriginalReportID = getOriginalReportID(report.parentReportID, parentReportAction, parentReportActions); - clearAllRelatedReportActionErrors(report.parentReportID, parentReportAction, 'child', parentErrorKeys); + clearAllRelatedReportActionErrors(report.parentReportID, parentReportAction, parentOriginalReportID, 'child', parentErrorKeys); } if (reportAction.childReportID && ignore !== 'child') { const childActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportAction.childReportID}`] ?? {}; for (const action of Object.values(childActions)) { const childErrorKeys = Object.keys(action.errors ?? {}).filter((err) => errorKeys.includes(err)); - clearAllRelatedReportActionErrors(reportAction.childReportID, action, 'parent', childErrorKeys); + const childOriginalReportID = getOriginalReportID(reportAction.childReportID, action, childActions); + clearAllRelatedReportActionErrors(reportAction.childReportID, action, childOriginalReportID, 'parent', childErrorKeys); } } } diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index bf803efb60e1..7e109f7db019 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -280,6 +280,10 @@ type PureReportActionItemProps = { /** The transaction thread report associated with the report for this action, if any */ transactionThreadReport?: OnyxEntry; + /** Array of report actions for the report for this action */ + // eslint-disable-next-line react/no-unused-prop-types + reportActions: OnyxTypes.ReportAction[]; + /** Report action belonging to the report's parent */ parentReportAction: OnyxEntry; @@ -358,7 +362,7 @@ type PureReportActionItemProps = { /** 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 */ + /** ID of the original report from which the given reportAction is first created. See the method docs for getOriginalReportID() for more details. */ originalReportID?: string; /** Original report from which the given reportAction is first created */ @@ -435,7 +439,7 @@ type PureReportActionItemProps = { clearError?: (transactionID: string) => void; /** Function to clear all errors from a report action */ - clearAllRelatedReportActionErrors?: (reportID: string | undefined, reportAction: OnyxTypes.ReportAction | null | undefined, ignore?: IgnoreDirection, keys?: string[]) => void; + clearAllRelatedReportActionErrors?: (reportID: string | undefined, reportAction: ReportAction | null | undefined, originalReportID: string | undefined, ignore?: IgnoreDirection, keys?: string[]) => void; /** Function to dismiss the actionable whisper for tracking expenses */ dismissTrackExpenseActionableWhisper?: (reportID: string | undefined, reportAction: OnyxEntry) => void; @@ -542,12 +546,11 @@ function PureReportActionItem({ reportMetadata, }: PureReportActionItemProps) { const {transitionActionSheetState} = ActionSheetAwareScrollView.useActionSheetAwareScrollViewActions(); - const {translate, formatPhoneNumber, localeCompare, formatTravelDate, getLocalDateFromDatetime, datetimeToCalendarTime} = useLocalize(); + const {translate, formatPhoneNumber, localeCompare, formatTravelDate, getLocalDateFromDatetime} = useLocalize(); const {showConfirmModal} = useConfirmModal(); const personalDetail = useCurrentUserPersonalDetails(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const reportID = report?.reportID ?? action?.reportID; - const childReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${action.childReportID}`]; const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -612,14 +615,14 @@ function PureReportActionItem({ const transactionID = isMoneyRequestAction(action) ? getOriginalMessage(action)?.IOUTransactionID : undefined; if (isSendingMoney && transactionID && reportID) { const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`]; - cleanUpMoneyRequest(transactionID, action, reportID, report, chatReport, undefined, true); + cleanUpMoneyRequest(transactionID, action, reportID, report, chatReport, undefined, true, originalReportID); return; } if (transactionID) { clearError(transactionID); } - clearAllRelatedReportActionErrors(reportID, action); - }, [action, isSendingMoney, clearAllRelatedReportActionErrors, reportID, allReports, report, clearError]); + clearAllRelatedReportActionErrors(reportID, action, originalReportID); + }, [action, isSendingMoney, clearAllRelatedReportActionErrors, reportID, allReports, report, clearError, originalReportID]); const showDismissReceiptErrorModal = useCallback(async () => { const result = await showConfirmModal({ @@ -823,6 +826,7 @@ function PureReportActionItem({ () => ({ anchor: popoverAnchorRef.current, report, + reportActions: reportActionsFromOnyx, isReportArchived, action, transactionThreadReport, @@ -831,7 +835,7 @@ function PureReportActionItem({ isDisabled: false, shouldDisplayContextMenu, }), - [report, action, toggleContextMenuFromActiveReportAction, transactionThreadReport, handleShowContextMenu, shouldDisplayContextMenu, isReportArchived], + [report, reportActionsFromOnyx, action, toggleContextMenuFromActiveReportAction, transactionThreadReport, handleShowContextMenu, shouldDisplayContextMenu, isReportArchived], ); const attachmentContextValue = useMemo(() => { @@ -875,7 +879,7 @@ function PureReportActionItem({ 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); }, })); } @@ -898,7 +902,7 @@ function PureReportActionItem({ 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); }, })); } @@ -911,7 +915,7 @@ function PureReportActionItem({ shouldUseLocalization: false, key: `${action.reportActionID}-followup-${followup.text}`, onPress: () => { - resolveSuggestedFollowup(reportActionReport, reportID, action, followup, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); + resolveSuggestedFollowup(reportActionReport, reportID, action, followup, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE); }, })); } @@ -1225,6 +1229,7 @@ function PureReportActionItem({ contextMenuAnchor={popoverAnchorRef.current} isHovered={hovered} isWhisper={isWhisper} + isInvoice={action.childType === CONST.REPORT.CHAT_TYPE.INVOICE} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} onPaymentOptionsShow={() => setIsPaymentMethodPopoverActive(true)} onPaymentOptionsHide={() => setIsPaymentMethodPopoverActive(false)} @@ -1305,8 +1310,7 @@ function PureReportActionItem({ ); } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED) || isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED) || isMarkAsClosedAction(action)) { @@ -1319,14 +1323,13 @@ function PureReportActionItem({ ); } else if (hasPendingDEWSubmit(reportMetadata, isDEWPolicy) && isPendingAdd) { children = ; } else { - children = ; + children = ; } } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.APPROVED)) { const wasAutoApproved = getOriginalMessage(action)?.automaticAction ?? false; @@ -1616,7 +1619,6 @@ function PureReportActionItem({ )} @@ -1624,13 +1626,12 @@ function PureReportActionItem({ } else if (isActionableJoinRequest(action)) { children = ( - + {actionableItemButtons.length > 0 && ( )} @@ -1640,12 +1641,10 @@ function PureReportActionItem({ } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.DEMOTED_FROM_WORKSPACE)) { children = ; } else if (isCardIssuedAction(action)) { - const shouldNavigateToCardDetails = isPolicyAdmin(policy); children = ( ); } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_INTEGRATION)) { @@ -1744,7 +1743,6 @@ function PureReportActionItem({ items={actionableItemButtons} shouldUseLocalization layout="vertical" - isBackgroundHovered={hovered} /> )} @@ -1820,9 +1818,9 @@ function PureReportActionItem({ } shouldUseLocalization={!isConciergeOptions && !actionContainsFollowUps} primaryTextNumberOfLines={actionableButtonsNoLines} - isBackgroundHovered={hovered} styles={{ - text: [styles.textAlignLeft, styles.breakWord], + text: [isConciergeOptions || actionContainsFollowUps ? styles.textAlignLeft : undefined], + button: actionContainsFollowUps ? [styles.actionableItemButton, hovered && styles.actionableItemButtonBackgroundHovered] : undefined, container: [ actionContainsFollowUps && shouldUseNarrowLayout ? styles.alignItemsStretch : undefined, actionContainsFollowUps ? styles.mt5 : undefined, @@ -2045,12 +2043,6 @@ function PureReportActionItem({ ); }; - // Calculating accessibilityLabel for chat message with sender, date and time and the message content. - const displayName = getDisplayNameOrDefault(personalDetails?.[action.actorAccountID ?? CONST.DEFAULT_NUMBER_ID]); - const formattedTimestamp = datetimeToCalendarTime(action.created, false); - const plainMessage = getReportActionText(action); - const accessibilityLabel = `${displayName}, ${formattedTimestamp}, ${plainMessage}`; - return ( {shouldShowCreatedAction && createdActionContent} @@ -2071,9 +2063,8 @@ function PureReportActionItem({ onSecondaryInteraction={showPopover} preventDefaultContextMenu={draftMessage === undefined && !hasErrors} withoutFocusOnSecondaryInteraction - accessibilityLabel={accessibilityLabel} - accessibilityHint={translate('accessibilityHints.chatMessage')} - accessibilityRole={CONST.ROLE.BUTTON} + accessibilityLabel={translate('accessibilityHints.chatMessage')} + accessible sentryLabel={CONST.SENTRY_LABEL.REPORT.PURE_REPORT_ACTION_ITEM} > { prevProps.report?.description === nextProps.report?.description && isCompletedTaskReport(prevProps.report) === isCompletedTaskReport(nextProps.report) && prevProps.report?.managerID === nextProps.report?.managerID && - prevProps.index === nextProps.index && prevProps.shouldHideThreadDividerLine === nextProps.shouldHideThreadDividerLine && prevProps.report?.total === nextProps.report?.total && prevProps.report?.nonReimbursableTotal === nextProps.report?.nonReimbursableTotal && @@ -2200,6 +2190,7 @@ export default memo(PureReportActionItem, (prevProps, nextProps) => { prevProps.linkedReportActionID === nextProps.linkedReportActionID && deepEqual(prevProps.report?.fieldList, nextProps.report?.fieldList) && deepEqual(prevProps.transactionThreadReport, nextProps.transactionThreadReport) && + deepEqual(prevProps.reportActions, nextProps.reportActions) && deepEqual(prevParentReportAction, nextParentReportAction) && prevProps.draftMessage === nextProps.draftMessage && prevProps.iouReport?.reportID === nextProps.iouReport?.reportID && From 388d9a9a89a0694b373ad71cceef5a8114c7926b Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 18 Feb 2026 14:54:28 -0700 Subject: [PATCH 3/7] Revert a bunch of random changes --- .../inbox/report/PureReportActionItem.tsx | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index 7e109f7db019..bf803efb60e1 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -280,10 +280,6 @@ type PureReportActionItemProps = { /** The transaction thread report associated with the report for this action, if any */ transactionThreadReport?: OnyxEntry; - /** Array of report actions for the report for this action */ - // eslint-disable-next-line react/no-unused-prop-types - reportActions: OnyxTypes.ReportAction[]; - /** Report action belonging to the report's parent */ parentReportAction: OnyxEntry; @@ -362,7 +358,7 @@ type PureReportActionItemProps = { /** 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. See the method docs for getOriginalReportID() for more details. */ + /** ID of the original report from which the given reportAction is first created */ originalReportID?: string; /** Original report from which the given reportAction is first created */ @@ -439,7 +435,7 @@ type PureReportActionItemProps = { clearError?: (transactionID: string) => void; /** Function to clear all errors from a report action */ - clearAllRelatedReportActionErrors?: (reportID: string | undefined, reportAction: ReportAction | null | undefined, originalReportID: string | undefined, ignore?: IgnoreDirection, keys?: string[]) => void; + clearAllRelatedReportActionErrors?: (reportID: string | undefined, reportAction: OnyxTypes.ReportAction | null | undefined, ignore?: IgnoreDirection, keys?: string[]) => void; /** Function to dismiss the actionable whisper for tracking expenses */ dismissTrackExpenseActionableWhisper?: (reportID: string | undefined, reportAction: OnyxEntry) => void; @@ -546,11 +542,12 @@ function PureReportActionItem({ reportMetadata, }: PureReportActionItemProps) { const {transitionActionSheetState} = ActionSheetAwareScrollView.useActionSheetAwareScrollViewActions(); - const {translate, formatPhoneNumber, localeCompare, formatTravelDate, getLocalDateFromDatetime} = useLocalize(); + const {translate, formatPhoneNumber, localeCompare, formatTravelDate, getLocalDateFromDatetime, datetimeToCalendarTime} = useLocalize(); const {showConfirmModal} = useConfirmModal(); const personalDetail = useCurrentUserPersonalDetails(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const reportID = report?.reportID ?? action?.reportID; + const childReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${action.childReportID}`]; const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -615,14 +612,14 @@ function PureReportActionItem({ const transactionID = isMoneyRequestAction(action) ? getOriginalMessage(action)?.IOUTransactionID : undefined; if (isSendingMoney && transactionID && reportID) { const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`]; - cleanUpMoneyRequest(transactionID, action, reportID, report, chatReport, undefined, true, originalReportID); + cleanUpMoneyRequest(transactionID, action, reportID, report, chatReport, undefined, true); return; } if (transactionID) { clearError(transactionID); } - clearAllRelatedReportActionErrors(reportID, action, originalReportID); - }, [action, isSendingMoney, clearAllRelatedReportActionErrors, reportID, allReports, report, clearError, originalReportID]); + clearAllRelatedReportActionErrors(reportID, action); + }, [action, isSendingMoney, clearAllRelatedReportActionErrors, reportID, allReports, report, clearError]); const showDismissReceiptErrorModal = useCallback(async () => { const result = await showConfirmModal({ @@ -826,7 +823,6 @@ function PureReportActionItem({ () => ({ anchor: popoverAnchorRef.current, report, - reportActions: reportActionsFromOnyx, isReportArchived, action, transactionThreadReport, @@ -835,7 +831,7 @@ function PureReportActionItem({ isDisabled: false, shouldDisplayContextMenu, }), - [report, reportActionsFromOnyx, action, toggleContextMenuFromActiveReportAction, transactionThreadReport, handleShowContextMenu, shouldDisplayContextMenu, isReportArchived], + [report, action, toggleContextMenuFromActiveReportAction, transactionThreadReport, handleShowContextMenu, shouldDisplayContextMenu, isReportArchived], ); const attachmentContextValue = useMemo(() => { @@ -879,7 +875,7 @@ function PureReportActionItem({ text: `${i + 1} - ${option}`, key: `${action.reportActionID}-conciergeCategoryOptions-${option}`, onPress: () => { - resolveConciergeCategoryOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE); + resolveConciergeCategoryOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); }, })); } @@ -902,7 +898,7 @@ function PureReportActionItem({ text: `${i + 1} - ${option}`, key: `${action.reportActionID}-conciergeDescriptionOptions-${option}`, onPress: () => { - resolveConciergeDescriptionOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE); + resolveConciergeDescriptionOptions(reportActionReport, reportID, action.reportActionID, option, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); }, })); } @@ -915,7 +911,7 @@ function PureReportActionItem({ shouldUseLocalization: false, key: `${action.reportActionID}-followup-${followup.text}`, onPress: () => { - resolveSuggestedFollowup(reportActionReport, reportID, action, followup, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE); + resolveSuggestedFollowup(reportActionReport, reportID, action, followup, personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, currentUserAccountID); }, })); } @@ -1229,7 +1225,6 @@ function PureReportActionItem({ contextMenuAnchor={popoverAnchorRef.current} isHovered={hovered} isWhisper={isWhisper} - isInvoice={action.childType === CONST.REPORT.CHAT_TYPE.INVOICE} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} onPaymentOptionsShow={() => setIsPaymentMethodPopoverActive(true)} onPaymentOptionsHide={() => setIsPaymentMethodPopoverActive(false)} @@ -1310,7 +1305,8 @@ function PureReportActionItem({ ); } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED) || isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED) || isMarkAsClosedAction(action)) { @@ -1323,13 +1319,14 @@ function PureReportActionItem({ ); } else if (hasPendingDEWSubmit(reportMetadata, isDEWPolicy) && isPendingAdd) { children = ; } else { - children = ; + children = ; } } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.APPROVED)) { const wasAutoApproved = getOriginalMessage(action)?.automaticAction ?? false; @@ -1619,6 +1616,7 @@ function PureReportActionItem({ )} @@ -1626,12 +1624,13 @@ function PureReportActionItem({ } else if (isActionableJoinRequest(action)) { children = ( - + {actionableItemButtons.length > 0 && ( )} @@ -1641,10 +1640,12 @@ function PureReportActionItem({ } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.DEMOTED_FROM_WORKSPACE)) { children = ; } else if (isCardIssuedAction(action)) { + const shouldNavigateToCardDetails = isPolicyAdmin(policy); children = ( ); } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_INTEGRATION)) { @@ -1743,6 +1744,7 @@ function PureReportActionItem({ items={actionableItemButtons} shouldUseLocalization layout="vertical" + isBackgroundHovered={hovered} /> )} @@ -1818,9 +1820,9 @@ function PureReportActionItem({ } shouldUseLocalization={!isConciergeOptions && !actionContainsFollowUps} primaryTextNumberOfLines={actionableButtonsNoLines} + isBackgroundHovered={hovered} styles={{ - text: [isConciergeOptions || actionContainsFollowUps ? styles.textAlignLeft : undefined], - button: actionContainsFollowUps ? [styles.actionableItemButton, hovered && styles.actionableItemButtonBackgroundHovered] : undefined, + text: [styles.textAlignLeft, styles.breakWord], container: [ actionContainsFollowUps && shouldUseNarrowLayout ? styles.alignItemsStretch : undefined, actionContainsFollowUps ? styles.mt5 : undefined, @@ -2043,6 +2045,12 @@ function PureReportActionItem({ ); }; + // Calculating accessibilityLabel for chat message with sender, date and time and the message content. + const displayName = getDisplayNameOrDefault(personalDetails?.[action.actorAccountID ?? CONST.DEFAULT_NUMBER_ID]); + const formattedTimestamp = datetimeToCalendarTime(action.created, false); + const plainMessage = getReportActionText(action); + const accessibilityLabel = `${displayName}, ${formattedTimestamp}, ${plainMessage}`; + return ( {shouldShowCreatedAction && createdActionContent} @@ -2063,8 +2071,9 @@ function PureReportActionItem({ onSecondaryInteraction={showPopover} preventDefaultContextMenu={draftMessage === undefined && !hasErrors} withoutFocusOnSecondaryInteraction - accessibilityLabel={translate('accessibilityHints.chatMessage')} - accessible + accessibilityLabel={accessibilityLabel} + accessibilityHint={translate('accessibilityHints.chatMessage')} + accessibilityRole={CONST.ROLE.BUTTON} sentryLabel={CONST.SENTRY_LABEL.REPORT.PURE_REPORT_ACTION_ITEM} > { prevProps.report?.description === nextProps.report?.description && isCompletedTaskReport(prevProps.report) === isCompletedTaskReport(nextProps.report) && prevProps.report?.managerID === nextProps.report?.managerID && + prevProps.index === nextProps.index && prevProps.shouldHideThreadDividerLine === nextProps.shouldHideThreadDividerLine && prevProps.report?.total === nextProps.report?.total && prevProps.report?.nonReimbursableTotal === nextProps.report?.nonReimbursableTotal && @@ -2190,7 +2200,6 @@ export default memo(PureReportActionItem, (prevProps, nextProps) => { prevProps.linkedReportActionID === nextProps.linkedReportActionID && deepEqual(prevProps.report?.fieldList, nextProps.report?.fieldList) && deepEqual(prevProps.transactionThreadReport, nextProps.transactionThreadReport) && - deepEqual(prevProps.reportActions, nextProps.reportActions) && deepEqual(prevParentReportAction, nextParentReportAction) && prevProps.draftMessage === nextProps.draftMessage && prevProps.iouReport?.reportID === nextProps.iouReport?.reportID && From f1257f4d0345b9787aafe9bbf4e61be0ac6f9707 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 18 Feb 2026 14:56:11 -0700 Subject: [PATCH 4/7] Pass original report ID --- src/pages/inbox/report/PureReportActionItem.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index bf803efb60e1..a6d3b8921375 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -435,7 +435,7 @@ type PureReportActionItemProps = { clearError?: (transactionID: string) => void; /** Function to clear all errors from a report action */ - clearAllRelatedReportActionErrors?: (reportID: string | undefined, reportAction: OnyxTypes.ReportAction | null | undefined, ignore?: IgnoreDirection, keys?: string[]) => void; + clearAllRelatedReportActionErrors?: (reportID: string | undefined, reportAction: ReportAction | null | undefined, originalReportID: string | undefined, ignore?: IgnoreDirection, keys?: string[]) => void; /** Function to dismiss the actionable whisper for tracking expenses */ dismissTrackExpenseActionableWhisper?: (reportID: string | undefined, reportAction: OnyxEntry) => void; @@ -612,14 +612,14 @@ function PureReportActionItem({ const transactionID = isMoneyRequestAction(action) ? getOriginalMessage(action)?.IOUTransactionID : undefined; if (isSendingMoney && transactionID && reportID) { const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`]; - cleanUpMoneyRequest(transactionID, action, reportID, report, chatReport, undefined, true); + cleanUpMoneyRequest(transactionID, action, reportID, report, chatReport, undefined, true, originalReportID); return; } if (transactionID) { clearError(transactionID); } - clearAllRelatedReportActionErrors(reportID, action); - }, [action, isSendingMoney, clearAllRelatedReportActionErrors, reportID, allReports, report, clearError]); + clearAllRelatedReportActionErrors(reportID, action, originalReportID); + }, [action, isSendingMoney, clearAllRelatedReportActionErrors, reportID, allReports, report, clearError, originalReportID]); const showDismissReceiptErrorModal = useCallback(async () => { const result = await showConfirmModal({ From 04cd5557a10fde62ee81db9af973366470e5d9a4 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 18 Feb 2026 15:12:16 -0700 Subject: [PATCH 5/7] Fix errors --- .../ReportActionItem/MoneyRequestReceiptView.tsx | 12 ++++++++++-- src/libs/actions/IOU/index.ts | 2 +- src/pages/inbox/report/PureReportActionItem.tsx | 10 ++++++++-- tests/actions/IOUTest.ts | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx index 9cdaa53d9ff5..65223e596730 100644 --- a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx +++ b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx @@ -27,7 +27,6 @@ import { canEditMoneyRequest, canUserPerformWriteAction as canUserPerformWriteActionReportUtils, getCreationReportErrors, - getOriginalReportID, isInvoiceReport, isPaidGroupPolicy, isTrackExpenseReportNew, @@ -270,7 +269,16 @@ function MoneyRequestReceiptView({ return; } if (parentReportAction) { - cleanUpMoneyRequest(transaction?.transactionID ?? linkedTransactionID, parentReportAction, report.reportID, iouReport, chatIOUReport, isChatIOUReportArchived, true, originalReportID); + cleanUpMoneyRequest( + transaction?.transactionID ?? linkedTransactionID, + parentReportAction, + report.reportID, + iouReport, + chatIOUReport, + isChatIOUReportArchived, + originalReportID, + true, + ); return; } } diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index 2363ae27f269..8101be227246 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -8842,8 +8842,8 @@ function cleanUpMoneyRequest( iouReport: OnyxEntry, chatReport: OnyxEntry, isChatIOUReportArchived: boolean | undefined, - isSingleTransactionView = false, originalReportID: string | undefined, + isSingleTransactionView = false, ) { const {shouldDeleteTransactionThread, shouldDeleteIOUReport, updatedReportAction, updatedIOUReport, updatedReportPreviewAction, transactionThreadID, reportPreviewAction} = prepareToCleanUpMoneyRequest(transactionID, reportAction, iouReport, chatReport, isChatIOUReportArchived, false); diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index a6d3b8921375..759cec8412e3 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -435,7 +435,13 @@ type PureReportActionItemProps = { clearError?: (transactionID: string) => void; /** Function to clear all errors from a report action */ - clearAllRelatedReportActionErrors?: (reportID: string | undefined, reportAction: ReportAction | null | undefined, originalReportID: string | undefined, ignore?: IgnoreDirection, keys?: string[]) => void; + clearAllRelatedReportActionErrors?: ( + reportID: string | undefined, + reportAction: OnyxTypes.ReportAction | null | undefined, + originalReportID: string | undefined, + ignore?: IgnoreDirection, + keys?: string[], + ) => void; /** Function to dismiss the actionable whisper for tracking expenses */ dismissTrackExpenseActionableWhisper?: (reportID: string | undefined, reportAction: OnyxEntry) => void; @@ -612,7 +618,7 @@ function PureReportActionItem({ const transactionID = isMoneyRequestAction(action) ? getOriginalMessage(action)?.IOUTransactionID : undefined; if (isSendingMoney && transactionID && reportID) { const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`]; - cleanUpMoneyRequest(transactionID, action, reportID, report, chatReport, undefined, true, originalReportID); + cleanUpMoneyRequest(transactionID, action, reportID, report, chatReport, undefined, originalReportID, true); return; } if (transactionID) { diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index b60de7b26c9a..2d633f18474e 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -2551,7 +2551,7 @@ describe('actions/IOU', () => { () => new Promise((resolve) => { if (iouReportID) { - clearAllRelatedReportActionErrors(iouReportID, iouAction ?? null); + clearAllRelatedReportActionErrors(iouReportID, iouAction ?? null, undefined); } resolve(); }), From 7c5d0a13d87ad0bf411061b8655b3a92b23558aa Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 18 Feb 2026 15:17:38 -0700 Subject: [PATCH 6/7] Style --- src/libs/actions/ReportActions.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index b210596356f8..39529ea60884 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -78,7 +78,13 @@ function clearReportActionErrors(reportID: string, reportAction: ReportAction, o ignore: `undefined` means we want to check both parent and children report actions ignore: `parent` or `child` means we want to ignore checking parent or child report actions because they've been previously checked */ -function clearAllRelatedReportActionErrors(reportID: string | undefined, reportAction: ReportAction | null | undefined, originalReportID: string | undefined, ignore?: IgnoreDirection, keys?: string[]) { +function clearAllRelatedReportActionErrors( + reportID: string | undefined, + reportAction: ReportAction | null | undefined, + originalReportID: string | undefined, + ignore?: IgnoreDirection, + keys?: string[], +) { const errorKeys = keys ?? Object.keys(reportAction?.errors ?? {}); if (!reportAction || errorKeys.length === 0 || !reportID) { return; From 22f435e99481de0089497ffc8a904112b43da7cd Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 18 Feb 2026 15:43:18 -0700 Subject: [PATCH 7/7] fix test --- tests/actions/IOUTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 2d633f18474e..70e6d95673d1 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -2551,7 +2551,7 @@ describe('actions/IOU', () => { () => new Promise((resolve) => { if (iouReportID) { - clearAllRelatedReportActionErrors(iouReportID, iouAction ?? null, undefined); + clearAllRelatedReportActionErrors(iouReportID, iouAction ?? null, iouReportID); } resolve(); }),