diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index f0b6039512c1..1d95e93fed07 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -91,6 +91,9 @@ const propTypes = { /** Denotes whether it is a workspace avatar or not */ isWorkspaceAvatar: PropTypes.bool, + + /** Whether it is a receipt attachment or not */ + isReceiptAttachment: PropTypes.bool, }; const defaultProps = { @@ -108,6 +111,7 @@ const defaultProps = { onModalHide: () => {}, onCarouselAttachmentChange: () => {}, isWorkspaceAvatar: false, + isReceiptAttachment: false, }; function AttachmentModal(props) { @@ -119,7 +123,6 @@ function AttachmentModal(props) { const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false); const [isDeleteReceiptConfirmModalVisible, setIsDeleteReceiptConfirmModalVisible] = useState(false); const [isAuthTokenRequired, setIsAuthTokenRequired] = useState(props.isAuthTokenRequired); - const [isAttachmentReceipt, setIsAttachmentReceipt] = useState(null); const [attachmentInvalidReasonTitle, setAttachmentInvalidReasonTitle] = useState(''); const [attachmentInvalidReason, setAttachmentInvalidReason] = useState(null); const [source, setSource] = useState(props.source); @@ -155,7 +158,6 @@ function AttachmentModal(props) { (attachment) => { setSource(attachment.source); setFile(attachment.file); - setIsAttachmentReceipt(attachment.isReceipt); setIsAuthTokenRequired(attachment.isAuthTokenRequired); onCarouselAttachmentChange(attachment); }, @@ -358,7 +360,7 @@ function AttachmentModal(props) { const sourceForAttachmentView = props.source || source; const threeDotsMenuItems = useMemo(() => { - if (!isAttachmentReceipt || !props.parentReport || !props.parentReportActions) { + if (!props.isReceiptAttachment || !props.parentReport || !props.parentReportActions) { return []; } const menuItems = []; @@ -393,17 +395,17 @@ function AttachmentModal(props) { } return menuItems; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isAttachmentReceipt, props.parentReport, props.parentReportActions, props.policy, props.transaction, file]); + }, [props.isReceiptAttachment, props.parentReport, props.parentReportActions, props.policy, props.transaction, file]); // There are a few things that shouldn't be set until we absolutely know if the file is a receipt or an attachment. - // isAttachmentReceipt will be null until its certain what the file is, in which case it will then be true|false. + // props.isReceiptAttachment will be null until its certain what the file is, in which case it will then be true|false. let headerTitle = props.headerTitle; let shouldShowDownloadButton = false; let shouldShowThreeDotsButton = false; - if (!_.isNull(isAttachmentReceipt)) { - headerTitle = translate(isAttachmentReceipt ? 'common.receipt' : 'common.attachment'); - shouldShowDownloadButton = props.allowDownload && isDownloadButtonReadyToBeShown && !isAttachmentReceipt && !isOffline; - shouldShowThreeDotsButton = isAttachmentReceipt && isModalOpen; + if (!_.isNull(props.isReceiptAttachment)) { + headerTitle = translate(props.isReceiptAttachment ? 'common.receipt' : 'common.attachment'); + shouldShowDownloadButton = props.allowDownload && isDownloadButtonReadyToBeShown && !props.isReceiptAttachment && !isOffline; + shouldShowThreeDotsButton = props.isReceiptAttachment && isModalOpen; } return ( @@ -444,7 +446,7 @@ function AttachmentModal(props) { shouldOverlay /> - {!_.isEmpty(props.report) ? ( + {!_.isEmpty(props.report) && !props.isReceiptAttachment ? ( )} - {isAttachmentReceipt && ( + {props.isReceiptAttachment && ( )} - {!isAttachmentReceipt && ( + {!props.isReceiptAttachment && ( { - if (!ReportActionsUtils.shouldReportActionBeVisible(action, key)) { + if (!ReportActionsUtils.shouldReportActionBeVisible(action, key) || ReportActionsUtils.isMoneyRequestAction(action)) { return; } - // We're handling receipts differently here because receipt images are not - // part of the report action message, the images are constructed client-side - if (ReportActionsUtils.isMoneyRequestAction(action)) { - const transactionID = lodashGet(action, ['originalMessage', 'IOUTransactionID']); - if (!transactionID) { - return; - } - - if (TransactionUtils.hasReceipt(transaction)) { - const {image} = ReceiptUtils.getThumbnailAndImageURIs(transaction); - const isLocalFile = typeof image === 'string' && _.some(CONST.ATTACHMENT_LOCAL_URL_PREFIX, (prefix) => image.startsWith(prefix)); - attachments.unshift({ - source: tryResolveUrlFromApiRoot(image), - isAuthTokenRequired: !isLocalFile, - file: {name: transaction.filename}, - isReceipt: true, - transactionID, - }); - return; - } - } - const decision = _.get(action, ['message', 0, 'moderationDecision', 'decision'], ''); const hasBeenFlagged = decision === CONST.MODERATION.MODERATOR_DECISION_PENDING_HIDE || decision === CONST.MODERATION.MODERATOR_DECISION_HIDDEN; const html = _.get(action, ['message', 0, 'html'], '').replace('/>', `data-flagged="${hasBeenFlagged}" data-id="${action.reportActionID}"/>`); diff --git a/src/components/Attachments/AttachmentCarousel/index.js b/src/components/Attachments/AttachmentCarousel/index.js index 58a4b684815d..4e4bbfd74969 100644 --- a/src/components/Attachments/AttachmentCarousel/index.js +++ b/src/components/Attachments/AttachmentCarousel/index.js @@ -1,4 +1,3 @@ -import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {FlatList, Keyboard, PixelRatio, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; @@ -29,7 +28,7 @@ const viewabilityConfig = { itemVisiblePercentThreshold: 95, }; -function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate, setDownloadButtonVisibility, translate, transaction}) { +function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate, setDownloadButtonVisibility, translate}) { const theme = useTheme(); const styles = useThemeStyles(); const scrollRef = useRef(null); @@ -41,21 +40,12 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, const [attachments, setAttachments] = useState([]); const [activeSource, setActiveSource] = useState(source); const [shouldShowArrows, setShouldShowArrows, autoHideArrows, cancelAutoHideArrows] = useCarouselArrows(); - const [isReceipt, setIsReceipt] = useState(false); - const compareImage = useCallback( - (attachment) => { - if (attachment.isReceipt && isReceipt) { - return attachment.transactionID === transaction.transactionID; - } - return attachment.source === source; - }, - [source, isReceipt, transaction], - ); + const compareImage = useCallback((attachment) => attachment.source === source, [source]); useEffect(() => { const parentReportAction = parentReportActions[report.parentReportActionID]; - const attachmentsFromReport = extractAttachmentsFromReport(parentReportAction, reportActions, transaction); + const attachmentsFromReport = extractAttachmentsFromReport(parentReportAction, reportActions); const initialPage = _.findIndex(attachmentsFromReport, compareImage); @@ -90,12 +80,10 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, // to get the index of the current page const entry = _.first(viewableItems); if (!entry) { - setIsReceipt(false); setActiveSource(null); return; } - setIsReceipt(entry.item.isReceipt); setPage(entry.index); setActiveSource(entry.item.source); @@ -244,15 +232,6 @@ export default compose( canEvict: false, }, }), - // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file - withOnyx({ - transaction: { - key: ({report, parentReportActions}) => { - const parentReportAction = lodashGet(parentReportActions, [report.parentReportActionID]); - return `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(parentReportAction, 'originalMessage.IOUTransactionID', 0)}`; - }, - }, - }), withLocalize, withWindowDimensions, )(AttachmentCarousel); diff --git a/src/components/Attachments/AttachmentCarousel/index.native.js b/src/components/Attachments/AttachmentCarousel/index.native.js index 6bf4e63c01e7..4a62335a492d 100644 --- a/src/components/Attachments/AttachmentCarousel/index.native.js +++ b/src/components/Attachments/AttachmentCarousel/index.native.js @@ -1,4 +1,3 @@ -import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {Keyboard, PixelRatio, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; @@ -18,7 +17,7 @@ import extractAttachmentsFromReport from './extractAttachmentsFromReport'; import AttachmentCarouselPager from './Pager'; import useCarouselArrows from './useCarouselArrows'; -function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate, setDownloadButtonVisibility, translate, transaction, onClose}) { +function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate, setDownloadButtonVisibility, translate, onClose}) { const styles = useThemeStyles(); const pagerRef = useRef(null); @@ -28,21 +27,12 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, const [activeSource, setActiveSource] = useState(source); const [isPinchGestureRunning, setIsPinchGestureRunning] = useState(true); const [shouldShowArrows, setShouldShowArrows, autoHideArrows, cancelAutoHideArrows] = useCarouselArrows(); - const [isReceipt, setIsReceipt] = useState(false); - const compareImage = useCallback( - (attachment) => { - if (attachment.isReceipt && isReceipt) { - return attachment.transactionID === transaction.transactionID; - } - return attachment.source === source; - }, - [source, isReceipt, transaction], - ); + const compareImage = useCallback((attachment) => attachment.source === source, [source]); useEffect(() => { const parentReportAction = parentReportActions[report.parentReportActionID]; - const attachmentsFromReport = extractAttachmentsFromReport(parentReportAction, reportActions, transaction); + const attachmentsFromReport = extractAttachmentsFromReport(parentReportAction, reportActions); const initialPage = _.findIndex(attachmentsFromReport, compareImage); @@ -77,9 +67,7 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, const item = attachments[newPageIndex]; setPage(newPageIndex); - setIsReceipt(item.isReceipt); setActiveSource(item.source); - onNavigate(item); }, [setShouldShowArrows, attachments, onNavigate], @@ -186,14 +174,5 @@ export default compose( canEvict: false, }, }), - // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file - withOnyx({ - transaction: { - key: ({report, parentReportActions}) => { - const parentReportAction = lodashGet(parentReportActions, [report.parentReportActionID]); - return `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(parentReportAction, 'originalMessage.IOUTransactionID', 0)}`; - }, - }, - }), withLocalize, )(AttachmentCarousel); diff --git a/src/components/BigNumberPad.js b/src/components/BigNumberPad.tsx similarity index 64% rename from src/components/BigNumberPad.js rename to src/components/BigNumberPad.tsx index df3ac8fd41fb..812e9e78635b 100644 --- a/src/components/BigNumberPad.js +++ b/src/components/BigNumberPad.tsx @@ -1,29 +1,20 @@ -import PropTypes from 'prop-types'; import React, {useState} from 'react'; import {View} from 'react-native'; -import _ from 'underscore'; +import useLocalize from '@hooks/useLocalize'; import useWindowDimensions from '@hooks/useWindowDimensions'; import ControlSelection from '@libs/ControlSelection'; import useThemeStyles from '@styles/useThemeStyles'; import Button from './Button'; -import withLocalize, {withLocalizePropTypes} from './withLocalize'; -const propTypes = { +type BigNumberPadProps = { /** Callback to inform parent modal with key pressed */ - numberPressed: PropTypes.func.isRequired, + numberPressed: (key: string) => void; /** Callback to inform parent modal whether user is long pressing the "<" (backspace) button */ - longPressHandlerStateChanged: PropTypes.func, + longPressHandlerStateChanged?: (isUserLongPressingBackspace: boolean) => void; /** Used to locate this view from native classes. */ - id: PropTypes.string, - - ...withLocalizePropTypes, -}; - -const defaultProps = { - longPressHandlerStateChanged: () => {}, - id: 'numPadView', + id?: string; }; const padNumbers = [ @@ -31,62 +22,69 @@ const padNumbers = [ ['4', '5', '6'], ['7', '8', '9'], ['.', '0', '<'], -]; +] as const; + +function BigNumberPad({numberPressed, longPressHandlerStateChanged = () => {}, id = 'numPadView'}: BigNumberPadProps) { + const {toLocaleDigit} = useLocalize(); -function BigNumberPad(props) { const styles = useThemeStyles(); - const [timer, setTimer] = useState(null); + const [timer, setTimer] = useState(null); const {isExtraSmallScreenHeight} = useWindowDimensions(); /** * Handle long press key on number pad. * Only handles the '<' key and starts the continuous input timer. - * - * @param {String} key */ - const handleLongPress = (key) => { + const handleLongPress = (key: string) => { if (key !== '<') { return; } - props.longPressHandlerStateChanged(true); + longPressHandlerStateChanged(true); const newTimer = setInterval(() => { - props.numberPressed(key); + numberPressed(key); }, 100); + setTimer(newTimer); }; return ( - {_.map(padNumbers, (row, rowIndex) => ( + {padNumbers.map((row) => ( - {_.map(row, (column, columnIndex) => { + {row.map((column, columnIndex) => { // Adding margin between buttons except first column to // avoid unccessary space before the first column. const marginLeft = columnIndex > 0 ? styles.ml3 : {}; + return (