diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index c65faef53748..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 type {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'; @@ -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 aa5d0513f0d7..710adeb1589e 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.tsx +++ b/src/components/ReportActionItem/ReportActionItemImage.tsx @@ -2,6 +2,7 @@ import Str from 'expensify-common/lib/str'; import React from 'react'; import type {ReactElement} from 'react'; 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'; @@ -17,10 +18,10 @@ import type {Transaction} from '@src/types/onyx'; type ReportActionItemImageProps = { /** thumbnail URI for the image */ - thumbnail?: string | number; + 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/ReportActionItem/ReportActionItemImages.tsx b/src/components/ReportActionItem/ReportActionItemImages.tsx index c24defb8ac08..00b91bf4f862 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 @@ -79,7 +72,7 @@ function ReportActionItemImages({images, size, total, isHovered = false}: Report const borderStyle = shouldShowBorder ? styles.reportActionItemImageBorder : {}; return ( ; - /** The role of the current user in the policy */ - role: PropTypes.string, + /** ChatReport associated with iouReport */ + chatReport: OnyxEntry; - /** Whether Scheduled Submit is turned on for this policy */ - isHarvestingEnabled: PropTypes.bool, - }), + /** Active IOU Report for current report */ + iouReport: OnyxEntry; - /* Onyx Props */ - /** chatReport associated with iouReport */ - chatReport: reportPropTypes, + /** Session info for the currently logged in user. */ + session: OnyxEntry; - /** Extra styles to pass to View wrapper */ - // eslint-disable-next-line react/forbid-prop-types - containerStyles: PropTypes.arrayOf(PropTypes.object), + /** All the transactions, used to update ReportPreview label and status */ + transactions: OnyxCollection; - /** Active IOU Report for current report */ - iouReport: PropTypes.shape({ - /** AccountID of the manager in this iou report */ - managerID: PropTypes.number, + /** All of the transaction violations */ + transactionViolations: OnyxCollection; +}; - /** AccountID of the creator of this iou report */ - ownerAccountID: PropTypes.number, +type ReportPreviewProps = ReportPreviewOnyxProps & { + /** All the data of the action */ + action: ReportAction; - /** Outstanding amount in cents of this transaction */ - total: PropTypes.number, + /** The associated chatReport */ + chatReportID: string; - /** Currency of outstanding amount of this transaction */ - currency: PropTypes.string, + /** The active IOUReport, used for Onyx subscription */ + iouReportID: string; - /** Is the iouReport waiting for the submitter to add a credit bank account? */ - isWaitingOnBankAccount: PropTypes.bool, - }), + /** The report's policyID, used for Onyx subscription */ + policyID: string; - /** Session info for the currently logged in user. */ - session: PropTypes.shape({ - /** Currently logged in user accountID */ - accountID: PropTypes.number, - }), + /** 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, - - /** All the transactions, used to update ReportPreview label and status */ - transactions: PropTypes.objectOf(transactionPropTypes), + isWhisper?: boolean; - /** All of the transaction violations */ - transactionViolations: transactionViolationsPropType, - - ...withLocalizePropTypes, -}; - -const defaultProps = { - contextMenuAnchor: null, - chatReport: {}, - containerStyles: [], - iouReport: {}, - checkIfContextMenuActive: () => {}, - session: { - accountID: null, - }, - isWhisper: false, - transactionViolations: { - violations: [], - }, - policy: { - isHarvestingEnabled: false, - }, - transactions: {}, + /** Whether the corresponding report action item is hovered */ + isHovered?: boolean; }; -function ReportPreview(props) { +function ReportPreview({ + iouReport, + session, + policy, + iouReportID, + policyID, + chatReportID, + chatReport, + action, + containerStyles, + contextMenuAnchor, + transactions, + transactionViolations, + isHovered = false, + isWhisper = false, + checkIfContextMenuActive = () => {}, +}: ReportPreviewProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -143,49 +106,51 @@ function ReportPreview(props) { 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 = 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 isAutoReimbursable = ReportUtils.canBeAutoReimbursed(props.iouReport, props.policy); - - 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 isAutoReimbursable = ReportUtils.canBeAutoReimbursed(iouReport, policy); + + 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) || (canUseViolations && ReportUtils.hasViolations(props.iouReportID, props.transactionViolations)); + const hasErrors = (hasReceipts && hasMissingSmartscanFields) || (canUseViolations && ReportUtils.hasViolations(iouReportID, transactionViolations)); 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; - if (TransactionUtils.isPartialMerchant(formattedMerchant)) { + if (TransactionUtils.isPartialMerchant(formattedMerchant ?? '')) { formattedMerchant = 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 - numberOfScanningReceipts, scanningReceipts: numberOfScanningReceipts, }); @@ -194,63 +159,67 @@ 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: TranslationPaths = 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); - 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 && !isAutoReimbursable, - [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, isAutoReimbursable, props.iouReport], + () => isPayer && !isDraftExpenseReport && !iouSettled && !iouReport?.isWaitingOnBankAccount && reimbursableSpend !== 0 && !iouCanceled && !isAutoReimbursable, + [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, isAutoReimbursable, iouReport], ); const shouldShowApproveButton = useMemo(() => { if (!isPaidGroupPolicy) { @@ -260,25 +229,25 @@ 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)} + 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 && ( )} @@ -297,7 +266,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} @@ -342,7 +312,7 @@ function ReportPreview(props) { success={isWaitingForSubmissionFromCurrentUser} text={translate('common.submit')} style={styles.mt3} - onPress={() => IOU.submitReport(props.iouReport)} + onPress={() => iouReport && IOU.submitReport(iouReport)} /> )} @@ -353,30 +323,25 @@ 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, - }, - transactions: { - key: ONYXKEYS.COLLECTION.TRANSACTION, - }, - transactionViolations: { - key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, - }, - }), -)(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, + }, + transactions: { + key: ONYXKEYS.COLLECTION.TRANSACTION, + }, + transactionViolations: { + key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, + }, +})(ReportPreview); 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; diff --git a/src/components/ShowContextMenuContext.ts b/src/components/ShowContextMenuContext.ts index 17557051bef9..374ca8a2f1e5 100644 --- a/src/components/ShowContextMenuContext.ts +++ b/src/components/ShowContextMenuContext.ts @@ -1,15 +1,16 @@ 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'; 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/components/ThumbnailImage.tsx b/src/components/ThumbnailImage.tsx index a1778e2feaee..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 type {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'; @@ -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 7fca9f54b744..52f64b2defb1 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -73,3 +73,4 @@ function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string // eslint-disable-next-line import/prefer-default-export export {getThumbnailAndImageURIs}; +export type {ThumbnailAndImageURI}; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 58b0489314a0..228db29aea6c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -17,12 +17,11 @@ import ROUTES from '@src/ROUTES'; import type {Beta, PersonalDetails, PersonalDetailsList, Policy, PolicyReportField, Report, ReportAction, ReportMetadata, Session, Transaction, TransactionViolation} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; -import type {ChangeLog, IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from '@src/types/onyx/OriginalMessage'; +import type {ChangeLog, 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} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; @@ -1580,7 +1579,7 @@ function getPersonalDetailsForAccountID(accountID: number): Partial, + paymentType: PaymentMethodType, iouReportID = '', isSettlingUp = false, isSendMoneyFlow = false, diff --git a/src/libs/onyxSubscribe.ts b/src/libs/onyxSubscribe.ts index 4572ca35a4f2..af775842fc16 100644 --- a/src/libs/onyxSubscribe.ts +++ b/src/libs/onyxSubscribe.ts @@ -1,6 +1,6 @@ import type {ConnectOptions} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import type {OnyxKey} from '@src/ONYXKEYS'; +import type {OnyxCollectionKey, OnyxKey} from '@src/ONYXKEYS'; /** * Connect to onyx data. Same params as Onyx.connect(), but returns a function to unsubscribe. @@ -8,7 +8,7 @@ import type {OnyxKey} from '@src/ONYXKEYS'; * @param mapping Same as for Onyx.connect() * @return Unsubscribe callback */ -function onyxSubscribe(mapping: ConnectOptions) { +function onyxSubscribe(mapping: ConnectOptions) { const connectionId = Onyx.connect(mapping); return () => Onyx.disconnect(connectionId); } diff --git a/src/libs/tryResolveUrlFromApiRoot.ts b/src/libs/tryResolveUrlFromApiRoot.ts index adf717d500be..8eb5bdba0129 100644 --- a/src/libs/tryResolveUrlFromApiRoot.ts +++ b/src/libs/tryResolveUrlFromApiRoot.ts @@ -1,3 +1,4 @@ +import type {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); diff --git a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx index b28374fae04a..272548214ece 100644 --- a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx @@ -2,7 +2,7 @@ import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; /* eslint-disable no-restricted-imports */ -import type {EmitterSubscription, GestureResponderEvent, NativeTouchEvent, Text as RNText, View} from 'react-native'; +import type {EmitterSubscription, GestureResponderEvent, NativeTouchEvent, View} from 'react-native'; import {Dimensions} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import ConfirmModal from '@components/ConfirmModal'; @@ -16,7 +16,7 @@ import CONST from '@src/CONST'; import type {ReportAction} from '@src/types/onyx'; import BaseReportActionContextMenu from './BaseReportActionContextMenu'; import type {ContextMenuAction} from './ContextMenuActions'; -import type {ContextMenuType, ReportActionContextMenu} from './ReportActionContextMenu'; +import type {ContextMenuAnchor, ContextMenuType, ReportActionContextMenu} from './ReportActionContextMenu'; type Location = { x: number; @@ -67,7 +67,7 @@ function PopoverReportActionContextMenu(_props: never, ref: ForwardedRef(null); const anchorRef = useRef(null); const dimensionsEventListener = useRef(null); - const contextMenuAnchorRef = useRef(null); + const contextMenuAnchorRef = useRef(null); const contextMenuTargetNode = useRef(null); const onPopoverShow = useRef(() => {}); diff --git a/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts b/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts index 4c478d901c6a..d8570bd14510 100644 --- a/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts +++ b/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts @@ -16,11 +16,13 @@ type OnCancel = () => void; type ContextMenuType = ValueOf; +type ContextMenuAnchor = View | RNText | null | undefined; + type ShowContextMenu = ( type: ContextMenuType, event: GestureResponderEvent | MouseEvent, selection: string, - contextMenuAnchor: View | RNText | null, + contextMenuAnchor: ContextMenuAnchor, reportID?: string, reportActionID?: string, originalReportID?: string, @@ -99,7 +101,7 @@ function showContextMenu( type: ContextMenuType, event: GestureResponderEvent | MouseEvent, selection: string, - contextMenuAnchor: View | RNText | null, + contextMenuAnchor: ContextMenuAnchor, reportID = '0', reportActionID = '0', originalReportID = '0', @@ -180,4 +182,4 @@ function clearActiveReportAction() { } export {contextMenuRef, showContextMenu, hideContextMenu, isActiveReportAction, clearActiveReportAction, showDeleteModal, hideDeleteModal}; -export type {ContextMenuType, ShowContextMenu, ReportActionContextMenu}; +export type {ContextMenuType, ShowContextMenu, ReportActionContextMenu, ContextMenuAnchor}; diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 19c62f692c84..070b91e2d920 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' @@ -43,7 +45,7 @@ type IOUMessage = { lastModified?: string; participantAccountIDs?: number[]; type: ValueOf; - paymentType?: DeepValueOf; + paymentType?: PaymentMethodType; /** Only exists when we are sending money */ IOUDetails?: IOUDetails; }; @@ -284,4 +286,5 @@ export type { OriginalMessageAddComment, OriginalMessageSource, OriginalMessageReimbursementDequeued, + PaymentMethodType, }; 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;