From 96ab01362abc8d873581edea907fc1d58a7549bc Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 11 Jan 2024 15:45:41 +0100 Subject: [PATCH 01/15] [TS migration] Migrate 'ReportActionItemReportPreview.js' component --- .../{ReportPreview.js => ReportPreview.tsx} | 295 ++++++++---------- src/libs/ReportUtils.ts | 2 +- src/libs/onyxSubscribe.ts | 4 +- .../ContextMenu/ReportActionContextMenu.ts | 7 +- 4 files changed, 143 insertions(+), 165 deletions(-) rename src/components/ReportActionItem/{ReportPreview.js => ReportPreview.tsx} (61%) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.tsx similarity index 61% rename from src/components/ReportActionItem/ReportPreview.js rename to src/components/ReportActionItem/ReportPreview.tsx index abc7e3954200..cd2c87a3a585 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -1,23 +1,19 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; +import type {StyleProp, ViewStyle} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {OnyxEntry} from 'react-native-onyx/lib/types'; import Button from '@components/Button'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; -import refPropTypes from '@components/refPropTypes'; import SettlementButton from '@components/SettlementButton'; import {showContextMenuForReport} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import ControlSelection from '@libs/ControlSelection'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; @@ -27,103 +23,78 @@ import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportActionUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; -import reportActionPropTypes from '@pages/home/report/reportActionPropTypes'; -import reportPropTypes from '@pages/reportPropTypes'; +import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Policy, Report, ReportAction, Session} from '@src/types/onyx'; +import type DeepValueOf from '@src/types/utils/DeepValueOf'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import ReportActionItemImages from './ReportActionItemImages'; -const propTypes = { - /** All the data of the action */ - action: PropTypes.shape(reportActionPropTypes).isRequired, - - /** The associated chatReport */ - chatReportID: PropTypes.string.isRequired, - - /** The active IOUReport, used for Onyx subscription */ - // eslint-disable-next-line react/no-unused-prop-types - iouReportID: PropTypes.string.isRequired, +type PaymentVerbTranslationPath = 'iou.payerSpent' | 'iou.payerOwes' | 'iou.payerPaid'; - /** The report's policyID, used for Onyx subscription */ - policyID: PropTypes.string.isRequired, +type PaymentMethodType = DeepValueOf; +type ReportPreviewOnyxProps = { /** The policy tied to the money request report */ - policy: PropTypes.shape({ - /** Name of the policy */ - name: PropTypes.string, - - /** Type of the policy */ - type: PropTypes.string, - - /** The role of the current user in the policy */ - role: PropTypes.string, + policy: OnyxEntry; - /** Whether Scheduled Submit is turned on for this policy */ - isHarvestingEnabled: PropTypes.bool, - }), - - /* Onyx Props */ - /** chatReport associated with iouReport */ - chatReport: reportPropTypes, - - /** Extra styles to pass to View wrapper */ - // eslint-disable-next-line react/forbid-prop-types - containerStyles: PropTypes.arrayOf(PropTypes.object), + /** ChatReport associated with iouReport */ + chatReport: OnyxEntry; /** Active IOU Report for current report */ - iouReport: PropTypes.shape({ - /** AccountID of the manager in this iou report */ - managerID: PropTypes.number, + iouReport: OnyxEntry; - /** AccountID of the creator of this iou report */ - ownerAccountID: PropTypes.number, + /** Session info for the currently logged in user. */ + session: OnyxEntry; +}; - /** Outstanding amount in cents of this transaction */ - total: PropTypes.number, +type ReportPreviewProps = ReportPreviewOnyxProps & { + /** All the data of the action */ + action: ReportAction; - /** Currency of outstanding amount of this transaction */ - currency: PropTypes.string, + /** The associated chatReport */ + chatReportID: string; - /** Is the iouReport waiting for the submitter to add a credit bank account? */ - isWaitingOnBankAccount: PropTypes.bool, - }), + /** The active IOUReport, used for Onyx subscription */ + iouReportID: string; - /** Session info for the currently logged in user. */ - session: PropTypes.shape({ - /** Currently logged in user accountID */ - accountID: PropTypes.number, - }), + /** The report's policyID, used for Onyx subscription */ + policyID: string; + + /** Extra styles to pass to View wrapper */ + containerStyles?: StyleProp; /** Popover context menu anchor, used for showing context menu */ - contextMenuAnchor: refPropTypes, + contextMenuAnchor?: ContextMenuAnchor; /** Callback for updating context menu active state, used for showing context menu */ - checkIfContextMenuActive: PropTypes.func, + checkIfContextMenuActive?: () => void; /** Whether a message is a whisper */ - isWhisper: PropTypes.bool, + isWhisper?: boolean; - ...withLocalizePropTypes, + /** Whether the corresponding report action item is hovered */ + isHovered?: boolean; }; -const defaultProps = { - contextMenuAnchor: null, - chatReport: {}, - containerStyles: [], - iouReport: {}, - checkIfContextMenuActive: () => {}, - session: { - accountID: null, - }, - isWhisper: false, - policy: { - isHarvestingEnabled: false, - }, -}; - -function ReportPreview(props) { +function ReportPreview({ + iouReport, + session, + policy, + iouReportID, + policyID, + chatReportID, + chatReport, + action, + containerStyles, + contextMenuAnchor, + isHovered = false, + isWhisper = false, + checkIfContextMenuActive = () => {}, +}: ReportPreviewProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -133,35 +104,37 @@ function ReportPreview(props) { const [hasOnlyDistanceRequests, setHasOnlyDistanceRequests] = useState(false); const [hasNonReimbursableTransactions, setHasNonReimbursableTransactions] = useState(false); - const managerID = props.iouReport.managerID || 0; - const isCurrentUserManager = managerID === lodashGet(props.session, 'accountID'); - const {totalDisplaySpend, reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(props.iouReport); - const policyType = lodashGet(props.policy, 'type'); - - const iouSettled = ReportUtils.isSettled(props.iouReportID); - const iouCanceled = ReportUtils.isArchivedRoom(props.chatReport); - const numberOfRequests = ReportActionUtils.getNumberOfMoneyRequests(props.action); - const moneyRequestComment = lodashGet(props.action, 'childLastMoneyRequestComment', ''); - const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(props.chatReport); - const isDraftExpenseReport = isPolicyExpenseChat && ReportUtils.isDraftExpenseReport(props.iouReport); - - const isApproved = ReportUtils.isReportApproved(props.iouReport); - const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(props.iouReport); - const transactionsWithReceipts = ReportUtils.getTransactionsWithReceipts(props.iouReportID); - const numberOfScanningReceipts = _.filter(transactionsWithReceipts, (transaction) => TransactionUtils.isReceiptBeingScanned(transaction)).length; + const managerID = iouReport?.managerID ?? 0; + const isCurrentUserManager = managerID === session?.accountID; + const {totalDisplaySpend, reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(iouReport); + const policyType = policy?.type; + + const iouSettled = ReportUtils.isSettled(iouReportID); + const iouCanceled = ReportUtils.isArchivedRoom(chatReport); + const numberOfRequests = ReportActionUtils.getNumberOfMoneyRequests(action); + const moneyRequestComment = action?.childLastMoneyRequestComment ?? ''; + const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport); + const isDraftExpenseReport = isPolicyExpenseChat && ReportUtils.isDraftExpenseReport(iouReport); + + const isApproved = ReportUtils.isReportApproved(iouReport); + const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(iouReport); + const transactionsWithReceipts = ReportUtils.getTransactionsWithReceipts(iouReportID); + const numberOfScanningReceipts = transactionsWithReceipts.filter((transaction) => TransactionUtils.isReceiptBeingScanned(transaction)).length; const hasReceipts = transactionsWithReceipts.length > 0; const isScanning = hasReceipts && areAllRequestsBeingSmartScanned; const hasErrors = hasReceipts && hasMissingSmartscanFields; const lastThreeTransactionsWithReceipts = transactionsWithReceipts.slice(-3); - const lastThreeReceipts = _.map(lastThreeTransactionsWithReceipts, (transaction) => ReceiptUtils.getThumbnailAndImageURIs(transaction)); + const lastThreeReceipts = lastThreeTransactionsWithReceipts.map((transaction) => ReceiptUtils.getThumbnailAndImageURIs(transaction)); let formattedMerchant = numberOfRequests === 1 && hasReceipts ? TransactionUtils.getMerchant(transactionsWithReceipts[0]) : null; - const hasPendingWaypoints = formattedMerchant && hasOnlyDistanceRequests && _.every(transactionsWithReceipts, (transaction) => lodashGet(transaction, 'pendingFields.waypoints', null)); - if (hasPendingWaypoints) { - formattedMerchant = formattedMerchant.replace(CONST.REGEX.FIRST_SPACE, props.translate('common.tbd')); + const hasPendingWaypoints = formattedMerchant && hasOnlyDistanceRequests && transactionsWithReceipts.every((transaction) => transaction.pendingFields?.waypoints); + if (formattedMerchant && hasPendingWaypoints) { + formattedMerchant = formattedMerchant.replace(CONST.REGEX.FIRST_SPACE, translate('common.tbd')); } const previewSubtitle = + // Formatted merchant can be an empty string + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing formattedMerchant || - props.translate('iou.requestCount', { + translate('iou.requestCount', { count: numberOfRequests, scanningReceipts: numberOfScanningReceipts, }); @@ -170,67 +143,71 @@ function ReportPreview(props) { // The submit button should be success green colour only if the user is submitter and the policy does not have Scheduled Submit turned on const isWaitingForSubmissionFromCurrentUser = useMemo( - () => props.chatReport.isOwnPolicyExpenseChat && !props.policy.isHarvestingEnabled, - [props.chatReport.isOwnPolicyExpenseChat, props.policy.isHarvestingEnabled], + () => chatReport?.isOwnPolicyExpenseChat && !policy?.isHarvestingEnabled, + [chatReport?.isOwnPolicyExpenseChat, policy?.isHarvestingEnabled], ); - const getDisplayAmount = () => { + const getDisplayAmount = (): string => { if (hasPendingWaypoints) { - return props.translate('common.tbd'); + return translate('common.tbd'); } if (totalDisplaySpend) { - return CurrencyUtils.convertToDisplayString(totalDisplaySpend, props.iouReport.currency); + return CurrencyUtils.convertToDisplayString(totalDisplaySpend, iouReport?.currency); } if (isScanning) { - return props.translate('iou.receiptScanning'); + return translate('iou.receiptScanning'); } if (hasOnlyDistanceRequests) { - return props.translate('common.tbd'); + return translate('common.tbd'); } // If iouReport is not available, get amount from the action message (Ex: "Domain20821's Workspace owes $33.00" or "paid ₫60" or "paid -₫60 elsewhere") let displayAmount = ''; - const actionMessage = lodashGet(props.action, ['message', 0, 'text'], ''); + const actionMessage = action.message?.[0]?.text ?? ''; const splits = actionMessage.split(' '); - for (let i = 0; i < splits.length; i++) { - if (/\d/.test(splits[i])) { - displayAmount = splits[i]; + + splits.forEach((split) => { + if (!/\d/.test(split)) { + return; } - } + + displayAmount = split; + }); + return displayAmount; }; const getPreviewMessage = () => { if (isScanning) { - return props.translate('common.receipt'); + return translate('common.receipt'); } - const payerOrApproverName = isPolicyExpenseChat ? ReportUtils.getPolicyName(props.chatReport) : ReportUtils.getDisplayNameForParticipant(managerID, true); + const payerOrApproverName = isPolicyExpenseChat ? ReportUtils.getPolicyName(chatReport) : ReportUtils.getDisplayNameForParticipant(managerID, true); if (isApproved) { - return props.translate('iou.managerApproved', {manager: payerOrApproverName}); + return translate('iou.managerApproved', {manager: payerOrApproverName}); } - const managerName = isPolicyExpenseChat ? ReportUtils.getPolicyName(props.chatReport) : ReportUtils.getDisplayNameForParticipant(managerID, true); - let paymentVerb = hasNonReimbursableTransactions ? 'iou.payerSpent' : 'iou.payerOwes'; - if (iouSettled || props.iouReport.isWaitingOnBankAccount) { + const managerName = isPolicyExpenseChat ? ReportUtils.getPolicyName(chatReport) : ReportUtils.getDisplayNameForParticipant(managerID, true); + let paymentVerb: PaymentVerbTranslationPath = hasNonReimbursableTransactions ? 'iou.payerSpent' : 'iou.payerOwes'; + if (iouSettled || iouReport?.isWaitingOnBankAccount) { paymentVerb = 'iou.payerPaid'; } - return props.translate(paymentVerb, {payer: managerName}); + return translate(paymentVerb, {payer: managerName}); }; - const bankAccountRoute = ReportUtils.getBankAccountRoute(props.chatReport); + const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport); useEffect(() => { const unsubscribeOnyxTransaction = onyxSubscribe({ key: ONYXKEYS.COLLECTION.TRANSACTION, waitForCollectionCallback: true, callback: (allTransactions) => { - if (_.isEmpty(allTransactions)) { + if (isEmptyObject(allTransactions)) { return; } - sethasMissingSmartscanFields(ReportUtils.hasMissingSmartscanFields(props.iouReportID)); - setAreAllRequestsBeingSmartScanned(ReportUtils.areAllRequestsBeingSmartScanned(props.iouReportID, props.action)); - setHasOnlyDistanceRequests(ReportUtils.hasOnlyDistanceRequestTransactions(props.iouReportID)); - setHasNonReimbursableTransactions(ReportUtils.hasNonReimbursableTransactions(props.iouReportID)); + sethasMissingSmartscanFields(ReportUtils.hasMissingSmartscanFields(iouReportID)); + setAreAllRequestsBeingSmartScanned(ReportUtils.areAllRequestsBeingSmartScanned(iouReportID, action)); + setHasOnlyDistanceRequests(ReportUtils.hasOnlyDistanceRequestTransactions(iouReportID)); + setHasNonReimbursableTransactions(ReportUtils.hasNonReimbursableTransactions(iouReportID)); }, }); @@ -240,15 +217,15 @@ function ReportPreview(props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(props.chatReport); - const isPolicyAdmin = policyType !== CONST.POLICY.TYPE.PERSONAL && lodashGet(props.policy, 'role') === CONST.POLICY.ROLE.ADMIN; + const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport); + const isPolicyAdmin = policyType !== CONST.POLICY.TYPE.PERSONAL && policy?.role === CONST.POLICY.ROLE.ADMIN; const isPayer = isPaidGroupPolicy ? // In a paid group policy, the admin approver can pay the report directly by skipping the approval step isPolicyAdmin && (isApproved || isCurrentUserManager) : isPolicyAdmin || (isMoneyRequestReport && isCurrentUserManager); const shouldShowPayButton = useMemo( - () => isPayer && !isDraftExpenseReport && !iouSettled && !props.iouReport.isWaitingOnBankAccount && reimbursableSpend !== 0 && !iouCanceled, - [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, props.iouReport], + () => isPayer && !isDraftExpenseReport && !iouSettled && !iouReport?.isWaitingOnBankAccount && reimbursableSpend !== 0 && !iouCanceled, + [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, iouReport], ); const shouldShowApproveButton = useMemo(() => { if (!isPaidGroupPolicy) { @@ -258,25 +235,27 @@ function ReportPreview(props) { }, [isPaidGroupPolicy, isCurrentUserManager, isDraftExpenseReport, isApproved, iouSettled]); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; return ( - - + + { - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(props.iouReportID)); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(iouReportID)); }} onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} - onLongPress={(event) => showContextMenuForReport(event, props.contextMenuAnchor, props.chatReportID, props.action, props.checkIfContextMenuActive)} + // @ts-expect-error TODO: Remove this once ShowContextMenuContext (https://github.com/Expensify/App/issues/24980) is migrated to TypeScript. + onLongPress={(event) => showContextMenuForReport(event, contextMenuAnchor, chatReportID, action, checkIfContextMenuActive)} style={[styles.flexRow, styles.justifyContentBetween, styles.reportPreviewBox]} role="button" - accessibilityLabel={props.translate('iou.viewDetails')} + accessibilityLabel={translate('iou.viewDetails')} > - + {hasReceipts && ( )} @@ -295,7 +274,7 @@ function ReportPreview(props) { {getDisplayAmount()} - {ReportUtils.isSettled(props.iouReportID) && ( + {ReportUtils.isSettled(iouReportID) && ( IOU.payMoneyRequest(paymentType, props.chatReport, props.iouReport)} + // @ts-expect-error TODO: Remove this once SettlementButton (https://github.com/Expensify/App/issues/25100) is migrated to TypeScript. + currency={iouReport?.currency} + policyID={policyID} + chatReportID={chatReportID} + iouReport={iouReport} + onPress={(paymentType: PaymentMethodType) => chatReport && iouReport && IOU.payMoneyRequest(paymentType, chatReport, iouReport)} enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} addBankAccountRoute={bankAccountRoute} shouldHidePaymentOptions={!shouldShowPayButton} @@ -340,7 +320,7 @@ function ReportPreview(props) { success={isWaitingForSubmissionFromCurrentUser} text={translate('common.submit')} style={styles.mt3} - onPress={() => IOU.submitReport(props.iouReport)} + onPress={() => iouReport && IOU.submitReport(iouReport)} /> )} @@ -351,24 +331,19 @@ function ReportPreview(props) { ); } -ReportPreview.propTypes = propTypes; -ReportPreview.defaultProps = defaultProps; ReportPreview.displayName = 'ReportPreview'; -export default compose( - withLocalize, - withOnyx({ - policy: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - }, - chatReport: { - key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, - }, - iouReport: { - key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, - }, - session: { - key: ONYXKEYS.SESSION, - }, - }), -)(ReportPreview); +export default withOnyx({ + policy: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + }, + chatReport: { + key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, + }, + iouReport: { + key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, + }, + session: { + key: ONYXKEYS.SESSION, + }, +})(ReportPreview); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e619cb3c80dd..95c64f9b5e21 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1488,7 +1488,7 @@ function getPersonalDetailsForAccountID(accountID: number): Partial(mapping: ConnectOptions) { +function onyxSubscribe(mapping: ConnectOptions) { const connectionId = Onyx.connect(mapping); return () => Onyx.disconnect(connectionId); } diff --git a/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts b/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts index 5b64d90da5da..9553d8207a2f 100644 --- a/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts +++ b/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts @@ -14,11 +14,13 @@ type OnCancel = () => void; type ContextMenuType = ValueOf; +type ContextMenuAnchor = View | RNText | null; + type ShowContextMenu = ( type: ContextMenuType, event: GestureResponderEvent | MouseEvent, selection: string, - contextMenuAnchor: View | RNText | null, + contextMenuAnchor: ContextMenuAnchor, reportID?: string, reportActionID?: string, originalReportID?: string, @@ -96,7 +98,7 @@ function showContextMenu( type: ContextMenuType, event: GestureResponderEvent | MouseEvent, selection: string, - contextMenuAnchor: View | RNText | null, + contextMenuAnchor: ContextMenuAnchor, reportID = '0', reportActionID = '0', originalReportID = '0', @@ -175,3 +177,4 @@ function clearActiveReportAction() { } export {contextMenuRef, showContextMenu, hideContextMenu, isActiveReportAction, clearActiveReportAction, showDeleteModal, hideDeleteModal}; +export type {ContextMenuAnchor}; From 273406e9e907101a6acc16a640433d776cadb02a Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 12 Jan 2024 09:27:48 +0100 Subject: [PATCH 02/15] Reuse PaymentMethodType in other places --- src/components/ReportActionItem/ReportPreview.tsx | 4 +--- src/libs/ReportUtils.ts | 5 ++--- src/types/onyx/OriginalMessage.ts | 5 ++++- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index cd2c87a3a585..1a347c039826 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -29,14 +29,12 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Policy, Report, ReportAction, Session} from '@src/types/onyx'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; +import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import ReportActionItemImages from './ReportActionItemImages'; type PaymentVerbTranslationPath = 'iou.payerSpent' | 'iou.payerOwes' | 'iou.payerPaid'; -type PaymentMethodType = DeepValueOf; - type ReportPreviewOnyxProps = { /** The policy tied to the money request report */ policy: OnyxEntry; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 95c64f9b5e21..a3331e5f500e 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -16,12 +16,11 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Beta, Login, PersonalDetails, PersonalDetailsList, Policy, Report, ReportAction, ReportMetadata, Session, Transaction} from '@src/types/onyx'; import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; -import type {IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from '@src/types/onyx/OriginalMessage'; +import type {IOUMessage, OriginalMessageActionName, OriginalMessageCreated, PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type {NotificationPreference} from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; import type {Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; @@ -2697,7 +2696,7 @@ function buildOptimisticIOUReportAction( comment: string, participants: Participant[], transactionID: string, - paymentType: DeepValueOf, + paymentType: PaymentMethodType, iouReportID = '', isSettlingUp = false, isSendMoneyFlow = false, diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index c4e30157bf6f..527915cd0017 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -2,6 +2,8 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; +type PaymentMethodType = DeepValueOf; + type ActionName = DeepValueOf; type OriginalMessageActionName = | 'ADDCOMMENT' @@ -42,7 +44,7 @@ type IOUMessage = { lastModified?: string; participantAccountIDs?: number[]; type: ValueOf; - paymentType?: DeepValueOf; + paymentType?: PaymentMethodType; /** Only exists when we are sending money */ IOUDetails?: IOUDetails; }; @@ -266,4 +268,5 @@ export type { OriginalMessageIOU, OriginalMessageCreated, OriginalMessageAddComment, + PaymentMethodType, }; From ef8050c96208e0bf4e2bba5f514fbf2f9ac38490 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 15 Jan 2024 14:02:57 +0100 Subject: [PATCH 03/15] Remove extra double negation, fix lint error --- src/components/ReportActionItem/ReportPreview.tsx | 4 ++-- src/libs/ReportUtils.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 1a347c039826..ecaa55ee0ef0 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -247,13 +247,13 @@ function ReportPreview({ role="button" accessibilityLabel={translate('iou.viewDetails')} > - + {hasReceipts && ( )} diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index da861d550566..b8e3d9c569ee 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -16,7 +16,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Beta, Login, PersonalDetails, PersonalDetailsList, Policy, Report, ReportAction, ReportMetadata, Session, Transaction} from '@src/types/onyx'; import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; -import type {IOUMessage, OriginalMessageActionName, OriginalMessageCreated, ReimbursementDeQueuedMessage, PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import type {IOUMessage, OriginalMessageActionName, OriginalMessageCreated, PaymentMethodType, ReimbursementDeQueuedMessage} from '@src/types/onyx/OriginalMessage'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type {NotificationPreference} from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; From 449e6915ed77bd79f240e088c2739217eaf4f8ea Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 16 Jan 2024 13:18:54 +0100 Subject: [PATCH 04/15] TS fixes after merging main --- .../ReportActionItem/ReportPreview.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 2c3cc4c13204..1378e3b169f0 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -2,7 +2,7 @@ import React, {useMemo} from 'react'; import {View} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx/lib/types'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx/lib/types'; import Button from '@components/Button'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -27,7 +27,7 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Policy, Report, ReportAction, Session} from '@src/types/onyx'; +import type {Policy, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import ReportActionItemImages from './ReportActionItemImages'; @@ -45,6 +45,9 @@ type ReportPreviewOnyxProps = { /** Session info for the currently logged in user. */ session: OnyxEntry; + + /** All the transactions, used to update ReportPreview label and status */ + transactions: OnyxCollection; }; type ReportPreviewProps = ReportPreviewOnyxProps & { @@ -87,6 +90,7 @@ function ReportPreview({ action, containerStyles, contextMenuAnchor, + transactions, isHovered = false, isWhisper = false, checkIfContextMenuActive = () => {}, @@ -97,14 +101,14 @@ function ReportPreview({ const {hasMissingSmartscanFields, areAllRequestsBeingSmartScanned, hasOnlyDistanceRequests, hasNonReimbursableTransactions} = useMemo( () => ({ - hasMissingSmartscanFields: ReportUtils.hasMissingSmartscanFields(props.iouReportID), - areAllRequestsBeingSmartScanned: ReportUtils.areAllRequestsBeingSmartScanned(props.iouReportID, props.action), - hasOnlyDistanceRequests: ReportUtils.hasOnlyDistanceRequestTransactions(props.iouReportID), - hasNonReimbursableTransactions: ReportUtils.hasNonReimbursableTransactions(props.iouReportID), + hasMissingSmartscanFields: ReportUtils.hasMissingSmartscanFields(iouReportID), + areAllRequestsBeingSmartScanned: ReportUtils.areAllRequestsBeingSmartScanned(iouReportID, action), + hasOnlyDistanceRequests: ReportUtils.hasOnlyDistanceRequestTransactions(iouReportID), + hasNonReimbursableTransactions: ReportUtils.hasNonReimbursableTransactions(iouReportID), }), // When transactions get updated these status may have changed, so that is a case where we also want to run this. // eslint-disable-next-line react-hooks/exhaustive-deps - [props.transactions, props.iouReportID, props.action], + [transactions, iouReportID, action], ); const managerID = iouReport?.managerID ?? 0; From 23c94b6cd3becc7177c112f4eed879b9b52d1ded Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 17 Jan 2024 08:55:47 +0100 Subject: [PATCH 05/15] Use ContextMenuAnchor type for anchor typing --- src/components/ReportActionItem/ReportPreview.tsx | 1 - src/components/ShowContextMenuContext.ts | 5 +++-- .../report/ContextMenu/PopoverReportActionContextMenu.tsx | 6 +----- .../home/report/ContextMenu/ReportActionContextMenu.ts | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 1378e3b169f0..57e93bc3c172 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -228,7 +228,6 @@ function ReportPreview({ }} onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} - // @ts-expect-error TODO: Remove this once ShowContextMenuContext (https://github.com/Expensify/App/issues/24980) is migrated to TypeScript. onLongPress={(event) => showContextMenuForReport(event, contextMenuAnchor, chatReportID, action, checkIfContextMenuActive)} style={[styles.flexRow, styles.justifyContentBetween, styles.reportPreviewBox]} role="button" diff --git a/src/components/ShowContextMenuContext.ts b/src/components/ShowContextMenuContext.ts index 17557051bef9..41636aadfd91 100644 --- a/src/components/ShowContextMenuContext.ts +++ b/src/components/ShowContextMenuContext.ts @@ -5,11 +5,12 @@ import type {OnyxEntry} from 'react-native-onyx'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as ReportUtils from '@libs/ReportUtils'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; +import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import CONST from '@src/CONST'; import type {Report, ReportAction} from '@src/types/onyx'; type ShowContextMenuContextProps = { - anchor: RNText | null; + anchor: ContextMenuAnchor; report: OnyxEntry; action: OnyxEntry; checkIfContextMenuActive: () => void; @@ -36,7 +37,7 @@ ShowContextMenuContext.displayName = 'ShowContextMenuContext'; */ function showContextMenuForReport( event: GestureResponderEvent | MouseEvent, - anchor: RNText | null, + anchor: ContextMenuAnchor, reportID: string, action: OnyxEntry, checkIfContextMenuActive: () => void, diff --git a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx index 46b783bca3f9..bbd048b0d82c 100644 --- a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx @@ -12,11 +12,7 @@ import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import type {ReportAction} from '@src/types/onyx'; import BaseReportActionContextMenu from './BaseReportActionContextMenu'; -import type {ContextMenuType, ReportActionContextMenu} from './ReportActionContextMenu'; - -type ContextMenuAnchorCallback = (x: number, y: number) => void; - -type ContextMenuAnchor = {measureInWindow: (callback: ContextMenuAnchorCallback) => void}; +import type {ContextMenuAnchor, ContextMenuType, ReportActionContextMenu} from './ReportActionContextMenu'; type Location = { x: number; diff --git a/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts b/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts index 19d46c1fdc4a..7080c02775ce 100644 --- a/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts +++ b/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts @@ -15,7 +15,7 @@ type OnCancel = () => void; type ContextMenuType = ValueOf; -type ContextMenuAnchor = View | RNText | null; +type ContextMenuAnchor = View | RNText | null | undefined; type ShowContextMenu = ( type: ContextMenuType, From b2ca9a6fd7970bde6b938b196b9f35b176d53ac2 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 17 Jan 2024 09:12:49 +0100 Subject: [PATCH 06/15] Fix lint error --- src/components/ShowContextMenuContext.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ShowContextMenuContext.ts b/src/components/ShowContextMenuContext.ts index 41636aadfd91..374ca8a2f1e5 100644 --- a/src/components/ShowContextMenuContext.ts +++ b/src/components/ShowContextMenuContext.ts @@ -1,6 +1,6 @@ import {createContext} from 'react'; // eslint-disable-next-line no-restricted-imports -import type {GestureResponderEvent, Text as RNText} from 'react-native'; +import type {GestureResponderEvent} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as ReportUtils from '@libs/ReportUtils'; From a53da9d6b5071c0a52d50569c368801d3d377c71 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 17 Jan 2024 09:22:18 +0100 Subject: [PATCH 07/15] Fix lint error in import --- src/libs/ReportUtils.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 71f1ba3927c1..e7750dff6368 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -16,7 +16,14 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Beta, Login, PersonalDetails, PersonalDetailsList, Policy, PolicyReportField, Report, ReportAction, ReportMetadata, Session, Transaction} from '@src/types/onyx'; import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; -import type {IOUMessage, OriginalMessageActionName, OriginalMessageCreated, OriginalMessageReimbursementDequeued, PaymentMethodType, ReimbursementDeQueuedMessage} from '@src/types/onyx/OriginalMessage'; +import type { + IOUMessage, + OriginalMessageActionName, + OriginalMessageCreated, + OriginalMessageReimbursementDequeued, + PaymentMethodType, + ReimbursementDeQueuedMessage, +} from '@src/types/onyx/OriginalMessage'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type {NotificationPreference} from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; From ed8e47eb0d2a93b3f69e76c384eb08e2c517a649 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 18 Jan 2024 09:01:23 +0100 Subject: [PATCH 08/15] Fix ts error --- src/components/ReportActionItem/ReportPreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index c04945302de4..785992de70b8 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -133,7 +133,7 @@ function ReportPreview({ const lastThreeTransactionsWithReceipts = transactionsWithReceipts.slice(-3); const lastThreeReceipts = lastThreeTransactionsWithReceipts.map((transaction) => ReceiptUtils.getThumbnailAndImageURIs(transaction)); let formattedMerchant = numberOfRequests === 1 && hasReceipts ? TransactionUtils.getMerchant(transactionsWithReceipts[0]) : null; - if (TransactionUtils.isPartialMerchant(formattedMerchant)) { + if (TransactionUtils.isPartialMerchant(formattedMerchant ?? '')) { formattedMerchant = null; } const hasPendingWaypoints = formattedMerchant && hasOnlyDistanceRequests && transactionsWithReceipts.every((transaction) => transaction.pendingFields?.waypoints); From 2ccf45fb79d55efb4a1c6a23796e2a093494a66a Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 19 Jan 2024 09:17:44 +0100 Subject: [PATCH 09/15] Update TransactionViolations type --- src/components/ReportActionItem/ReportPreview.tsx | 5 ++++- src/types/onyx/TransactionViolation.ts | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 15d9ce2dceba..9c2075dd927f 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -28,7 +28,7 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Policy, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; +import type {Policy, Report, ReportAction, Session, Transaction, TransactionViolations} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import ReportActionItemImages from './ReportActionItemImages'; @@ -49,6 +49,9 @@ type ReportPreviewOnyxProps = { /** All the transactions, used to update ReportPreview label and status */ transactions: OnyxCollection; + + /** All of the transaction violations */ + transactionViolations: OnyxCollection; }; type ReportPreviewProps = ReportPreviewOnyxProps & { diff --git a/src/types/onyx/TransactionViolation.ts b/src/types/onyx/TransactionViolation.ts index 20603adc7cfa..e7be24d5cb18 100644 --- a/src/types/onyx/TransactionViolation.ts +++ b/src/types/onyx/TransactionViolation.ts @@ -31,7 +31,7 @@ type TransactionViolation = { }; }; -type TransactionViolations = Record; +type TransactionViolations = TransactionViolation[]; export type {TransactionViolation, ViolationName}; export default TransactionViolations; From 7910c46358b43b293f58a9b2462800f2f946b86f Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 22 Jan 2024 10:12:14 +0100 Subject: [PATCH 10/15] TS fixes after merging main --- .../ReportActionItem/ReportActionItemImage.tsx | 2 +- .../ReportActionItem/ReportActionItemImages.tsx | 11 ++--------- src/components/ReportActionItem/ReportPreview.tsx | 1 - src/libs/ReceiptUtils.ts | 8 ++++---- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/components/ReportActionItem/ReportActionItemImage.tsx b/src/components/ReportActionItem/ReportActionItemImage.tsx index aa5d0513f0d7..d408f815e70c 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.tsx +++ b/src/components/ReportActionItem/ReportActionItemImage.tsx @@ -17,7 +17,7 @@ import type {Transaction} from '@src/types/onyx'; type ReportActionItemImageProps = { /** thumbnail URI for the image */ - thumbnail?: string | number; + thumbnail?: string | number | null; /** URI for the image or local numeric reference for the image */ image: string | number; diff --git a/src/components/ReportActionItem/ReportActionItemImages.tsx b/src/components/ReportActionItem/ReportActionItemImages.tsx index c24defb8ac08..06edea95fb89 100644 --- a/src/components/ReportActionItem/ReportActionItemImages.tsx +++ b/src/components/ReportActionItem/ReportActionItemImages.tsx @@ -6,20 +6,13 @@ import Text from '@components/Text'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import type {ThumbnailAndImageURI} from '@libs/ReceiptUtils'; import variables from '@styles/variables'; -import type {Transaction} from '@src/types/onyx'; import ReportActionItemImage from './ReportActionItemImage'; -type Image = { - thumbnail: string | number; - image: string | number; - transaction: Transaction; - isLocalFile: boolean; -}; - type ReportActionItemImagesProps = { /** array of image and thumbnail URIs */ - images: Image[]; + images: ThumbnailAndImageURI[]; // We're not providing default values for size and total and disabling the ESLint rule // because we want them to default to the length of images, but we can't set default props diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 9c2075dd927f..42097cb84f4e 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -245,7 +245,6 @@ function ReportPreview({ {hasReceipts && ( Date: Tue, 23 Jan 2024 10:03:28 +0100 Subject: [PATCH 11/15] Fix TS issue --- src/libs/ReceiptUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index ae2658677dd9..9c6f10c12935 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -29,7 +29,7 @@ type FileNameAndExtension = { */ function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string | null = null, receiptFileName: string | null = null): ThumbnailAndImageURI { if (Object.hasOwn(transaction?.pendingFields ?? {}, 'waypoints')) { - return {thumbnail: null, image: ReceiptGeneric, isLocalFile: true}; + return {thumbnail: null, image: ReceiptGeneric as string | number, isLocalFile: true}; } // URI to image, i.e. blob:new.expensify.com/9ef3a018-4067-47c6-b29f-5f1bd35f213d or expensify.com/receipts/w_e616108497ef940b7210ec6beb5a462d01a878f4.jpg From ed0c95df80bfd015cac64f7e74d0acb08e1bd691 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 23 Jan 2024 10:36:24 +0100 Subject: [PATCH 12/15] Update image typing --- src/components/ImageWithSizeCalculation.tsx | 4 ++-- .../ReportActionItem/ReportActionItemImage.tsx | 6 +++--- src/components/ThumbnailImage.tsx | 4 ++-- src/libs/ReceiptUtils.ts | 9 +++++---- src/libs/tryResolveUrlFromApiRoot.ts | 9 +++++---- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index c65faef53748..634492c8e602 100644 --- a/src/components/ImageWithSizeCalculation.tsx +++ b/src/components/ImageWithSizeCalculation.tsx @@ -1,6 +1,6 @@ import delay from 'lodash/delay'; import React, {useEffect, useRef, useState} from 'react'; -import type {StyleProp, ViewStyle} from 'react-native'; +import {ImageSourcePropType, StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; import Log from '@libs/Log'; @@ -19,7 +19,7 @@ type OnLoadNativeEvent = { type ImageWithSizeCalculationProps = { /** Url for image to display */ - url: string | number; + url: string | ImageSourcePropType; /** Any additional styles to apply */ style?: StyleProp; diff --git a/src/components/ReportActionItem/ReportActionItemImage.tsx b/src/components/ReportActionItem/ReportActionItemImage.tsx index d408f815e70c..d8cbc6b0afcb 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.tsx +++ b/src/components/ReportActionItem/ReportActionItemImage.tsx @@ -1,7 +1,7 @@ import Str from 'expensify-common/lib/str'; import React from 'react'; import type {ReactElement} from 'react'; -import {View} from 'react-native'; +import {ImageSourcePropType, View} from 'react-native'; import AttachmentModal from '@components/AttachmentModal'; import EReceiptThumbnail from '@components/EReceiptThumbnail'; import Image from '@components/Image'; @@ -17,10 +17,10 @@ import type {Transaction} from '@src/types/onyx'; type ReportActionItemImageProps = { /** thumbnail URI for the image */ - thumbnail?: string | number | null; + thumbnail?: string | ImageSourcePropType | null; /** URI for the image or local numeric reference for the image */ - image: string | number; + image: string | ImageSourcePropType; /** whether or not to enable the image preview modal */ enablePreviewModal?: boolean; diff --git a/src/components/ThumbnailImage.tsx b/src/components/ThumbnailImage.tsx index a1778e2feaee..753c4b783b9c 100644 --- a/src/components/ThumbnailImage.tsx +++ b/src/components/ThumbnailImage.tsx @@ -1,6 +1,6 @@ import lodashClamp from 'lodash/clamp'; import React, {useCallback, useState} from 'react'; -import type {StyleProp, ViewStyle} from 'react-native'; +import {ImageSourcePropType, StyleProp, ViewStyle} from 'react-native'; import {Dimensions, View} from 'react-native'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -10,7 +10,7 @@ import ImageWithSizeCalculation from './ImageWithSizeCalculation'; type ThumbnailImageProps = { /** Source URL for the preview image */ - previewSourceURL: string | number; + previewSourceURL: string | ImageSourcePropType; /** Any additional styles to apply */ style?: StyleProp; diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index 9c6f10c12935..b1b21b7a3e27 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -1,4 +1,5 @@ import Str from 'expensify-common/lib/str'; +import {ImageSourcePropType} from 'react-native'; import ReceiptDoc from '@assets/images/receipt-doc.png'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; import ReceiptHTML from '@assets/images/receipt-html.png'; @@ -9,8 +10,8 @@ import type {Transaction} from '@src/types/onyx'; import * as FileUtils from './fileDownload/FileUtils'; type ThumbnailAndImageURI = { - image: number | string; - thumbnail: number | string | null; + image: ImageSourcePropType | string; + thumbnail: ImageSourcePropType | string | null; transaction?: Transaction; isLocalFile?: boolean; }; @@ -29,7 +30,7 @@ type FileNameAndExtension = { */ function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string | null = null, receiptFileName: string | null = null): ThumbnailAndImageURI { if (Object.hasOwn(transaction?.pendingFields ?? {}, 'waypoints')) { - return {thumbnail: null, image: ReceiptGeneric as string | number, isLocalFile: true}; + return {thumbnail: null, image: ReceiptGeneric, isLocalFile: true}; } // URI to image, i.e. blob:new.expensify.com/9ef3a018-4067-47c6-b29f-5f1bd35f213d or expensify.com/receipts/w_e616108497ef940b7210ec6beb5a462d01a878f4.jpg @@ -67,7 +68,7 @@ function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string } const isLocalFile = typeof path === 'number' || path.startsWith('blob:') || path.startsWith('file:') || path.startsWith('/'); - return {thumbnail: image as string | number, image: path, isLocalFile}; + return {thumbnail: image, image: path, isLocalFile}; } // eslint-disable-next-line import/prefer-default-export diff --git a/src/libs/tryResolveUrlFromApiRoot.ts b/src/libs/tryResolveUrlFromApiRoot.ts index adf717d500be..c5503bc9476c 100644 --- a/src/libs/tryResolveUrlFromApiRoot.ts +++ b/src/libs/tryResolveUrlFromApiRoot.ts @@ -1,3 +1,4 @@ +import {ImageSourcePropType} from 'react-native'; import Config from '@src/CONFIG'; import type {Request} from '@src/types/onyx'; import * as ApiUtils from './ApiUtils'; @@ -18,12 +19,12 @@ const ORIGIN_PATTERN = new RegExp(`^(${ORIGINS_TO_REPLACE.join('|')})`); * - Unmatched URLs (non expensify) are returned with no modifications */ function tryResolveUrlFromApiRoot(url: string): string; -function tryResolveUrlFromApiRoot(url: number): number; -function tryResolveUrlFromApiRoot(url: string | number): string | number; -function tryResolveUrlFromApiRoot(url: string | number): string | number { +function tryResolveUrlFromApiRoot(url: ImageSourcePropType): number; +function tryResolveUrlFromApiRoot(url: string | ImageSourcePropType): string | ImageSourcePropType; +function tryResolveUrlFromApiRoot(url: string | ImageSourcePropType): string | ImageSourcePropType { // in native, when we import an image asset, it will have a number representation which can be used in `source` of Image // in this case we can skip the url resolving - if (typeof url === 'number') { + if (typeof url !== 'string') { return url; } const apiRoot = ApiUtils.getApiRoot({shouldUseSecure: false} as Request); From 4fcb65b0889ff76327038c2cc6ae86c56196286c Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 23 Jan 2024 10:40:36 +0100 Subject: [PATCH 13/15] Fix lint error --- src/components/ImageWithSizeCalculation.tsx | 2 +- src/components/ReportActionItem/ReportActionItemImage.tsx | 3 ++- src/components/ThumbnailImage.tsx | 2 +- src/libs/ReceiptUtils.ts | 2 +- src/libs/tryResolveUrlFromApiRoot.ts | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index 634492c8e602..d0559327274a 100644 --- a/src/components/ImageWithSizeCalculation.tsx +++ b/src/components/ImageWithSizeCalculation.tsx @@ -1,6 +1,6 @@ import delay from 'lodash/delay'; import React, {useEffect, useRef, useState} from 'react'; -import {ImageSourcePropType, StyleProp, ViewStyle} from 'react-native'; +import type {ImageSourcePropType, StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; import Log from '@libs/Log'; diff --git a/src/components/ReportActionItem/ReportActionItemImage.tsx b/src/components/ReportActionItem/ReportActionItemImage.tsx index d8cbc6b0afcb..710adeb1589e 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.tsx +++ b/src/components/ReportActionItem/ReportActionItemImage.tsx @@ -1,7 +1,8 @@ import Str from 'expensify-common/lib/str'; import React from 'react'; import type {ReactElement} from 'react'; -import {ImageSourcePropType, View} from 'react-native'; +import {View} from 'react-native'; +import type {ImageSourcePropType} from 'react-native'; import AttachmentModal from '@components/AttachmentModal'; import EReceiptThumbnail from '@components/EReceiptThumbnail'; import Image from '@components/Image'; diff --git a/src/components/ThumbnailImage.tsx b/src/components/ThumbnailImage.tsx index 753c4b783b9c..5950bae5205c 100644 --- a/src/components/ThumbnailImage.tsx +++ b/src/components/ThumbnailImage.tsx @@ -1,6 +1,6 @@ import lodashClamp from 'lodash/clamp'; import React, {useCallback, useState} from 'react'; -import {ImageSourcePropType, StyleProp, ViewStyle} from 'react-native'; +import type {ImageSourcePropType, StyleProp, ViewStyle} from 'react-native'; import {Dimensions, View} from 'react-native'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index b1b21b7a3e27..52f64b2defb1 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -1,5 +1,5 @@ import Str from 'expensify-common/lib/str'; -import {ImageSourcePropType} from 'react-native'; +import type {ImageSourcePropType} from 'react-native'; import ReceiptDoc from '@assets/images/receipt-doc.png'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; import ReceiptHTML from '@assets/images/receipt-html.png'; diff --git a/src/libs/tryResolveUrlFromApiRoot.ts b/src/libs/tryResolveUrlFromApiRoot.ts index c5503bc9476c..8eb5bdba0129 100644 --- a/src/libs/tryResolveUrlFromApiRoot.ts +++ b/src/libs/tryResolveUrlFromApiRoot.ts @@ -1,4 +1,4 @@ -import {ImageSourcePropType} from 'react-native'; +import type {ImageSourcePropType} from 'react-native'; import Config from '@src/CONFIG'; import type {Request} from '@src/types/onyx'; import * as ApiUtils from './ApiUtils'; From 033aaa1c4430e24c5892d01639200b14bf53b75e Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 23 Jan 2024 10:51:15 +0100 Subject: [PATCH 14/15] Fix lint error --- src/components/ReportActionItem/ReportActionItemImages.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/ReportActionItemImages.tsx b/src/components/ReportActionItem/ReportActionItemImages.tsx index 06edea95fb89..00b91bf4f862 100644 --- a/src/components/ReportActionItem/ReportActionItemImages.tsx +++ b/src/components/ReportActionItem/ReportActionItemImages.tsx @@ -72,7 +72,7 @@ function ReportActionItemImages({images, size, total, isHovered = false}: Report const borderStyle = shouldShowBorder ? styles.reportActionItemImageBorder : {}; return ( Date: Tue, 23 Jan 2024 13:30:19 +0100 Subject: [PATCH 15/15] Reuse ContextMenuAnchor type, use TranslationPaths type --- src/components/ReportActionItem/ReportPreview.tsx | 5 ++--- src/components/ReportActionItem/TaskPreview.tsx | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 42097cb84f4e..bb021fe52db1 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -26,14 +26,13 @@ import * as TransactionUtils from '@libs/TransactionUtils'; import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Policy, Report, ReportAction, Session, Transaction, TransactionViolations} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import ReportActionItemImages from './ReportActionItemImages'; -type PaymentVerbTranslationPath = 'iou.payerSpent' | 'iou.payerOwes' | 'iou.payerPaid'; - type ReportPreviewOnyxProps = { /** The policy tied to the money request report */ policy: OnyxEntry; @@ -202,7 +201,7 @@ function ReportPreview({ return translate('iou.managerApproved', {manager: payerOrApproverName}); } const managerName = isPolicyExpenseChat ? ReportUtils.getPolicyName(chatReport) : ReportUtils.getDisplayNameForParticipant(managerID, true); - let paymentVerb: PaymentVerbTranslationPath = hasNonReimbursableTransactions ? 'iou.payerSpent' : 'iou.payerOwes'; + let paymentVerb: TranslationPaths = hasNonReimbursableTransactions ? 'iou.payerSpent' : 'iou.payerOwes'; if (iouSettled || iouReport?.isWaitingOnBankAccount) { paymentVerb = 'iou.payerPaid'; } diff --git a/src/components/ReportActionItem/TaskPreview.tsx b/src/components/ReportActionItem/TaskPreview.tsx index cbd166d79d3a..16af68d9677c 100644 --- a/src/components/ReportActionItem/TaskPreview.tsx +++ b/src/components/ReportActionItem/TaskPreview.tsx @@ -1,7 +1,5 @@ import Str from 'expensify-common/lib/str'; import React from 'react'; -// eslint-disable-next-line no-restricted-imports -import type {Text as RNText} from 'react-native'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; @@ -24,6 +22,7 @@ import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import * as TaskUtils from '@libs/TaskUtils'; +import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import * as Session from '@userActions/Session'; import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; @@ -65,7 +64,7 @@ type TaskPreviewProps = WithCurrentUserPersonalDetailsProps & chatReportID: string; /** Popover context menu anchor, used for showing context menu */ - contextMenuAnchor: RNText | null; + contextMenuAnchor: ContextMenuAnchor; /** Callback for updating context menu active state, used for showing context menu */ checkIfContextMenuActive: () => void;