diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 8f59cf311d86..10c72dbd8841 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -65,7 +65,7 @@ import AnimatedEmptyStateBackground from '@pages/home/report/AnimatedEmptyStateB import {cleanUpMoneyRequest, updateMoneyRequestBillable} from '@userActions/IOU'; import {navigateToConciergeChatAndDeleteReport} from '@userActions/Report'; import {clearAllRelatedReportActionErrors} from '@userActions/ReportActions'; -import {clearError} from '@userActions/Transaction'; +import {clearError, getLastModifiedExpense, revert} from '@userActions/Transaction'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -510,6 +510,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals clearAllRelatedReportActionErrors(report.reportID, parentReportAction); return; } + revert(transaction?.transactionID ?? linkedTransactionID, getLastModifiedExpense(report?.reportID)); clearError(transaction.transactionID); clearAllRelatedReportActionErrors(report.reportID, parentReportAction); }} diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 9ec9bd5f09e7..d2f996c3e965 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -15,6 +15,7 @@ import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, RecentWaypoint, ReportAction, ReportActions, ReviewDuplicates, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; +import type {OriginalMessageModifiedExpense} from '@src/types/onyx/OriginalMessage'; import type {OnyxData} from '@src/types/onyx/Request'; import type {WaypointCollection} from '@src/types/onyx/Transaction'; import type TransactionState from '@src/types/utils/TransactionStateType'; @@ -476,6 +477,23 @@ function clearError(transactionID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {errors: null, errorFields: {route: null, waypoints: null, routes: null}}); } +function getLastModifiedExpense(reportID?: string): OriginalMessageModifiedExpense | undefined { + const modifiedExpenseActions = Object.values(ReportActionsUtils.getAllReportActions(reportID)).filter(ReportActionsUtils.isModifiedExpenseAction); + modifiedExpenseActions.sort((a, b) => Number(a.reportActionID) - Number(b.reportActionID)); + return ReportActionsUtils.getOriginalMessage(modifiedExpenseActions.at(-1)); +} + +function revert(transactionID?: string, originalMessage?: OriginalMessageModifiedExpense | undefined) { + const transaction = TransactionUtils.getTransaction(transactionID); + + if (transaction && originalMessage?.oldAmount && originalMessage.oldCurrency && 'amount' in originalMessage && 'currency' in originalMessage) { + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, { + modifiedAmount: transaction?.amount && transaction?.amount < 0 ? -Math.abs(originalMessage.oldAmount) : originalMessage.oldAmount, + modifiedCurrency: originalMessage.oldCurrency, + }); + } +} + function markAsCash(transactionID: string | undefined, transactionThreadReportID: string | undefined) { if (!transactionID || !transactionThreadReportID) { return; @@ -571,4 +589,6 @@ export { sanitizeRecentWaypoints, getAllTransactionViolationsLength, getAllTransactions, + getLastModifiedExpense, + revert, }; diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index b809268cb58f..e29f422c8f18 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -39,27 +39,86 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import ControlSelection from '@libs/ControlSelection'; -import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import * as ErrorUtils from '@libs/ErrorUtils'; +import {canUseTouchScreen} from '@libs/DeviceCapabilities'; +import type {OnyxDataWithErrors} from '@libs/ErrorUtils'; +import {getLatestErrorMessageField} from '@libs/ErrorUtils'; import focusComposerWithDelay from '@libs/focusComposerWithDelay'; -import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; +import {formatPhoneNumber} from '@libs/LocalePhoneNumber'; import Navigation from '@libs/Navigation/Navigation'; import Permissions from '@libs/Permissions'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import * as ReportUtils from '@libs/ReportUtils'; +import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; +import {getCleanedTagName} from '@libs/PolicyUtils'; +import { + extractLinksFromMessageHtml, + getAllReportActions, + getDismissedViolationMessageText, + getIOUReportIDFromReportActionPreview, + getOriginalMessage, + getPolicyChangeLogAddEmployeeMessage, + getPolicyChangeLogChangeRoleMessage, + getPolicyChangeLogDeleteMemberMessage, + getRemovedConnectionMessage, + getRemovedFromApprovalChainMessage, + getRenamedAction, + getReportActionMessage, + getReportActionText, + getWhisperedTo, + isActionableAddPaymentCard, + isActionableJoinRequest, + isActionableMentionWhisper, + isActionableReportMentionWhisper, + isActionableTrackExpense, + isActionOfType, + isAddCommentAction, + isChronosOOOListAction, + isCreatedTaskReportAction, + isDeletedAction, + isDeletedParentAction as isDeletedParentActionUtils, + isMessageDeleted, + isModifiedExpenseAction, + isMoneyRequestAction, + isPendingRemove, + isReimbursementDeQueuedAction, + isReimbursementQueuedAction, + isRenamedAction, + isTagModificationAction, + isTaskAction, + isTripPreview, + isUnapprovedAction, + isWhisperActionTargetedToOthers, +} from '@libs/ReportActionsUtils'; +import { + canWriteInReport, + chatIncludesConcierge, + getDisplayNamesWithTooltips, + getIconsForParticipants, + getIOUApprovedMessage, + getIOUForwardedMessage, + getIOUSubmittedMessage, + getIOUUnapprovedMessage, + getReportAutomaticallyApprovedMessage, + getReportAutomaticallySubmittedMessage, + getWhisperDisplayNames, + getWorkspaceNameUpdatedMessage, + isArchivedNonExpenseReport, + isChatThread, + isCompletedTaskReport, + isReportMessageAttachment, + isTaskReport, + shouldDisplayThreadReplies as shouldDisplayThreadRepliesUtils, +} from '@libs/ReportUtils'; import type {MissingPaymentMethod} from '@libs/ReportUtils'; import SelectionScraper from '@libs/SelectionScraper'; import shouldRenderAddPaymentCard from '@libs/shouldRenderAppPaymentCard'; import {ReactionListContext} from '@pages/home/ReportScreenContext'; -import * as BankAccounts from '@userActions/BankAccounts'; -import * as EmojiPickerAction from '@userActions/EmojiPickerAction'; -import * as Member from '@userActions/Policy/Member'; -import * as Report from '@userActions/Report'; +import {openPersonalBankAccountSetupView} from '@userActions/BankAccounts'; +import {hideEmojiPicker, isActive} from '@userActions/EmojiPickerAction'; +import {acceptJoinRequest, declineJoinRequest} from '@userActions/Policy/Member'; +import {expandURLPreview} from '@userActions/Report'; import type {IgnoreDirection} from '@userActions/ReportActions'; -import * as Session from '@userActions/Session'; -import * as User from '@userActions/User'; +import {isAnonymousUser, signOutAndRedirectToSignIn} from '@userActions/Session'; +import {getLastModifiedExpense, revert} from '@userActions/Transaction'; +import {isBlockedFromConcierge} from '@userActions/User'; import CONST from '@src/CONST'; import type {IOUAction} from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -69,8 +128,8 @@ import type {JoinWorkspaceResolution} from '@src/types/onyx/OriginalMessage'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import {RestrictedReadOnlyContextMenuActions} from './ContextMenu/ContextMenuActions'; import MiniReportActionContextMenu from './ContextMenu/MiniReportActionContextMenu'; -import * as ReportActionContextMenu from './ContextMenu/ReportActionContextMenu'; -import {hideContextMenu} from './ContextMenu/ReportActionContextMenu'; +import type {ContextMenuAnchor} from './ContextMenu/ReportActionContextMenu'; +import {hideContextMenu, hideDeleteModal, isActiveReportAction, showContextMenu} from './ContextMenu/ReportActionContextMenu'; import LinkPreviewer from './LinkPreviewer'; import ReportActionItemBasicMessage from './ReportActionItemBasicMessage'; import ReportActionItemContentCreated from './ReportActionItemContentCreated'; @@ -300,7 +359,7 @@ function PureReportActionItem({ const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const [isContextMenuActive, setIsContextMenuActive] = useState(() => ReportActionContextMenu.isActiveReportAction(action.reportActionID)); + const [isContextMenuActive, setIsContextMenuActive] = useState(() => isActiveReportAction(action.reportActionID)); const [isEmojiPickerActive, setIsEmojiPickerActive] = useState(); const [isPaymentMethodPopoverActive, setIsPaymentMethodPopoverActive] = useState(); @@ -309,36 +368,32 @@ function PureReportActionItem({ const reactionListRef = useContext(ReactionListContext); const {updateHiddenAttachments} = useContext(ReportAttachmentsContext); const textInputRef = useRef(null); - const popoverAnchorRef = useRef>(null); + const popoverAnchorRef = useRef>(null); const downloadedPreviews = useRef([]); const prevDraftMessage = usePrevious(draftMessage); const isReportActionLinked = linkedReportActionID && action.reportActionID && linkedReportActionID === action.reportActionID; const reportScrollManager = useReportScrollManager(); - const isActionableWhisper = - ReportActionsUtils.isActionableMentionWhisper(action) || ReportActionsUtils.isActionableTrackExpense(action) || ReportActionsUtils.isActionableReportMentionWhisper(action); - const originalMessage = ReportActionsUtils.getOriginalMessage(action); + const isActionableWhisper = isActionableMentionWhisper(action) || isActionableTrackExpense(action) || isActionableReportMentionWhisper(action); + const originalMessage = getOriginalMessage(action); const highlightedBackgroundColorIfNeeded = useMemo( () => (isReportActionLinked ? StyleUtils.getBackgroundColorStyle(theme.messageHighlightBG) : {}), [StyleUtils, isReportActionLinked, theme.messageHighlightBG], ); - const isDeletedParentAction = ReportActionsUtils.isDeletedParentAction(action); + const isDeletedParentAction = isDeletedParentActionUtils(action); const isOriginalMessageAnObject = originalMessage && typeof originalMessage === 'object'; const hasResolutionInOriginalMessage = isOriginalMessageAnObject && 'resolution' in originalMessage; const prevActionResolution = usePrevious(isActionableWhisper && hasResolutionInOriginalMessage ? originalMessage?.resolution : null); // IOUDetails only exists when we are sending money - const isSendingMoney = - ReportActionsUtils.isMoneyRequestAction(action) && - ReportActionsUtils.getOriginalMessage(action)?.type === CONST.IOU.REPORT_ACTION_TYPE.PAY && - ReportActionsUtils.getOriginalMessage(action)?.IOUDetails; + const isSendingMoney = isMoneyRequestAction(action) && getOriginalMessage(action)?.type === CONST.IOU.REPORT_ACTION_TYPE.PAY && getOriginalMessage(action)?.IOUDetails; const updateHiddenState = useCallback( (isHiddenValue: boolean) => { setIsHidden(isHiddenValue); const message = Array.isArray(action.message) ? action.message?.at(-1) : action.message; - const isAttachment = ReportUtils.isReportMessageAttachment(message); + const isAttachment = isReportMessageAttachment(message); if (!isAttachment) { return; } @@ -347,16 +402,31 @@ function PureReportActionItem({ [action.reportActionID, action.message, updateHiddenAttachments], ); + const onClose = () => { + let transactionID; + if (isMoneyRequestAction(action)) { + transactionID = getOriginalMessage(action)?.IOUTransactionID; + revert(transactionID, getLastModifiedExpense(reportID)); + } else if (isModifiedExpenseAction(action)) { + transactionID = getOriginalMessage(Object.values(getAllReportActions(reportID)).find(isMoneyRequestAction))?.IOUTransactionID; + revert(transactionID, getOriginalMessage(action)); + } + if (transactionID) { + clearError(transactionID); + } + clearAllRelatedReportActionErrors(reportID, action); + }; + useEffect( () => () => { // ReportActionContextMenu, EmojiPicker and PopoverReactionList are global components, // we should also hide them when the current component is destroyed - if (ReportActionContextMenu.isActiveReportAction(action.reportActionID)) { - ReportActionContextMenu.hideContextMenu(); - ReportActionContextMenu.hideDeleteModal(); + if (isActiveReportAction(action.reportActionID)) { + hideContextMenu(); + hideDeleteModal(); } - if (EmojiPickerAction.isActive(action.reportActionID)) { - EmojiPickerAction.hideEmojiPicker(true); + if (isActive(action.reportActionID)) { + hideEmojiPicker(true); } if (reactionListRef?.current?.isActiveReportAction(action.reportActionID)) { reactionListRef?.current?.hideReactionList(); @@ -367,11 +437,11 @@ function PureReportActionItem({ useEffect(() => { // We need to hide EmojiPicker when this is a deleted parent action - if (!isDeletedParentAction || !EmojiPickerAction.isActive(action.reportActionID)) { + if (!isDeletedParentAction || !isActive(action.reportActionID)) { return; } - EmojiPickerAction.hideEmojiPicker(true); + hideEmojiPicker(true); }, [isDeletedParentAction, action.reportActionID]); useEffect(() => { @@ -387,17 +457,17 @@ function PureReportActionItem({ return; } - const urls = ReportActionsUtils.extractLinksFromMessageHtml(action); + const urls = extractLinksFromMessageHtml(action); if (lodashIsEqual(downloadedPreviews.current, urls) || action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { return; } downloadedPreviews.current = urls; - Report.expandURLPreview(reportID, action.reportActionID); + expandURLPreview(reportID, action.reportActionID); }, [action, reportID]); useEffect(() => { - if (draftMessage === undefined || !ReportActionsUtils.isDeletedAction(action)) { + if (draftMessage === undefined || !isDeletedAction(action)) { return; } deleteReportActionDraft(reportID, action); @@ -405,7 +475,7 @@ function PureReportActionItem({ // Hide the message if it is being moderated for a higher offense, or is hidden by a moderator // Removed messages should not be shown anyway and should not need this flow - const latestDecision = ReportActionsUtils.getReportActionMessage(action)?.moderationDecision?.decision ?? ''; + const latestDecision = getReportActionMessage(action)?.moderationDecision?.decision ?? ''; useEffect(() => { if (action.actionName !== CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT) { return; @@ -419,10 +489,7 @@ function PureReportActionItem({ } setModerationDecision(latestDecision); - if ( - ![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === latestDecision) && - !ReportActionsUtils.isPendingRemove(action) - ) { + if (![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === latestDecision) && !isPendingRemove(action)) { setIsHidden(true); return; } @@ -430,10 +497,10 @@ function PureReportActionItem({ }, [latestDecision, action]); const toggleContextMenuFromActiveReportAction = useCallback(() => { - setIsContextMenuActive(ReportActionContextMenu.isActiveReportAction(action.reportActionID)); + setIsContextMenuActive(isActiveReportAction(action.reportActionID)); }, [action.reportActionID]); - const disabledActions = useMemo(() => (!ReportUtils.canWriteInReport(report) ? RestrictedReadOnlyContextMenuActions : []), [report]); + const disabledActions = useMemo(() => (!canWriteInReport(report) ? RestrictedReadOnlyContextMenuActions : []), [report]); /** * Show the ReportActionContextMenu modal popover. @@ -449,7 +516,7 @@ function PureReportActionItem({ setIsContextMenuActive(true); const selection = SelectionScraper.getCurrentSelection(); - ReportActionContextMenu.showContextMenu( + showContextMenu( CONST.CONTEXT_MENU_TYPES.REPORT_ACTION, event, selection, @@ -522,7 +589,7 @@ function PureReportActionItem({ const mentionReportContextValue = useMemo(() => ({currentReportID: report?.reportID ?? '-1'}), [report?.reportID]); const actionableItemButtons: ActionableItem[] = useMemo(() => { - if (ReportActionsUtils.isActionableAddPaymentCard(action) && userBillingFundID === undefined && shouldRenderAddPaymentCard()) { + if (isActionableAddPaymentCard(action) && userBillingFundID === undefined && shouldRenderAddPaymentCard()) { return [ { text: 'subscription.cardSection.addCardButton', @@ -536,12 +603,12 @@ function PureReportActionItem({ ]; } - if (!isActionableWhisper && (!ReportActionsUtils.isActionableJoinRequest(action) || ReportActionsUtils.getOriginalMessage(action)?.choice !== ('' as JoinWorkspaceResolution))) { + if (!isActionableWhisper && (!isActionableJoinRequest(action) || getOriginalMessage(action)?.choice !== ('' as JoinWorkspaceResolution))) { return []; } - if (ReportActionsUtils.isActionableTrackExpense(action)) { - const transactionID = ReportActionsUtils.getOriginalMessage(action)?.transactionID; + if (isActionableTrackExpense(action)) { + const transactionID = getOriginalMessage(action)?.transactionID; return [ { text: 'actionableMentionTrackExpense.submit', @@ -578,23 +645,23 @@ function PureReportActionItem({ ]; } - if (ReportActionsUtils.isActionableJoinRequest(action)) { + if (isActionableJoinRequest(action)) { return [ { text: 'actionableMentionJoinWorkspaceOptions.accept', key: `${action.reportActionID}-actionableMentionJoinWorkspace-${CONST.REPORT.ACTIONABLE_MENTION_JOIN_WORKSPACE_RESOLUTION.ACCEPT}`, - onPress: () => Member.acceptJoinRequest(reportID, action), + onPress: () => acceptJoinRequest(reportID, action), isPrimary: true, }, { text: 'actionableMentionJoinWorkspaceOptions.decline', key: `${action.reportActionID}-actionableMentionJoinWorkspace-${CONST.REPORT.ACTIONABLE_MENTION_JOIN_WORKSPACE_RESOLUTION.DECLINE}`, - onPress: () => Member.declineJoinRequest(reportID, action), + onPress: () => declineJoinRequest(reportID, action), }, ]; } - if (ReportActionsUtils.isActionableReportMentionWhisper(action)) { + if (isActionableReportMentionWhisper(action)) { return [ { text: 'common.yes', @@ -646,19 +713,19 @@ function PureReportActionItem({ // Show the MoneyRequestPreview for when expense is present if ( - ReportActionsUtils.isMoneyRequestAction(action) && - ReportActionsUtils.getOriginalMessage(action) && + isMoneyRequestAction(action) && + getOriginalMessage(action) && // For the pay flow, we only want to show MoneyRequestAction when sending money. When paying, we display a regular system message - (ReportActionsUtils.getOriginalMessage(action)?.type === CONST.IOU.REPORT_ACTION_TYPE.CREATE || - ReportActionsUtils.getOriginalMessage(action)?.type === CONST.IOU.REPORT_ACTION_TYPE.SPLIT || - ReportActionsUtils.getOriginalMessage(action)?.type === CONST.IOU.REPORT_ACTION_TYPE.TRACK) + (getOriginalMessage(action)?.type === CONST.IOU.REPORT_ACTION_TYPE.CREATE || + getOriginalMessage(action)?.type === CONST.IOU.REPORT_ACTION_TYPE.SPLIT || + getOriginalMessage(action)?.type === CONST.IOU.REPORT_ACTION_TYPE.TRACK) ) { // There is no single iouReport for bill splits, so only 1:1 requests require an iouReportID - const iouReportID = ReportActionsUtils.getOriginalMessage(action)?.IOUReportID ? ReportActionsUtils.getOriginalMessage(action)?.IOUReportID?.toString() ?? '-1' : '-1'; + const iouReportID = getOriginalMessage(action)?.IOUReportID ? getOriginalMessage(action)?.IOUReportID?.toString() ?? '-1' : '-1'; children = ( ); - } else if (ReportActionsUtils.isTripPreview(action)) { + } else if (isTripPreview(action)) { children = ( ); - } else if (ReportActionsUtils.isTaskAction(action)) { + } else if (isTaskAction(action)) { children = ; - } else if (ReportActionsUtils.isCreatedTaskReportAction(action)) { + } else if (isCreatedTaskReportAction(action)) { children = ( ); - } else if (ReportActionsUtils.isReimbursementQueuedAction(action)) { - const linkedReport = ReportUtils.isChatThread(report) ? parentReport : report; - const submitterDisplayName = LocalePhoneNumber.formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails?.[linkedReport?.ownerAccountID ?? -1])); - const paymentType = ReportActionsUtils.getOriginalMessage(action)?.paymentType ?? ''; + } else if (isReimbursementQueuedAction(action)) { + const linkedReport = isChatThread(report) ? parentReport : report; + const submitterDisplayName = formatPhoneNumber(getDisplayNameOrDefault(personalDetails?.[linkedReport?.ownerAccountID ?? -1])); + const paymentType = getOriginalMessage(action)?.paymentType ?? ''; children = ( - BankAccounts.openPersonalBankAccountSetupView(Navigation.getTopmostReportId() ?? linkedReport?.reportID, undefined, undefined, isUserValidated) - } + onPress={() => openPersonalBankAccountSetupView(Navigation.getTopmostReportId() ?? linkedReport?.reportID, undefined, undefined, isUserValidated)} pressOnEnter large /> @@ -764,39 +829,36 @@ function PureReportActionItem({ ); - } else if (ReportActionsUtils.isReimbursementDeQueuedAction(action)) { + } else if (isReimbursementDeQueuedAction(action)) { children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE) { children = ; - } else if ( - ReportActionsUtils.isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED) || - ReportActionsUtils.isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED) - ) { - const wasSubmittedViaHarvesting = ReportActionsUtils.getOriginalMessage(action)?.harvesting ?? false; + } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED) || isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED)) { + const wasSubmittedViaHarvesting = getOriginalMessage(action)?.harvesting ?? false; if (wasSubmittedViaHarvesting) { children = ( - ${ReportUtils.getReportAutomaticallySubmittedMessage(action)}`} /> + ${getReportAutomaticallySubmittedMessage(action)}`} /> ); } else { - children = ; + children = ; } - } else if (ReportActionsUtils.isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.APPROVED)) { - const wasAutoApproved = ReportActionsUtils.getOriginalMessage(action)?.automaticAction ?? false; + } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.APPROVED)) { + const wasAutoApproved = getOriginalMessage(action)?.automaticAction ?? false; if (wasAutoApproved) { children = ( - ${ReportUtils.getReportAutomaticallyApprovedMessage(action)}`} /> + ${getReportAutomaticallyApprovedMessage(action)}`} /> ); } else { - children = ; + children = ; } - } else if (ReportActionsUtils.isUnapprovedAction(action)) { - children = ; - } else if (ReportActionsUtils.isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.FORWARDED)) { - const wasAutoForwarded = ReportActionsUtils.getOriginalMessage(action)?.automaticAction ?? false; + } else if (isUnapprovedAction(action)) { + children = ; + } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.FORWARDED)) { + const wasAutoForwarded = getOriginalMessage(action)?.automaticAction ?? false; if (wasAutoForwarded) { children = ( @@ -804,7 +866,7 @@ function PureReportActionItem({ ); } else { - children = ; + children = ; } } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.REJECTED) { children = ; @@ -815,27 +877,27 @@ function PureReportActionItem({ } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.HOLD) { children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.HOLD_COMMENT) { - children = ; + children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.UNHOLD) { children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.MERGED_WITH_CASH_TRANSACTION) { children = ; - } else if (ReportActionsUtils.isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.DISMISSED_VIOLATION)) { - children = ; - } else if (ReportActionsUtils.isTagModificationAction(action.actionName)) { - children = ; + } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.DISMISSED_VIOLATION)) { + children = ; + } else if (isTagModificationAction(action.actionName)) { + children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_NAME) { - children = ; + children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.ADD_EMPLOYEE) { - children = ; + children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_EMPLOYEE) { - children = ; + children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.DELETE_EMPLOYEE) { - children = ; - } else if (ReportActionsUtils.isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.REMOVED_FROM_APPROVAL_CHAIN)) { - children = ; + children = ; + } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.REMOVED_FROM_APPROVAL_CHAIN)) { + children = ; } else if ( - ReportActionsUtils.isActionOfType( + isActionOfType( action, CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED, CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED_VIRTUAL, @@ -849,20 +911,19 @@ function PureReportActionItem({ policyID={report?.policyID} /> ); - } else if (ReportActionsUtils.isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_INTEGRATION)) { + } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_INTEGRATION)) { children = ; - } else if (ReportActionsUtils.isRenamedAction(action)) { - const message = ReportActionsUtils.getRenamedAction(action); + } else if (isRenamedAction(action)) { + const message = getRenamedAction(action); children = ; - } else if (ReportActionsUtils.isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.INTEGRATION_SYNC_FAILED)) { - const {label, errorMessage} = ReportActionsUtils.getOriginalMessage(action) ?? {label: '', errorMessage: ''}; + } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.INTEGRATION_SYNC_FAILED)) { + const {label, errorMessage} = getOriginalMessage(action) ?? {label: '', errorMessage: ''}; children = ; - } else if (ReportActionsUtils.isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.DELETE_INTEGRATION)) { - children = ; + } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.DELETE_INTEGRATION)) { + children = ; } else { const hasBeenFlagged = - ![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === moderationDecision) && - !ReportActionsUtils.isPendingRemove(action); + ![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === moderationDecision) && !isPendingRemove(action); children = ( @@ -897,7 +958,7 @@ function PureReportActionItem({ {actionableItemButtons.length > 0 && ( )} @@ -910,8 +971,7 @@ function PureReportActionItem({ index={index} ref={textInputRef} shouldDisableEmojiPicker={ - (ReportUtils.chatIncludesConcierge(report) && User.isBlockedFromConcierge(blockedFromConcierge)) || - ReportUtils.isArchivedNonExpenseReport(report, reportNameValuePairs) + (chatIncludesConcierge(report) && isBlockedFromConcierge(blockedFromConcierge)) || isArchivedNonExpenseReport(report, reportNameValuePairs) } isGroupPolicyReport={!!report?.policyID && report.policyID !== CONST.POLICY.ID_FAKE} /> @@ -923,7 +983,7 @@ function PureReportActionItem({ } const numberOfThreadReplies = action.childVisibleActionCount ?? 0; - const shouldDisplayThreadReplies = ReportUtils.shouldDisplayThreadReplies(action, isThreadReportParentAction); + const shouldDisplayThreadReplies = shouldDisplayThreadRepliesUtils(action, isThreadReportParentAction); const oldestFourAccountIDs = action.childOldestFourAccountIDs ?.split(',') @@ -939,18 +999,18 @@ function PureReportActionItem({ !isEmptyObject(item))} /> )} - {!ReportActionsUtils.isMessageDeleted(action) && ( + {!isMessageDeleted(action) && ( { - if (Session.isAnonymousUser()) { + if (isAnonymousUser()) { hideContextMenu(false); InteractionManager.runAfterInteractions(() => { - Session.signOutAndRedirectToSignIn(); + signOutAndRedirectToSignIn(); }); } else { toggleReaction(emoji, ignoreSkinToneOnCompare); @@ -968,7 +1028,7 @@ function PureReportActionItem({ numberOfReplies={numberOfThreadReplies} mostRecentReply={`${action.childLastVisibleActionCreated}`} isHovered={hovered} - icons={ReportUtils.getIconsForParticipants(oldestFourAccountIDs, personalDetails)} + icons={getIconsForParticipants(oldestFourAccountIDs, personalDetails)} onSecondaryInteraction={showPopover} /> @@ -1003,8 +1063,7 @@ function PureReportActionItem({ iouReport={iouReport} isHovered={hovered} hasBeenFlagged={ - ![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === moderationDecision) && - !ReportActionsUtils.isPendingRemove(action) + ![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === moderationDecision) && !isPendingRemove(action) } > {content} @@ -1016,9 +1075,7 @@ function PureReportActionItem({ }; if (action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED) { - const transactionID = ReportActionsUtils.isMoneyRequestAction(parentReportActionForTransactionThread) - ? ReportActionsUtils.getOriginalMessage(parentReportActionForTransactionThread)?.IOUTransactionID - : '-1'; + const transactionID = isMoneyRequestAction(parentReportActionForTransactionThread) ? getOriginalMessage(parentReportActionForTransactionThread)?.IOUTransactionID : '-1'; return ( ); } - if (ReportActionsUtils.isChronosOOOListAction(action)) { + if (isChronosOOOListAction(action)) { return ( 1; - const iouReportID = - ReportActionsUtils.isMoneyRequestAction(action) && ReportActionsUtils.getOriginalMessage(action)?.IOUReportID - ? (ReportActionsUtils.getOriginalMessage(action)?.IOUReportID ?? '').toString() - : '-1'; + const iouReportID = isMoneyRequestAction(action) && getOriginalMessage(action)?.IOUReportID ? (getOriginalMessage(action)?.IOUReportID ?? '').toString() : '-1'; const transactionsWithReceipts = getTransactionsWithReceipts(iouReportID); const isWhisper = whisperedTo.length > 0 && transactionsWithReceipts.length === 0; const whisperedToPersonalDetails = isWhisper ? (Object.values(personalDetails ?? {}).filter((details) => whisperedTo.includes(details?.accountID ?? -1)) as OnyxTypes.PersonalDetails[]) : []; const isWhisperOnlyVisibleByUser = isWhisper && isCurrentUserTheOnlyParticipant(whisperedTo); - const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedToPersonalDetails, isMultipleParticipant) : []; + const displayNamesWithTooltips = isWhisper ? getDisplayNamesWithTooltips(whisperedToPersonalDetails, isMultipleParticipant) : []; return ( shouldUseNarrowLayout && DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} + onPressIn={() => shouldUseNarrowLayout && canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} onSecondaryInteraction={showPopover} preventDefaultContextMenu={draftMessage === undefined && !hasErrors} @@ -1131,21 +1180,15 @@ function PureReportActionItem({ )} > { - const transactionID = ReportActionsUtils.isMoneyRequestAction(action) ? ReportActionsUtils.getOriginalMessage(action)?.IOUTransactionID : undefined; - if (transactionID) { - clearError(transactionID); - } - clearAllRelatedReportActionErrors(reportID, action); - }} + onClose={onClose} // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing pendingAction={ draftMessage !== undefined ? undefined : action.pendingAction ?? (action.isOptimisticAction ? CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD : undefined) } shouldHideOnDelete={!isThreadReportParentAction} - errors={linkedTransactionRouteError ?? ErrorUtils.getLatestErrorMessageField(action as ErrorUtils.OnyxDataWithErrors)} + errors={linkedTransactionRouteError ?? getLatestErrorMessageField(action as OnyxDataWithErrors)} errorRowStyles={[styles.ml10, styles.mr2]} - needsOffscreenAlphaCompositing={ReportActionsUtils.isMoneyRequestAction(action)} + needsOffscreenAlphaCompositing={isMoneyRequestAction(action)} shouldDisableStrikeThrough > {isWhisper && ( @@ -1162,7 +1205,7 @@ function PureReportActionItem({   { prevProps.report?.parentReportID === nextProps.report?.parentReportID && prevProps.report?.parentReportActionID === nextProps.report?.parentReportActionID && // TaskReport's created actions render the TaskView, which updates depending on certain fields in the TaskReport - ReportUtils.isTaskReport(prevProps.report) === ReportUtils.isTaskReport(nextProps.report) && + isTaskReport(prevProps.report) === isTaskReport(nextProps.report) && prevProps.action.actionName === nextProps.action.actionName && prevProps.report?.reportName === nextProps.report?.reportName && prevProps.report?.description === nextProps.report?.description && - ReportUtils.isCompletedTaskReport(prevProps.report) === ReportUtils.isCompletedTaskReport(nextProps.report) && + isCompletedTaskReport(prevProps.report) === isCompletedTaskReport(nextProps.report) && prevProps.report?.managerID === nextProps.report?.managerID && prevProps.shouldHideThreadDividerLine === nextProps.shouldHideThreadDividerLine && prevProps.report?.total === nextProps.report?.total &&