From ffb21f156543aa780d9ab15585952a916aea2c9d Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 9 Mar 2024 03:25:09 +0530 Subject: [PATCH 001/932] add POC for fallingback to default attachment view when pdf fails to load --- .../BaseAttachmentViewPdf.js | 4 + .../DefaultAttachmentView.tsx | 59 +++++++++++++ .../AttachmentView/AttachmentViewPdf/index.js | 3 +- .../AttachmentViewPdf/propTypes.js | 8 ++ .../Attachments/AttachmentView/index.js | 86 ++++++++++++------- .../InvertedFlatList/BaseInvertedFlatList.tsx | 2 +- src/components/PDFView/WebPDFDocument.js | 4 +- src/components/PDFView/index.js | 1 + src/components/PDFView/index.native.js | 59 ++++++++++--- src/styles/index.ts | 14 +++ 10 files changed, 192 insertions(+), 48 deletions(-) create mode 100644 src/components/Attachments/AttachmentView/AttachmentViewPdf/DefaultAttachmentView.tsx diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js index 2f16b63aacc6..3f3b995c02ba 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js @@ -28,6 +28,8 @@ function BaseAttachmentViewPdf({ onLoadComplete, errorLabelStyles, style, + isUsedAsChatAttachment, + onError, }) { const attachmentCarouselPagerContext = useContext(AttachmentCarouselPagerContext); const isScrollEnabled = attachmentCarouselPagerContext === null ? undefined : attachmentCarouselPagerContext.isScrollEnabled; @@ -89,6 +91,8 @@ function BaseAttachmentViewPdf({ onScaleChanged={onScaleChanged} onLoadComplete={onLoadComplete} errorLabelStyles={errorLabelStyles} + isUsedAsChatAttachment={isUsedAsChatAttachment} + onError={onError} /> ); } diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/DefaultAttachmentView.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/DefaultAttachmentView.tsx new file mode 100644 index 000000000000..ad102fd5caad --- /dev/null +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/DefaultAttachmentView.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import {ActivityIndicator, View} from 'react-native'; +import type {StyleProp, ViewStyle} from 'react-native'; +import _ from 'underscore'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; +import Text from '@components/Text'; +import Tooltip from '@components/Tooltip'; +import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; + +type DefaultAttachmentViewProps = { + file: File; + shouldShowLoadingSpinnerIcon: boolean; + shouldShowDownloadIcon: boolean; + containerStyles: StyleProp[]; +}; + +function DefaultAttachmentView({file, shouldShowLoadingSpinnerIcon, shouldShowDownloadIcon, containerStyles}: DefaultAttachmentViewProps) { + const theme = useTheme(); + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + return ( + + + + + + {file && file.name} + {!shouldShowLoadingSpinnerIcon && shouldShowDownloadIcon && ( + + + + + + )} + {shouldShowLoadingSpinnerIcon && ( + + + + + + )} + + ); +} + +export default DefaultAttachmentView; diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js index d6a402613c34..de740f4fdd1f 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js @@ -2,7 +2,7 @@ import React, {memo} from 'react'; import PDFView from '@components/PDFView'; import {attachmentViewPdfDefaultProps, attachmentViewPdfPropTypes} from './propTypes'; -function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, errorLabelStyles, style}) { +function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, errorLabelStyles, style, isUsedAsChatAttachment, onError}) { return ( ); } diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js index a34010f0ba8b..bcb0fabde352 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js @@ -15,6 +15,12 @@ const attachmentViewPdfPropTypes = { /** Styles for the error label */ errorLabelStyles: stylePropTypes, + + /** Callback when the pdf fails to load */ + onError: PropTypes.func, + + /** Whether the attachment is used as a chat attachment */ + isUsedAsChatAttachment: PropTypes.bool, }; const attachmentViewPdfDefaultProps = { @@ -23,6 +29,8 @@ const attachmentViewPdfDefaultProps = { }, style: [], errorLabelStyles: [], + onError: () => {}, + isUsedAsChatAttachment: false, }; export {attachmentViewPdfPropTypes, attachmentViewPdfDefaultProps}; diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index f6a56dc73088..76e546be8930 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -1,16 +1,13 @@ import Str from 'expensify-common/lib/str'; import PropTypes from 'prop-types'; import React, {memo, useEffect, useState} from 'react'; -import {ActivityIndicator, ScrollView, View} from 'react-native'; +import {ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import * as AttachmentsPropTypes from '@components/Attachments/propTypes'; import DistanceEReceipt from '@components/DistanceEReceipt'; import EReceipt from '@components/EReceipt'; import Icon from '@components/Icon'; -import * as Expensicons from '@components/Icon/Expensicons'; -import Text from '@components/Text'; -import Tooltip from '@components/Tooltip'; import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -25,6 +22,7 @@ import variables from '@styles/variables'; import ONYXKEYS from '@src/ONYXKEYS'; import AttachmentViewImage from './AttachmentViewImage'; import AttachmentViewPdf from './AttachmentViewPdf'; +import DefaultAttachmentView from './AttachmentViewPdf/DefaultAttachmentView'; import AttachmentViewVideo from './AttachmentViewVideo'; import {attachmentViewDefaultProps, attachmentViewPropTypes} from './propTypes'; @@ -107,6 +105,7 @@ function AttachmentView({ const StyleUtils = useStyleUtils(); const [loadComplete, setLoadComplete] = useState(false); const isVideo = (typeof source === 'string' && Str.isVideo(source)) || (file && Str.isVideo(file.name)); + const [isPdfFailedToLoad, setIsPdfFailedToLoad] = useState(false); useEffect(() => { if (!isFocused && !(file && isUsedInAttachmentModal)) { @@ -169,6 +168,17 @@ function AttachmentView({ } }; + if (isPdfFailedToLoad) { + return ( + + ); + } + // We need the following View component on android native // So that the event will propagate properly and // the Password protected preview will be shown for pdf attachement we are about to send. @@ -186,6 +196,10 @@ function AttachmentView({ errorLabelStyles={isUsedInAttachmentModal ? [styles.textLabel, styles.textLarge] : [styles.cursorAuto]} style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1} isUsedInCarousel={isUsedInCarousel} + isUsedAsChatAttachment={!(isUsedInAttachmentModal || isUsedInCarousel)} + onError={() => { + setIsPdfFailedToLoad(true); + }} /> ); @@ -229,36 +243,42 @@ function AttachmentView({ } return ( - - - - + + // + // + // + // - {file && file.name} - {!shouldShowLoadingSpinnerIcon && shouldShowDownloadIcon && ( - - - - - - )} - {shouldShowLoadingSpinnerIcon && ( - - - - - - )} - + // {file && file.name} + // {!shouldShowLoadingSpinnerIcon && shouldShowDownloadIcon && ( + // + // + // + // + // + // )} + // {shouldShowLoadingSpinnerIcon && ( + // + // + // + // + // + // )} + // ); } diff --git a/src/components/InvertedFlatList/BaseInvertedFlatList.tsx b/src/components/InvertedFlatList/BaseInvertedFlatList.tsx index e28400505280..d0a4322300c3 100644 --- a/src/components/InvertedFlatList/BaseInvertedFlatList.tsx +++ b/src/components/InvertedFlatList/BaseInvertedFlatList.tsx @@ -4,7 +4,7 @@ import type {FlatListProps} from 'react-native'; import FlatList from '@components/FlatList'; const WINDOW_SIZE = 15; -const AUTOSCROLL_TO_TOP_THRESHOLD = 128; +const AUTOSCROLL_TO_TOP_THRESHOLD = 250; function BaseInvertedFlatList(props: FlatListProps, ref: ForwardedRef) { return ( diff --git a/src/components/PDFView/WebPDFDocument.js b/src/components/PDFView/WebPDFDocument.js index dd9d1e066b19..d2e93e3ba506 100644 --- a/src/components/PDFView/WebPDFDocument.js +++ b/src/components/PDFView/WebPDFDocument.js @@ -48,6 +48,8 @@ const propTypes = { * - `undefined` if password isn't needed to view the PDF file * - `null` if the password is required but hasn't been provided yet */ password: PropTypes.string, + /** Callback invoked when the PDF document fails to load */ + onError: PropTypes.func.isRequired, }; const defaultProps = { @@ -95,7 +97,7 @@ const WebPDFDocument = memo( return ( } - error={{translate('attachmentView.failedToLoadPDF')}} + onLoadError={onError} file={sourceURL} options={{ cMapUrl: 'cmaps/', diff --git a/src/components/PDFView/index.js b/src/components/PDFView/index.js index 9706f8e06cc1..2e39594fc210 100644 --- a/src/components/PDFView/index.js +++ b/src/components/PDFView/index.js @@ -270,6 +270,7 @@ class PDFView extends Component { pageWidth={pageWidth} password={this.state.password} initiatePasswordChallenge={this.initiatePasswordChallenge} + onError={this.props.onError} /> {(this.state.password === PDFViewConstants.REQUIRED_PASSWORD_MISSING || this.state.isCheckingPassword) && ( diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index 558f6636a325..6c0f5acd1081 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -2,17 +2,22 @@ import React, {useCallback, useEffect, useState} from 'react'; import {View} from 'react-native'; import PDF from 'react-native-pdf'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; import KeyboardAvoidingView from '@components/KeyboardAvoidingView'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; -import Text from '@components/Text'; import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import variables from '@styles/variables'; import CONST from '@src/CONST'; import PDFPasswordForm from './PDFPasswordForm'; import {defaultProps, propTypes as pdfViewPropTypes} from './pdfViewPropTypes'; +import Text from '@components/Text'; const propTypes = { ...pdfViewPropTypes, @@ -33,7 +38,10 @@ const propTypes = { * is (temporarily) rendered. */ -function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused, onScaleChanged, sourceURL, errorLabelStyles}) { +const THUMBNAIL_HEIGHT = 250; +const THUMBNAIL_WIDTH = 250; + +function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused, onScaleChanged, sourceURL, errorLabelStyles, onError, isUsedAsChatAttachment}) { const [shouldRequestPassword, setShouldRequestPassword] = useState(false); const [shouldAttemptPDFLoad, setShouldAttemptPDFLoad] = useState(true); const [shouldShowLoadingIndicator, setShouldShowLoadingIndicator] = useState(true); @@ -46,6 +54,8 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused const themeStyles = useThemeStyles(); const {isKeyboardShown} = useKeyboardState(); const StyleUtils = useStyleUtils(); + const {isOffline} = useNetwork(); + const theme = useTheme(); useEffect(() => { onToggleKeyboard(isKeyboardShown); @@ -84,6 +94,7 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused setShouldShowLoadingIndicator(false); setShouldRequestPassword(false); setShouldAttemptPDFLoad(false); + onError(error); }; /** @@ -113,9 +124,33 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused setSuccessToLoadPDF(true); onLoadComplete(path); }; + const rendeFailedToLoadPDF = () => { + if (!isUsedAsChatAttachment) { + return ( + + {translate('attachmentView.failedToLoadPDF')} + + ); + } + + return ( + + + + + + ); + }; function renderPDFView() { - const pdfStyles = [themeStyles.imageModalPDF, StyleUtils.getWidthAndHeightStyle(windowWidth, windowHeight)]; + const pdfHeight = isUsedAsChatAttachment ? THUMBNAIL_HEIGHT : windowHeight; + const pdfWeight = isUsedAsChatAttachment ? THUMBNAIL_WIDTH : windowWidth; + const pdfStyles = [StyleUtils.getWidthAndHeightStyle(pdfWeight, pdfHeight), themeStyles.imageModalPDF]; // If we haven't yet successfully validated the password and loaded the PDF, // then we need to hide the react-native-pdf/PDF component so that PDFPasswordForm @@ -124,22 +159,18 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused if (shouldRequestPassword) { pdfStyles.push(themeStyles.invisible); } - - const containerStyles = shouldRequestPassword && isSmallScreenWidth ? [themeStyles.w100, themeStyles.flex1] : [themeStyles.alignItemsCenter, themeStyles.flex1]; + const containerStyles = + (shouldRequestPassword && isSmallScreenWidth) || isUsedAsChatAttachment ? [themeStyles.w100, themeStyles.flex1] : [themeStyles.alignItemsCenter, themeStyles.flex1]; + const loadingIndicatorStyles = isUsedAsChatAttachment ? [themeStyles.chatItemPDFAttachmentLoading, StyleUtils.getWidthAndHeightStyle(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)] : []; return ( - {failedToLoadPDF && ( - - {translate('attachmentView.failedToLoadPDF')} - - )} {shouldAttemptPDFLoad && ( } - source={{uri: sourceURL, cache: true, expiration: 864000}} + renderActivityIndicator={() => } + source={{uri: sourceURL}} style={pdfStyles} onError={handleFailureToLoadPDF} password={password} @@ -162,6 +193,10 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused ); } + // // uncomment this code to see failedToLoadPDF + // if (failedToLoadPDF) { + // return rendeFailedToLoadPDF(); + // } return onPress && !successToLoadPDF ? ( borderRadius: variables.buttonBorderRadius, }, + componentBorderRadiusNormal: { + borderRadius: variables.componentBorderRadiusNormal, + }, + bottomTabBarContainer: { flexDirection: 'row', height: variables.bottomTabHeight, @@ -2111,6 +2115,16 @@ const styles = (theme: ThemeColors) => width: 200, }, + chatItemPDFAttachmentLoading: { + backgroundColor: 'transparent', + borderColor: theme.border, + borderWidth: 1, + borderRadius: variables.componentBorderRadiusNormal, + textAlign: 'center', + verticalAlign: 'middle', + opacity: 1, + }, + sidebarVisible: { borderRightWidth: 1, }, From bde48faf072512f5d79d29ce293ae83b44241171 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 9 Mar 2024 03:26:09 +0530 Subject: [PATCH 002/932] adds chnages for attachement view --- .../Attachments/AttachmentView/index.js | 45 +------------------ 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 76e546be8930..f15b235144cb 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -12,7 +12,6 @@ import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContex import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CachedPDFPaths from '@libs/actions/CachedPDFPaths'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; @@ -100,7 +99,6 @@ function AttachmentView({ optionalVideoDuration, }) { const {updateCurrentlyPlayingURL} = usePlaybackContext(); - const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const [loadComplete, setLoadComplete] = useState(false); @@ -155,7 +153,7 @@ function AttachmentView({ // Check both source and file.name since PDFs dragged into the text field // will appear with a source that is a blob - if ((_.isString(source) && Str.isPDF(source)) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename')))) { + if (!isPdfFailedToLoad && ((_.isString(source) && Str.isPDF(source)) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename'))))) { const encryptedSourceUrl = isAuthTokenRequired ? addEncryptedAuthTokenToURL(source) : source; const onPDFLoadComplete = (path) => { @@ -168,17 +166,6 @@ function AttachmentView({ } }; - if (isPdfFailedToLoad) { - return ( - - ); - } - // We need the following View component on android native // So that the event will propagate properly and // the Password protected preview will be shown for pdf attachement we are about to send. @@ -249,36 +236,6 @@ function AttachmentView({ shouldShowLoadingSpinnerIcon={shouldShowLoadingSpinnerIcon} containerStyles={containerStyles} /> - // - // - // - // - - // {file && file.name} - // {!shouldShowLoadingSpinnerIcon && shouldShowDownloadIcon && ( - // - // - // - // - // - // )} - // {shouldShowLoadingSpinnerIcon && ( - // - // - // - // - // - // )} - // ); } From 757661bf2c41ad8f7bc0abeed6102202890c7679 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 9 Mar 2024 03:32:34 +0530 Subject: [PATCH 003/932] formatting --- src/components/PDFView/index.native.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index 6c0f5acd1081..492a12e1dc66 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -6,6 +6,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import KeyboardAvoidingView from '@components/KeyboardAvoidingView'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; +import Text from '@components/Text'; import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -17,7 +18,6 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import PDFPasswordForm from './PDFPasswordForm'; import {defaultProps, propTypes as pdfViewPropTypes} from './pdfViewPropTypes'; -import Text from '@components/Text'; const propTypes = { ...pdfViewPropTypes, From 26c1f13c9fd6b7ede36ecf38c1d17e0ebb69fd7b Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 9 Mar 2024 03:41:08 +0530 Subject: [PATCH 004/932] fixes conflicts --- .../Attachments/AttachmentView/index.js | 435 ++++++++++-------- 1 file changed, 247 insertions(+), 188 deletions(-) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 492a12e1dc66..461548f0d2b1 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -1,219 +1,278 @@ -import React, {useCallback, useEffect, useState} from 'react'; -import {View} from 'react-native'; -import PDF from 'react-native-pdf'; -import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import Str from 'expensify-common/lib/str'; +import PropTypes from 'prop-types'; +import React, {memo, useEffect, useState} from 'react'; +import {ActivityIndicator, View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; +import * as AttachmentsPropTypes from '@components/Attachments/propTypes'; +import DistanceEReceipt from '@components/DistanceEReceipt'; +import EReceipt from '@components/EReceipt'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; -import KeyboardAvoidingView from '@components/KeyboardAvoidingView'; -import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; +import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; -import useKeyboardState from '@hooks/useKeyboardState'; -import useLocalize from '@hooks/useLocalize'; +import Tooltip from '@components/Tooltip'; +import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext'; +import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import useWindowDimensions from '@hooks/useWindowDimensions'; +import * as CachedPDFPaths from '@libs/actions/CachedPDFPaths'; +import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; +import compose from '@libs/compose'; +import * as TransactionUtils from '@libs/TransactionUtils'; import variables from '@styles/variables'; -import CONST from '@src/CONST'; -import PDFPasswordForm from './PDFPasswordForm'; -import {defaultProps, propTypes as pdfViewPropTypes} from './pdfViewPropTypes'; +import ONYXKEYS from '@src/ONYXKEYS'; +import AttachmentViewImage from './AttachmentViewImage'; +import AttachmentViewPdf from './AttachmentViewPdf'; +import AttachmentViewVideo from './AttachmentViewVideo'; +import {attachmentViewDefaultProps, attachmentViewPropTypes} from './propTypes'; const propTypes = { - ...pdfViewPropTypes, + ...attachmentViewPropTypes, + ...withLocalizePropTypes, + + /** URL to full-sized attachment, SVG function, or numeric static image on native platforms */ + source: AttachmentsPropTypes.attachmentSourcePropType.isRequired, + + /** Flag to show/hide download icon */ + shouldShowDownloadIcon: PropTypes.bool, + + /** Flag to show the loading indicator */ + shouldShowLoadingSpinnerIcon: PropTypes.bool, + + /** Notify parent that the UI should be modified to accommodate keyboard */ + onToggleKeyboard: PropTypes.func, + + /** Extra styles to pass to View wrapper */ + // eslint-disable-next-line react/forbid-prop-types + containerStyles: PropTypes.arrayOf(PropTypes.object), + + /** Denotes whether it is a workspace avatar or not */ + isWorkspaceAvatar: PropTypes.bool, + + /** Denotes whether it is an icon (ex: SVG) */ + maybeIcon: PropTypes.bool, + + /** The id of the transaction related to the attachment */ + // eslint-disable-next-line react/no-unused-prop-types + transactionID: PropTypes.string, + + /** The id of the report action related to the attachment */ + reportActionID: PropTypes.string, + + isHovered: PropTypes.bool, + + optionalVideoDuration: PropTypes.number, }; -/** - * On the native layer, we use react-native-pdf/PDF to display PDFs. If a PDF is - * password-protected we render a PDFPasswordForm to request a password - * from the user. - * - * In order to render things nicely during a password challenge we need - * to keep track of additional state. In particular, the - * react-native-pdf/PDF component is both conditionally rendered and hidden - * depending upon the situation. It needs to be rerendered on each password - * submission because it doesn't dynamically handle updates to its - * password property. And we need to hide it during password challenges - * so that PDFPasswordForm doesn't bounce when react-native-pdf/PDF - * is (temporarily) rendered. - */ - -const THUMBNAIL_HEIGHT = 250; -const THUMBNAIL_WIDTH = 250; - -function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused, onScaleChanged, sourceURL, errorLabelStyles, onError, isUsedAsChatAttachment}) { - const [shouldRequestPassword, setShouldRequestPassword] = useState(false); - const [shouldAttemptPDFLoad, setShouldAttemptPDFLoad] = useState(true); - const [shouldShowLoadingIndicator, setShouldShowLoadingIndicator] = useState(true); - const [isPasswordInvalid, setIsPasswordInvalid] = useState(false); - const [failedToLoadPDF, setFailedToLoadPDF] = useState(false); - const [successToLoadPDF, setSuccessToLoadPDF] = useState(false); - const [password, setPassword] = useState(''); - const {windowWidth, windowHeight, isSmallScreenWidth} = useWindowDimensions(); - const {translate} = useLocalize(); - const themeStyles = useThemeStyles(); - const {isKeyboardShown} = useKeyboardState(); - const StyleUtils = useStyleUtils(); - const {isOffline} = useNetwork(); +const defaultProps = { + ...attachmentViewDefaultProps, + shouldShowDownloadIcon: false, + shouldShowLoadingSpinnerIcon: false, + onToggleKeyboard: () => {}, + containerStyles: [], + isWorkspaceAvatar: false, + maybeIcon: false, + transactionID: '', + reportActionID: '', + isHovered: false, + optionalVideoDuration: 0, +}; + +function AttachmentView({ + source, + file, + isAuthTokenRequired, + onPress, + shouldShowLoadingSpinnerIcon, + shouldShowDownloadIcon, + containerStyles, + onToggleKeyboard, + translate, + isFocused, + isUsedInCarousel, + isUsedInAttachmentModal, + isWorkspaceAvatar, + maybeIcon, + fallbackSource, + transaction, + reportActionID, + isHovered, + optionalVideoDuration, +}) { + const {updateCurrentlyPlayingURL} = usePlaybackContext(); const theme = useTheme(); + const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + const [loadComplete, setLoadComplete] = useState(false); + const isVideo = (typeof source === 'string' && Str.isVideo(source)) || (file && Str.isVideo(file.name)); useEffect(() => { - onToggleKeyboard(isKeyboardShown); - }); - - /** - * Initiate password challenge if message received from react-native-pdf/PDF - * indicates that a password is required or invalid. - * - * For a password challenge the message is "Password required or incorrect password." - * Note that the message doesn't specify whether the password is simply empty or - * invalid. - */ - - const initiatePasswordChallenge = useCallback(() => { - setShouldShowLoadingIndicator(false); - - // Render password form, and don't render PDF and loading indicator. - setShouldRequestPassword(true); - setShouldAttemptPDFLoad(false); - - // The message provided by react-native-pdf doesn't indicate whether this - // is an initial password request or if the password is invalid. So we just assume - // that if a password was already entered then it's an invalid password error. - if (password) { - setIsPasswordInvalid(true); - } - }, [password]); - - const handleFailureToLoadPDF = (error) => { - if (error.message.match(/password/i)) { - initiatePasswordChallenge(); + if (!isFocused && !(file && isUsedInAttachmentModal)) { return; } - setFailedToLoadPDF(true); - setShouldShowLoadingIndicator(false); - setShouldRequestPassword(false); - setShouldAttemptPDFLoad(false); - onError(error); - }; - - /** - * When the password is submitted via PDFPasswordForm, save the password - * in state and attempt to load the PDF. Also show the loading indicator - * since react-native-pdf/PDF will need to reload the PDF. - * - * @param {String} pdfPassword Password submitted via PDFPasswordForm - */ - const attemptPDFLoadWithPassword = (pdfPassword) => { - // Render react-native-pdf/PDF so that it can validate the password. - // Note that at this point in the password challenge, shouldRequestPassword is true. - // Thus react-native-pdf/PDF will be rendered - but not visible. - setPassword(pdfPassword); - setShouldAttemptPDFLoad(true); - setShouldShowLoadingIndicator(true); - }; - /** - * After the PDF is successfully loaded hide PDFPasswordForm and the loading - * indicator. - * @param {Number} numberOfPages - * @param {Number} path - Path to cache location - */ - const finishPDFLoad = (numberOfPages, path) => { - setShouldRequestPassword(false); - setShouldShowLoadingIndicator(false); - setSuccessToLoadPDF(true); - onLoadComplete(path); - }; - const rendeFailedToLoadPDF = () => { - if (!isUsedAsChatAttachment) { - return ( - - {translate('attachmentView.failedToLoadPDF')} - - ); + updateCurrentlyPlayingURL(isVideo ? source : null); + }, [isFocused, isVideo, source, updateCurrentlyPlayingURL, file, isUsedInAttachmentModal]); + + const [imageError, setImageError] = useState(false); + + useNetwork({onReconnect: () => setImageError(false)}); + + // Handles case where source is a component (ex: SVG) or a number + // Number may represent a SVG or an image + if ((maybeIcon && typeof source === 'number') || _.isFunction(source)) { + let iconFillColor = ''; + let additionalStyles = []; + if (isWorkspaceAvatar) { + const defaultWorkspaceAvatarColor = StyleUtils.getDefaultWorkspaceAvatarColor(file.name); + iconFillColor = defaultWorkspaceAvatarColor.fill; + additionalStyles = [defaultWorkspaceAvatarColor]; } return ( - - - - + + ); + } + + if (TransactionUtils.hasEReceipt(transaction)) { + return ( + + + + ); - }; - - function renderPDFView() { - const pdfHeight = isUsedAsChatAttachment ? THUMBNAIL_HEIGHT : windowHeight; - const pdfWeight = isUsedAsChatAttachment ? THUMBNAIL_WIDTH : windowWidth; - const pdfStyles = [StyleUtils.getWidthAndHeightStyle(pdfWeight, pdfHeight), themeStyles.imageModalPDF]; - - // If we haven't yet successfully validated the password and loaded the PDF, - // then we need to hide the react-native-pdf/PDF component so that PDFPasswordForm - // is positioned nicely. We're specifically hiding it because we still need to render - // the PDF component so that it can validate the password. - if (shouldRequestPassword) { - pdfStyles.push(themeStyles.invisible); - } - const containerStyles = - (shouldRequestPassword && isSmallScreenWidth) || isUsedAsChatAttachment ? [themeStyles.w100, themeStyles.flex1] : [themeStyles.alignItemsCenter, themeStyles.flex1]; - const loadingIndicatorStyles = isUsedAsChatAttachment ? [themeStyles.chatItemPDFAttachmentLoading, StyleUtils.getWidthAndHeightStyle(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)] : []; + } + + // Check both source and file.name since PDFs dragged into the text field + // will appear with a source that is a blob + if ((_.isString(source) && Str.isPDF(source)) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename')))) { + const encryptedSourceUrl = isAuthTokenRequired ? addEncryptedAuthTokenToURL(source) : source; + + const onPDFLoadComplete = (path) => { + const id = (transaction && transaction.transactionID) || reportActionID; + if (path && id) { + CachedPDFPaths.add(id, path); + } + if (!loadComplete) { + setLoadComplete(true); + } + }; + // We need the following View component on android native + // So that the event will propagate properly and + // the Password protected preview will be shown for pdf attachement we are about to send. return ( - - {shouldAttemptPDFLoad && ( - } - source={{uri: sourceURL}} - style={pdfStyles} - onError={handleFailureToLoadPDF} - password={password} - onLoadComplete={finishPDFLoad} - onPageSingleTap={onPress} - onScaleChanged={onScaleChanged} - /> - )} - {shouldRequestPassword && ( - - setIsPasswordInvalid(false)} - isPasswordInvalid={isPasswordInvalid} - shouldShowLoadingIndicator={shouldShowLoadingIndicator} - /> - - )} + + ); } - // // uncomment this code to see failedToLoadPDF - // if (failedToLoadPDF) { - // return rendeFailedToLoadPDF(); - // } - - return onPress && !successToLoadPDF ? ( - - {renderPDFView()} - - ) : ( - renderPDFView() + + if (TransactionUtils.isDistanceRequest(transaction)) { + return ; + } + + // For this check we use both source and file.name since temporary file source is a blob + // both PDFs and images will appear as images when pasted into the text field. + // We also check for numeric source since this is how static images (used for preview) are represented in RN. + const isImage = typeof source === 'number' || Str.isImage(source); + if (isImage || (file && Str.isImage(file.name))) { + return ( + { + setImageError(true); + }} + /> + ); + } + + if (isVideo) { + return ( + + ); + } + + return ( + + + + + + {file && file.name} + {!shouldShowLoadingSpinnerIcon && shouldShowDownloadIcon && ( + + + + + + )} + {shouldShowLoadingSpinnerIcon && ( + + + + + + )} + ); } -PDFView.displayName = 'PDFView'; -PDFView.propTypes = propTypes; -PDFView.defaultProps = defaultProps; +AttachmentView.propTypes = propTypes; +AttachmentView.defaultProps = defaultProps; +AttachmentView.displayName = 'AttachmentView'; -export default PDFView; +export default compose( + memo, + withLocalize, + withOnyx({ + transaction: { + key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + }, + }), +)(AttachmentView); From 36b581464aa1c9f6fbbb08dbc70f939b55017756 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 9 Mar 2024 03:57:23 +0530 Subject: [PATCH 005/932] fix conflict after merge --- .../DefaultAttachmentView.tsx | 59 ------------------- .../Attachments/AttachmentView/index.js | 7 ++- src/components/PDFView/index.native.js | 1 + 3 files changed, 7 insertions(+), 60 deletions(-) delete mode 100644 src/components/Attachments/AttachmentView/AttachmentViewPdf/DefaultAttachmentView.tsx diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/DefaultAttachmentView.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/DefaultAttachmentView.tsx deleted file mode 100644 index ad102fd5caad..000000000000 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/DefaultAttachmentView.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React from 'react'; -import {ActivityIndicator, View} from 'react-native'; -import type {StyleProp, ViewStyle} from 'react-native'; -import _ from 'underscore'; -import Icon from '@components/Icon'; -import * as Expensicons from '@components/Icon/Expensicons'; -import Text from '@components/Text'; -import Tooltip from '@components/Tooltip'; -import useLocalize from '@hooks/useLocalize'; -import useTheme from '@hooks/useTheme'; -import useThemeStyles from '@hooks/useThemeStyles'; - -type DefaultAttachmentViewProps = { - file: File; - shouldShowLoadingSpinnerIcon: boolean; - shouldShowDownloadIcon: boolean; - containerStyles: StyleProp[]; -}; - -function DefaultAttachmentView({file, shouldShowLoadingSpinnerIcon, shouldShowDownloadIcon, containerStyles}: DefaultAttachmentViewProps) { - const theme = useTheme(); - const styles = useThemeStyles(); - const {translate} = useLocalize(); - - return ( - - - - - - {file && file.name} - {!shouldShowLoadingSpinnerIcon && shouldShowDownloadIcon && ( - - - - - - )} - {shouldShowLoadingSpinnerIcon && ( - - - - - - )} - - ); -} - -export default DefaultAttachmentView; diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 461548f0d2b1..fc11ccfd8fac 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -107,6 +107,7 @@ function AttachmentView({ const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const [loadComplete, setLoadComplete] = useState(false); + const [isPdfFailedToLoad, setIsPdfFailedToLoad] = useState(false); const isVideo = (typeof source === 'string' && Str.isVideo(source)) || (file && Str.isVideo(file.name)); useEffect(() => { @@ -157,7 +158,7 @@ function AttachmentView({ // Check both source and file.name since PDFs dragged into the text field // will appear with a source that is a blob - if ((_.isString(source) && Str.isPDF(source)) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename')))) { + if (!isPdfFailedToLoad && ((_.isString(source) && Str.isPDF(source)) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename'))))) { const encryptedSourceUrl = isAuthTokenRequired ? addEncryptedAuthTokenToURL(source) : source; const onPDFLoadComplete = (path) => { @@ -187,6 +188,10 @@ function AttachmentView({ errorLabelStyles={isUsedInAttachmentModal ? [styles.textLabel, styles.textLarge] : [styles.cursorAuto]} style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1} isUsedInCarousel={isUsedInCarousel} + isUsedAsChatAttachment={!(isUsedInAttachmentModal || isUsedInCarousel)} + onError={() => { + setIsPdfFailedToLoad(true); + }} /> ); diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index 492a12e1dc66..f7901b035d8b 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -124,6 +124,7 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused setSuccessToLoadPDF(true); onLoadComplete(path); }; + // eslint-disable-next-line no-unused-vars const rendeFailedToLoadPDF = () => { if (!isUsedAsChatAttachment) { return ( From 75a733c60b7f82a3221bb24a10b744da622978db Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Thu, 14 Mar 2024 01:32:31 +0530 Subject: [PATCH 006/932] clean up and lint fix --- .../AttachmentViewPdf/BaseAttachmentViewPdf.js | 2 -- .../AttachmentView/AttachmentViewPdf/index.js | 4 ++-- .../AttachmentView/AttachmentViewPdf/propTypes.js | 3 --- src/components/Attachments/AttachmentView/index.js | 6 +----- src/components/PDFView/index.native.js | 11 +++++------ 5 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js index 3f3b995c02ba..289909a66dab 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js @@ -29,7 +29,6 @@ function BaseAttachmentViewPdf({ errorLabelStyles, style, isUsedAsChatAttachment, - onError, }) { const attachmentCarouselPagerContext = useContext(AttachmentCarouselPagerContext); const isScrollEnabled = attachmentCarouselPagerContext === null ? undefined : attachmentCarouselPagerContext.isScrollEnabled; @@ -92,7 +91,6 @@ function BaseAttachmentViewPdf({ onLoadComplete={onLoadComplete} errorLabelStyles={errorLabelStyles} isUsedAsChatAttachment={isUsedAsChatAttachment} - onError={onError} /> ); } diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js index de740f4fdd1f..af665d73a3ba 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js @@ -2,7 +2,7 @@ import React, {memo} from 'react'; import PDFView from '@components/PDFView'; import {attachmentViewPdfDefaultProps, attachmentViewPdfPropTypes} from './propTypes'; -function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, errorLabelStyles, style, isUsedAsChatAttachment, onError}) { +function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, errorLabelStyles, style, isUsedAsChatAttachment}) { return ( ); } diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js index bcb0fabde352..b6ad86124f63 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js @@ -16,9 +16,6 @@ const attachmentViewPdfPropTypes = { /** Styles for the error label */ errorLabelStyles: stylePropTypes, - /** Callback when the pdf fails to load */ - onError: PropTypes.func, - /** Whether the attachment is used as a chat attachment */ isUsedAsChatAttachment: PropTypes.bool, }; diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index fc11ccfd8fac..6ba3035afc86 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -107,7 +107,6 @@ function AttachmentView({ const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const [loadComplete, setLoadComplete] = useState(false); - const [isPdfFailedToLoad, setIsPdfFailedToLoad] = useState(false); const isVideo = (typeof source === 'string' && Str.isVideo(source)) || (file && Str.isVideo(file.name)); useEffect(() => { @@ -158,7 +157,7 @@ function AttachmentView({ // Check both source and file.name since PDFs dragged into the text field // will appear with a source that is a blob - if (!isPdfFailedToLoad && ((_.isString(source) && Str.isPDF(source)) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename'))))) { + if ((_.isString(source) && Str.isPDF(source)) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename')))) { const encryptedSourceUrl = isAuthTokenRequired ? addEncryptedAuthTokenToURL(source) : source; const onPDFLoadComplete = (path) => { @@ -189,9 +188,6 @@ function AttachmentView({ style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1} isUsedInCarousel={isUsedInCarousel} isUsedAsChatAttachment={!(isUsedInAttachmentModal || isUsedInCarousel)} - onError={() => { - setIsPdfFailedToLoad(true); - }} /> ); diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index f7901b035d8b..addbcec9b3bf 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -41,7 +41,7 @@ const propTypes = { const THUMBNAIL_HEIGHT = 250; const THUMBNAIL_WIDTH = 250; -function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused, onScaleChanged, sourceURL, errorLabelStyles, onError, isUsedAsChatAttachment}) { +function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused, onScaleChanged, sourceURL, errorLabelStyles, isUsedAsChatAttachment}) { const [shouldRequestPassword, setShouldRequestPassword] = useState(false); const [shouldAttemptPDFLoad, setShouldAttemptPDFLoad] = useState(true); const [shouldShowLoadingIndicator, setShouldShowLoadingIndicator] = useState(true); @@ -94,7 +94,6 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused setShouldShowLoadingIndicator(false); setShouldRequestPassword(false); setShouldAttemptPDFLoad(false); - onError(error); }; /** @@ -194,10 +193,10 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused ); } - // // uncomment this code to see failedToLoadPDF - // if (failedToLoadPDF) { - // return rendeFailedToLoadPDF(); - // } + + if (failedToLoadPDF) { + return rendeFailedToLoadPDF(); + } return onPress && !successToLoadPDF ? ( Date: Fri, 15 Mar 2024 15:07:54 +0700 Subject: [PATCH 007/932] display backend unreachability message --- src/CONST.ts | 3 +- src/Expensify.tsx | 4 +- src/components/OfflineIndicator.tsx | 27 ++++++++++++-- src/hooks/useNetwork.ts | 6 +-- src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/libs/NetworkConnection.ts | 57 +++++++++++++++++------------ src/libs/actions/Network.ts | 6 ++- src/types/onyx/Network.ts | 2 + 9 files changed, 75 insertions(+), 32 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index fa44cda20720..472c980ebaca 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -507,6 +507,7 @@ const CONST = { EMPTY_ARRAY, EMPTY_OBJECT, USE_EXPENSIFY_URL, + STATUS_EXPENSIFY_URL: 'https://status.expensify.com/', GOOGLE_MEET_URL_ANDROID: 'https://meet.google.com', GOOGLE_DOC_IMAGE_LINK_MATCH: 'googleusercontent.com', IMAGE_BASE64_MATCH: 'base64', @@ -917,7 +918,7 @@ const CONST = { DEFAULT_TIME_ZONE: {automatic: true, selected: 'America/Los_Angeles'}, DEFAULT_ACCOUNT_DATA: {errors: null, success: '', isLoading: false}, DEFAULT_CLOSE_ACCOUNT_DATA: {errors: null, success: '', isLoading: false}, - DEFAULT_NETWORK_DATA: {isOffline: false}, + DEFAULT_NETWORK_DATA: {isOffline: false, isBackendReachable: true}, FORMS: { LOGIN_FORM: 'LoginForm', VALIDATE_CODE_FORM: 'ValidateCodeForm', diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 5681be838ca8..7a2a2189b0fa 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -139,7 +139,9 @@ function Expensify({ ActiveClientManager.init(); // Used for the offline indicator appearing when someone is offline - NetworkConnection.subscribeToNetInfo(); + const unsubscribeNetInfo = NetworkConnection.subscribeToNetInfo(); + + return () => unsubscribeNetInfo(); }, []); useEffect(() => { diff --git a/src/components/OfflineIndicator.tsx b/src/components/OfflineIndicator.tsx index 1a61b6622783..a550a2a6a563 100644 --- a/src/components/OfflineIndicator.tsx +++ b/src/components/OfflineIndicator.tsx @@ -7,9 +7,11 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import variables from '@styles/variables'; +import CONST from '@src/CONST'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import Text from './Text'; +import TextLink from './TextLink'; type OfflineIndicatorProps = { /** Optional styles for container element that will override the default styling for the offline indicator */ @@ -23,7 +25,7 @@ function OfflineIndicator({style, containerStyles}: OfflineIndicatorProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); - const {isOffline} = useNetwork(); + const {isOffline, isBackendReachable} = useNetwork(); const {isSmallScreenWidth} = useWindowDimensions(); const computedStyles = useMemo((): StyleProp => { @@ -34,7 +36,8 @@ function OfflineIndicator({style, containerStyles}: OfflineIndicatorProps) { return isSmallScreenWidth ? styles.offlineIndicatorMobile : styles.offlineIndicator; }, [containerStyles, isSmallScreenWidth, styles.offlineIndicatorMobile, styles.offlineIndicator]); - if (!isOffline) { + // Truthy isBackendReachable implies both online and normal backend reachability + if (isBackendReachable) { return null; } @@ -46,7 +49,25 @@ function OfflineIndicator({style, containerStyles}: OfflineIndicatorProps) { width={variables.iconSizeSmall} height={variables.iconSizeSmall} /> - {translate('common.youAppearToBeOffline')} + + { + // Do not reorder the condition, we only show unreachability message when online and backend is unreachable + isOffline ? ( + translate('common.youAppearToBeOffline') + ) : ( + <> + {translate('common.weMightHaveProblem')} + + {CONST.STATUS_EXPENSIFY_URL} + + . + + ) + } + ); } diff --git a/src/hooks/useNetwork.ts b/src/hooks/useNetwork.ts index 9d5e1e75d7c8..4dfa3a322df8 100644 --- a/src/hooks/useNetwork.ts +++ b/src/hooks/useNetwork.ts @@ -6,13 +6,13 @@ type UseNetworkProps = { onReconnect?: () => void; }; -type UseNetwork = {isOffline: boolean}; +type UseNetwork = {isOffline: boolean; isBackendReachable: boolean}; export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = {}): UseNetwork { const callback = useRef(onReconnect); callback.current = onReconnect; - const {isOffline} = useContext(NetworkContext) ?? CONST.DEFAULT_NETWORK_DATA; + const {isOffline, isBackendReachable} = useContext(NetworkContext) ?? CONST.DEFAULT_NETWORK_DATA; const prevOfflineStatusRef = useRef(isOffline); useEffect(() => { // If we were offline before and now we are not offline then we just reconnected @@ -29,5 +29,5 @@ export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = { prevOfflineStatusRef.current = isOffline; }, [isOffline]); - return {isOffline: isOffline ?? false}; + return {isOffline: isOffline ?? false, isBackendReachable: isBackendReachable ?? true}; } diff --git a/src/languages/en.ts b/src/languages/en.ts index eecd81c54123..da67933011a5 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -266,6 +266,7 @@ export default { conciergeHelp: 'Please reach out to Concierge for help.', maxParticipantsReached: ({count}: MaxParticipantsReachedParams) => `You've selected the maximum number (${count}) of participants.`, youAppearToBeOffline: 'You appear to be offline.', + weMightHaveProblem: 'We might have a problem. Check out ', thisFeatureRequiresInternet: 'This feature requires an active internet connection to be used.', areYouSure: 'Are you sure?', verify: 'Verify', diff --git a/src/languages/es.ts b/src/languages/es.ts index cd36f9071de6..3e3b6be636eb 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -256,6 +256,7 @@ export default { conciergeHelp: 'Por favor, contacta con Concierge para obtener ayuda.', maxParticipantsReached: ({count}: MaxParticipantsReachedParams) => `Has seleccionado el número máximo (${count}) de participantes.`, youAppearToBeOffline: 'Parece que estás desconectado.', + weMightHaveProblem: 'We might have a problem. Check out', thisFeatureRequiresInternet: 'Esta función requiere una conexión a Internet activa para ser utilizada.', areYouSure: '¿Estás seguro?', verify: 'Verifique', diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index f5c391aad09c..0a3987e97152 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -2,7 +2,6 @@ import NetInfo from '@react-native-community/netinfo'; import throttle from 'lodash/throttle'; import Onyx from 'react-native-onyx'; import CONFIG from '@src/CONFIG'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import * as NetworkActions from './actions/Network'; import AppStateMonitor from './AppStateMonitor'; @@ -39,6 +38,7 @@ function setOfflineStatus(isCurrentlyOffline: boolean): void { // When reconnecting, ie, going from offline to online, all the reconnection callbacks // are triggered (this is usually Actions that need to re-download data from the server) if (isOffline && !isCurrentlyOffline) { + NetworkActions.setIsBackendReachable(true); triggerReconnectionCallbacks('offline status changed'); } @@ -72,35 +72,41 @@ Onyx.connect({ * internet connectivity or not. This is more reliable than the Pusher * `disconnected` event which takes about 10-15 seconds to emit. */ -function subscribeToNetInfo(): void { - // Note: We are disabling the configuration for NetInfo when using the local web API since requests can get stuck in a 'Pending' state and are not reliable indicators for "offline". +function subscribeToNetInfo() { + let backendReachabilityCheckInterval: NodeJS.Timeout; + // Note: We are disabling the reachability check when using the local web API since requests can get stuck in a 'Pending' state and are not reliable indicators for "offline". // If you need to test the "recheck" feature then switch to the production API proxy server. if (!CONFIG.IS_USING_LOCAL_WEB) { - // Calling NetInfo.configure (re)checks current state. We use it to force a recheck whenever we (re)subscribe - NetInfo.configure({ - // By default, NetInfo uses `/` for `reachabilityUrl` - // When App is served locally (or from Electron) this address is always reachable - even offline + // Set interval to (re)checks current state every 15 seconds + const BACKEND_REACHABILITY_CHECK_INTERVAL = 15000; + + backendReachabilityCheckInterval = setInterval(() => { + // Offline status also implies backend unreachability + if (isOffline) { + return; + } + // When App is served locally (or from Electron) is always reachable - even offline // Using the API url ensures reachability is tested over internet - reachabilityUrl: `${CONFIG.EXPENSIFY.DEFAULT_API_ROOT}api?command=Ping`, - reachabilityMethod: 'GET', - reachabilityTest: (response) => { - if (!response.ok) { - return Promise.resolve(false); - } - return response - .json() - .then((json) => Promise.resolve(json.jsonCode === 200)) - .catch(() => Promise.resolve(false)); - }, - - // If a check is taking longer than this time we're considered offline - reachabilityRequestTimeout: CONST.NETWORK.MAX_PENDING_TIME_MS, - }); + fetch(`${CONFIG.EXPENSIFY.DEFAULT_API_ROOT}api?command=Ping`, { + method: 'GET', + cache: 'no-cache', + }) + .then((response) => { + if (!response.ok) { + return Promise.resolve(false); + } + return response + .json() + .then((json) => Promise.resolve(json.jsonCode === 200)) + .catch(() => Promise.resolve(false)); + }) + .then(NetworkActions.setIsBackendReachable); + }, BACKEND_REACHABILITY_CHECK_INTERVAL); } // Subscribe to the state change event via NetInfo so we can update // whether a user has internet connectivity or not. - NetInfo.addEventListener((state) => { + const unsubscribeNetInfo = NetInfo.addEventListener((state) => { Log.info('[NetworkConnection] NetInfo state change', false, {...state}); if (shouldForceOffline) { Log.info('[NetworkConnection] Not setting offline status because shouldForceOffline = true'); @@ -108,6 +114,11 @@ function subscribeToNetInfo(): void { } setOfflineStatus(state.isInternetReachable === false); }); + + return () => { + clearInterval(backendReachabilityCheckInterval); + unsubscribeNetInfo(); + }; } function listenForReconnect() { diff --git a/src/libs/actions/Network.ts b/src/libs/actions/Network.ts index e71094eded05..72a6bfcb3beb 100644 --- a/src/libs/actions/Network.ts +++ b/src/libs/actions/Network.ts @@ -1,6 +1,10 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; +function setIsBackendReachable(isBackendReachable: boolean) { + Onyx.merge(ONYXKEYS.NETWORK, {isBackendReachable}); +} + function setIsOffline(isOffline: boolean) { Onyx.merge(ONYXKEYS.NETWORK, {isOffline}); } @@ -20,4 +24,4 @@ function setShouldFailAllRequests(shouldFailAllRequests: boolean) { Onyx.merge(ONYXKEYS.NETWORK, {shouldFailAllRequests}); } -export {setIsOffline, setShouldForceOffline, setShouldFailAllRequests, setTimeSkew}; +export {setIsBackendReachable, setIsOffline, setShouldForceOffline, setShouldFailAllRequests, setTimeSkew}; diff --git a/src/types/onyx/Network.ts b/src/types/onyx/Network.ts index 173ca486b53c..d4162d1ffdb7 100644 --- a/src/types/onyx/Network.ts +++ b/src/types/onyx/Network.ts @@ -2,6 +2,8 @@ type Network = { /** Is the network currently offline or not */ isOffline: boolean; + isBackendReachable: boolean; + /** Should the network be forced offline */ shouldForceOffline?: boolean; From 4b24bd98d2e9fbefb68b2615ea0d86b84074a3aa Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 15 Mar 2024 15:20:31 +0700 Subject: [PATCH 008/932] show status page hostname only --- src/CONST.ts | 2 +- src/components/OfflineIndicator.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 472c980ebaca..515054f3132b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -507,7 +507,7 @@ const CONST = { EMPTY_ARRAY, EMPTY_OBJECT, USE_EXPENSIFY_URL, - STATUS_EXPENSIFY_URL: 'https://status.expensify.com/', + STATUS_EXPENSIFY_URL: 'https://status.expensify.com', GOOGLE_MEET_URL_ANDROID: 'https://meet.google.com', GOOGLE_DOC_IMAGE_LINK_MATCH: 'googleusercontent.com', IMAGE_BASE64_MATCH: 'base64', diff --git a/src/components/OfflineIndicator.tsx b/src/components/OfflineIndicator.tsx index a550a2a6a563..bd57c176db65 100644 --- a/src/components/OfflineIndicator.tsx +++ b/src/components/OfflineIndicator.tsx @@ -61,7 +61,7 @@ function OfflineIndicator({style, containerStyles}: OfflineIndicatorProps) { href={CONST.STATUS_EXPENSIFY_URL} style={styles.link} > - {CONST.STATUS_EXPENSIFY_URL} + {new URL(CONST.STATUS_EXPENSIFY_URL).host} . From 7e8cf7fce86c9a782f164e99a2157beb0b79535a Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 15 Mar 2024 18:27:44 +0700 Subject: [PATCH 009/932] adjust polling timeout and status page link style --- src/CONST.ts | 1 + src/components/OfflineIndicator.tsx | 5 ++--- src/libs/NetworkConnection.ts | 11 +++++------ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 515054f3132b..9ab45c6537c2 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -912,6 +912,7 @@ const CONST = { MAX_RETRY_WAIT_TIME_MS: 10 * 1000, PROCESS_REQUEST_DELAY_MS: 1000, MAX_PENDING_TIME_MS: 10 * 1000, + REACHABILITY_TIMEOUT_MS: 60 * 1000, MAX_REQUEST_RETRIES: 10, }, WEEK_STARTS_ON: 1, // Monday diff --git a/src/components/OfflineIndicator.tsx b/src/components/OfflineIndicator.tsx index bd57c176db65..4ed01a531cd2 100644 --- a/src/components/OfflineIndicator.tsx +++ b/src/components/OfflineIndicator.tsx @@ -36,8 +36,7 @@ function OfflineIndicator({style, containerStyles}: OfflineIndicatorProps) { return isSmallScreenWidth ? styles.offlineIndicatorMobile : styles.offlineIndicator; }, [containerStyles, isSmallScreenWidth, styles.offlineIndicatorMobile, styles.offlineIndicator]); - // Truthy isBackendReachable implies both online and normal backend reachability - if (isBackendReachable) { + if (!isOffline && isBackendReachable) { return null; } @@ -59,7 +58,7 @@ function OfflineIndicator({style, containerStyles}: OfflineIndicatorProps) { {translate('common.weMightHaveProblem')} {new URL(CONST.STATUS_EXPENSIFY_URL).host} diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index 0a3987e97152..56f6f7e3c786 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -2,6 +2,7 @@ import NetInfo from '@react-native-community/netinfo'; import throttle from 'lodash/throttle'; import Onyx from 'react-native-onyx'; import CONFIG from '@src/CONFIG'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import * as NetworkActions from './actions/Network'; import AppStateMonitor from './AppStateMonitor'; @@ -77,15 +78,12 @@ function subscribeToNetInfo() { // Note: We are disabling the reachability check when using the local web API since requests can get stuck in a 'Pending' state and are not reliable indicators for "offline". // If you need to test the "recheck" feature then switch to the production API proxy server. if (!CONFIG.IS_USING_LOCAL_WEB) { - // Set interval to (re)checks current state every 15 seconds - const BACKEND_REACHABILITY_CHECK_INTERVAL = 15000; - + // Set interval to (re)check current state every 60 seconds backendReachabilityCheckInterval = setInterval(() => { // Offline status also implies backend unreachability if (isOffline) { return; } - // When App is served locally (or from Electron) is always reachable - even offline // Using the API url ensures reachability is tested over internet fetch(`${CONFIG.EXPENSIFY.DEFAULT_API_ROOT}api?command=Ping`, { method: 'GET', @@ -100,8 +98,9 @@ function subscribeToNetInfo() { .then((json) => Promise.resolve(json.jsonCode === 200)) .catch(() => Promise.resolve(false)); }) - .then(NetworkActions.setIsBackendReachable); - }, BACKEND_REACHABILITY_CHECK_INTERVAL); + .then(NetworkActions.setIsBackendReachable) + .catch(() => NetworkActions.setIsBackendReachable(false)); + }, CONST.NETWORK.REACHABILITY_TIMEOUT_MS); } // Subscribe to the state change event via NetInfo so we can update From c7b627d62df4769eb6eb3bed1bc4076b3023b05f Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 15 Mar 2024 18:46:37 +0700 Subject: [PATCH 010/932] update comment --- src/Expensify.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 7a2a2189b0fa..08fb4f269501 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -138,7 +138,7 @@ function Expensify({ // Initialize this client as being an active client ActiveClientManager.init(); - // Used for the offline indicator appearing when someone is offline + // Used for the offline indicator appearing when someone is offline or backend is unreachable const unsubscribeNetInfo = NetworkConnection.subscribeToNetInfo(); return () => unsubscribeNetInfo(); From e2b9966bd89653e470773a6eb2a2c663cfcb4bc3 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 9 Mar 2024 03:25:09 +0530 Subject: [PATCH 011/932] add POC for fallingback to default attachment view when pdf fails to load --- .../BaseAttachmentViewPdf.js | 2 + .../AttachmentView/AttachmentViewPdf/index.js | 4 +- .../AttachmentViewPdf/propTypes.js | 3 ++ .../Attachments/AttachmentView/index.js | 16 ++++++++ src/components/PDFView/index.native.js | 38 +------------------ 5 files changed, 25 insertions(+), 38 deletions(-) diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js index 289909a66dab..3f3b995c02ba 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js @@ -29,6 +29,7 @@ function BaseAttachmentViewPdf({ errorLabelStyles, style, isUsedAsChatAttachment, + onError, }) { const attachmentCarouselPagerContext = useContext(AttachmentCarouselPagerContext); const isScrollEnabled = attachmentCarouselPagerContext === null ? undefined : attachmentCarouselPagerContext.isScrollEnabled; @@ -91,6 +92,7 @@ function BaseAttachmentViewPdf({ onLoadComplete={onLoadComplete} errorLabelStyles={errorLabelStyles} isUsedAsChatAttachment={isUsedAsChatAttachment} + onError={onError} /> ); } diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js index af665d73a3ba..6377bc6e585a 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js @@ -2,7 +2,7 @@ import React, {memo} from 'react'; import PDFView from '@components/PDFView'; import {attachmentViewPdfDefaultProps, attachmentViewPdfPropTypes} from './propTypes'; -function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, errorLabelStyles, style, isUsedAsChatAttachment}) { +function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, errorLabelStyles, style, onError}) { return ( ); } diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js index b6ad86124f63..bcb0fabde352 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js @@ -16,6 +16,9 @@ const attachmentViewPdfPropTypes = { /** Styles for the error label */ errorLabelStyles: stylePropTypes, + /** Callback when the pdf fails to load */ + onError: PropTypes.func, + /** Whether the attachment is used as a chat attachment */ isUsedAsChatAttachment: PropTypes.bool, }; diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 6ba3035afc86..863f37733972 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -26,6 +26,7 @@ import variables from '@styles/variables'; import ONYXKEYS from '@src/ONYXKEYS'; import AttachmentViewImage from './AttachmentViewImage'; import AttachmentViewPdf from './AttachmentViewPdf'; +import DefaultAttachmentView from './AttachmentViewPdf/DefaultAttachmentView'; import AttachmentViewVideo from './AttachmentViewVideo'; import {attachmentViewDefaultProps, attachmentViewPropTypes} from './propTypes'; @@ -108,6 +109,7 @@ function AttachmentView({ const StyleUtils = useStyleUtils(); const [loadComplete, setLoadComplete] = useState(false); const isVideo = (typeof source === 'string' && Str.isVideo(source)) || (file && Str.isVideo(file.name)); + const [isPdfFailedToLoad, setIsPdfFailedToLoad] = useState(false); useEffect(() => { if (!isFocused && !(file && isUsedInAttachmentModal)) { @@ -170,6 +172,17 @@ function AttachmentView({ } }; + if (isPdfFailedToLoad) { + return ( + + ); + } + // We need the following View component on android native // So that the event will propagate properly and // the Password protected preview will be shown for pdf attachement we are about to send. @@ -188,6 +201,9 @@ function AttachmentView({ style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1} isUsedInCarousel={isUsedInCarousel} isUsedAsChatAttachment={!(isUsedInAttachmentModal || isUsedInCarousel)} + onError={() => { + setIsPdfFailedToLoad(true); + }} /> ); diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index addbcec9b3bf..2f9bb1a40573 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -2,19 +2,13 @@ import React, {useCallback, useEffect, useState} from 'react'; import {View} from 'react-native'; import PDF from 'react-native-pdf'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; -import Icon from '@components/Icon'; -import * as Expensicons from '@components/Icon/Expensicons'; import KeyboardAvoidingView from '@components/KeyboardAvoidingView'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; -import Text from '@components/Text'; import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; -import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import variables from '@styles/variables'; import CONST from '@src/CONST'; import PDFPasswordForm from './PDFPasswordForm'; import {defaultProps, propTypes as pdfViewPropTypes} from './pdfViewPropTypes'; @@ -41,7 +35,7 @@ const propTypes = { const THUMBNAIL_HEIGHT = 250; const THUMBNAIL_WIDTH = 250; -function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused, onScaleChanged, sourceURL, errorLabelStyles, isUsedAsChatAttachment}) { +function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused, onScaleChanged, sourceURL, onError, isUsedAsChatAttachment}) { const [shouldRequestPassword, setShouldRequestPassword] = useState(false); const [shouldAttemptPDFLoad, setShouldAttemptPDFLoad] = useState(true); const [shouldShowLoadingIndicator, setShouldShowLoadingIndicator] = useState(true); @@ -54,8 +48,6 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused const themeStyles = useThemeStyles(); const {isKeyboardShown} = useKeyboardState(); const StyleUtils = useStyleUtils(); - const {isOffline} = useNetwork(); - const theme = useTheme(); useEffect(() => { onToggleKeyboard(isKeyboardShown); @@ -94,6 +86,7 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused setShouldShowLoadingIndicator(false); setShouldRequestPassword(false); setShouldAttemptPDFLoad(false); + onError(error); }; /** @@ -123,29 +116,6 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused setSuccessToLoadPDF(true); onLoadComplete(path); }; - // eslint-disable-next-line no-unused-vars - const rendeFailedToLoadPDF = () => { - if (!isUsedAsChatAttachment) { - return ( - - {translate('attachmentView.failedToLoadPDF')} - - ); - } - - return ( - - - - - - ); - }; function renderPDFView() { const pdfHeight = isUsedAsChatAttachment ? THUMBNAIL_HEIGHT : windowHeight; @@ -194,10 +164,6 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused ); } - if (failedToLoadPDF) { - return rendeFailedToLoadPDF(); - } - return onPress && !successToLoadPDF ? ( Date: Sat, 16 Mar 2024 03:17:27 +0530 Subject: [PATCH 012/932] fixes missing prop --- .../Attachments/AttachmentView/AttachmentViewPdf/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js index 6377bc6e585a..f151bf84b7a8 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js @@ -2,7 +2,7 @@ import React, {memo} from 'react'; import PDFView from '@components/PDFView'; import {attachmentViewPdfDefaultProps, attachmentViewPdfPropTypes} from './propTypes'; -function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, errorLabelStyles, style, onError}) { +function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, errorLabelStyles, style, onError, isUsedAsChatAttachment}) { return ( ); } From b4ac9a1cb3518627a16a401654a80ef47ef6625d Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 16 Mar 2024 03:20:24 +0530 Subject: [PATCH 013/932] remove unused import --- src/components/Attachments/AttachmentView/index.js | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 863f37733972..56ebad34e963 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -26,7 +26,6 @@ import variables from '@styles/variables'; import ONYXKEYS from '@src/ONYXKEYS'; import AttachmentViewImage from './AttachmentViewImage'; import AttachmentViewPdf from './AttachmentViewPdf'; -import DefaultAttachmentView from './AttachmentViewPdf/DefaultAttachmentView'; import AttachmentViewVideo from './AttachmentViewVideo'; import {attachmentViewDefaultProps, attachmentViewPropTypes} from './propTypes'; @@ -159,7 +158,7 @@ function AttachmentView({ // Check both source and file.name since PDFs dragged into the text field // will appear with a source that is a blob - if ((_.isString(source) && Str.isPDF(source)) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename')))) { + if (!isPdfFailedToLoad && ((_.isString(source) && Str.isPDF(source)) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename'))))) { const encryptedSourceUrl = isAuthTokenRequired ? addEncryptedAuthTokenToURL(source) : source; const onPDFLoadComplete = (path) => { @@ -172,17 +171,6 @@ function AttachmentView({ } }; - if (isPdfFailedToLoad) { - return ( - - ); - } - // We need the following View component on android native // So that the event will propagate properly and // the Password protected preview will be shown for pdf attachement we are about to send. From 03726012f10ad31c29f082ba614a9175d0110b62 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 16 Mar 2024 03:23:31 +0530 Subject: [PATCH 014/932] change variable name --- src/components/PDFView/index.native.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index 2f9bb1a40573..c0f36f310346 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -32,8 +32,8 @@ const propTypes = { * is (temporarily) rendered. */ -const THUMBNAIL_HEIGHT = 250; -const THUMBNAIL_WIDTH = 250; +const LOADING_THUMBNAIL_HEIGHT = 250; +const LOADING_THUMBNAIL_WIDTH = 250; function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused, onScaleChanged, sourceURL, onError, isUsedAsChatAttachment}) { const [shouldRequestPassword, setShouldRequestPassword] = useState(false); @@ -118,8 +118,8 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused }; function renderPDFView() { - const pdfHeight = isUsedAsChatAttachment ? THUMBNAIL_HEIGHT : windowHeight; - const pdfWeight = isUsedAsChatAttachment ? THUMBNAIL_WIDTH : windowWidth; + const pdfHeight = isUsedAsChatAttachment ? LOADING_THUMBNAIL_HEIGHT : windowHeight; + const pdfWeight = isUsedAsChatAttachment ? LOADING_THUMBNAIL_WIDTH : windowWidth; const pdfStyles = [StyleUtils.getWidthAndHeightStyle(pdfWeight, pdfHeight), themeStyles.imageModalPDF]; // If we haven't yet successfully validated the password and loaded the PDF, @@ -131,7 +131,7 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused } const containerStyles = (shouldRequestPassword && isSmallScreenWidth) || isUsedAsChatAttachment ? [themeStyles.w100, themeStyles.flex1] : [themeStyles.alignItemsCenter, themeStyles.flex1]; - const loadingIndicatorStyles = isUsedAsChatAttachment ? [themeStyles.chatItemPDFAttachmentLoading, StyleUtils.getWidthAndHeightStyle(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)] : []; + const loadingIndicatorStyles = isUsedAsChatAttachment ? [themeStyles.chatItemPDFAttachmentLoading, StyleUtils.getWidthAndHeightStyle(LOADING_THUMBNAIL_WIDTH, LOADING_THUMBNAIL_HEIGHT)] : []; return ( From a20811ae16bab37acca9005f8b481a52f5be6d31 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 16 Mar 2024 03:57:40 +0530 Subject: [PATCH 015/932] fixes lint --- .../AttachmentCarousel/CarouselItem.js | 1 + .../Attachments/AttachmentView/index.js | 2 +- src/components/PDFView/WebPDFDocument.js | 9 +-------- src/components/PDFView/index.native.js | 18 +++++++++--------- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/components/Attachments/AttachmentCarousel/CarouselItem.js b/src/components/Attachments/AttachmentCarousel/CarouselItem.js index b2c9fed64467..e924cb8c13e9 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselItem.js +++ b/src/components/Attachments/AttachmentCarousel/CarouselItem.js @@ -109,6 +109,7 @@ function CarouselItem({item, onPress, isFocused, isModalHovered}) { isHovered={isModalHovered} isFocused={isFocused} optionalVideoDuration={item.duration} + isUsedInCarousel /> diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 56ebad34e963..4a167ba35c5d 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -188,7 +188,7 @@ function AttachmentView({ errorLabelStyles={isUsedInAttachmentModal ? [styles.textLabel, styles.textLarge] : [styles.cursorAuto]} style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1} isUsedInCarousel={isUsedInCarousel} - isUsedAsChatAttachment={!(isUsedInAttachmentModal || isUsedInCarousel)} + isUsedAsChatAttachment={!isUsedInAttachmentModal && !isUsedInCarousel} onError={() => { setIsPdfFailedToLoad(true); }} diff --git a/src/components/PDFView/WebPDFDocument.js b/src/components/PDFView/WebPDFDocument.js index d2e93e3ba506..3b1b3ba7c765 100644 --- a/src/components/PDFView/WebPDFDocument.js +++ b/src/components/PDFView/WebPDFDocument.js @@ -5,16 +5,11 @@ import {Document} from 'react-pdf'; import {VariableSizeList as List} from 'react-window'; import _ from 'underscore'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; -import Text from '@components/Text'; import stylePropTypes from '@styles/stylePropTypes'; import CONST from '@src/CONST'; import PageRenderer from './WebPDFPageRenderer'; const propTypes = { - /** Index of the PDF page to be displayed passed by VariableSizeList */ - errorLabelStyles: stylePropTypes, - /** Returns translated string for given locale and phrase */ - translate: PropTypes.func.isRequired, /** The source URL from which to load PDF file to be displayed */ sourceURL: PropTypes.string.isRequired, /** Callback invoked when the PDF document is loaded successfully */ @@ -53,7 +48,6 @@ const propTypes = { }; const defaultProps = { - errorLabelStyles: [], numPages: null, listStyle: undefined, password: undefined, @@ -61,8 +55,6 @@ const defaultProps = { const WebPDFDocument = memo( ({ - errorLabelStyles, - translate, sourceURL, onDocumentLoadSuccess, pageViewportsLength, @@ -78,6 +70,7 @@ const WebPDFDocument = memo( listStyle, initiatePasswordChallenge, password, + onError, }) => { const onPassword = useCallback( (callback, reason) => { diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index c0f36f310346..87262a566bc7 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -1,5 +1,5 @@ -import React, {useCallback, useEffect, useState} from 'react'; -import {View} from 'react-native'; +import React, { useCallback, useEffect, useState } from 'react'; +import { View } from 'react-native'; import PDF from 'react-native-pdf'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import KeyboardAvoidingView from '@components/KeyboardAvoidingView'; @@ -11,7 +11,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import CONST from '@src/CONST'; import PDFPasswordForm from './PDFPasswordForm'; -import {defaultProps, propTypes as pdfViewPropTypes} from './pdfViewPropTypes'; +import { defaultProps, propTypes as pdfViewPropTypes } from './pdfViewPropTypes'; const propTypes = { ...pdfViewPropTypes, @@ -35,7 +35,7 @@ const propTypes = { const LOADING_THUMBNAIL_HEIGHT = 250; const LOADING_THUMBNAIL_WIDTH = 250; -function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused, onScaleChanged, sourceURL, onError, isUsedAsChatAttachment}) { +function PDFView({ onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused, onScaleChanged, sourceURL, onError, isUsedAsChatAttachment }) { const [shouldRequestPassword, setShouldRequestPassword] = useState(false); const [shouldAttemptPDFLoad, setShouldAttemptPDFLoad] = useState(true); const [shouldShowLoadingIndicator, setShouldShowLoadingIndicator] = useState(true); @@ -43,10 +43,10 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused const [failedToLoadPDF, setFailedToLoadPDF] = useState(false); const [successToLoadPDF, setSuccessToLoadPDF] = useState(false); const [password, setPassword] = useState(''); - const {windowWidth, windowHeight, isSmallScreenWidth} = useWindowDimensions(); - const {translate} = useLocalize(); + const { windowWidth, windowHeight, isSmallScreenWidth } = useWindowDimensions(); + const { translate } = useLocalize(); const themeStyles = useThemeStyles(); - const {isKeyboardShown} = useKeyboardState(); + const { isKeyboardShown } = useKeyboardState(); const StyleUtils = useStyleUtils(); useEffect(() => { @@ -130,7 +130,7 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused pdfStyles.push(themeStyles.invisible); } const containerStyles = - (shouldRequestPassword && isSmallScreenWidth) || isUsedAsChatAttachment ? [themeStyles.w100, themeStyles.flex1] : [themeStyles.alignItemsCenter, themeStyles.flex1]; + isUsedAsChatAttachment || (shouldRequestPassword && isSmallScreenWidth) ? [themeStyles.w100, themeStyles.flex1] : [themeStyles.alignItemsCenter, themeStyles.flex1]; const loadingIndicatorStyles = isUsedAsChatAttachment ? [themeStyles.chatItemPDFAttachmentLoading, StyleUtils.getWidthAndHeightStyle(LOADING_THUMBNAIL_WIDTH, LOADING_THUMBNAIL_HEIGHT)] : []; return ( @@ -140,7 +140,7 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused fitPolicy={0} trustAllCerts={false} renderActivityIndicator={() => } - source={{uri: sourceURL}} + source={{ uri: sourceURL, cache: true, expiration: 864000 }} style={pdfStyles} onError={handleFailureToLoadPDF} password={password} From 3ace4d305de71b924bd53a82d9e54f864fc07461 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 16 Mar 2024 04:00:16 +0530 Subject: [PATCH 016/932] use correct conditional and use js docs for props --- .../BaseAnchorForAttachmentsOnly.tsx | 1 + src/components/Attachments/AttachmentView/index.js | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx index d389ac4b92f0..3e7189ce0304 100644 --- a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx +++ b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx @@ -64,6 +64,7 @@ function BaseAnchorForAttachmentsOnly({style, source = '', displayName = '', dow file={{name: displayName}} shouldShowDownloadIcon={!isOffline} shouldShowLoadingSpinnerIcon={isDownloading} + isUsedAsChatAttachment /> )} diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 4a167ba35c5d..0d25de1bd799 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -62,9 +62,14 @@ const propTypes = { /** The id of the report action related to the attachment */ reportActionID: PropTypes.string, + /** Whether the attachment is currently being hovered over */ isHovered: PropTypes.bool, + /** The duration of the video */ optionalVideoDuration: PropTypes.number, + + /** Whether the attachment is being used as a chat attachment */ + isUsedAsChatAttachment: PropTypes.bool, }; const defaultProps = { @@ -79,6 +84,7 @@ const defaultProps = { reportActionID: '', isHovered: false, optionalVideoDuration: 0, + isUsedAsChatAttachment: false, }; function AttachmentView({ @@ -101,6 +107,7 @@ function AttachmentView({ reportActionID, isHovered, optionalVideoDuration, + isUsedAsChatAttachment, }) { const {updateCurrentlyPlayingURL} = usePlaybackContext(); const theme = useTheme(); @@ -188,7 +195,7 @@ function AttachmentView({ errorLabelStyles={isUsedInAttachmentModal ? [styles.textLabel, styles.textLarge] : [styles.cursorAuto]} style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1} isUsedInCarousel={isUsedInCarousel} - isUsedAsChatAttachment={!isUsedInAttachmentModal && !isUsedInCarousel} + isUsedAsChatAttachment={isUsedAsChatAttachment} onError={() => { setIsPdfFailedToLoad(true); }} From 830a392f5097a91c9d3689c7f500bb54ec0c28e2 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 16 Mar 2024 04:06:08 +0530 Subject: [PATCH 017/932] prettier diffs --- src/components/PDFView/index.native.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index 87262a566bc7..1c3cd02308fb 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -1,5 +1,5 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { View } from 'react-native'; +import React, {useCallback, useEffect, useState} from 'react'; +import {View} from 'react-native'; import PDF from 'react-native-pdf'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import KeyboardAvoidingView from '@components/KeyboardAvoidingView'; @@ -11,7 +11,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import CONST from '@src/CONST'; import PDFPasswordForm from './PDFPasswordForm'; -import { defaultProps, propTypes as pdfViewPropTypes } from './pdfViewPropTypes'; +import {defaultProps, propTypes as pdfViewPropTypes} from './pdfViewPropTypes'; const propTypes = { ...pdfViewPropTypes, @@ -35,7 +35,7 @@ const propTypes = { const LOADING_THUMBNAIL_HEIGHT = 250; const LOADING_THUMBNAIL_WIDTH = 250; -function PDFView({ onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused, onScaleChanged, sourceURL, onError, isUsedAsChatAttachment }) { +function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused, onScaleChanged, sourceURL, onError, isUsedAsChatAttachment}) { const [shouldRequestPassword, setShouldRequestPassword] = useState(false); const [shouldAttemptPDFLoad, setShouldAttemptPDFLoad] = useState(true); const [shouldShowLoadingIndicator, setShouldShowLoadingIndicator] = useState(true); @@ -43,10 +43,10 @@ function PDFView({ onToggleKeyboard, onLoadComplete, fileName, onPress, isFocuse const [failedToLoadPDF, setFailedToLoadPDF] = useState(false); const [successToLoadPDF, setSuccessToLoadPDF] = useState(false); const [password, setPassword] = useState(''); - const { windowWidth, windowHeight, isSmallScreenWidth } = useWindowDimensions(); - const { translate } = useLocalize(); + const {windowWidth, windowHeight, isSmallScreenWidth} = useWindowDimensions(); + const {translate} = useLocalize(); const themeStyles = useThemeStyles(); - const { isKeyboardShown } = useKeyboardState(); + const {isKeyboardShown} = useKeyboardState(); const StyleUtils = useStyleUtils(); useEffect(() => { @@ -131,7 +131,9 @@ function PDFView({ onToggleKeyboard, onLoadComplete, fileName, onPress, isFocuse } const containerStyles = isUsedAsChatAttachment || (shouldRequestPassword && isSmallScreenWidth) ? [themeStyles.w100, themeStyles.flex1] : [themeStyles.alignItemsCenter, themeStyles.flex1]; - const loadingIndicatorStyles = isUsedAsChatAttachment ? [themeStyles.chatItemPDFAttachmentLoading, StyleUtils.getWidthAndHeightStyle(LOADING_THUMBNAIL_WIDTH, LOADING_THUMBNAIL_HEIGHT)] : []; + const loadingIndicatorStyles = isUsedAsChatAttachment + ? [themeStyles.chatItemPDFAttachmentLoading, StyleUtils.getWidthAndHeightStyle(LOADING_THUMBNAIL_WIDTH, LOADING_THUMBNAIL_HEIGHT)] + : []; return ( @@ -140,7 +142,7 @@ function PDFView({ onToggleKeyboard, onLoadComplete, fileName, onPress, isFocuse fitPolicy={0} trustAllCerts={false} renderActivityIndicator={() => } - source={{ uri: sourceURL, cache: true, expiration: 864000 }} + source={{uri: sourceURL, cache: true, expiration: 864000}} style={pdfStyles} onError={handleFailureToLoadPDF} password={password} From 3813026c23140b62b6b566ec587f4d1da9bdc15c Mon Sep 17 00:00:00 2001 From: Ishpaul Singh <104348397+ishpaul777@users.noreply.github.com> Date: Sat, 16 Mar 2024 04:10:54 +0530 Subject: [PATCH 018/932] Update src/components/Attachments/AttachmentCarousel/CarouselItem.js --- src/components/Attachments/AttachmentCarousel/CarouselItem.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Attachments/AttachmentCarousel/CarouselItem.js b/src/components/Attachments/AttachmentCarousel/CarouselItem.js index e924cb8c13e9..b2c9fed64467 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselItem.js +++ b/src/components/Attachments/AttachmentCarousel/CarouselItem.js @@ -109,7 +109,6 @@ function CarouselItem({item, onPress, isFocused, isModalHovered}) { isHovered={isModalHovered} isFocused={isFocused} optionalVideoDuration={item.duration} - isUsedInCarousel /> From a8cfafea38386727c62d8cdb4081fae9d0b0d62a Mon Sep 17 00:00:00 2001 From: Ishpaul Singh <104348397+ishpaul777@users.noreply.github.com> Date: Sat, 16 Mar 2024 04:21:58 +0530 Subject: [PATCH 019/932] remove unused styles --- src/styles/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index ffd05b97473d..2efb29a95ea5 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -516,10 +516,6 @@ const styles = (theme: ThemeColors) => borderRadius: variables.buttonBorderRadius, }, - componentBorderRadiusNormal: { - borderRadius: variables.componentBorderRadiusNormal, - }, - bottomTabBarContainer: { flexDirection: 'row', height: variables.bottomTabHeight, From f3be10027c4d786662253794c2cc7831170baf2a Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 18 Mar 2024 18:19:33 +0700 Subject: [PATCH 020/932] update spanish copy --- src/languages/es.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 9ac6e22648c0..9e5e4e0b6064 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -256,7 +256,7 @@ export default { conciergeHelp: 'Por favor, contacta con Concierge para obtener ayuda.', maxParticipantsReached: ({count}: MaxParticipantsReachedParams) => `Has seleccionado el número máximo (${count}) de participantes.`, youAppearToBeOffline: 'Parece que estás desconectado.', - weMightHaveProblem: 'We might have a problem. Check out', + weMightHaveProblem: 'We might have a problem. Check out ', thisFeatureRequiresInternet: 'Esta función requiere una conexión a Internet activa para ser utilizada.', areYouSure: '¿Estás seguro?', verify: 'Verifique', From d3faf922c7f9784c5b51edbdb208fa3126d6768d Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 18 Mar 2024 18:51:52 +0700 Subject: [PATCH 021/932] Update Espanol copy --- src/languages/es.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 9e5e4e0b6064..3ccf191221fb 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -256,7 +256,7 @@ export default { conciergeHelp: 'Por favor, contacta con Concierge para obtener ayuda.', maxParticipantsReached: ({count}: MaxParticipantsReachedParams) => `Has seleccionado el número máximo (${count}) de participantes.`, youAppearToBeOffline: 'Parece que estás desconectado.', - weMightHaveProblem: 'We might have a problem. Check out ', + weMightHaveProblem: 'Peude que te tengamos un problema. Echa un vistazo a ', thisFeatureRequiresInternet: 'Esta función requiere una conexión a Internet activa para ser utilizada.', areYouSure: '¿Estás seguro?', verify: 'Verifique', From a45f6f0ba20490a03bbc5c74d77097eb30a3cd1b Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 19 Mar 2024 13:37:32 +0530 Subject: [PATCH 022/932] remove error label styles --- .../AttachmentViewPdf/BaseAttachmentViewPdf.js | 2 -- .../Attachments/AttachmentView/AttachmentViewPdf/index.js | 3 +-- .../AttachmentView/AttachmentViewPdf/propTypes.js | 4 ---- src/components/Attachments/AttachmentView/index.js | 1 - src/components/PDFView/index.js | 1 - src/components/PDFView/pdfViewPropTypes.js | 8 +++++--- 6 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js index 3f3b995c02ba..8ae5a483c9fa 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js @@ -26,7 +26,6 @@ function BaseAttachmentViewPdf({ onScaleChanged: onScaleChangedProp, onToggleKeyboard, onLoadComplete, - errorLabelStyles, style, isUsedAsChatAttachment, onError, @@ -90,7 +89,6 @@ function BaseAttachmentViewPdf({ onToggleKeyboard={onToggleKeyboard} onScaleChanged={onScaleChanged} onLoadComplete={onLoadComplete} - errorLabelStyles={errorLabelStyles} isUsedAsChatAttachment={isUsedAsChatAttachment} onError={onError} /> diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js index f151bf84b7a8..1d5ffbccf40c 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.js @@ -2,7 +2,7 @@ import React, {memo} from 'react'; import PDFView from '@components/PDFView'; import {attachmentViewPdfDefaultProps, attachmentViewPdfPropTypes} from './propTypes'; -function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, errorLabelStyles, style, onError, isUsedAsChatAttachment}) { +function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, style, onError, isUsedAsChatAttachment}) { return ( diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js index bcb0fabde352..75984e6ac031 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js @@ -13,9 +13,6 @@ const attachmentViewPdfPropTypes = { /** Additional style props */ style: stylePropTypes, - /** Styles for the error label */ - errorLabelStyles: stylePropTypes, - /** Callback when the pdf fails to load */ onError: PropTypes.func, @@ -28,7 +25,6 @@ const attachmentViewPdfDefaultProps = { name: '', }, style: [], - errorLabelStyles: [], onError: () => {}, isUsedAsChatAttachment: false, }; diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 0d25de1bd799..701f6343eb25 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -192,7 +192,6 @@ function AttachmentView({ onPress={onPress} onToggleKeyboard={onToggleKeyboard} onLoadComplete={onPDFLoadComplete} - errorLabelStyles={isUsedInAttachmentModal ? [styles.textLabel, styles.textLarge] : [styles.cursorAuto]} style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1} isUsedInCarousel={isUsedInCarousel} isUsedAsChatAttachment={isUsedAsChatAttachment} diff --git a/src/components/PDFView/index.js b/src/components/PDFView/index.js index 2e39594fc210..838fd843b089 100644 --- a/src/components/PDFView/index.js +++ b/src/components/PDFView/index.js @@ -254,7 +254,6 @@ class PDFView extends Component { > {}, onLoadComplete: () => {}, isFocused: false, - errorLabelStyles: [], }; export {propTypes, defaultProps}; From aae3f37b09dbb9ee0dd95d7d69464f7ff9a1de7c Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 20 Mar 2024 01:07:35 +0700 Subject: [PATCH 023/932] update comments --- src/components/OfflineIndicator.tsx | 2 +- src/libs/NetworkConnection.ts | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/OfflineIndicator.tsx b/src/components/OfflineIndicator.tsx index 4ed01a531cd2..bb497fad5b4e 100644 --- a/src/components/OfflineIndicator.tsx +++ b/src/components/OfflineIndicator.tsx @@ -50,7 +50,7 @@ function OfflineIndicator({style, containerStyles}: OfflineIndicatorProps) { /> { - // Do not reorder the condition, we only show unreachability message when online and backend is unreachable + // If we reversed the ternary, unreachability message would always show even when offline isOffline ? ( translate('common.youAppearToBeOffline') ) : ( diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index cbfb4f9bfba3..96acc4c7d336 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -69,16 +69,14 @@ Onyx.connect({ }); /** - * Set up the event listener for NetInfo to tell whether the user has - * internet connectivity or not. This is more reliable than the Pusher - * `disconnected` event which takes about 10-15 seconds to emit. + * Monitor internet connectivity and perform periodic backend reachability checks */ function subscribeToNetInfo() { let backendReachabilityCheckInterval: NodeJS.Timeout; // Note: We are disabling the reachability check when using the local web API since requests can get stuck in a 'Pending' state and are not reliable indicators for "offline". // If you need to test the "recheck" feature then switch to the production API proxy server. if (!CONFIG.IS_USING_LOCAL_WEB) { - // Set interval to (re)check current state every 60 seconds + // Set interval to periodically (re)check backend status backendReachabilityCheckInterval = setInterval(() => { // Offline status also implies backend unreachability if (isOffline) { @@ -103,8 +101,11 @@ function subscribeToNetInfo() { }, CONST.NETWORK.REACHABILITY_TIMEOUT_MS); } - // Subscribe to the state change event via NetInfo so we can update - // whether a user has internet connectivity or not. + /** + * Set up the event listener for NetInfo to tell whether the user has + * internet connectivity or not. This is more reliable than the Pusher + * `disconnected` event which takes about 10-15 seconds to emit. + */ const unsubscribeNetInfo = NetInfo.addEventListener((state) => { Log.info('[NetworkConnection] NetInfo state change', false, {...state}); if (shouldForceOffline) { From 4c84f89b10cda8b663c5dd7168ba6447cac0d9cb Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 20 Mar 2024 17:45:51 +0700 Subject: [PATCH 024/932] refactor: extract subscribeToBackendReachability and modify minor comments --- src/Expensify.tsx | 4 +-- src/libs/NetworkConnection.ts | 68 ++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 08fb4f269501..4f4a9e53bf5a 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -139,9 +139,9 @@ function Expensify({ ActiveClientManager.init(); // Used for the offline indicator appearing when someone is offline or backend is unreachable - const unsubscribeNetInfo = NetworkConnection.subscribeToNetInfo(); + const unsubscribeNetworkStatus = NetworkConnection.subscribeToNetworkStatus(); - return () => unsubscribeNetInfo(); + return () => unsubscribeNetworkStatus(); }, []); useEffect(() => { diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index 10703eade109..511ecdd5614a 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -68,44 +68,46 @@ Onyx.connect({ }, }); +/** + * Set interval to periodically (re)check backend status + * @returns for use with clearInterval + */ +function subscribeToBackendReachability() { + return setInterval(() => { + // Offline status also implies backend unreachability + if (isOffline) { + return; + } + // Using the API url ensures reachability is tested over internet + fetch(`${CONFIG.EXPENSIFY.DEFAULT_API_ROOT}api/Ping`, { + method: 'GET', + cache: 'no-cache', + }) + .then((response) => { + if (!response.ok) { + return Promise.resolve(false); + } + return response + .json() + .then((json) => Promise.resolve(json.jsonCode === 200)) + .catch(() => Promise.resolve(false)); + }) + .then(NetworkActions.setIsBackendReachable) + .catch(() => NetworkActions.setIsBackendReachable(false)); + }, CONST.NETWORK.REACHABILITY_TIMEOUT_MS); +} + /** * Monitor internet connectivity and perform periodic backend reachability checks */ -function subscribeToNetInfo() { - let backendReachabilityCheckInterval: NodeJS.Timeout; +function subscribeToNetworkStatus() { // Note: We are disabling the reachability check when using the local web API since requests can get stuck in a 'Pending' state and are not reliable indicators for "offline". // If you need to test the "recheck" feature then switch to the production API proxy server. - if (!CONFIG.IS_USING_LOCAL_WEB) { - // Set interval to periodically (re)check backend status - backendReachabilityCheckInterval = setInterval(() => { - // Offline status also implies backend unreachability - if (isOffline) { - return; - } - // Using the API url ensures reachability is tested over internet - fetch(`${CONFIG.EXPENSIFY.DEFAULT_API_ROOT}api/Ping`, { - method: 'GET', - cache: 'no-cache', - }) - .then((response) => { - if (!response.ok) { - return Promise.resolve(false); - } - return response - .json() - .then((json) => Promise.resolve(json.jsonCode === 200)) - .catch(() => Promise.resolve(false)); - }) - .then(NetworkActions.setIsBackendReachable) - .catch(() => NetworkActions.setIsBackendReachable(false)); - }, CONST.NETWORK.REACHABILITY_TIMEOUT_MS); - } + const backendReachabilityCheckInterval = !CONFIG.IS_USING_LOCAL_WEB ? subscribeToBackendReachability() : undefined; - /** - * Set up the event listener for NetInfo to tell whether the user has - * internet connectivity or not. This is more reliable than the Pusher - * `disconnected` event which takes about 10-15 seconds to emit. - */ + // Set up the event listener for NetInfo to tell whether the user has + // internet connectivity or not. This is more reliable than the Pusher + // `disconnected` event which takes about 10-15 seconds to emit. const unsubscribeNetInfo = NetInfo.addEventListener((state) => { Log.info('[NetworkConnection] NetInfo state change', false, {...state}); if (shouldForceOffline) { @@ -167,5 +169,5 @@ export default { onReconnect, triggerReconnectionCallbacks, recheckNetworkConnection, - subscribeToNetInfo, + subscribeToNetworkStatus, }; From 0b5dec8bc4cf9b28cf7c4b05be55189b23f5474a Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 20 Mar 2024 17:49:38 +0700 Subject: [PATCH 025/932] refactor: add returns doc --- src/libs/NetworkConnection.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index 511ecdd5614a..eb87da2b0684 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -72,7 +72,7 @@ Onyx.connect({ * Set interval to periodically (re)check backend status * @returns for use with clearInterval */ -function subscribeToBackendReachability() { +function subscribeToBackendReachability(): NodeJS.Timeout { return setInterval(() => { // Offline status also implies backend unreachability if (isOffline) { @@ -99,8 +99,9 @@ function subscribeToBackendReachability() { /** * Monitor internet connectivity and perform periodic backend reachability checks + * @returns unsubscribe method */ -function subscribeToNetworkStatus() { +function subscribeToNetworkStatus(): () => void { // Note: We are disabling the reachability check when using the local web API since requests can get stuck in a 'Pending' state and are not reliable indicators for "offline". // If you need to test the "recheck" feature then switch to the production API proxy server. const backendReachabilityCheckInterval = !CONFIG.IS_USING_LOCAL_WEB ? subscribeToBackendReachability() : undefined; From b7be2f2acff75a13bd15543275c8dddca3348ed2 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 20 Mar 2024 18:19:16 +0700 Subject: [PATCH 026/932] refactor: return cleanup interval function --- src/libs/NetworkConnection.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index eb87da2b0684..57ae5be3f9fa 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -70,10 +70,10 @@ Onyx.connect({ /** * Set interval to periodically (re)check backend status - * @returns for use with clearInterval + * @returns clearInterval cleanup */ -function subscribeToBackendReachability(): NodeJS.Timeout { - return setInterval(() => { +function subscribeToBackendReachability(): () => void { + const intervalID = setInterval(() => { // Offline status also implies backend unreachability if (isOffline) { return; @@ -95,6 +95,10 @@ function subscribeToBackendReachability(): NodeJS.Timeout { .then(NetworkActions.setIsBackendReachable) .catch(() => NetworkActions.setIsBackendReachable(false)); }, CONST.NETWORK.REACHABILITY_TIMEOUT_MS); + + return () => { + clearInterval(intervalID); + }; } /** @@ -104,7 +108,7 @@ function subscribeToBackendReachability(): NodeJS.Timeout { function subscribeToNetworkStatus(): () => void { // Note: We are disabling the reachability check when using the local web API since requests can get stuck in a 'Pending' state and are not reliable indicators for "offline". // If you need to test the "recheck" feature then switch to the production API proxy server. - const backendReachabilityCheckInterval = !CONFIG.IS_USING_LOCAL_WEB ? subscribeToBackendReachability() : undefined; + const unsubscribeFromBackendReachability = !CONFIG.IS_USING_LOCAL_WEB ? subscribeToBackendReachability() : undefined; // Set up the event listener for NetInfo to tell whether the user has // internet connectivity or not. This is more reliable than the Pusher @@ -119,7 +123,7 @@ function subscribeToNetworkStatus(): () => void { }); return () => { - clearInterval(backendReachabilityCheckInterval); + unsubscribeFromBackendReachability?.(); unsubscribeNetInfo(); }; } From cfd7a8b5de5c63f058364e9dfc6811c5def6401b Mon Sep 17 00:00:00 2001 From: Brandon Henry Date: Tue, 26 Mar 2024 21:10:53 -0500 Subject: [PATCH 027/932] added new styling for headermessage --- src/components/SelectionList/BaseSelectionList.tsx | 3 ++- src/components/SelectionList/types.ts | 3 +++ src/pages/SearchPage/index.tsx | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 015fd284c0b4..6e31f84b24ed 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -69,6 +69,7 @@ function BaseSelectionList( listHeaderWrapperStyle, isRowMultilineSupported = false, textInputRef, + headerMessageStyle = [styles.ph5, styles.pb5] }: BaseSelectionListProps, ref: ForwardedRef, ) { @@ -514,7 +515,7 @@ function BaseSelectionList( )} {!!headerMessage && ( - + {headerMessage} )} diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 8b070e1aa5cb..9a2cbdd4c13c 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -229,6 +229,9 @@ type BaseSelectionListProps = Partial & { /** Message to display at the top of the list */ headerMessage?: string; + /** Styles to apply to the header message */ + headerMessageStyle?: StyleProp; + /** Text to display on the confirm button */ confirmButtonText?: string; diff --git a/src/pages/SearchPage/index.tsx b/src/pages/SearchPage/index.tsx index 07096ce6c2d5..f535bb014943 100644 --- a/src/pages/SearchPage/index.tsx +++ b/src/pages/SearchPage/index.tsx @@ -168,6 +168,7 @@ function SearchPage({betas, reports, isSearchingForReports, navigation}: SearchP textInputHint={offlineMessage} onChangeText={setSearchValue} headerMessage={headerMessage} + headerMessageStyle={headerMessage === translate('common.noResultsFound') ? [themeStyles.ph4, themeStyles.pb5] : undefined} onLayout={setPerformanceTimersEnd} autoFocus onSelectRow={selectReport} From 32f350390c152890ab3caa67a47fc2bae9833e8b Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 29 Mar 2024 02:03:33 +0530 Subject: [PATCH 028/932] fix conflicts --- .../Attachments/AttachmentView/AttachmentViewPdf/types.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts b/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts index f1f141a625db..da48480e8479 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts @@ -13,6 +13,10 @@ type AttachmentViewPdfProps = Pick void; + + onError?: () => void; + + isUsedAsChatAttachment?: boolean; }; export default AttachmentViewPdfProps; From 1c814c51a14085a59de008c08db62912a7fe9476 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 29 Mar 2024 02:09:57 +0530 Subject: [PATCH 029/932] fix lint --- .../Attachments/AttachmentView/AttachmentViewPdf/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx index d38ffcf7abab..78265c3a9a6a 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx @@ -2,7 +2,7 @@ import React, {memo} from 'react'; import PDFView from '@components/PDFView'; import type AttachmentViewPdfProps from './types'; -function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, style, onError, isUsedAsChatAttachment} : AttachmentViewPdfProps) { +function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, style, onError, isUsedAsChatAttachment}: AttachmentViewPdfProps) { return ( Date: Fri, 29 Mar 2024 03:38:54 +0530 Subject: [PATCH 030/932] fallback to defaultview component --- .../AttachmentViewPdf/index.tsx | 13 +++- .../AttachmentView/AttachmentViewPdf/types.ts | 5 ++ .../DefaultAttachmentView/index.tsx | 58 ++++++++++++++++ .../Attachments/AttachmentView/index.tsx | 68 ++++++------------- src/components/PDFView/index.js | 3 +- 5 files changed, 96 insertions(+), 51 deletions(-) create mode 100644 src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx index 78265c3a9a6a..10bcf28e7797 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx @@ -2,7 +2,17 @@ import React, {memo} from 'react'; import PDFView from '@components/PDFView'; import type AttachmentViewPdfProps from './types'; -function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, style, onError, isUsedAsChatAttachment}: AttachmentViewPdfProps) { +function AttachmentViewPdf({ + file, + encryptedSourceUrl, + isFocused, + onPress, + onToggleKeyboard, + onLoadComplete, + style, + renderFallbackAttachmentView, + isUsedAsChatAttachment, +}: AttachmentViewPdfProps) { return ( ); diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts b/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts index da48480e8479..ecc6e5016446 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts @@ -14,9 +14,14 @@ type AttachmentViewPdfProps = Pick void; + /** Triggered when the PDF fails to load */ onError?: () => void; + /** Whether the PDF is used as a chat attachment */ isUsedAsChatAttachment?: boolean; + + /** Render a fallback view when the PDF fails to load */ + renderFallbackAttachmentView?: () => JSX.Element; }; export default AttachmentViewPdfProps; diff --git a/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx b/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx new file mode 100644 index 000000000000..7360144bf09c --- /dev/null +++ b/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; +import {ActivityIndicator, View} from 'react-native'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; +import Text from '@components/Text'; +import Tooltip from '@components/Tooltip'; +import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; + +type DefaultAttachmentViewProps = { + fileName: string; + shouldShowDownloadIcon?: boolean; + shouldShowLoadingSpinnerIcon?: boolean; + containerStyles?: StyleProp; +}; + +function DefaultAttachmentView({fileName, shouldShowDownloadIcon, shouldShowLoadingSpinnerIcon, containerStyles}: DefaultAttachmentViewProps) { + const theme = useTheme(); + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + return ( + + + + + + {fileName} + {!shouldShowLoadingSpinnerIcon && shouldShowDownloadIcon && ( + + + + + + )} + {shouldShowLoadingSpinnerIcon && ( + + + + + + )} + + ); +} + +export default DefaultAttachmentView; diff --git a/src/components/Attachments/AttachmentView/index.tsx b/src/components/Attachments/AttachmentView/index.tsx index e3b609852e5e..1621d8d9509d 100644 --- a/src/components/Attachments/AttachmentView/index.tsx +++ b/src/components/Attachments/AttachmentView/index.tsx @@ -1,18 +1,15 @@ import Str from 'expensify-common/lib/str'; -import React, {memo, useEffect, useState} from 'react'; -import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native'; -import {ActivityIndicator, View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; -import type {Attachment, AttachmentSource} from '@components/Attachments/types'; +import React, { memo, useEffect, useState } from 'react'; +import type { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native'; +import { View } from 'react-native'; +import type { OnyxEntry } from 'react-native-onyx'; +import { withOnyx } from 'react-native-onyx'; +import type { Attachment, AttachmentSource } from '@components/Attachments/types'; import DistanceEReceipt from '@components/DistanceEReceipt'; import EReceipt from '@components/EReceipt'; import Icon from '@components/Icon'; -import * as Expensicons from '@components/Icon/Expensicons'; import ScrollView from '@components/ScrollView'; -import Text from '@components/Text'; -import Tooltip from '@components/Tooltip'; -import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext'; +import { usePlaybackContext } from '@components/VideoPlayerContexts/PlaybackContext'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -21,13 +18,14 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as CachedPDFPaths from '@libs/actions/CachedPDFPaths'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; import * as TransactionUtils from '@libs/TransactionUtils'; -import type {ColorValue} from '@styles/utils/types'; +import type { ColorValue } from '@styles/utils/types'; import variables from '@styles/variables'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Transaction} from '@src/types/onyx'; +import type { Transaction } from '@src/types/onyx'; import AttachmentViewImage from './AttachmentViewImage'; import AttachmentViewPdf from './AttachmentViewPdf'; import AttachmentViewVideo from './AttachmentViewVideo'; +import DefaultAttachmentView from './DefaultAttachmentView'; type AttachmentViewOnyxProps = { transaction: OnyxEntry; @@ -95,8 +93,8 @@ function AttachmentView({ duration, isUsedAsChatAttachment, }: AttachmentViewProps) { - const {translate} = useLocalize(); - const {updateCurrentlyPlayingURL} = usePlaybackContext(); + const { translate } = useLocalize(); + const { updateCurrentlyPlayingURL } = usePlaybackContext(); const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -113,7 +111,7 @@ function AttachmentView({ const [imageError, setImageError] = useState(false); - useNetwork({onReconnect: () => setImageError(false)}); + useNetwork({ onReconnect: () => setImageError(false) }); // Handles case where source is a component (ex: SVG) or a number // Number may represent a SVG or an image @@ -239,36 +237,12 @@ function AttachmentView({ } return ( - - - - - - {file?.name} - {!shouldShowLoadingSpinnerIcon && shouldShowDownloadIcon && ( - - - - - - )} - {shouldShowLoadingSpinnerIcon && ( - - - - - - )} - + ); } @@ -277,9 +251,9 @@ AttachmentView.displayName = 'AttachmentView'; export default memo( withOnyx({ transaction: { - key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + key: ({ transactionID }) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, }, })(AttachmentView), ); -export type {AttachmentViewProps}; +export type { AttachmentViewProps }; diff --git a/src/components/PDFView/index.js b/src/components/PDFView/index.js index e69b52b74e95..bd4f9a584378 100644 --- a/src/components/PDFView/index.js +++ b/src/components/PDFView/index.js @@ -6,7 +6,6 @@ import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; -import Text from '@components/Text'; import withLocalize from '@components/withLocalize'; import withThemeStyles from '@components/withThemeStyles'; import withWindowDimensions from '@components/withWindowDimensions'; @@ -94,7 +93,7 @@ class PDFView extends Component { maxCanvasHeight={this.props.maxCanvasHeight} maxCanvasArea={this.props.maxCanvasArea} LoadingComponent={} - ErrorComponent={{this.props.translate('attachmentView.failedToLoadPDF')}} + ErrorComponent={this.props.renderFallbackAttachmentView} renderPasswordForm={({isPasswordInvalid, onSubmit, onPasswordChange}) => ( Date: Fri, 29 Mar 2024 04:38:22 +0530 Subject: [PATCH 031/932] fix prop pass --- .../Attachments/AttachmentView/AttachmentViewPdf/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx index 10bcf28e7797..3afb02db5068 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx @@ -24,6 +24,7 @@ function AttachmentViewPdf({ onToggleKeyboard={onToggleKeyboard} onLoadComplete={onLoadComplete} isUsedAsChatAttachment={isUsedAsChatAttachment} + renderFallbackAttachmentView={renderFallbackAttachmentView} /> ); } From 7d5d22a60c27c70622f097ae0ebd92c0e8dcf03e Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 29 Mar 2024 05:01:20 +0530 Subject: [PATCH 032/932] prettier diffs --- .../Attachments/AttachmentView/index.tsx | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/Attachments/AttachmentView/index.tsx b/src/components/Attachments/AttachmentView/index.tsx index 1621d8d9509d..d08f10582c52 100644 --- a/src/components/Attachments/AttachmentView/index.tsx +++ b/src/components/Attachments/AttachmentView/index.tsx @@ -1,15 +1,15 @@ import Str from 'expensify-common/lib/str'; -import React, { memo, useEffect, useState } from 'react'; -import type { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native'; -import { View } from 'react-native'; -import type { OnyxEntry } from 'react-native-onyx'; -import { withOnyx } from 'react-native-onyx'; -import type { Attachment, AttachmentSource } from '@components/Attachments/types'; +import React, {memo, useEffect, useState} from 'react'; +import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; +import type {Attachment, AttachmentSource} from '@components/Attachments/types'; import DistanceEReceipt from '@components/DistanceEReceipt'; import EReceipt from '@components/EReceipt'; import Icon from '@components/Icon'; import ScrollView from '@components/ScrollView'; -import { usePlaybackContext } from '@components/VideoPlayerContexts/PlaybackContext'; +import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -18,10 +18,10 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as CachedPDFPaths from '@libs/actions/CachedPDFPaths'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; import * as TransactionUtils from '@libs/TransactionUtils'; -import type { ColorValue } from '@styles/utils/types'; +import type {ColorValue} from '@styles/utils/types'; import variables from '@styles/variables'; import ONYXKEYS from '@src/ONYXKEYS'; -import type { Transaction } from '@src/types/onyx'; +import type {Transaction} from '@src/types/onyx'; import AttachmentViewImage from './AttachmentViewImage'; import AttachmentViewPdf from './AttachmentViewPdf'; import AttachmentViewVideo from './AttachmentViewVideo'; @@ -93,8 +93,8 @@ function AttachmentView({ duration, isUsedAsChatAttachment, }: AttachmentViewProps) { - const { translate } = useLocalize(); - const { updateCurrentlyPlayingURL } = usePlaybackContext(); + const {translate} = useLocalize(); + const {updateCurrentlyPlayingURL} = usePlaybackContext(); const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -111,7 +111,7 @@ function AttachmentView({ const [imageError, setImageError] = useState(false); - useNetwork({ onReconnect: () => setImageError(false) }); + useNetwork({onReconnect: () => setImageError(false)}); // Handles case where source is a component (ex: SVG) or a number // Number may represent a SVG or an image @@ -251,9 +251,9 @@ AttachmentView.displayName = 'AttachmentView'; export default memo( withOnyx({ transaction: { - key: ({ transactionID }) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, }, })(AttachmentView), ); -export type { AttachmentViewProps }; +export type {AttachmentViewProps}; From 91daa59c3cdc13d35038ae40db35a013e12f9438 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Fri, 29 Mar 2024 22:50:15 +0300 Subject: [PATCH 033/932] implemented offline indicator for video --- src/components/VideoPlayer/BaseVideoPlayer.js | 20 ++++++++++++++++--- src/languages/en.ts | 1 + src/languages/es.ts | 1 + 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/components/VideoPlayer/BaseVideoPlayer.js b/src/components/VideoPlayer/BaseVideoPlayer.js index 91737ad3938a..9b247905db37 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.js +++ b/src/components/VideoPlayer/BaseVideoPlayer.js @@ -3,13 +3,17 @@ import {Video, VideoFullscreenUpdate} from 'expo-av'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; +import BlockingView from '@components/BlockingViews/BlockingView'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import Hoverable from '@components/Hoverable'; +import * as Expensicons from '@components/Icon/Expensicons'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import {useFullScreenContext} from '@components/VideoPlayerContexts/FullScreenContext'; import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext'; import VideoPopoverMenu from '@components/VideoPopoverMenu'; +import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; @@ -73,6 +77,8 @@ function BaseVideoPlayer({ const isCurrentlyURLSet = currentlyPlayingURL === url; const isUploading = _.some(CONST.ATTACHMENT_LOCAL_URL_PREFIX, (prefix) => url.startsWith(prefix)); const videoStateRef = useRef(null); + const {translate} = useLocalize(); + const theme = useTheme(); const togglePlayCurrentVideo = useCallback(() => { videoResumeTryNumber.current = 0; @@ -275,9 +281,17 @@ function BaseVideoPlayer({ )} - - {(isLoading || isBuffering) && } - + {((isLoading && !isOffline) || isBuffering) && } + {isLoading && isOffline && ( + + + + )} {shouldShowVideoControls && !isLoading && (isPopoverVisible || isHovered || canUseTouchScreen) && ( `You've selected the maximum number (${count}) of participants.`, youAppearToBeOffline: 'You appear to be offline.', thisFeatureRequiresInternet: 'This feature requires an active internet connection to be used.', + attachementWillBeAvailableOnceBackOnline: 'Attachment will become available once back online.', areYouSure: 'Are you sure?', verify: 'Verify', yesContinue: 'Yes, continue', diff --git a/src/languages/es.ts b/src/languages/es.ts index da4a17e76fdc..fb417cf5364f 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -257,6 +257,7 @@ export default { maxParticipantsReached: ({count}: MaxParticipantsReachedParams) => `Has seleccionado el número máximo (${count}) de participantes.`, youAppearToBeOffline: 'Parece que estás desconectado.', thisFeatureRequiresInternet: 'Esta función requiere una conexión a Internet activa para ser utilizada.', + attachementWillBeAvailableOnceBackOnline: 'El archivo adjunto estará disponible cuando vuelvas a estar en línea.', areYouSure: '¿Estás seguro?', verify: 'Verifique', yesContinue: 'Sí, continuar', From 0af56c671213f1b2ade9f045259edcf27db8a7ef Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 30 Mar 2024 01:50:13 +0530 Subject: [PATCH 034/932] remove renderfallbackview prop --- .../AttachmentViewPdf/BaseAttachmentViewPdf.tsx | 4 ++-- .../AttachmentView/AttachmentViewPdf/index.tsx | 14 ++------------ .../AttachmentView/AttachmentViewPdf/types.ts | 9 +++------ .../AttachmentView/DefaultAttachmentView/index.tsx | 4 ++-- .../Attachments/AttachmentView/index.tsx | 6 +++--- src/components/PDFView/index.js | 9 ++++++++- src/components/PDFView/index.native.js | 4 ++-- src/components/PDFView/pdfViewPropTypes.js | 5 ++++- 8 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.tsx index 9971c2d03820..eb22535d1d5f 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.tsx @@ -15,7 +15,7 @@ function BaseAttachmentViewPdf({ onLoadComplete, style, isUsedAsChatAttachment, - onError, + onLoadError, }: AttachmentViewPdfProps) { const attachmentCarouselPagerContext = useContext(AttachmentCarouselPagerContext); const isScrollEnabled = attachmentCarouselPagerContext === null ? undefined : attachmentCarouselPagerContext.isScrollEnabled; @@ -78,7 +78,7 @@ function BaseAttachmentViewPdf({ onScaleChanged={onScaleChanged} onLoadComplete={onLoadComplete} isUsedAsChatAttachment={isUsedAsChatAttachment} - onError={onError} + onLoadError={onLoadError} /> ); } diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx index 3afb02db5068..c1ded9782a7a 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx @@ -2,17 +2,7 @@ import React, {memo} from 'react'; import PDFView from '@components/PDFView'; import type AttachmentViewPdfProps from './types'; -function AttachmentViewPdf({ - file, - encryptedSourceUrl, - isFocused, - onPress, - onToggleKeyboard, - onLoadComplete, - style, - renderFallbackAttachmentView, - isUsedAsChatAttachment, -}: AttachmentViewPdfProps) { +function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, style, isUsedAsChatAttachment, containerStyles}: AttachmentViewPdfProps) { return ( ); } diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts b/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts index ecc6e5016446..1c3cd8adfba4 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts @@ -8,20 +8,17 @@ type AttachmentViewPdfProps = Pick; - /** Styles for the error label */ - errorLabelStyles?: StyleProp; - /** Triggered when the PDF's onScaleChanged event is triggered */ onScaleChanged?: (scale: number) => void; /** Triggered when the PDF fails to load */ - onError?: () => void; + onLoadError?: () => void; /** Whether the PDF is used as a chat attachment */ isUsedAsChatAttachment?: boolean; - /** Render a fallback view when the PDF fails to load */ - renderFallbackAttachmentView?: () => JSX.Element; + /** Additional container styles */ + containerStyles?: StyleProp; }; export default AttachmentViewPdfProps; diff --git a/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx b/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx index 7360144bf09c..23d39ec096b5 100644 --- a/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx +++ b/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx @@ -10,13 +10,13 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; type DefaultAttachmentViewProps = { - fileName: string; + fileName?: string; shouldShowDownloadIcon?: boolean; shouldShowLoadingSpinnerIcon?: boolean; containerStyles?: StyleProp; }; -function DefaultAttachmentView({fileName, shouldShowDownloadIcon, shouldShowLoadingSpinnerIcon, containerStyles}: DefaultAttachmentViewProps) { +function DefaultAttachmentView({fileName = '', shouldShowLoadingSpinnerIcon = false, shouldShowDownloadIcon, containerStyles}: DefaultAttachmentViewProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); diff --git a/src/components/Attachments/AttachmentView/index.tsx b/src/components/Attachments/AttachmentView/index.tsx index d08f10582c52..5e9a1b2bf4f6 100644 --- a/src/components/Attachments/AttachmentView/index.tsx +++ b/src/components/Attachments/AttachmentView/index.tsx @@ -176,9 +176,9 @@ function AttachmentView({ onToggleKeyboard={onToggleKeyboard} onLoadComplete={onPDFLoadComplete} style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1} - isUsedInCarousel={isUsedInCarousel} isUsedAsChatAttachment={isUsedAsChatAttachment} - onError={() => { + containerStyles={containerStyles} + onLoadError={() => { setIsPdfFailedToLoad(true); }} /> @@ -238,7 +238,7 @@ function AttachmentView({ return ( } - ErrorComponent={this.props.renderFallbackAttachmentView} + ErrorComponent={ + + } renderPasswordForm={({isPasswordInvalid, onSubmit, onPasswordChange}) => ( Date: Sat, 30 Mar 2024 01:57:32 +0530 Subject: [PATCH 035/932] fix the comment for the prop --- src/components/Attachments/AttachmentView/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Attachments/AttachmentView/index.tsx b/src/components/Attachments/AttachmentView/index.tsx index 5e9a1b2bf4f6..f32265bdc01f 100644 --- a/src/components/Attachments/AttachmentView/index.tsx +++ b/src/components/Attachments/AttachmentView/index.tsx @@ -68,7 +68,7 @@ type AttachmentViewProps = AttachmentViewOnyxProps & /* Whether it is hovered or not */ isHovered?: boolean; - /** Duration of the video */ + /** Whether the attachment is used as a chat attachment */ isUsedAsChatAttachment?: boolean; }; From 6436ddbef5f2928851b20ba4448489fbb2c74b8b Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 30 Mar 2024 02:32:19 +0530 Subject: [PATCH 036/932] fixes loading for pdf on web --- src/components/FullscreenLoadingIndicator.tsx | 5 +++-- src/components/PDFView/index.js | 9 ++++++++- src/components/PDFView/pdfViewPropTypes.js | 3 +++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/components/FullscreenLoadingIndicator.tsx b/src/components/FullscreenLoadingIndicator.tsx index bd3082db5fa4..57c2d7dc955e 100644 --- a/src/components/FullscreenLoadingIndicator.tsx +++ b/src/components/FullscreenLoadingIndicator.tsx @@ -6,13 +6,14 @@ import useThemeStyles from '@hooks/useThemeStyles'; type FullScreenLoadingIndicatorProps = { style?: StyleProp; + isFullScreen?: boolean; }; -function FullScreenLoadingIndicator({style}: FullScreenLoadingIndicatorProps) { +function FullScreenLoadingIndicator({style, isFullScreen = true}: FullScreenLoadingIndicatorProps) { const theme = useTheme(); const styles = useThemeStyles(); return ( - + } + LoadingComponent={} ErrorComponent={ Date: Sat, 30 Mar 2024 02:40:55 +0530 Subject: [PATCH 037/932] fixes proptype warning --- src/components/PDFView/index.js | 30 +++++++++++++++++----- src/components/PDFView/pdfViewPropTypes.js | 3 --- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/components/PDFView/index.js b/src/components/PDFView/index.js index 64904fc84b3e..d2ee3d448718 100644 --- a/src/components/PDFView/index.js +++ b/src/components/PDFView/index.js @@ -8,8 +8,8 @@ import DefaultAttachmentView from '@components/Attachments/AttachmentView/Defaul import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import withLocalize from '@components/withLocalize'; +import withStyleUtils, {withStyleUtilsPropTypes} from '@components/withStyleUtils'; import withThemeStyles from '@components/withThemeStyles'; -import withStyleUtils from '@components/withStyleUtils'; import withWindowDimensions from '@components/withWindowDimensions'; import compose from '@libs/compose'; import variables from '@styles/variables'; @@ -17,10 +17,16 @@ import * as CanvasSize from '@userActions/CanvasSize'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import PDFPasswordForm from './PDFPasswordForm'; -import * as pdfViewPropTypes from './pdfViewPropTypes'; +import {propTypes as pdfViewPropTypes, defaultProps as pdfViewDefaultProps} from './pdfViewPropTypes'; const LOADING_THUMBNAIL_HEIGHT = 250; const LOADING_THUMBNAIL_WIDTH = 250; + +const PDFViewPropTypes = { + ...pdfViewPropTypes, + ...withStyleUtilsPropTypes, +}; + class PDFView extends Component { constructor(props) { super(props); @@ -83,8 +89,13 @@ class PDFView extends Component { const styles = this.props.themeStyles; const outerContainerStyle = [styles.w100, styles.h100, styles.justifyContentCenter, styles.alignItemsCenter]; const loadingIndicatorStyles = this.props.isUsedAsChatAttachment - ? [this.props.themeStyles.chatItemPDFAttachmentLoading, this.props.StyleUtils.getWidthAndHeightStyle(LOADING_THUMBNAIL_WIDTH, LOADING_THUMBNAIL_HEIGHT), styles.justifyContentCenter, styles.alignItemsCenter] - : []; + ? [ + this.props.themeStyles.chatItemPDFAttachmentLoading, + this.props.StyleUtils.getWidthAndHeightStyle(LOADING_THUMBNAIL_WIDTH, LOADING_THUMBNAIL_HEIGHT), + styles.justifyContentCenter, + styles.alignItemsCenter, + ] + : []; return ( } + LoadingComponent={ + + } ErrorComponent={ Date: Sat, 30 Mar 2024 02:47:58 +0530 Subject: [PATCH 038/932] prettier diffs --- src/components/PDFView/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PDFView/index.js b/src/components/PDFView/index.js index d2ee3d448718..5051e75266cb 100644 --- a/src/components/PDFView/index.js +++ b/src/components/PDFView/index.js @@ -17,7 +17,7 @@ import * as CanvasSize from '@userActions/CanvasSize'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import PDFPasswordForm from './PDFPasswordForm'; -import {propTypes as pdfViewPropTypes, defaultProps as pdfViewDefaultProps} from './pdfViewPropTypes'; +import {defaultProps as pdfViewDefaultProps, propTypes as pdfViewPropTypes} from './pdfViewPropTypes'; const LOADING_THUMBNAIL_HEIGHT = 250; const LOADING_THUMBNAIL_WIDTH = 250; From 496791196798dce7ea14db3a6c7a6acf4f4a4bc8 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 1 Apr 2024 17:10:21 +0700 Subject: [PATCH 039/932] remove redundant comment --- src/components/OfflineIndicator.tsx | 31 +++++++++++++---------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/components/OfflineIndicator.tsx b/src/components/OfflineIndicator.tsx index bb497fad5b4e..26a9dd740e24 100644 --- a/src/components/OfflineIndicator.tsx +++ b/src/components/OfflineIndicator.tsx @@ -49,23 +49,20 @@ function OfflineIndicator({style, containerStyles}: OfflineIndicatorProps) { height={variables.iconSizeSmall} /> - { - // If we reversed the ternary, unreachability message would always show even when offline - isOffline ? ( - translate('common.youAppearToBeOffline') - ) : ( - <> - {translate('common.weMightHaveProblem')} - - {new URL(CONST.STATUS_EXPENSIFY_URL).host} - - . - - ) - } + {isOffline ? ( + translate('common.youAppearToBeOffline') + ) : ( + <> + {translate('common.weMightHaveProblem')} + + {new URL(CONST.STATUS_EXPENSIFY_URL).host} + + . + + )} ); From 4213ccf99135100c4963a4e127dd7689efa3797b Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 1 Apr 2024 17:10:30 +0700 Subject: [PATCH 040/932] fix typecheck --- .storybook/preview.tsx | 2 +- src/types/onyx/Network.ts | 1 + tests/unit/APITest.ts | 38 +++++++++++++++++++------------------- tests/unit/NetworkTest.ts | 10 +++++----- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 4767c7d81343..9c6738704c45 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -16,7 +16,7 @@ import './fonts.css'; Onyx.init({ keys: ONYXKEYS, initialKeyStates: { - [ONYXKEYS.NETWORK]: {isOffline: false}, + [ONYXKEYS.NETWORK]: {isOffline: false, isBackendReachable: true}, }, }); diff --git a/src/types/onyx/Network.ts b/src/types/onyx/Network.ts index d4162d1ffdb7..e9a56ba013a8 100644 --- a/src/types/onyx/Network.ts +++ b/src/types/onyx/Network.ts @@ -2,6 +2,7 @@ type Network = { /** Is the network currently offline or not */ isOffline: boolean; + /** Is the backend reachable when online */ isBackendReachable: boolean; /** Should the network be forced offline */ diff --git a/tests/unit/APITest.ts b/tests/unit/APITest.ts index 359288b2a1ef..b9404973d418 100644 --- a/tests/unit/APITest.ts +++ b/tests/unit/APITest.ts @@ -68,7 +68,7 @@ describe('APITests', () => { const xhr = jest.spyOn(HttpUtils, 'xhr').mockRejectedValue(new Error('Unexpected xhr call')); // Given we're offline - return Onyx.set(ONYXKEYS.NETWORK, {isOffline: true}) + return Onyx.set(ONYXKEYS.NETWORK, {isOffline: true, isBackendReachable: false}) .then(() => { // When API Writes and Reads are called // @ts-expect-error - mocking the parameter @@ -104,7 +104,7 @@ describe('APITests', () => { // Given we have some requests made while we're offline return ( Onyx.multiSet({ - [ONYXKEYS.NETWORK]: {isOffline: true}, + [ONYXKEYS.NETWORK]: {isOffline: true, isBackendReachable: false}, [ONYXKEYS.CREDENTIALS]: {autoGeneratedLogin: 'test', autoGeneratedPassword: 'passwd'}, [ONYXKEYS.SESSION]: {authToken: 'testToken'}, }) @@ -122,7 +122,7 @@ describe('APITests', () => { }) // When we resume connectivity - .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false})) + .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true})) .then(waitForBatchedUpdates) .then(() => { expect(NetworkStore.isOffline()).toBe(false); @@ -158,7 +158,7 @@ describe('APITests', () => { // Given we have some requests made while we're offline return ( - Onyx.set(ONYXKEYS.NETWORK, {isOffline: true}) + Onyx.set(ONYXKEYS.NETWORK, {isOffline: true, isBackendReachable: false}) .then(() => { // When API Write commands are made // @ts-expect-error - mocking the parameter @@ -169,7 +169,7 @@ describe('APITests', () => { }) // When we resume connectivity - .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false})) + .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true})) .then(waitForBatchedUpdates) .then(() => { // Then requests should remain persisted until the xhr call is resolved @@ -222,7 +222,7 @@ describe('APITests', () => { // Given we have a request made while we're offline return ( - Onyx.set(ONYXKEYS.NETWORK, {isOffline: true}) + Onyx.set(ONYXKEYS.NETWORK, {isOffline: true, isBackendReachable: false}) .then(() => { // When API Write commands are made // @ts-expect-error - mocking the parameter @@ -231,7 +231,7 @@ describe('APITests', () => { }) // When we resume connectivity - .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false})) + .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true})) .then(waitForBatchedUpdates) .then(() => { // Then there has only been one request so far @@ -310,7 +310,7 @@ describe('APITests', () => { Onyx.merge(ONYXKEYS.CREDENTIALS, {autoGeneratedLogin: 'test', autoGeneratedPassword: 'passwd'}); return ( waitForBatchedUpdates() - .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: true})) + .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: true, isBackendReachable: false})) .then(() => { // @ts-expect-error - mocking the parameter API.write('Mock', {param1: 'value1'}); @@ -318,7 +318,7 @@ describe('APITests', () => { }) // When we resume connectivity - .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false})) + .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true})) .then(waitForBatchedUpdates) .then(() => { const nonLogCalls = xhr.mock.calls.filter(([commandName]) => commandName !== 'Log'); @@ -341,7 +341,7 @@ describe('APITests', () => { const xhr = jest.spyOn(HttpUtils, 'xhr').mockResolvedValue({jsonCode: CONST.JSON_CODE.SUCCESS}); return Onyx.multiSet({ [ONYXKEYS.SESSION]: {authToken: 'anyToken'}, - [ONYXKEYS.NETWORK]: {isOffline: true}, + [ONYXKEYS.NETWORK]: {isOffline: true, isBackendReachable: false}, [ONYXKEYS.CREDENTIALS]: {autoGeneratedLogin: 'test_user', autoGeneratedPassword: 'psswd'}, }) .then(() => { @@ -363,7 +363,7 @@ describe('APITests', () => { return waitForBatchedUpdates(); }) - .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false})) + .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true})) .then(waitForBatchedUpdates) .then(() => { // Then expect all 7 calls to have been made and for the Writes to be made in the order that we made them @@ -384,7 +384,7 @@ describe('APITests', () => { const xhr = jest.spyOn(HttpUtils, 'xhr').mockResolvedValueOnce({jsonCode: CONST.JSON_CODE.NOT_AUTHENTICATED}).mockResolvedValue({jsonCode: CONST.JSON_CODE.SUCCESS}); return Onyx.multiSet({ - [ONYXKEYS.NETWORK]: {isOffline: true}, + [ONYXKEYS.NETWORK]: {isOffline: true, isBackendReachable: false}, [ONYXKEYS.SESSION]: {authToken: 'test'}, [ONYXKEYS.CREDENTIALS]: {autoGeneratedLogin: 'test', autoGeneratedPassword: 'passwd'}, }) @@ -404,7 +404,7 @@ describe('APITests', () => { API.write('MockCommand', {content: 'value6'}); return waitForBatchedUpdates(); }) - .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false})) + .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true})) .then(waitForBatchedUpdates) .then(() => { // Then expect only 8 calls to have been made total and for them to be made in the order that we made them despite requiring reauthentication @@ -434,7 +434,7 @@ describe('APITests', () => { return Onyx.multiSet({ [ONYXKEYS.SESSION]: {authToken: 'oldToken'}, - [ONYXKEYS.NETWORK]: {isOffline: false}, + [ONYXKEYS.NETWORK]: {isOffline: false, isBackendReachable: true}, [ONYXKEYS.CREDENTIALS]: {autoGeneratedLogin: 'test_user', autoGeneratedPassword: 'psswd'}, }) .then(() => { @@ -448,7 +448,7 @@ describe('APITests', () => { forceNetworkRequest: false, }); - Onyx.set(ONYXKEYS.NETWORK, {isOffline: true}); + Onyx.set(ONYXKEYS.NETWORK, {isOffline: true, isBackendReachable: false}); expect(NetworkStore.isOffline()).toBe(false); expect(NetworkStore.isAuthenticating()).toBe(false); return waitForBatchedUpdates(); @@ -467,7 +467,7 @@ describe('APITests', () => { waitForBatchedUpdates(); // Come back from offline to trigger the sequential queue flush - Onyx.set(ONYXKEYS.NETWORK, {isOffline: false}); + Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true}); }) .then(() => { // When we wait for the sequential queue to finish @@ -514,7 +514,7 @@ describe('APITests', () => { // Given a simulated a condition where the credentials have not yet been read from storage and we are offline return Onyx.multiSet({ - [ONYXKEYS.NETWORK]: {isOffline: true}, + [ONYXKEYS.NETWORK]: {isOffline: true, isBackendReachable: false}, [ONYXKEYS.CREDENTIALS]: {}, [ONYXKEYS.SESSION]: null, }) @@ -531,7 +531,7 @@ describe('APITests', () => { expect(PersistedRequests.getAll().length).toBe(1); // When we go online and wait for promises to resolve - return Onyx.set(ONYXKEYS.NETWORK, {isOffline: false}); + return Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true}); }) .then(waitForBatchedUpdates) .then(() => { @@ -555,7 +555,7 @@ describe('APITests', () => { test('Write request will move directly to the SequentialQueue when we are online and block non-Write requests', () => { const xhr = jest.spyOn(HttpUtils, 'xhr'); - return Onyx.set(ONYXKEYS.NETWORK, {isOffline: false}) + return Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true}) .then(() => { // GIVEN that we are online expect(NetworkStore.isOffline()).toBe(false); diff --git a/tests/unit/NetworkTest.ts b/tests/unit/NetworkTest.ts index 63b275a1a6b6..eef607f8036b 100644 --- a/tests/unit/NetworkTest.ts +++ b/tests/unit/NetworkTest.ts @@ -110,7 +110,7 @@ describe('NetworkTests', () => { // This should first trigger re-authentication and then a Failed to fetch PersonalDetails.openPersonalDetails(); return waitForBatchedUpdates() - .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false})) + .then(() => Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true})) .then(() => { expect(isOffline).toBe(false); @@ -261,7 +261,7 @@ describe('NetworkTests', () => { const logHmmmSpy = jest.spyOn(Log, 'hmmm'); // Given we have a request made while online - return Onyx.set(ONYXKEYS.NETWORK, {isOffline: false}) + return Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true}) .then(() => { Network.post('MockBadNetworkResponse', {param1: 'value1'}); return waitForBatchedUpdates(); @@ -277,7 +277,7 @@ describe('NetworkTests', () => { const logAlertSpy = jest.spyOn(Log, 'alert'); // Given we have a request made while online - return Onyx.set(ONYXKEYS.NETWORK, {isOffline: false}) + return Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true}) .then(() => { Network.post('MockBadNetworkResponse', {param1: 'value1'}); return waitForBatchedUpdates(); @@ -293,7 +293,7 @@ describe('NetworkTests', () => { const onResolved = jest.fn(); // Given we have a request made while online - return Onyx.set(ONYXKEYS.NETWORK, {isOffline: false}) + return Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true}) .then(() => { expect(NetworkStore.isOffline()).toBe(false); @@ -314,7 +314,7 @@ describe('NetworkTests', () => { // GIVEN a mock that will return a "cancelled" request error global.fetch = jest.fn().mockRejectedValue(new DOMException('Aborted', CONST.ERROR.REQUEST_CANCELLED)); - return Onyx.set(ONYXKEYS.NETWORK, {isOffline: false}) + return Onyx.set(ONYXKEYS.NETWORK, {isOffline: false, isBackendReachable: true}) .then(() => { // WHEN we make a few requests and then cancel them Network.post('MockCommandOne'); From 5c860463ab31c2772a937e9570aa82586a8241c7 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 1 Apr 2024 17:23:54 +0700 Subject: [PATCH 041/932] rename constant --- src/CONST.ts | 2 +- src/libs/NetworkConnection.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 74938324357a..4b1786fdf643 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -976,7 +976,7 @@ const CONST = { MAX_RETRY_WAIT_TIME_MS: 10 * 1000, PROCESS_REQUEST_DELAY_MS: 1000, MAX_PENDING_TIME_MS: 10 * 1000, - REACHABILITY_TIMEOUT_MS: 60 * 1000, + BACKEND_CHECK_INTERVAL_MS: 60 * 1000, MAX_REQUEST_RETRIES: 10, }, WEEK_STARTS_ON: 1, // Monday diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index 57ae5be3f9fa..4048cc209c59 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -94,7 +94,7 @@ function subscribeToBackendReachability(): () => void { }) .then(NetworkActions.setIsBackendReachable) .catch(() => NetworkActions.setIsBackendReachable(false)); - }, CONST.NETWORK.REACHABILITY_TIMEOUT_MS); + }, CONST.NETWORK.BACKEND_CHECK_INTERVAL_MS); return () => { clearInterval(intervalID); From 5de949bb681aa01fa9fee121d78878dcb9c67023 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 2 Apr 2024 15:48:30 +0700 Subject: [PATCH 042/932] Modify comment --- src/libs/NetworkConnection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index 4048cc209c59..4383a921662f 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -106,7 +106,7 @@ function subscribeToBackendReachability(): () => void { * @returns unsubscribe method */ function subscribeToNetworkStatus(): () => void { - // Note: We are disabling the reachability check when using the local web API since requests can get stuck in a 'Pending' state and are not reliable indicators for "offline". + // Note: We are disabling the reachability check when using the local web API since requests can get stuck in a 'Pending' state and are not reliable indicators for reachability. // If you need to test the "recheck" feature then switch to the production API proxy server. const unsubscribeFromBackendReachability = !CONFIG.IS_USING_LOCAL_WEB ? subscribeToBackendReachability() : undefined; From 9bd4c96250b25ac591c8904fa9709510dbcfda50 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 2 Apr 2024 23:32:51 +0530 Subject: [PATCH 043/932] adds onError prop in react-fast-pdf --- package-lock.json | 246 +++++++----------- package.json | 4 +- .../AttachmentViewPdf/index.tsx | 3 +- .../DefaultAttachmentView/index.tsx | 6 + src/components/FullscreenLoadingIndicator.tsx | 5 +- src/components/PDFView/index.js | 48 ++-- 6 files changed, 130 insertions(+), 182 deletions(-) diff --git a/package-lock.json b/package-lock.json index 809b4bd5c4df..7fc027a16e9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,7 +72,7 @@ "react-content-loader": "^7.0.0", "react-dom": "18.1.0", "react-error-boundary": "^4.0.11", - "react-fast-pdf": "^1.0.6", + "react-fast-pdf": "https://github.com/ishpaul777/react-fast-pdf/tree/fix-blankArea-after-loading-pdf", "react-map-gl": "^7.1.3", "react-native": "0.73.2", "react-native-android-location-enabler": "^2.0.1", @@ -3098,8 +3098,10 @@ }, "node_modules/@expensify/react-native-live-markdown": { "version": "0.1.35", - "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.35.tgz", - "integrity": "sha512-W0FFIiU/sT+AwIrIOUHiNAHYjODAkEdYsf75tfBbkA6v2byHPxUlbzaJrZEQc0HgbvtAfTf9iQQqGWjNqe4pog==", + "license": "MIT", + "workspaces": [ + "example" + ], "engines": { "node": ">= 18.0.0" }, @@ -6402,8 +6404,6 @@ }, "node_modules/@jest/types": { "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", "optional": true, @@ -6795,8 +6795,9 @@ "license": "BSD-3-Clause" }, "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.10", - "license": "BSD-3-Clause", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", "optional": true, "dependencies": { "detect-libc": "^2.0.0", @@ -6815,7 +6816,8 @@ }, "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { "version": "3.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "optional": true, "dependencies": { "semver": "^6.0.0" @@ -6829,26 +6831,13 @@ }, "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { "version": "6.3.1", - "license": "ISC", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "optional": true, "bin": { "semver": "bin/semver.js" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { - "version": "5.0.0", - "license": "ISC", - "optional": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@mapbox/point-geometry": { "version": "0.1.0", "license": "ISC" @@ -8187,8 +8176,7 @@ }, "node_modules/@react-native-community/cli-doctor/node_modules/ip": { "version": "1.1.9", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", - "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==" + "license": "MIT" }, "node_modules/@react-native-community/cli-doctor/node_modules/strip-ansi": { "version": "5.2.0", @@ -8271,8 +8259,7 @@ }, "node_modules/@react-native-community/cli-hermes/node_modules/ip": { "version": "1.1.9", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", - "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==" + "license": "MIT" }, "node_modules/@react-native-community/cli-hermes/node_modules/supports-color": { "version": "7.2.0", @@ -8445,8 +8432,7 @@ }, "node_modules/@react-native-community/cli-server-api/node_modules/@jest/types": { "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", @@ -8460,8 +8446,7 @@ }, "node_modules/@react-native-community/cli-server-api/node_modules/@types/yargs": { "version": "15.0.19", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", - "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -8488,8 +8473,7 @@ }, "node_modules/@react-native-community/cli-server-api/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8517,8 +8501,7 @@ }, "node_modules/@react-native-community/cli-server-api/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -8542,8 +8525,7 @@ }, "node_modules/@react-native-community/cli-server-api/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -19039,8 +19021,6 @@ }, "node_modules/@types/yargs": { "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "dev": true, "license": "MIT", "optional": true, @@ -19051,8 +19031,6 @@ }, "node_modules/@types/yargs-parser": { "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "license": "MIT" }, "node_modules/@types/yauzl": { @@ -19991,8 +19969,6 @@ }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", "dev": true, "license": "BSD-2-Clause" }, @@ -20007,7 +19983,8 @@ }, "node_modules/abbrev": { "version": "1.1.1", - "license": "ISC", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "optional": true }, "node_modules/abort-controller": { @@ -20934,8 +20911,6 @@ }, "node_modules/async": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true, "license": "MIT" }, @@ -22399,9 +22374,8 @@ }, "node_modules/bs-logger": { "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, + "license": "MIT", "dependencies": { "fast-json-stable-stringify": "2.x" }, @@ -22920,8 +22894,9 @@ }, "node_modules/canvas": { "version": "2.11.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", + "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", "hasInstallScript": true, - "license": "MIT", "optional": true, "dependencies": { "@mapbox/node-pre-gyp": "^1.0.0", @@ -23334,8 +23309,7 @@ }, "node_modules/cliui": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -23661,9 +23635,8 @@ }, "node_modules/concurrently": { "version": "8.2.2", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", - "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.2", "date-fns": "^2.30.0", @@ -23688,9 +23661,8 @@ }, "node_modules/concurrently/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -23703,9 +23675,8 @@ }, "node_modules/concurrently/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -23719,9 +23690,8 @@ }, "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -23731,9 +23701,8 @@ }, "node_modules/concurrently/node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -23743,24 +23712,21 @@ }, "node_modules/concurrently/node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/concurrently/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/concurrently/node_modules/supports-color": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -25335,8 +25301,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.1", - "license": "Apache-2.0", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "optional": true, "engines": { "node": ">=8" @@ -25671,8 +25638,6 @@ }, "node_modules/duplexer": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true, "license": "MIT" }, @@ -27640,8 +27605,7 @@ }, "node_modules/expo-image": { "version": "1.11.0", - "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-1.11.0.tgz", - "integrity": "sha512-CfkSGWIGidxxqzErke16bCS9aefS04qvgjk+T9Nc34LAb3ysBAqCv5hoCnvylHOvi/7jTCC/ouLm5oLLqkDSRQ==", + "license": "MIT", "dependencies": { "@react-native/assets-registry": "~0.73.1" }, @@ -27651,16 +27615,14 @@ }, "node_modules/expo-image-loader": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-4.6.0.tgz", - "integrity": "sha512-RHQTDak7/KyhWUxikn2yNzXL7i2cs16cMp6gEAgkHOjVhoCJQoOJ0Ljrt4cKQ3IowxgCuOrAgSUzGkqs7omj8Q==", + "license": "MIT", "peerDependencies": { "expo": "*" } }, "node_modules/expo-image-manipulator": { "version": "11.8.0", - "resolved": "https://registry.npmjs.org/expo-image-manipulator/-/expo-image-manipulator-11.8.0.tgz", - "integrity": "sha512-ZWVrHnYmwJq6h7auk+ropsxcNi+LyZcPFKQc8oy+JA0SaJosfShvkCm7RADWAunHmfPCmjHrhwPGEu/rs7WG/A==", + "license": "MIT", "dependencies": { "expo-image-loader": "~4.6.0" }, @@ -29657,8 +29619,6 @@ }, "node_modules/html-entities": { "version": "2.5.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", - "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", "funding": [ { "type": "github", @@ -29668,7 +29628,8 @@ "type": "patreon", "url": "https://patreon.com/mdevils" } - ] + ], + "license": "MIT" }, "node_modules/html-escaper": { "version": "2.0.2", @@ -30573,9 +30534,8 @@ }, "node_modules/ip": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", - "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ip-regex": { "version": "2.1.0", @@ -34355,8 +34315,6 @@ }, "node_modules/klaw-sync": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", - "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", "dev": true, "license": "MIT", "dependencies": { @@ -34676,9 +34634,8 @@ }, "node_modules/lodash.memoize": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -36710,6 +36667,21 @@ "url": "https://github.com/sponsors/antelle" } }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-package-data": { "version": "2.5.0", "license": "BSD-2-Clause", @@ -37624,8 +37596,6 @@ }, "node_modules/patch-package": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", - "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", "dev": true, "license": "MIT", "dependencies": { @@ -37655,8 +37625,6 @@ }, "node_modules/patch-package/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { @@ -37671,8 +37639,6 @@ }, "node_modules/patch-package/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -37688,8 +37654,6 @@ }, "node_modules/patch-package/node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -37701,15 +37665,11 @@ }, "node_modules/patch-package/node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, "node_modules/patch-package/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -37718,8 +37678,6 @@ }, "node_modules/patch-package/node_modules/open": { "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", "dev": true, "license": "MIT", "dependencies": { @@ -37735,8 +37693,6 @@ }, "node_modules/patch-package/node_modules/rimraf": { "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "license": "ISC", "dependencies": { @@ -37748,8 +37704,6 @@ }, "node_modules/patch-package/node_modules/slash": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true, "license": "MIT", "engines": { @@ -37758,8 +37712,6 @@ }, "node_modules/patch-package/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -37771,8 +37723,6 @@ }, "node_modules/patch-package/node_modules/tmp": { "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "license": "MIT", "dependencies": { @@ -38956,8 +38906,7 @@ }, "node_modules/react-content-loader": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/react-content-loader/-/react-content-loader-7.0.0.tgz", - "integrity": "sha512-xaBwpO7eiJyEc4ndym+g6wcruV9W2y3DKqbw4U48QFBsv0IeAVZO+aCUb8GptlDLWM8n5zi2HcFSGlj5r+53Tg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -39051,8 +39000,8 @@ }, "node_modules/react-fast-pdf": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/react-fast-pdf/-/react-fast-pdf-1.0.6.tgz", - "integrity": "sha512-CdAnBSZaLCGLSEuiqWLzzXhV9Wvdf1VRixaXCrb3NFrXyeltahF7PY+u7eU6ynrWZGmNI6g0cMLPv0DQhJEeew==", + "resolved": "git+ssh://git@github.com/ishpaul777/react-fast-pdf.git#0a8a073ad5f0d376c1827a2dacfafd197500bf3b", + "license": "MIT", "dependencies": { "react-pdf": "^7.7.0", "react-window": "^1.8.10" @@ -39109,14 +39058,6 @@ } } }, - "node_modules/react-fast-pdf/node_modules/warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/react-freeze": { "version": "1.0.3", "license": "MIT", @@ -39407,6 +39348,7 @@ "node_modules/react-native-image-size": { "version": "1.1.3", "resolved": "git+ssh://git@github.com/Expensify/react-native-image-size.git#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002", + "integrity": "sha512-uIZLaqqjSivO+iBGx3VpZRSn/cYy18ct6S1H35gK8n74eJFb/Ds6qUJ+jGw5PUt0KEzw+aXLgPq6gHDXT5Q29A==", "license": "MIT" }, "node_modules/react-native-key-command": { @@ -39836,8 +39778,7 @@ }, "node_modules/react-native/node_modules/@jest/types": { "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", @@ -39851,8 +39792,7 @@ }, "node_modules/react-native/node_modules/@types/yargs": { "version": "15.0.19", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", - "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -39879,8 +39819,7 @@ }, "node_modules/react-native/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -39920,8 +39859,7 @@ }, "node_modules/react-native/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -39976,8 +39914,7 @@ }, "node_modules/react-native/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -40593,8 +40530,7 @@ }, "node_modules/react-window": { "version": "1.8.10", - "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz", - "integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.0.0", "memoize-one": ">=3.1.1 <6" @@ -41551,9 +41487,8 @@ }, "node_modules/rxjs": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } @@ -42048,6 +41983,8 @@ }, "node_modules/simple-concat": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", "funding": [ { "type": "github", @@ -42062,12 +41999,12 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "optional": true }, "node_modules/simple-get": { "version": "3.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", "optional": true, "dependencies": { "decompress-response": "^4.2.0", @@ -42077,7 +42014,8 @@ }, "node_modules/simple-get/node_modules/decompress-response": { "version": "4.2.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", "optional": true, "dependencies": { "mimic-response": "^2.0.0" @@ -42088,7 +42026,8 @@ }, "node_modules/simple-get/node_modules/mimic-response": { "version": "2.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", "optional": true, "engines": { "node": ">=8" @@ -42537,8 +42476,6 @@ }, "node_modules/spawn-command": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", - "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", "dev": true }, "node_modules/spdx-correct": { @@ -44234,9 +44171,8 @@ }, "node_modules/ts-jest": { "version": "29.1.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", - "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==", "dev": true, + "license": "MIT", "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", @@ -44277,9 +44213,8 @@ }, "node_modules/ts-jest/node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } @@ -45340,6 +45275,14 @@ "version": "0.1.1", "license": "MIT" }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/watchpack": { "version": "2.4.0", "license": "MIT", @@ -46625,8 +46568,7 @@ }, "node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -46650,16 +46592,14 @@ }, "node_modules/yargs/node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yargs/node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", "engines": { "node": ">=12" } diff --git a/package.json b/package.json index 9f1fbc894e1f..3b770debe4f0 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ "react-content-loader": "^7.0.0", "react-dom": "18.1.0", "react-error-boundary": "^4.0.11", - "react-fast-pdf": "^1.0.6", + "react-fast-pdf": "https://github.com/ishpaul777/react-fast-pdf/tree/fix-blankArea-after-loading-pdf", "react-map-gl": "^7.1.3", "react-native": "0.73.2", "react-native-android-location-enabler": "^2.0.1", @@ -248,8 +248,8 @@ "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-remove-console": "^6.9.4", "clean-webpack-plugin": "^4.0.0", - "copy-webpack-plugin": "^10.1.0", "concurrently": "^8.2.2", + "copy-webpack-plugin": "^10.1.0", "css-loader": "^6.7.2", "diff-so-fancy": "^1.3.0", "dotenv": "^16.0.3", diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx index c1ded9782a7a..500d14082be7 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx @@ -2,7 +2,7 @@ import React, {memo} from 'react'; import PDFView from '@components/PDFView'; import type AttachmentViewPdfProps from './types'; -function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, style, isUsedAsChatAttachment, containerStyles}: AttachmentViewPdfProps) { +function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, style, isUsedAsChatAttachment, containerStyles, onLoadError}: AttachmentViewPdfProps) { return ( ); } diff --git a/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx b/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx index 23d39ec096b5..6130aeed866c 100644 --- a/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx +++ b/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx @@ -10,9 +10,13 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; type DefaultAttachmentViewProps = { + /** The name of the file */ fileName?: string; + /** Should show the download icon */ shouldShowDownloadIcon?: boolean; + /** Should show the loading spinner icon */ shouldShowLoadingSpinnerIcon?: boolean; + /** Additional styles for the container */ containerStyles?: StyleProp; }; @@ -55,4 +59,6 @@ function DefaultAttachmentView({fileName = '', shouldShowLoadingSpinnerIcon = fa ); } +DefaultAttachmentView.displayName = 'DefaultAttachmentView'; + export default DefaultAttachmentView; diff --git a/src/components/FullscreenLoadingIndicator.tsx b/src/components/FullscreenLoadingIndicator.tsx index 57c2d7dc955e..bd3082db5fa4 100644 --- a/src/components/FullscreenLoadingIndicator.tsx +++ b/src/components/FullscreenLoadingIndicator.tsx @@ -6,14 +6,13 @@ import useThemeStyles from '@hooks/useThemeStyles'; type FullScreenLoadingIndicatorProps = { style?: StyleProp; - isFullScreen?: boolean; }; -function FullScreenLoadingIndicator({style, isFullScreen = true}: FullScreenLoadingIndicatorProps) { +function FullScreenLoadingIndicator({style}: FullScreenLoadingIndicatorProps) { const theme = useTheme(); const styles = useThemeStyles(); return ( - + + + + ); + } + + return ; + } + renderPDFView() { const styles = this.props.themeStyles; const outerContainerStyle = [styles.w100, styles.h100, styles.justifyContentCenter, styles.alignItemsCenter]; - const loadingIndicatorStyles = this.props.isUsedAsChatAttachment - ? [ - this.props.themeStyles.chatItemPDFAttachmentLoading, - this.props.StyleUtils.getWidthAndHeightStyle(LOADING_THUMBNAIL_WIDTH, LOADING_THUMBNAIL_HEIGHT), - styles.justifyContentCenter, - styles.alignItemsCenter, - ] - : []; return ( - } - ErrorComponent={ - - } + LoadingComponent={this.renderLoadingIndicator()} + shouldShowErrorComponent={false} + onLoadError={this.props.onLoadError} renderPasswordForm={({isPasswordInvalid, onSubmit, onPasswordChange}) => ( Date: Thu, 4 Apr 2024 23:39:14 +0300 Subject: [PATCH 044/932] Created attachment offline indicator component --- src/components/AttachmentOfflineIndicator.tsx | 59 +++++++++++++++++++ src/components/VideoPlayer/BaseVideoPlayer.js | 11 ++-- src/components/VideoPlayerPreview/index.tsx | 1 + 3 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 src/components/AttachmentOfflineIndicator.tsx diff --git a/src/components/AttachmentOfflineIndicator.tsx b/src/components/AttachmentOfflineIndicator.tsx new file mode 100644 index 000000000000..476af1dcd31c --- /dev/null +++ b/src/components/AttachmentOfflineIndicator.tsx @@ -0,0 +1,59 @@ +import React, {useMemo} from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; +import useNetwork from '@hooks/useNetwork'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import variables from '@styles/variables'; +import Icon from './Icon'; +import * as Expensicons from './Icon/Expensicons'; +import Text from './Text'; + +type AttachmentOfflineIndicatorProps = { + /** Optional styles for container element that will override the default styling for the offline indicator */ + containerStyles?: StyleProp; + + /** Optional styles for the container */ + style?: StyleProp; +}; + +function AttachmentOfflineIndicator({containerStyles, title, subtitle, isPreview = false}: AttachmentOfflineIndicatorProps) { + const theme = useTheme(); + const styles = useThemeStyles(); + const {isOffline} = useNetwork(); + const {isSmallScreenWidth} = useWindowDimensions(); + + const computedStyles = useMemo((): StyleProp => { + if (containerStyles) { + return containerStyles; + } + + return isSmallScreenWidth ? styles.offlineIndicatorMobile : styles.offlineIndicator; + }, [containerStyles, isSmallScreenWidth, styles.offlineIndicatorMobile, styles.offlineIndicator]); + + if (!isOffline) { + return null; + } + + return ( + + + {!isPreview && ( + + {title} + {subtitle} + + )} + + ); +} + +AttachmentOfflineIndicator.displayName = 'OfflineIndicator'; + +export default AttachmentOfflineIndicator; diff --git a/src/components/VideoPlayer/BaseVideoPlayer.js b/src/components/VideoPlayer/BaseVideoPlayer.js index d1533b2bf487..2cbd22e173d5 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.js +++ b/src/components/VideoPlayer/BaseVideoPlayer.js @@ -3,17 +3,15 @@ import {Video, VideoFullscreenUpdate} from 'expo-av'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; -import BlockingView from '@components/BlockingViews/BlockingView'; +import AttachmentOfflineIndicator from '@components/AttachmentOfflineIndicator'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import Hoverable from '@components/Hoverable'; -import * as Expensicons from '@components/Icon/Expensicons'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import {useFullScreenContext} from '@components/VideoPlayerContexts/FullScreenContext'; import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext'; import VideoPopoverMenu from '@components/VideoPopoverMenu'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; @@ -44,6 +42,7 @@ function BaseVideoPlayer({ // user hovers the mouse over the carousel arrows, but this UI bug feels much less troublesome for now. // eslint-disable-next-line no-unused-vars isVideoHovered, + isPreview, }) { const styles = useThemeStyles(); const { @@ -78,7 +77,6 @@ function BaseVideoPlayer({ const isUploading = _.some(CONST.ATTACHMENT_LOCAL_URL_PREFIX, (prefix) => url.startsWith(prefix)); const videoStateRef = useRef(null); const {translate} = useLocalize(); - const theme = useTheme(); const togglePlayCurrentVideo = useCallback(() => { videoResumeTryNumber.current = 0; @@ -285,11 +283,10 @@ function BaseVideoPlayer({ {((isLoading && !isOffline) || isBuffering) && } {isLoading && isOffline && ( - )} diff --git a/src/components/VideoPlayerPreview/index.tsx b/src/components/VideoPlayerPreview/index.tsx index 37ddacb1f0db..dcbbbc900074 100644 --- a/src/components/VideoPlayerPreview/index.tsx +++ b/src/components/VideoPlayerPreview/index.tsx @@ -83,6 +83,7 @@ function VideoPlayerPreview({videoUrl, thumbnailUrl, fileName, videoDimensions, videoDuration={videoDuration} shouldUseSmallVideoControls style={[styles.w100, styles.h100]} + isPreview /> Date: Fri, 5 Apr 2024 20:51:27 +0300 Subject: [PATCH 045/932] did code cleanup --- src/components/AttachmentOfflineIndicator.tsx | 30 +++++++------------ src/components/VideoPlayer/types.ts | 1 + 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/components/AttachmentOfflineIndicator.tsx b/src/components/AttachmentOfflineIndicator.tsx index 476af1dcd31c..76830abb42e8 100644 --- a/src/components/AttachmentOfflineIndicator.tsx +++ b/src/components/AttachmentOfflineIndicator.tsx @@ -1,36 +1,28 @@ -import React, {useMemo} from 'react'; -import type {StyleProp, ViewStyle} from 'react-native'; +import React from 'react'; import {View} from 'react-native'; import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import useWindowDimensions from '@hooks/useWindowDimensions'; import variables from '@styles/variables'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import Text from './Text'; type AttachmentOfflineIndicatorProps = { - /** Optional styles for container element that will override the default styling for the offline indicator */ - containerStyles?: StyleProp; + /** Whether the offline indicator is displayed for the attachment preview. */ + isPreview?: boolean; - /** Optional styles for the container */ - style?: StyleProp; + /** Title text to be displayed. */ + title: string; + + /** Subtitle text to be displayed. */ + subtitle: string; }; -function AttachmentOfflineIndicator({containerStyles, title, subtitle, isPreview = false}: AttachmentOfflineIndicatorProps) { +function AttachmentOfflineIndicator({title, subtitle, isPreview = false}: AttachmentOfflineIndicatorProps) { const theme = useTheme(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); - const {isSmallScreenWidth} = useWindowDimensions(); - - const computedStyles = useMemo((): StyleProp => { - if (containerStyles) { - return containerStyles; - } - - return isSmallScreenWidth ? styles.offlineIndicatorMobile : styles.offlineIndicator; - }, [containerStyles, isSmallScreenWidth, styles.offlineIndicatorMobile, styles.offlineIndicator]); if (!isOffline) { return null; @@ -39,7 +31,7 @@ function AttachmentOfflineIndicator({containerStyles, title, subtitle, isPreview return ( ; shouldPlay?: boolean; + isPreview?: boolean; }; export type {VideoPlayerProps, VideoWithOnFullScreenUpdate}; From e6153f2fb460b2e7fc6843cea89e158d15c3c07c Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Fri, 5 Apr 2024 20:59:44 +0300 Subject: [PATCH 046/932] fix lint and type --- src/components/VideoPlayer/BaseVideoPlayer.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/VideoPlayer/BaseVideoPlayer.tsx b/src/components/VideoPlayer/BaseVideoPlayer.tsx index 0d6635db1e77..e01f2c7a2c8b 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.tsx +++ b/src/components/VideoPlayer/BaseVideoPlayer.tsx @@ -19,7 +19,7 @@ import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import CONST from '@src/CONST'; import shouldReplayVideo from './shouldReplayVideo'; -import type {VideoWithOnFullScreenUpdate} from './types'; +import type {VideoPlayerProps, VideoWithOnFullScreenUpdate} from './types'; import * as VideoUtils from './utils'; import VideoPlayerControls from './VideoPlayerControls'; @@ -44,9 +44,8 @@ function BaseVideoPlayer({ // but current workaround is just not to use it here for now. This causes not displaying the video controls when // user hovers the mouse over the carousel arrows, but this UI bug feels much less troublesome for now. // eslint-disable-next-line no-unused-vars - isVideoHovered = false, isPreview, -}) { +}: VideoPlayerProps) { const styles = useThemeStyles(); const {pauseVideo, playVideo, currentlyPlayingURL, sharedElement, originalParent, shareVideoPlayerElements, currentVideoPlayerRef, updateCurrentlyPlayingURL, videoResumeTryNumber} = usePlaybackContext(); From cbf52fe51b31d4d29b88e21095e97d4df865a077 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Fri, 5 Apr 2024 21:01:30 +0300 Subject: [PATCH 047/932] revert unnecessary change --- src/components/VideoPlayer/BaseVideoPlayer.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/VideoPlayer/BaseVideoPlayer.tsx b/src/components/VideoPlayer/BaseVideoPlayer.tsx index e01f2c7a2c8b..b771e3fc61be 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.tsx +++ b/src/components/VideoPlayer/BaseVideoPlayer.tsx @@ -43,7 +43,8 @@ function BaseVideoPlayer({ // isVideoHovered caused a bug with unexpected video switching. We are investigating the root cause of the issue, // but current workaround is just not to use it here for now. This causes not displaying the video controls when // user hovers the mouse over the carousel arrows, but this UI bug feels much less troublesome for now. - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars + isVideoHovered = false, isPreview, }: VideoPlayerProps) { const styles = useThemeStyles(); From b3f14d666b65e008f0d207a9ab0f28d5b11d45dd Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Fri, 5 Apr 2024 21:04:52 +0300 Subject: [PATCH 048/932] removed unnecessary view --- src/components/VideoPlayer/BaseVideoPlayer.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/VideoPlayer/BaseVideoPlayer.tsx b/src/components/VideoPlayer/BaseVideoPlayer.tsx index b771e3fc61be..e7eb78b12ae0 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.tsx +++ b/src/components/VideoPlayer/BaseVideoPlayer.tsx @@ -308,13 +308,11 @@ function BaseVideoPlayer({ {((isLoading && !isOffline) || isBuffering) && } {isLoading && isOffline && ( - - - + )} {controlsStatus !== CONST.VIDEO_PLAYER.CONTROLS_STATUS.HIDE && !isLoading && (isPopoverVisible || isHovered || canUseTouchScreen) && ( Date: Fri, 5 Apr 2024 22:06:54 +0300 Subject: [PATCH 049/932] implemented for image view --- src/components/AttachmentOfflineIndicator.tsx | 14 +++++--------- src/components/ImageView/index.tsx | 6 +++++- src/components/VideoPlayer/BaseVideoPlayer.tsx | 8 +------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/components/AttachmentOfflineIndicator.tsx b/src/components/AttachmentOfflineIndicator.tsx index 76830abb42e8..b22478330bff 100644 --- a/src/components/AttachmentOfflineIndicator.tsx +++ b/src/components/AttachmentOfflineIndicator.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {View} from 'react-native'; +import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -11,18 +12,13 @@ import Text from './Text'; type AttachmentOfflineIndicatorProps = { /** Whether the offline indicator is displayed for the attachment preview. */ isPreview?: boolean; - - /** Title text to be displayed. */ - title: string; - - /** Subtitle text to be displayed. */ - subtitle: string; }; -function AttachmentOfflineIndicator({title, subtitle, isPreview = false}: AttachmentOfflineIndicatorProps) { +function AttachmentOfflineIndicator({isPreview = false}: AttachmentOfflineIndicatorProps) { const theme = useTheme(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); + const {translate} = useLocalize(); if (!isOffline) { return null; @@ -38,8 +34,8 @@ function AttachmentOfflineIndicator({title, subtitle, isPreview = false}: Attach /> {!isPreview && ( - {title} - {subtitle} + {translate('common.youAppearToBeOffline')} + {translate('common.attachementWillBeAvailableOnceBackOnline')} )} diff --git a/src/components/ImageView/index.tsx b/src/components/ImageView/index.tsx index 5d09e7abf41d..2316577158f8 100644 --- a/src/components/ImageView/index.tsx +++ b/src/components/ImageView/index.tsx @@ -2,11 +2,13 @@ import type {SyntheticEvent} from 'react'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import type {GestureResponderEvent, LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; +import AttachmentOfflineIndicator from '@components/AttachmentOfflineIndicator'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import Image from '@components/Image'; import RESIZE_MODES from '@components/Image/resizeModes'; import type {ImageOnLoadEvent} from '@components/Image/types'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; +import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; @@ -33,6 +35,7 @@ function ImageView({isAuthTokenRequired = false, url, fileName, onError}: ImageV const [imgHeight, setImgHeight] = useState(0); const [zoomScale, setZoomScale] = useState(0); const [zoomDelta, setZoomDelta] = useState(); + const {isOffline} = useNetwork(); const scrollableRef = useRef(null); const canUseTouchScreen = DeviceCapabilities.canUseTouchScreen(); @@ -243,7 +246,8 @@ function ImageView({isAuthTokenRequired = false, url, fileName, onError}: ImageV /> - {isLoading && } + {isLoading && !isOffline && } + {isLoading && } ); } diff --git a/src/components/VideoPlayer/BaseVideoPlayer.tsx b/src/components/VideoPlayer/BaseVideoPlayer.tsx index e7eb78b12ae0..10d5051c5234 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.tsx +++ b/src/components/VideoPlayer/BaseVideoPlayer.tsx @@ -307,13 +307,7 @@ function BaseVideoPlayer({ )} {((isLoading && !isOffline) || isBuffering) && } - {isLoading && isOffline && ( - - )} + {isLoading && } {controlsStatus !== CONST.VIDEO_PLAYER.CONTROLS_STATUS.HIDE && !isLoading && (isPopoverVisible || isHovered || canUseTouchScreen) && ( Date: Fri, 5 Apr 2024 22:42:34 +0300 Subject: [PATCH 050/932] implemented for thumbnail preview --- src/components/AttachmentOfflineIndicator.tsx | 2 +- src/components/ImageWithSizeCalculation.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/AttachmentOfflineIndicator.tsx b/src/components/AttachmentOfflineIndicator.tsx index b22478330bff..8a831b02b407 100644 --- a/src/components/AttachmentOfflineIndicator.tsx +++ b/src/components/AttachmentOfflineIndicator.tsx @@ -25,7 +25,7 @@ function AttachmentOfflineIndicator({isPreview = false}: AttachmentOfflineIndica } return ( - + - {isLoading && !isImageCached && } + {isLoading && !isImageCached && !isOffline && } + {isLoading && !isImageCached && } ); } From da534453d52c1fb2993b2e31451503bfedd83bd3 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Fri, 5 Apr 2024 23:01:48 +0300 Subject: [PATCH 051/932] minor fix --- src/components/VideoPlayer/BaseVideoPlayer.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/VideoPlayer/BaseVideoPlayer.tsx b/src/components/VideoPlayer/BaseVideoPlayer.tsx index 10d5051c5234..08036890d35c 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.tsx +++ b/src/components/VideoPlayer/BaseVideoPlayer.tsx @@ -70,7 +70,6 @@ function BaseVideoPlayer({ const isCurrentlyURLSet = currentlyPlayingURL === url; const isUploading = CONST.ATTACHMENT_LOCAL_URL_PREFIX.some((prefix) => url.startsWith(prefix)); const videoStateRef = useRef(null); - const {translate} = useLocalize(); const togglePlayCurrentVideo = useCallback(() => { videoResumeTryNumber.current = 0; From e714b2fea5cf1b0475c5f78f031ca2786553522b Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sun, 7 Apr 2024 01:51:43 +0530 Subject: [PATCH 052/932] resolve conflicts --- src/components/PDFView/index.tsx | 48 +++++++++++++++++++++++--------- src/components/PDFView/types.ts | 9 ++++-- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/components/PDFView/index.tsx b/src/components/PDFView/index.tsx index ff28a8f88849..df6543290b96 100644 --- a/src/components/PDFView/index.tsx +++ b/src/components/PDFView/index.tsx @@ -1,30 +1,36 @@ import 'core-js/features/array/at'; // eslint-disable-next-line no-restricted-imports -import type {CSSProperties} from 'react'; -import React, {memo, useCallback, useEffect, useState} from 'react'; -import {PDFPreviewer} from 'react-fast-pdf'; -import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import type { CSSProperties } from 'react'; +import React, { memo, useCallback, useEffect, useState } from 'react'; +import { PDFPreviewer } from 'react-fast-pdf'; +import { ActivityIndicator, View } from 'react-native'; +import { withOnyx } from 'react-native-onyx'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; -import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; +import useTheme from '@hooks/useTheme'; +import useStyleUtils from '@hooks/useStyleUtils'; import useWindowDimensions from '@hooks/useWindowDimensions'; import variables from '@styles/variables'; import * as CanvasSize from '@userActions/CanvasSize'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import PDFPasswordForm from './PDFPasswordForm'; -import type {PDFViewOnyxProps, PDFViewProps} from './types'; +import type { PDFViewOnyxProps, PDFViewProps } from './types'; -function PDFView({onToggleKeyboard, fileName, onPress, isFocused, sourceURL, errorLabelStyles, maxCanvasArea, maxCanvasHeight, maxCanvasWidth, style}: PDFViewProps) { +const LOADING_THUMBNAIL_HEIGHT = 250; +const LOADING_THUMBNAIL_WIDTH = 250; + +function PDFView({ onToggleKeyboard, fileName, onPress, isFocused, sourceURL, maxCanvasArea, maxCanvasHeight, maxCanvasWidth, style, isUsedAsChatAttachment, onLoadError }: PDFViewProps) { const [isKeyboardOpen, setIsKeyboardOpen] = useState(false); const styles = useThemeStyles(); - const {windowHeight, isSmallScreenWidth} = useWindowDimensions(); + const StyleUtils = useStyleUtils(); + const theme = useTheme(); + const { windowHeight, isSmallScreenWidth } = useWindowDimensions(); const prevWindowHeight = usePrevious(windowHeight); - const {translate} = useLocalize(); + const { translate } = useLocalize(); /** * On small screens notify parent that the keyboard has opened or closed. @@ -79,6 +85,21 @@ function PDFView({onToggleKeyboard, fileName, onPress, isFocused, sourceURL, err } }, [isKeyboardOpen, prevWindowHeight, toggleKeyboardOnSmallScreens, windowHeight]); + const renderLoadingIndicator = () => { + if (isUsedAsChatAttachment) { + return ( + + + + ); + } + + return ; + } + const renderPDFView = () => { const outerContainerStyle = [styles.w100, styles.h100, styles.justifyContentCenter, styles.alignItemsCenter]; @@ -95,9 +116,10 @@ function PDFView({onToggleKeyboard, fileName, onPress, isFocused, sourceURL, err maxCanvasWidth={maxCanvasWidth} maxCanvasHeight={maxCanvasHeight} maxCanvasArea={maxCanvasArea} - LoadingComponent={} - ErrorComponent={{translate('attachmentView.failedToLoadPDF')}} - renderPasswordForm={({isPasswordInvalid, onSubmit, onPasswordChange}) => ( + LoadingComponent={renderLoadingIndicator()} + shouldShowErrorComponent={false} + onLoadError={onLoadError} + renderPasswordForm={({ isPasswordInvalid, onSubmit, onPasswordChange }) => ( ; + /** Callback to call when the PDF fails to load */ + onLoadError?: () => void; + + /** Whether the PDF is used as a chat attachment */ + isUsedAsChatAttachment?: boolean; }; type PDFViewOnyxProps = { From 15eea966597d1465650cfe61dd3dec2face0d9f5 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sun, 7 Apr 2024 01:52:50 +0530 Subject: [PATCH 053/932] code formatting --- .../AttachmentViewPdf/index.tsx | 13 ++++++- src/components/PDFView/index.native.tsx | 5 +-- src/components/PDFView/index.tsx | 35 +++++++++++-------- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx index 9984779557eb..ba108092ab8f 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx @@ -2,7 +2,18 @@ import React, {memo} from 'react'; import PDFView from '@components/PDFView'; import type AttachmentViewPdfProps from './types'; -function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, style, isUsedAsChatAttachment, containerStyles, onLoadError}: AttachmentViewPdfProps) { +function AttachmentViewPdf({ + file, + encryptedSourceUrl, + isFocused, + onPress, + onToggleKeyboard, + onLoadComplete, + style, + isUsedAsChatAttachment, + containerStyles, + onLoadError, +}: AttachmentViewPdfProps) { return ( { if (isUsedAsChatAttachment) { return ( - + ; - } + }; const renderPDFView = () => { const outerContainerStyle = [styles.w100, styles.h100, styles.justifyContentCenter, styles.alignItemsCenter]; @@ -119,7 +126,7 @@ function PDFView({ onToggleKeyboard, fileName, onPress, isFocused, sourceURL, ma LoadingComponent={renderLoadingIndicator()} shouldShowErrorComponent={false} onLoadError={onLoadError} - renderPasswordForm={({ isPasswordInvalid, onSubmit, onPasswordChange }) => ( + renderPasswordForm={({isPasswordInvalid, onSubmit, onPasswordChange}) => ( Date: Sun, 7 Apr 2024 02:16:30 +0530 Subject: [PATCH 054/932] remove unnecessary change --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79f0a58cc10d..5e00815f1e3e 100644 --- a/package.json +++ b/package.json @@ -248,8 +248,8 @@ "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-remove-console": "^6.9.4", "clean-webpack-plugin": "^4.0.0", - "concurrently": "^8.2.2", "copy-webpack-plugin": "^10.1.0", + "concurrently": "^8.2.2", "css-loader": "^6.7.2", "diff-so-fancy": "^1.3.0", "dotenv": "^16.0.3", From 69129794b08fe9d66175d41bb0c35d9083965562 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sun, 7 Apr 2024 02:21:23 +0530 Subject: [PATCH 055/932] fix type checks --- .../Attachments/AttachmentView/AttachmentViewPdf/index.tsx | 2 -- .../Attachments/AttachmentView/AttachmentViewPdf/types.ts | 3 --- src/components/Attachments/AttachmentView/index.tsx | 1 - 3 files changed, 6 deletions(-) diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx index ba108092ab8f..3cae1ee45f1b 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx @@ -11,7 +11,6 @@ function AttachmentViewPdf({ onLoadComplete, style, isUsedAsChatAttachment, - containerStyles, onLoadError, }: AttachmentViewPdfProps) { return ( @@ -24,7 +23,6 @@ function AttachmentViewPdf({ onToggleKeyboard={onToggleKeyboard} onLoadComplete={onLoadComplete} isUsedAsChatAttachment={isUsedAsChatAttachment} - containerStyles={containerStyles} onLoadError={onLoadError} /> ); diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts b/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts index 1c3cd8adfba4..c20593449d58 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts @@ -16,9 +16,6 @@ type AttachmentViewPdfProps = Pick; }; export default AttachmentViewPdfProps; diff --git a/src/components/Attachments/AttachmentView/index.tsx b/src/components/Attachments/AttachmentView/index.tsx index f32265bdc01f..bc8f7476b4d5 100644 --- a/src/components/Attachments/AttachmentView/index.tsx +++ b/src/components/Attachments/AttachmentView/index.tsx @@ -177,7 +177,6 @@ function AttachmentView({ onLoadComplete={onPDFLoadComplete} style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1} isUsedAsChatAttachment={isUsedAsChatAttachment} - containerStyles={containerStyles} onLoadError={() => { setIsPdfFailedToLoad(true); }} From 917095c5f6f29feb6c5b9d1bc29d56acd2cc9c06 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sun, 7 Apr 2024 02:23:07 +0530 Subject: [PATCH 056/932] lint code --- src/components/Attachments/AttachmentView/index.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/Attachments/AttachmentView/index.tsx b/src/components/Attachments/AttachmentView/index.tsx index bc8f7476b4d5..1743a1904ad3 100644 --- a/src/components/Attachments/AttachmentView/index.tsx +++ b/src/components/Attachments/AttachmentView/index.tsx @@ -163,6 +163,10 @@ function AttachmentView({ } }; + const onPDFLoadError = () => { + setIsPdfFailedToLoad(true); + } + // We need the following View component on android native // So that the event will propagate properly and // the Password protected preview will be shown for pdf attachement we are about to send. @@ -177,9 +181,7 @@ function AttachmentView({ onLoadComplete={onPDFLoadComplete} style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1} isUsedAsChatAttachment={isUsedAsChatAttachment} - onLoadError={() => { - setIsPdfFailedToLoad(true); - }} + onLoadError={onPDFLoadError} /> ); From c9681471de4445131308598d7c644f8ffd9c7b1d Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sun, 7 Apr 2024 02:24:22 +0530 Subject: [PATCH 057/932] prettier diffs --- .../AttachmentView/AttachmentViewPdf/index.tsx | 12 +----------- src/components/Attachments/AttachmentView/index.tsx | 2 +- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx index 3cae1ee45f1b..e8f6568f98c0 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx @@ -2,17 +2,7 @@ import React, {memo} from 'react'; import PDFView from '@components/PDFView'; import type AttachmentViewPdfProps from './types'; -function AttachmentViewPdf({ - file, - encryptedSourceUrl, - isFocused, - onPress, - onToggleKeyboard, - onLoadComplete, - style, - isUsedAsChatAttachment, - onLoadError, -}: AttachmentViewPdfProps) { +function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, style, isUsedAsChatAttachment, onLoadError}: AttachmentViewPdfProps) { return ( { setIsPdfFailedToLoad(true); - } + }; // We need the following View component on android native // So that the event will propagate properly and From cadf1f1dee2058299d5ed4e792b75ebb7fbaac6d Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sun, 7 Apr 2024 02:32:19 +0530 Subject: [PATCH 058/932] resolve lint failure --- src/components/PDFView/index.native.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/PDFView/index.native.tsx b/src/components/PDFView/index.native.tsx index 0fb063f57e8b..db76c005b37a 100644 --- a/src/components/PDFView/index.native.tsx +++ b/src/components/PDFView/index.native.tsx @@ -127,9 +127,9 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused if (shouldRequestPassword) { pdfStyles.push(themeStyles.invisible); } - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const containerStyles = - isUsedAsChatAttachment || (shouldRequestPassword && isSmallScreenWidth) ? [themeStyles.w100, themeStyles.flex1] : [themeStyles.alignItemsCenter, themeStyles.flex1]; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + (isUsedAsChatAttachment || (shouldRequestPassword && isSmallScreenWidth)) ? [themeStyles.w100, themeStyles.flex1] : [themeStyles.alignItemsCenter, themeStyles.flex1]; const loadingIndicatorStyles = isUsedAsChatAttachment ? [themeStyles.chatItemPDFAttachmentLoading, StyleUtils.getWidthAndHeightStyle(LOADING_THUMBNAIL_WIDTH, LOADING_THUMBNAIL_HEIGHT)] : []; From 54eb29517e53a484d21a7a9a5a37d2bfdf91f05c Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sun, 7 Apr 2024 02:39:32 +0530 Subject: [PATCH 059/932] prettier diffs --- src/components/PDFView/index.native.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PDFView/index.native.tsx b/src/components/PDFView/index.native.tsx index db76c005b37a..7d523ef13805 100644 --- a/src/components/PDFView/index.native.tsx +++ b/src/components/PDFView/index.native.tsx @@ -129,7 +129,7 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused } const containerStyles = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - (isUsedAsChatAttachment || (shouldRequestPassword && isSmallScreenWidth)) ? [themeStyles.w100, themeStyles.flex1] : [themeStyles.alignItemsCenter, themeStyles.flex1]; + isUsedAsChatAttachment || (shouldRequestPassword && isSmallScreenWidth) ? [themeStyles.w100, themeStyles.flex1] : [themeStyles.alignItemsCenter, themeStyles.flex1]; const loadingIndicatorStyles = isUsedAsChatAttachment ? [themeStyles.chatItemPDFAttachmentLoading, StyleUtils.getWidthAndHeightStyle(LOADING_THUMBNAIL_WIDTH, LOADING_THUMBNAIL_HEIGHT)] : []; From 2a69717b6ce7afd5350777351018c84c588bbe64 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Mon, 8 Apr 2024 20:27:35 +0300 Subject: [PATCH 060/932] implemented offline indicator for native --- src/components/Lightbox/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx index 86a52c2baf6c..e5261771c5a4 100644 --- a/src/components/Lightbox/index.tsx +++ b/src/components/Lightbox/index.tsx @@ -2,12 +2,14 @@ import React, {useCallback, useContext, useEffect, useMemo, useState} from 'reac import type {LayoutChangeEvent, StyleProp, ViewStyle} from 'react-native'; import {ActivityIndicator, PixelRatio, StyleSheet, View} from 'react-native'; import {useSharedValue} from 'react-native-reanimated'; +import AttachmentOfflineIndicator from '@components/AttachmentOfflineIndicator'; import AttachmentCarouselPagerContext from '@components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext'; import Image from '@components/Image'; import type {ImageOnLoadEvent} from '@components/Image/types'; import MultiGestureCanvas, {DEFAULT_ZOOM_RANGE} from '@components/MultiGestureCanvas'; import type {CanvasSize, ContentSize, OnScaleChangedCallback, ZoomRange} from '@components/MultiGestureCanvas/types'; import {getCanvasFitScale} from '@components/MultiGestureCanvas/utils'; +import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import NUMBER_OF_CONCURRENT_LIGHTBOXES from './numberOfConcurrentLightboxes'; @@ -40,6 +42,7 @@ type LightboxProps = { function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChangedProp, onError, style, zoomRange = DEFAULT_ZOOM_RANGE}: LightboxProps) { const StyleUtils = useStyleUtils(); const styles = useThemeStyles(); + const {isOffline} = useNetwork(); /** * React hooks must be used in the render function of the component at top-level and unconditionally. @@ -243,12 +246,13 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan )} {/* Show activity indicator while the lightbox is still loading the image. */} - {isLoading && ( + {isLoading && !isOffline && ( )} + {isLoading && } )} From bf1786e5a387a9663f3f11cfee6290bb7bc58ac6 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Mon, 8 Apr 2024 20:32:31 +0300 Subject: [PATCH 061/932] minor fix --- src/components/VideoPlayer/BaseVideoPlayer.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/VideoPlayer/BaseVideoPlayer.tsx b/src/components/VideoPlayer/BaseVideoPlayer.tsx index 08036890d35c..2ddb19716c8e 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.tsx +++ b/src/components/VideoPlayer/BaseVideoPlayer.tsx @@ -12,7 +12,6 @@ import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeed import {useFullScreenContext} from '@components/VideoPlayerContexts/FullScreenContext'; import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext'; import VideoPopoverMenu from '@components/VideoPopoverMenu'; -import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; From 395468dc259e680429e64971479f72a40f2431a3 Mon Sep 17 00:00:00 2001 From: BrtqKr Date: Tue, 9 Apr 2024 11:39:38 +0200 Subject: [PATCH 062/932] receipt scan ui change wip --- assets/images/receipt-scan.svg | 14 ++++++++++++++ src/components/Icon/Expensicons.ts | 2 ++ .../MoneyRequestPreviewContent.tsx | 5 +++++ src/languages/en.ts | 3 ++- src/languages/es.ts | 1 + src/pages/home/report/ReportActionsList.tsx | 2 ++ 6 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 assets/images/receipt-scan.svg diff --git a/assets/images/receipt-scan.svg b/assets/images/receipt-scan.svg new file mode 100644 index 000000000000..c93986de3c9b --- /dev/null +++ b/assets/images/receipt-scan.svg @@ -0,0 +1,14 @@ + + + + + + + diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index 1fcf0d07276c..ba00ad684473 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -120,6 +120,7 @@ import Printer from '@assets/images/printer.svg'; import Profile from '@assets/images/profile.svg'; import QrCode from '@assets/images/qrcode.svg'; import QuestionMark from '@assets/images/question-mark-circle.svg'; +import ReceiptScan from '@assets/images/receipt-scan.svg'; import ReceiptSearch from '@assets/images/receipt-search.svg'; import Receipt from '@assets/images/receipt.svg'; import RemoveMembers from '@assets/images/remove-members.svg'; @@ -283,6 +284,7 @@ export { QrCode, QuestionMark, Receipt, + ReceiptScan, RemoveMembers, ReceiptSearch, Rotate, diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index 2c6f14cec4c2..8d1b7880726b 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -312,6 +312,11 @@ function MoneyRequestPreviewContent({ )} + + {true && ( + {translate('iou.receiptScanInProgress')} + )} + diff --git a/src/languages/en.ts b/src/languages/en.ts index 55a4c586716a..ad5377d8349b 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -632,7 +632,8 @@ export default { posted: 'Posted', deleteReceipt: 'Delete receipt', routePending: 'Route pending...', - receiptScanning: 'Scan in progress…', + receiptScanning: 'Receipt scanning…', + receiptScanInProgress: 'Receipt scan in progress.', receiptMissingDetails: 'Receipt missing details', missingAmount: 'Missing amount', missingMerchant: 'Missing merchant', diff --git a/src/languages/es.ts b/src/languages/es.ts index 5956f1457005..61c6c82bbc1c 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -629,6 +629,7 @@ export default { deleteReceipt: 'Eliminar recibo', routePending: 'Ruta pendiente...', receiptScanning: 'Escaneo en curso…', + receiptScanInProgress: 'Escaneo en curso…', receiptMissingDetails: 'Recibo con campos vacíos', missingAmount: 'Falta importe', missingMerchant: 'Falta comerciante', diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index d1b9c420b0af..e261f1f3c38e 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -6,6 +6,7 @@ import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 're import {DeviceEventEmitter, InteractionManager} from 'react-native'; import type {LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; import InvertedFlatList from '@components/InvertedFlatList'; import {AUTOSCROLL_TO_TOP_THRESHOLD} from '@components/InvertedFlatList/BaseInvertedFlatList'; @@ -184,6 +185,7 @@ function ReportActionsList({ const hasFooterRendered = useRef(false); const lastVisibleActionCreatedRef = useRef(report.lastVisibleActionCreated); const lastReadTimeRef = useRef(report.lastReadTime); + Onyx.merge('transactions_8811441407757684730', {cardID: 1, merchant: 'Google', hasEReceipt: true, status: 'Pending'}); const sortedVisibleReportActions = useMemo( () => From 2e1f4ce29cca3c94151a02cc2f58a244517c25d2 Mon Sep 17 00:00:00 2001 From: BrtqKr Date: Tue, 9 Apr 2024 22:20:26 +0200 Subject: [PATCH 063/932] scanning receipt wip --- src/components/MoneyRequestHeader.tsx | 17 +++++++++++++++++ src/components/MoneyRequestHeaderStatusBar.tsx | 3 ++- .../MoneyRequestPreviewContent.tsx | 16 ++++++++++++---- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index f451f5f15581..ac6cfd911a6a 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -19,9 +19,13 @@ import type {OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import ConfirmModal from './ConfirmModal'; import HeaderWithBackButton from './HeaderWithBackButton'; import HoldBanner from './HoldBanner'; +import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; +import {ReceiptScan} from './Icon/Expensicons'; import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar'; import ProcessMoneyRequestHoldMenu from './ProcessMoneyRequestHoldMenu'; +import variables from '@styles/variables'; +import theme from '@styles/theme'; type MoneyRequestHeaderOnyxProps = { /** Session info for the currently logged in user. */ @@ -216,6 +220,19 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, ); } +function ScanningReceiptHeaderTitle() { + return ( + + + + ); +} + MoneyRequestHeader.displayName = 'MoneyRequestHeader'; const MoneyRequestHeaderWithTransaction = withOnyx>({ diff --git a/src/components/MoneyRequestHeaderStatusBar.tsx b/src/components/MoneyRequestHeaderStatusBar.tsx index 59ef4ee0bd26..828650fa8fba 100644 --- a/src/components/MoneyRequestHeaderStatusBar.tsx +++ b/src/components/MoneyRequestHeaderStatusBar.tsx @@ -1,3 +1,4 @@ +import type {ReactElement} from 'react'; import React from 'react'; import {View} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -5,7 +6,7 @@ import Text from './Text'; type MoneyRequestHeaderStatusBarProps = { /** Title displayed in badge */ - title: string; + title: string | ReactElement; /** Banner Description */ description: string; diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index 8d1b7880726b..745c13ce9d0d 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -7,6 +7,7 @@ import type {GestureResponderEvent} from 'react-native'; import ConfirmedRoute from '@components/ConfirmedRoute'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; +import {ReceiptScan} from '@components/Icon/Expensicons'; import MoneyRequestSkeletonView from '@components/MoneyRequestSkeletonView'; import MultipleAvatars from '@components/MultipleAvatars'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -30,6 +31,7 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import ViolationsUtils from '@libs/Violations/ViolationsUtils'; +import variables from '@styles/variables'; import * as PaymentMethods from '@userActions/PaymentMethods'; import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; @@ -312,11 +314,17 @@ function MoneyRequestPreviewContent({ )} - - {true && ( + {isScanning && ( + + {translate('iou.receiptScanInProgress')} - )} - + + )} From 9ee70da12544f7017ed8d70e1c04b833600edfa4 Mon Sep 17 00:00:00 2001 From: BrtqKr Date: Wed, 10 Apr 2024 08:15:15 +0200 Subject: [PATCH 064/932] finalize scanning receipt --- src/components/MoneyRequestHeader.tsx | 29 ++++++++----------- .../MoneyRequestHeaderStatusBar.tsx | 14 +++++---- src/languages/en.ts | 1 + src/languages/es.ts | 1 + 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index ac6cfd911a6a..2f80cf3c6e59 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -3,6 +3,7 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as HeaderUtils from '@libs/HeaderUtils'; @@ -10,6 +11,7 @@ import Navigation from '@libs/Navigation/Navigation'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; +import variables from '@styles/variables'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -24,8 +26,6 @@ import * as Expensicons from './Icon/Expensicons'; import {ReceiptScan} from './Icon/Expensicons'; import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar'; import ProcessMoneyRequestHoldMenu from './ProcessMoneyRequestHoldMenu'; -import variables from '@styles/variables'; -import theme from '@styles/theme'; type MoneyRequestHeaderOnyxProps = { /** Session info for the currently logged in user. */ @@ -58,6 +58,7 @@ type MoneyRequestHeaderProps = MoneyRequestHeaderOnyxProps & { function MoneyRequestHeader({session, parentReport, report, parentReportAction, transaction, shownHoldUseExplanation = false, policy}: MoneyRequestHeaderProps) { const styles = useThemeStyles(); + const theme = useTheme(); const {translate} = useLocalize(); const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); const [shouldShowHoldMenu, setShouldShowHoldMenu] = useState(false); @@ -192,8 +193,15 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, )} {isScanning && ( + } + description={translate('iou.receiptScanInProgressDescription')} shouldShowBorderBottom /> )} @@ -220,19 +228,6 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, ); } -function ScanningReceiptHeaderTitle() { - return ( - - - - ); -} - MoneyRequestHeader.displayName = 'MoneyRequestHeader'; const MoneyRequestHeaderWithTransaction = withOnyx>({ diff --git a/src/components/MoneyRequestHeaderStatusBar.tsx b/src/components/MoneyRequestHeaderStatusBar.tsx index 828650fa8fba..d21e66ba39eb 100644 --- a/src/components/MoneyRequestHeaderStatusBar.tsx +++ b/src/components/MoneyRequestHeaderStatusBar.tsx @@ -1,4 +1,4 @@ -import type {ReactElement} from 'react'; +import type {ReactNode} from 'react'; import React from 'react'; import {View} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -6,7 +6,7 @@ import Text from './Text'; type MoneyRequestHeaderStatusBarProps = { /** Title displayed in badge */ - title: string | ReactElement; + title: string | ReactNode; /** Banner Description */ description: string; @@ -20,9 +20,13 @@ function MoneyRequestHeaderStatusBar({title, description, shouldShowBorderBottom const borderBottomStyle = shouldShowBorderBottom ? styles.borderBottom : {}; return ( - - {title} - + {typeof title === 'string' ? ( + + {title} + + ) : ( + {title} + )} {description} diff --git a/src/languages/en.ts b/src/languages/en.ts index ad5377d8349b..c7ea86ff8064 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -634,6 +634,7 @@ export default { routePending: 'Route pending...', receiptScanning: 'Receipt scanning…', receiptScanInProgress: 'Receipt scan in progress.', + receiptScanInProgressDescription: 'Receipt scan in progress. Check back later or enter the details now.', receiptMissingDetails: 'Receipt missing details', missingAmount: 'Missing amount', missingMerchant: 'Missing merchant', diff --git a/src/languages/es.ts b/src/languages/es.ts index 61c6c82bbc1c..c9aabe62087a 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -630,6 +630,7 @@ export default { routePending: 'Ruta pendiente...', receiptScanning: 'Escaneo en curso…', receiptScanInProgress: 'Escaneo en curso…', + receiptScanInProgressDescription: 'Escaneo en curso.', receiptMissingDetails: 'Recibo con campos vacíos', missingAmount: 'Falta importe', missingMerchant: 'Falta comerciante', From 2a7fac59e5fde23d38751b0732de483d13c4da43 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 10 Apr 2024 14:32:00 +0300 Subject: [PATCH 065/932] revert light box change --- src/components/Lightbox/index.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx index e5261771c5a4..86a52c2baf6c 100644 --- a/src/components/Lightbox/index.tsx +++ b/src/components/Lightbox/index.tsx @@ -2,14 +2,12 @@ import React, {useCallback, useContext, useEffect, useMemo, useState} from 'reac import type {LayoutChangeEvent, StyleProp, ViewStyle} from 'react-native'; import {ActivityIndicator, PixelRatio, StyleSheet, View} from 'react-native'; import {useSharedValue} from 'react-native-reanimated'; -import AttachmentOfflineIndicator from '@components/AttachmentOfflineIndicator'; import AttachmentCarouselPagerContext from '@components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext'; import Image from '@components/Image'; import type {ImageOnLoadEvent} from '@components/Image/types'; import MultiGestureCanvas, {DEFAULT_ZOOM_RANGE} from '@components/MultiGestureCanvas'; import type {CanvasSize, ContentSize, OnScaleChangedCallback, ZoomRange} from '@components/MultiGestureCanvas/types'; import {getCanvasFitScale} from '@components/MultiGestureCanvas/utils'; -import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import NUMBER_OF_CONCURRENT_LIGHTBOXES from './numberOfConcurrentLightboxes'; @@ -42,7 +40,6 @@ type LightboxProps = { function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChangedProp, onError, style, zoomRange = DEFAULT_ZOOM_RANGE}: LightboxProps) { const StyleUtils = useStyleUtils(); const styles = useThemeStyles(); - const {isOffline} = useNetwork(); /** * React hooks must be used in the render function of the component at top-level and unconditionally. @@ -246,13 +243,12 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan )} {/* Show activity indicator while the lightbox is still loading the image. */} - {isLoading && !isOffline && ( + {isLoading && ( )} - {isLoading && } )} From bbfda4d6a9292afc983458922a22a40a543fbd70 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 10 Apr 2024 14:53:25 +0300 Subject: [PATCH 066/932] fix buffering case --- src/components/VideoPlayer/BaseVideoPlayer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/VideoPlayer/BaseVideoPlayer.tsx b/src/components/VideoPlayer/BaseVideoPlayer.tsx index 2ddb19716c8e..26a44364f807 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.tsx +++ b/src/components/VideoPlayer/BaseVideoPlayer.tsx @@ -305,7 +305,7 @@ function BaseVideoPlayer({ )} {((isLoading && !isOffline) || isBuffering) && } - {isLoading && } + {isLoading && !isBuffering && } {controlsStatus !== CONST.VIDEO_PLAYER.CONTROLS_STATUS.HIDE && !isLoading && (isPopoverVisible || isHovered || canUseTouchScreen) && ( Date: Wed, 10 Apr 2024 17:06:49 +0200 Subject: [PATCH 067/932] Add useActiveWorkspaceFromNavigationState --- .../useActiveWorkspaceFromNavigationState.ts | 13 ++++ src/libs/Navigation/types.ts | 4 +- src/libs/PolicyUtils.ts | 68 ++++++++----------- src/pages/home/sidebar/SidebarLinksData.tsx | 4 +- .../SidebarScreen/BaseSidebarScreen.tsx | 4 +- 5 files changed, 48 insertions(+), 45 deletions(-) create mode 100644 src/hooks/useActiveWorkspaceFromNavigationState.ts diff --git a/src/hooks/useActiveWorkspaceFromNavigationState.ts b/src/hooks/useActiveWorkspaceFromNavigationState.ts new file mode 100644 index 000000000000..8f9da3d9df37 --- /dev/null +++ b/src/hooks/useActiveWorkspaceFromNavigationState.ts @@ -0,0 +1,13 @@ +import {useNavigationState} from '@react-navigation/native'; +import type {BottomTabNavigatorParamList} from '@libs/Navigation/types'; + +/** + * Get the currently selected policy ID stored in the navigation state. This hook should only be called only from screens in BottomTab. + */ +function useActiveWorkspaceFromNavigationState() { + const activeWorkpsaceID = useNavigationState((state) => state.routes.at(-1)?.params?.policyID); + + return activeWorkpsaceID; +} + +export default useActiveWorkspaceFromNavigationState; diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 1f1e7ec9a459..04bb7797804e 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -662,8 +662,8 @@ type WelcomeVideoModalNavigatorParamList = { }; type BottomTabNavigatorParamList = { - [SCREENS.HOME]: undefined; - [SCREENS.SETTINGS.ROOT]: undefined; + [SCREENS.HOME]: {policyID?: string}; + [SCREENS.SETTINGS.ROOT]: {policyID?: string}; }; type SharedScreensParamList = { diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 665830ca7167..6c5e4ea695c2 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -8,9 +8,7 @@ import type {PersonalDetailsList, Policy, PolicyCategories, PolicyMembers, Polic import type {PolicyFeatureName, Rate} from '@src/types/onyx/Policy'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import getPolicyIDFromState from './Navigation/getPolicyIDFromState'; -import Navigation, {navigationRef} from './Navigation/Navigation'; -import type {RootStackParamList, State} from './Navigation/types'; +import Navigation from './Navigation/Navigation'; type MemberEmailsToAccountIDs = Record; @@ -310,51 +308,43 @@ function isPolicyFeatureEnabled(policy: OnyxEntry | EmptyObject, feature return Boolean(policy?.[featureName]); } -/** - * Get the currently selected policy ID stored in the navigation state. - */ -function getPolicyIDFromNavigationState() { - return getPolicyIDFromState(navigationRef.getRootState() as State); -} - export { + canEditTaxRate, + extractPolicyIDFromPath, getActivePolicies, + getCleanedTagName, + getCountOfEnabledTagsOfList, + getIneligibleInvitees, + getMemberAccountIDsForWorkspace, + getNumericValue, + getPathWithoutPolicyID, + getPolicyBrickRoadIndicatorStatus, + getPolicyMembersByIdWithoutCurrentUser, + getSortedTagKeys, + getTagList, + getTagListName, + getTagLists, + getTaxByID, + getUnitRateValue, + goBackFromInvalidPolicy, hasAccountingConnections, - hasPolicyMemberError, + hasCustomUnitsError, + hasPolicyCategoriesError, hasPolicyError, hasPolicyErrorFields, - hasCustomUnitsError, - getNumericValue, - getUnitRateValue, - getPolicyBrickRoadIndicatorStatus, - shouldShowPolicy, + hasPolicyMemberError, + hasTaxRateError, isExpensifyTeam, - isInstantSubmitEnabled, isFreeGroupPolicy, - isPolicyAdmin, - isTaxTrackingEnabled, - isSubmitAndClose, - getMemberAccountIDsForWorkspace, - getIneligibleInvitees, - getTagLists, - getTagListName, - getSortedTagKeys, - canEditTaxRate, - getTagList, - getCleanedTagName, - getCountOfEnabledTagsOfList, - isPendingDeletePolicy, - isPolicyMember, + isInstantSubmitEnabled, isPaidGroupPolicy, - extractPolicyIDFromPath, - getPathWithoutPolicyID, - getPolicyMembersByIdWithoutCurrentUser, - goBackFromInvalidPolicy, + isPendingDeletePolicy, + isPolicyAdmin, isPolicyFeatureEnabled, - hasTaxRateError, - getTaxByID, - hasPolicyCategoriesError, - getPolicyIDFromNavigationState, + isPolicyMember, + isSubmitAndClose, + isTaxTrackingEnabled, + shouldShowPolicy, }; export type {MemberEmailsToAccountIDs}; diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index e56962a331a2..4a90d6e96239 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -9,7 +9,7 @@ import type {EdgeInsets} from 'react-native-safe-area-context'; import type {ValueOf} from 'type-fest'; import type {CurrentReportIDContextValue} from '@components/withCurrentReportID'; import withCurrentReportID from '@components/withCurrentReportID'; -import useActiveWorkspace from '@hooks/useActiveWorkspace'; +import useActiveWorkspaceFromNavigationState from '@hooks/useActiveWorkspaceFromNavigationState'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -85,7 +85,7 @@ function SidebarLinksData({ const network = useNetwork(); const isFocused = useIsFocused(); const styles = useThemeStyles(); - const {activeWorkspaceID} = useActiveWorkspace(); + const activeWorkspaceID = useActiveWorkspaceFromNavigationState(); const {translate} = useLocalize(); const prevPriorityMode = usePrevious(priorityMode); const policyMemberAccountIDs = getPolicyMembersByIdWithoutCurrentUser(policyMembers, activeWorkspaceID, accountID); diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx index fe61af021d7f..a8df74cfc4e9 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx @@ -1,11 +1,11 @@ import React, {useEffect} from 'react'; import {View} from 'react-native'; import ScreenWrapper from '@components/ScreenWrapper'; +import useActiveWorkspaceFromNavigationState from '@hooks/useActiveWorkspaceFromNavigationState'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Browser from '@libs/Browser'; import TopBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar'; import Performance from '@libs/Performance'; -import {getPolicyIDFromNavigationState} from '@libs/PolicyUtils'; import SidebarLinksData from '@pages/home/sidebar/SidebarLinksData'; import Timing from '@userActions/Timing'; import CONST from '@src/CONST'; @@ -20,7 +20,7 @@ const startTimer = () => { function BaseSidebarScreen() { const styles = useThemeStyles(); - const activeWorkspaceID = getPolicyIDFromNavigationState(); + const activeWorkspaceID = useActiveWorkspaceFromNavigationState(); useEffect(() => { Performance.markStart(CONST.TIMING.SIDEBAR_LOADED); Timing.start(CONST.TIMING.SIDEBAR_LOADED, true); From 8f22291f1a8ea5b6b2953cf13e7a17202502f6ce Mon Sep 17 00:00:00 2001 From: BrtqKr Date: Wed, 10 Apr 2024 17:18:46 +0200 Subject: [PATCH 068/932] finalize pending transaction, cleanup wip --- assets/images/credit-card-hourglass.svg | 19 +++++++++++++++ src/components/Icon/Expensicons.ts | 2 ++ src/components/MoneyRequestHeader.tsx | 14 +++++++---- .../MoneyRequestPreviewContent.tsx | 10 -------- .../ReportActionItem/MoneyRequestView.tsx | 5 ---- .../ReportActionItem/ReportPreview.tsx | 14 +++++++++++ src/languages/en.ts | 3 ++- src/libs/CardUtils.ts | 2 +- src/pages/home/ReportScreen.tsx | 23 ++++++++++--------- src/pages/home/report/ReportActionItem.tsx | 1 + src/pages/home/report/ReportActionsList.tsx | 2 +- 11 files changed, 62 insertions(+), 33 deletions(-) create mode 100644 assets/images/credit-card-hourglass.svg diff --git a/assets/images/credit-card-hourglass.svg b/assets/images/credit-card-hourglass.svg new file mode 100644 index 000000000000..2acd013fbe59 --- /dev/null +++ b/assets/images/credit-card-hourglass.svg @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index ba00ad684473..78583f3af4d4 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -41,6 +41,7 @@ import Collapse from '@assets/images/collapse.svg'; import Concierge from '@assets/images/concierge.svg'; import Connect from '@assets/images/connect.svg'; import Copy from '@assets/images/copy.svg'; +import CreditCardHourglass from '@assets/images/credit-card-hourglass.svg'; import CreditCard from '@assets/images/creditcard.svg'; import DocumentPlus from '@assets/images/document-plus.svg'; import DocumentSlash from '@assets/images/document-slash.svg'; @@ -198,6 +199,7 @@ export { Connect, Copy, CreditCard, + CreditCardHourglass, DeletedRoomAvatar, Document, DocumentSlash, diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 2f80cf3c6e59..5ea46f339d46 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -23,7 +23,6 @@ import HeaderWithBackButton from './HeaderWithBackButton'; import HoldBanner from './HoldBanner'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; -import {ReceiptScan} from './Icon/Expensicons'; import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar'; import ProcessMoneyRequestHoldMenu from './ProcessMoneyRequestHoldMenu'; @@ -186,8 +185,15 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, /> {isPending && ( + } + description={translate('iou.transactionPendingDescription')} shouldShowBorderBottom={!isScanning} /> )} @@ -195,7 +201,7 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, 0; const isScanning = hasReceipts && areAllRequestsBeingSmartScanned; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const hasErrors = hasMissingSmartscanFields || (canUseViolations && ReportUtils.hasViolations(iouReportID, transactionViolations)) || ReportUtils.hasActionsWithErrors(iouReportID); const lastThreeTransactionsWithReceipts = transactionsWithReceipts.slice(-3); @@ -226,6 +228,7 @@ function ReportPreview({ const shouldShowSingleRequestMerchantOrDescription = numberOfRequests === 1 && (!!formattedMerchant || !!formattedDescription) && !(hasOnlyTransactionsWithPendingRoutes && !totalDisplaySpend); const shouldShowSubtitle = !isScanning && (shouldShowSingleRequestMerchantOrDescription || numberOfRequests > 1); + const shouldShowPendingSubtitle = numberOfPendingRequests === 1 && transactionsWithReceipts.length === 1; const {isSupportTextHtml, supportText} = useMemo(() => { if (formattedMerchant) { @@ -318,6 +321,17 @@ function ReportPreview({ )} + {shouldShowPendingSubtitle && ( + + + {translate('iou.transactionPending')} + + )} {shouldShowSettlementButton && ( diff --git a/src/languages/en.ts b/src/languages/en.ts index c7ea86ff8064..27637aeb9602 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -641,7 +641,7 @@ export default { receiptStatusTitle: 'Scanning…', receiptStatusText: "Only you can see this receipt when it's scanning. Check back later or enter the details now.", receiptScanningFailed: 'Receipt scanning failed. Enter the details manually.', - transactionPendingText: 'It takes a few days from the date the card was used for the transaction to post.', + transactionPendingDescription: 'Transaction pending. It can take a few days from the date the card was used for the transaction to post.', requestCount: ({count, scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => `${count} ${Str.pluralize('request', 'requests', count)}${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${ pendingReceipts > 0 ? `, ${pendingReceipts} pending` : '' @@ -737,6 +737,7 @@ export default { set: 'set', changed: 'changed', removed: 'removed', + transactionPending: 'Transaction pending.', }, notificationPreferencesPage: { header: 'Notification preferences', diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index c4d67adcd54a..0d8de86f63bc 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -46,7 +46,7 @@ function isExpensifyCard(cardID?: number) { * @returns boolean if the cardID is in the cardList from ONYX. Includes Expensify Cards. */ function isCorporateCard(cardID: number) { - return !!allCards[cardID]; + return true; } /** diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 940cba181db7..e8c00f807e06 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -323,7 +323,7 @@ function ReportScreen({ /> ); - if (isSingleTransactionView) { + if (true) { headerView = ( ReportActionsUtils.getOneTransactionThreadReportID(reportActions ?? []), [reportActions]); - if (ReportUtils.isMoneyRequestReport(report)) { - headerView = ( - - ); - } + // if (ReportUtils.isMoneyRequestReport(report)) { + // headerView = ( + // + // ); + // } /** * When false the ReportActionsView will completely unmount and we will show a loader until it returns true. @@ -603,6 +603,7 @@ function ReportScreen({ ); } + console.log('REPORT SCREEN'); return ( diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index eeeb5b95273c..02d139780ed0 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -832,6 +832,7 @@ function ReportActionItem({ ? (Object.values(personalDetails ?? {}).filter((details) => whisperedToAccountIDs.includes(details?.accountID ?? -1)) as OnyxTypes.PersonalDetails[]) : []; const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedToPersonalDetails, isMultipleParticipant) : []; + return ( @@ -196,6 +195,7 @@ function ReportActionsList({ ), [sortedReportActions, isOffline], ); + const lastActionIndex = sortedVisibleReportActions[0]?.reportActionID; const reportActionSize = useRef(sortedVisibleReportActions.length); const hasNewestReportAction = sortedReportActions?.[0].created === report.lastVisibleActionCreated; From efe88ce911f70c4d74fe90d08c42cb17c7b9b0c9 Mon Sep 17 00:00:00 2001 From: BrtqKr Date: Wed, 10 Apr 2024 17:31:16 +0200 Subject: [PATCH 069/932] cleanup --- src/languages/es.ts | 5 +++-- src/pages/home/report/ReportActionsList.tsx | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index c9aabe62087a..a6a19d2bf35c 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -630,14 +630,14 @@ export default { routePending: 'Ruta pendiente...', receiptScanning: 'Escaneo en curso…', receiptScanInProgress: 'Escaneo en curso…', - receiptScanInProgressDescription: 'Escaneo en curso.', + receiptScanInProgressDescription: ' Escaneando recibo. Vuelva a comprobarlo más tarde o introduzca los detalles ahora.', receiptMissingDetails: 'Recibo con campos vacíos', missingAmount: 'Falta importe', missingMerchant: 'Falta comerciante', receiptStatusTitle: 'Escaneando…', receiptStatusText: 'Solo tú puedes ver este recibo cuando se está escaneando. Vuelve más tarde o introduce los detalles ahora.', receiptScanningFailed: 'El escaneo de recibo ha fallado. Introduce los detalles manualmente.', - transactionPendingText: 'La transacción tarda unos días en contabilizarse desde la fecha en que se utilizó la tarjeta.', + transactionPendingDescription: 'La transacción tarda unos días en contabilizarse desde la fecha en que se utilizó la tarjeta.', requestCount: ({count, scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => `${count} ${Str.pluralize('solicitude', 'solicitudes', count)}${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${ pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : '' @@ -735,6 +735,7 @@ export default { set: 'estableció', changed: 'cambió', removed: 'eliminó', + transactionPending: 'Transaction pending.', }, notificationPreferencesPage: { header: 'Preferencias de avisos', diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 0068ed875b82..60a620f186cc 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -6,7 +6,6 @@ import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 're import {DeviceEventEmitter, InteractionManager} from 'react-native'; import type {LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import Onyx from 'react-native-onyx'; import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; import InvertedFlatList from '@components/InvertedFlatList'; import {AUTOSCROLL_TO_TOP_THRESHOLD} from '@components/InvertedFlatList/BaseInvertedFlatList'; From c3cd931cfaccaece3414103983483b7683c8f04f Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 10 Apr 2024 17:39:17 +0200 Subject: [PATCH 070/932] Remove redundant SCREENS.SETTINGS.ROOT route --- src/libs/Navigation/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 04bb7797804e..bffe333900e5 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -71,7 +71,6 @@ type BackToParams = { }; type SettingsNavigatorParamList = { - [SCREENS.SETTINGS.ROOT]: undefined; [SCREENS.SETTINGS.SHARE_CODE]: undefined; [SCREENS.SETTINGS.PROFILE.ROOT]: undefined; [SCREENS.SETTINGS.PROFILE.PRONOUNS]: undefined; From 9a664e5d4db4201533fe1d9c9180b6fe4bdf81a9 Mon Sep 17 00:00:00 2001 From: BrtqKr Date: Wed, 10 Apr 2024 17:43:25 +0200 Subject: [PATCH 071/932] cleanup --- src/pages/home/ReportScreen.tsx | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index e8c00f807e06..940cba181db7 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -323,7 +323,7 @@ function ReportScreen({ /> ); - if (true) { + if (isSingleTransactionView) { headerView = ( ReportActionsUtils.getOneTransactionThreadReportID(reportActions ?? []), [reportActions]); - // if (ReportUtils.isMoneyRequestReport(report)) { - // headerView = ( - // - // ); - // } + if (ReportUtils.isMoneyRequestReport(report)) { + headerView = ( + + ); + } /** * When false the ReportActionsView will completely unmount and we will show a loader until it returns true. @@ -603,7 +603,6 @@ function ReportScreen({ ); } - console.log('REPORT SCREEN'); return ( From 945872ab306ed01b352caf10a3a3aea01d6cc087 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 10 Apr 2024 17:47:50 +0200 Subject: [PATCH 072/932] Handle mocking useActiveWorkspaceFromNavigationState in tests --- tests/unit/SidebarOrderTest.ts | 1 + tests/unit/SidebarTest.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/unit/SidebarOrderTest.ts b/tests/unit/SidebarOrderTest.ts index 2758d43fb81e..ac74fb50245b 100644 --- a/tests/unit/SidebarOrderTest.ts +++ b/tests/unit/SidebarOrderTest.ts @@ -14,6 +14,7 @@ import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatch jest.mock('@libs/Permissions'); jest.mock('@hooks/usePermissions.ts'); jest.mock('@components/Icon/Expensicons'); +jest.mock('@src/hooks/useActiveWorkspaceFromNavigationState') const ONYXKEYS = { PERSONAL_DETAILS_LIST: 'personalDetailsList', diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index 23ea0d377634..ba2d950232b5 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -12,6 +12,7 @@ import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatch // Be sure to include the mocked Permissions and Expensicons libraries as well as the usePermissions hook or else the beta tests won't work jest.mock('@src/libs/Permissions'); jest.mock('@src/hooks/usePermissions.ts'); +jest.mock('@src/hooks/useActiveWorkspaceFromNavigationState') jest.mock('@src/components/Icon/Expensicons'); describe('Sidebar', () => { From 3be474286b2de7510841a1e2d80baf45f203ba4f Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 10 Apr 2024 17:55:50 +0200 Subject: [PATCH 073/932] Run prettier --- tests/unit/SidebarOrderTest.ts | 2 +- tests/unit/SidebarTest.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/SidebarOrderTest.ts b/tests/unit/SidebarOrderTest.ts index ac74fb50245b..e0858c4e78b9 100644 --- a/tests/unit/SidebarOrderTest.ts +++ b/tests/unit/SidebarOrderTest.ts @@ -14,7 +14,7 @@ import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatch jest.mock('@libs/Permissions'); jest.mock('@hooks/usePermissions.ts'); jest.mock('@components/Icon/Expensicons'); -jest.mock('@src/hooks/useActiveWorkspaceFromNavigationState') +jest.mock('@src/hooks/useActiveWorkspaceFromNavigationState'); const ONYXKEYS = { PERSONAL_DETAILS_LIST: 'personalDetailsList', diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index ba2d950232b5..e35a479c1add 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -12,7 +12,7 @@ import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatch // Be sure to include the mocked Permissions and Expensicons libraries as well as the usePermissions hook or else the beta tests won't work jest.mock('@src/libs/Permissions'); jest.mock('@src/hooks/usePermissions.ts'); -jest.mock('@src/hooks/useActiveWorkspaceFromNavigationState') +jest.mock('@src/hooks/useActiveWorkspaceFromNavigationState'); jest.mock('@src/components/Icon/Expensicons'); describe('Sidebar', () => { From d315efb2d08469d717e820189c2d4a388976cfee Mon Sep 17 00:00:00 2001 From: BrtqKr Date: Wed, 10 Apr 2024 17:57:36 +0200 Subject: [PATCH 074/932] cleanup --- src/libs/CardUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 0d8de86f63bc..c4d67adcd54a 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -46,7 +46,7 @@ function isExpensifyCard(cardID?: number) { * @returns boolean if the cardID is in the cardList from ONYX. Includes Expensify Cards. */ function isCorporateCard(cardID: number) { - return true; + return !!allCards[cardID]; } /** From 4e08e070c709b6e8350d3cbe05221a8b56098ac0 Mon Sep 17 00:00:00 2001 From: BrtqKr Date: Thu, 11 Apr 2024 00:26:01 +0200 Subject: [PATCH 075/932] cleanup translations --- src/languages/es.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index a6a19d2bf35c..877d745fa59c 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -628,16 +628,16 @@ export default { posted: 'Contabilizado', deleteReceipt: 'Eliminar recibo', routePending: 'Ruta pendiente...', - receiptScanning: 'Escaneo en curso…', - receiptScanInProgress: 'Escaneo en curso…', - receiptScanInProgressDescription: ' Escaneando recibo. Vuelva a comprobarlo más tarde o introduzca los detalles ahora.', + receiptScanning: 'Escaneando recibo…', + receiptScanInProgress: 'Escaneo en curso.', + receiptScanInProgressDescription: 'Escaneando recibo. Vuelva a comprobarlo más tarde o introduzca los detalles ahora.', receiptMissingDetails: 'Recibo con campos vacíos', missingAmount: 'Falta importe', missingMerchant: 'Falta comerciante', receiptStatusTitle: 'Escaneando…', receiptStatusText: 'Solo tú puedes ver este recibo cuando se está escaneando. Vuelve más tarde o introduce los detalles ahora.', receiptScanningFailed: 'El escaneo de recibo ha fallado. Introduce los detalles manualmente.', - transactionPendingDescription: 'La transacción tarda unos días en contabilizarse desde la fecha en que se utilizó la tarjeta.', + transactionPendingDescription: 'Transacción pendiente. Esto puede tardar algunos días en registrarse a partir de la fecha en que se utilizó la tarjeta.', requestCount: ({count, scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => `${count} ${Str.pluralize('solicitude', 'solicitudes', count)}${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${ pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : '' @@ -735,7 +735,7 @@ export default { set: 'estableció', changed: 'cambió', removed: 'eliminó', - transactionPending: 'Transaction pending.', + transactionPending: 'Transacción pendiente.', }, notificationPreferencesPage: { header: 'Preferencias de avisos', From 648e333f6178eb2b4699d024936d4ef281aacd7d Mon Sep 17 00:00:00 2001 From: BrtqKr Date: Thu, 11 Apr 2024 00:41:33 +0200 Subject: [PATCH 076/932] fix preview --- .../MoneyRequestPreviewContent.tsx | 12 ++++++++++++ src/pages/home/report/ReportActionsList.tsx | 1 + 2 files changed, 13 insertions(+) diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index b0363135e273..e149891b0365 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -87,6 +87,7 @@ function MoneyRequestPreviewContent({ const requestMerchant = truncate(merchant, {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); const hasReceipt = TransactionUtils.hasReceipt(transaction); const isScanning = hasReceipt && TransactionUtils.isReceiptBeingScanned(transaction); + const isPending = TransactionUtils.isPending(transaction); const isOnHold = TransactionUtils.isOnHold(transaction); const isSettlementOrApprovalPartial = Boolean(iouReport?.pendingFields?.partial); const isPartialHold = isSettlementOrApprovalPartial && isOnHold; @@ -315,6 +316,17 @@ function MoneyRequestPreviewContent({ {translate('iou.receiptScanInProgress')} )} + {isPending && ( + + + {translate('iou.transactionPending')} + + )} diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 60a620f186cc..0068ed875b82 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -6,6 +6,7 @@ import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 're import {DeviceEventEmitter, InteractionManager} from 'react-native'; import type {LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; import InvertedFlatList from '@components/InvertedFlatList'; import {AUTOSCROLL_TO_TOP_THRESHOLD} from '@components/InvertedFlatList/BaseInvertedFlatList'; From 6cb699dd7a02320971348b2c636e96852f2c5bb8 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Thu, 11 Apr 2024 14:05:43 +0300 Subject: [PATCH 077/932] fix touch screen case for image view --- src/components/ImageView/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ImageView/index.tsx b/src/components/ImageView/index.tsx index 2316577158f8..9865adb04d3d 100644 --- a/src/components/ImageView/index.tsx +++ b/src/components/ImageView/index.tsx @@ -213,7 +213,8 @@ function ImageView({isAuthTokenRequired = false, url, fileName, onError}: ImageV onLoad={imageLoad} onError={onError} /> - {(isLoading || zoomScale === 0) && } + {((isLoading && !isOffline) || zoomScale === 0) && } + {isLoading && } ); } From cbea4acce2f432b71940929f52b6d99c2daa1b22 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Thu, 11 Apr 2024 14:17:38 +0300 Subject: [PATCH 078/932] fix logic for zoom scale --- src/components/ImageView/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ImageView/index.tsx b/src/components/ImageView/index.tsx index 9865adb04d3d..f08941ef7d77 100644 --- a/src/components/ImageView/index.tsx +++ b/src/components/ImageView/index.tsx @@ -213,7 +213,7 @@ function ImageView({isAuthTokenRequired = false, url, fileName, onError}: ImageV onLoad={imageLoad} onError={onError} /> - {((isLoading && !isOffline) || zoomScale === 0) && } + {((isLoading && !isOffline) || (!isLoading && zoomScale === 0)) && } {isLoading && } ); From a3c565ae84371cb37342c62946603356a138551c Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Thu, 11 Apr 2024 14:03:48 +0200 Subject: [PATCH 079/932] Fix sidebar links perf tests --- tests/perf-test/SidebarLinks.perf-test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/perf-test/SidebarLinks.perf-test.tsx b/tests/perf-test/SidebarLinks.perf-test.tsx index 2848015d5c63..036d4ea84ff9 100644 --- a/tests/perf-test/SidebarLinks.perf-test.tsx +++ b/tests/perf-test/SidebarLinks.perf-test.tsx @@ -10,6 +10,7 @@ import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatch jest.mock('@libs/Permissions'); jest.mock('@hooks/usePermissions.ts'); +jest.mock('@src/hooks/useActiveWorkspaceFromNavigationState'); jest.mock('@libs/Navigation/Navigation'); jest.mock('@components/Icon/Expensicons'); From f2bcf10e8e12bef7242a95adbab46f0a3352e04c Mon Sep 17 00:00:00 2001 From: BrtqKr Date: Thu, 11 Apr 2024 15:57:59 +0200 Subject: [PATCH 080/932] wip --- ios/Podfile.lock | 2 +- src/components/MoneyRequestHeader.tsx | 2 ++ .../MoneyRequestPreviewContent.tsx | 1 + .../ReportActionItem/ReportPreview.tsx | 14 +++++++++++++- src/libs/ReportUtils.ts | 1 + src/pages/home/ReportScreen.tsx | 2 ++ src/pages/home/report/ReportActionsList.tsx | 19 +++++++++++++++++++ 7 files changed, 39 insertions(+), 2 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 32a8bca75bcd..94bd6e35f31d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1921,7 +1921,7 @@ SPEC CHECKSUMS: SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2 VisionCamera: 9f26224fce1233ffdad9fa4e56863e3de2190dc0 - Yoga: e64aa65de36c0832d04e8c7bd614396c77a80047 + Yoga: 13c8ef87792450193e117976337b8527b49e8c03 PODFILE CHECKSUM: a431c146e1501391834a2f299a74093bac53b530 diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 5ea46f339d46..909b6e6f71f6 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -165,6 +165,8 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, }); } + console.log('MONEY REQUEST HEADER'); + return ( <> diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index e149891b0365..2a56e2f2405d 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -67,6 +67,7 @@ function MoneyRequestPreviewContent({ const {isSmallScreenWidth, windowWidth} = useWindowDimensions(); const parser = new ExpensiMark(); + console.warn('TRANSACTION ', transaction); const sessionAccountID = session?.accountID; const managerID = iouReport?.managerID ?? -1; const ownerAccountID = iouReport?.ownerAccountID ?? -1; diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 40c9f1afcc21..5b9c2269028c 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -228,7 +228,8 @@ function ReportPreview({ const shouldShowSingleRequestMerchantOrDescription = numberOfRequests === 1 && (!!formattedMerchant || !!formattedDescription) && !(hasOnlyTransactionsWithPendingRoutes && !totalDisplaySpend); const shouldShowSubtitle = !isScanning && (shouldShowSingleRequestMerchantOrDescription || numberOfRequests > 1); - const shouldShowPendingSubtitle = numberOfPendingRequests === 1 && transactionsWithReceipts.length === 1; + const shouldShowScanningSubtitle = numberOfScanningReceipts === 1 && allTransactions.length === 1; + const shouldShowPendingSubtitle = numberOfPendingRequests === 1 && allTransactions.length === 1; const {isSupportTextHtml, supportText} = useMemo(() => { if (formattedMerchant) { @@ -321,6 +322,17 @@ function ReportPreview({ )} + {shouldShowScanningSubtitle && ( + + + {translate('iou.receiptScanInProgress')} + + )} {shouldShowPendingSubtitle && ( ): boolean { */ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; + console.log('REPORT ', report); return isIOURequest(report) || isExpenseRequest(report); } diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 940cba181db7..de02342ff414 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -323,6 +323,8 @@ function ReportScreen({ /> ); + console.warn('ReportUtils.isMoneyRequest(report) ', ReportUtils.isMoneyRequest(report)); + if (isSingleTransactionView) { headerView = ( { const unsubscriber = Visibility.onVisibilityChange(() => { setIsVisible(Visibility.isVisible()); From c3285b8e4baf6fc2cf64dd0cc17dd3c3fb10c6ec Mon Sep 17 00:00:00 2001 From: BrtqKr Date: Thu, 11 Apr 2024 16:18:56 +0200 Subject: [PATCH 081/932] cleanup --- src/components/MoneyRequestHeader.tsx | 2 -- .../MoneyRequestPreviewContent.tsx | 1 - src/libs/ReportUtils.ts | 1 - src/pages/home/ReportScreen.tsx | 2 -- src/pages/home/report/ReportActionsList.tsx | 20 ------------------- 5 files changed, 26 deletions(-) diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 909b6e6f71f6..5ea46f339d46 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -165,8 +165,6 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, }); } - console.log('MONEY REQUEST HEADER'); - return ( <> diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index 2a56e2f2405d..e149891b0365 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -67,7 +67,6 @@ function MoneyRequestPreviewContent({ const {isSmallScreenWidth, windowWidth} = useWindowDimensions(); const parser = new ExpensiMark(); - console.warn('TRANSACTION ', transaction); const sessionAccountID = session?.accountID; const managerID = iouReport?.managerID ?? -1; const ownerAccountID = iouReport?.ownerAccountID ?? -1; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f03a5051d634..fec64efaac7f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1282,7 +1282,6 @@ function isTrackExpenseReport(report: OnyxEntry): boolean { */ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; - console.log('REPORT ', report); return isIOURequest(report) || isExpenseRequest(report); } diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index de02342ff414..940cba181db7 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -323,8 +323,6 @@ function ReportScreen({ /> ); - console.warn('ReportUtils.isMoneyRequest(report) ', ReportUtils.isMoneyRequest(report)); - if (isSingleTransactionView) { headerView = ( { const unsubscriber = Visibility.onVisibilityChange(() => { setIsVisible(Visibility.isVisible()); From 9c1527be8952d96dbc1d758b08717b346833eb8d Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Thu, 11 Apr 2024 22:34:06 +0530 Subject: [PATCH 082/932] remove unnecessary styles --- src/components/PDFView/index.tsx | 2 -- src/styles/index.ts | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/PDFView/index.tsx b/src/components/PDFView/index.tsx index 2725e3c2a6aa..e63a4b5ac4c1 100644 --- a/src/components/PDFView/index.tsx +++ b/src/components/PDFView/index.tsx @@ -92,8 +92,6 @@ function PDFView({onToggleKeyboard, fileName, onPress, isFocused, sourceURL, max style={[ styles.chatItemPDFAttachmentLoading, StyleUtils.getWidthAndHeightStyle(LOADING_THUMBNAIL_WIDTH, LOADING_THUMBNAIL_HEIGHT), - styles.alignItemsCenter, - styles.justifyContentCenter, ]} > borderColor: theme.border, borderWidth: 1, borderRadius: variables.componentBorderRadiusNormal, - textAlign: 'center', - verticalAlign: 'middle', - opacity: 1, + ...flex.alignItemsCenter, + ...flex.justifyContentCenter, }, sidebarVisible: { From bffd5f03956f13c18f4d49cb25fc5ecc8d64b241 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 12 Apr 2024 11:24:10 +0700 Subject: [PATCH 083/932] implement internet reachability for android --- src/CONST.ts | 1 + src/libs/NetworkConnection.ts | 17 ++++++++++--- .../index.android.ts | 24 +++++++++++++++++++ src/libs/checkInternetReachability/index.ts | 5 ++++ src/libs/checkInternetReachability/types.ts | 3 +++ 5 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 src/libs/checkInternetReachability/index.android.ts create mode 100644 src/libs/checkInternetReachability/index.ts create mode 100644 src/libs/checkInternetReachability/types.ts diff --git a/src/CONST.ts b/src/CONST.ts index 0ce5458e0849..9e4e41a64c38 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -543,6 +543,7 @@ const CONST = { STATUS_EXPENSIFY_URL: 'https://status.expensify.com', GOOGLE_MEET_URL_ANDROID: 'https://meet.google.com', GOOGLE_DOC_IMAGE_LINK_MATCH: 'googleusercontent.com', + GOOGLE_CLOUD_URL: 'https://clients3.google.com/generate_204', IMAGE_BASE64_MATCH: 'base64', DEEPLINK_BASE_URL: 'new-expensify://', PDF_VIEWER_URL: '/pdf/web/viewer.html', diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index 4383a921662f..c2b348cc0f6f 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -6,6 +6,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import * as NetworkActions from './actions/Network'; import AppStateMonitor from './AppStateMonitor'; +import checkInternetReachability from './checkInternetReachability'; import Log from './Log'; let isOffline = false; @@ -69,10 +70,11 @@ Onyx.connect({ }); /** - * Set interval to periodically (re)check backend status + * Set interval to periodically (re)check backend status. + * Because backend unreachability might imply lost internet connection, we need to check internet reachability. * @returns clearInterval cleanup */ -function subscribeToBackendReachability(): () => void { +function subscribeToBackendAndInternetReachability(): () => void { const intervalID = setInterval(() => { // Offline status also implies backend unreachability if (isOffline) { @@ -92,6 +94,15 @@ function subscribeToBackendReachability(): () => void { .then((json) => Promise.resolve(json.jsonCode === 200)) .catch(() => Promise.resolve(false)); }) + .then((isBackendReachable: boolean) => { + if (isBackendReachable) { + return Promise.resolve(true); + } + return checkInternetReachability().then((isInternetReachable: boolean) => { + NetworkActions.setIsOffline(!isInternetReachable); + return Promise.resolve(false); + }); + }) .then(NetworkActions.setIsBackendReachable) .catch(() => NetworkActions.setIsBackendReachable(false)); }, CONST.NETWORK.BACKEND_CHECK_INTERVAL_MS); @@ -108,7 +119,7 @@ function subscribeToBackendReachability(): () => void { function subscribeToNetworkStatus(): () => void { // Note: We are disabling the reachability check when using the local web API since requests can get stuck in a 'Pending' state and are not reliable indicators for reachability. // If you need to test the "recheck" feature then switch to the production API proxy server. - const unsubscribeFromBackendReachability = !CONFIG.IS_USING_LOCAL_WEB ? subscribeToBackendReachability() : undefined; + const unsubscribeFromBackendReachability = !CONFIG.IS_USING_LOCAL_WEB ? subscribeToBackendAndInternetReachability() : undefined; // Set up the event listener for NetInfo to tell whether the user has // internet connectivity or not. This is more reliable than the Pusher diff --git a/src/libs/checkInternetReachability/index.android.ts b/src/libs/checkInternetReachability/index.android.ts new file mode 100644 index 000000000000..2dfb015dc9ed --- /dev/null +++ b/src/libs/checkInternetReachability/index.android.ts @@ -0,0 +1,24 @@ +import CONST from '@src/CONST'; +import type InternetReachabilityCheck from './types'; + +/** + * Although Android supports internet reachability check, it only does on initiating the connection. + * We need to implement a test for a highly-available endpoint to cover the case internet is lost during connection. + */ +export default function checkInternetReachability(): InternetReachabilityCheck { + // Using the API url ensures reachability is tested over internet + return fetch(CONST.GOOGLE_CLOUD_URL, { + method: 'GET', + cache: 'no-cache', + }) + .then((response) => { + if (!response.ok) { + return Promise.resolve(false); + } + return response + .json() + .then((json) => Promise.resolve(json.jsonCode === 204)) + .catch(() => Promise.resolve(false)); + }) + .catch(() => Promise.resolve(false)); +} diff --git a/src/libs/checkInternetReachability/index.ts b/src/libs/checkInternetReachability/index.ts new file mode 100644 index 000000000000..d7d0808c6efc --- /dev/null +++ b/src/libs/checkInternetReachability/index.ts @@ -0,0 +1,5 @@ +import type InternetReachabilityCheck from './types'; + +export default function checkInternetReachability(): InternetReachabilityCheck { + return Promise.resolve(true); +} diff --git a/src/libs/checkInternetReachability/types.ts b/src/libs/checkInternetReachability/types.ts new file mode 100644 index 000000000000..3e1cb96cb26c --- /dev/null +++ b/src/libs/checkInternetReachability/types.ts @@ -0,0 +1,3 @@ +type InternetReachabilityCheck = Promise; + +export default InternetReachabilityCheck; From e3644d1acd2a3bba3ae94caa5a1e5ee2219ad280 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 12 Apr 2024 08:22:14 +0200 Subject: [PATCH 084/932] Remove SCREENS.WORKSPACES_CENTRAL_PANE --- .../Navigators/FullScreenNavigator.tsx | 77 +++++++++++++++-- .../CustomFullScreenRouter.tsx | 20 +---- .../getTopmostWorkspacesCentralPaneName.ts | 17 +--- src/libs/Navigation/linkingConfig/config.ts | 82 +++++++++---------- .../linkingConfig/getAdaptedStateFromPath.ts | 6 +- src/libs/Navigation/types.ts | 56 ++++++++++++- 6 files changed, 172 insertions(+), 86 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx index 07b069462dd1..065e11495a01 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx @@ -5,7 +5,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import createCustomFullScreenNavigator from '@libs/Navigation/AppNavigator/createCustomFullScreenNavigator'; import getRootNavigatorScreenOptions from '@libs/Navigation/AppNavigator/getRootNavigatorScreenOptions'; -import * as ModalStackNavigators from '@libs/Navigation/AppNavigator/ModalStackNavigators'; import SCREENS from '@src/SCREENS'; const loadWorkspaceInitialPage = () => require('../../../../pages/workspace/WorkspaceInitialPage').default as React.ComponentType; @@ -17,19 +16,85 @@ function FullScreenNavigator() { const StyleUtils = useStyleUtils(); const {isSmallScreenWidth} = useWindowDimensions(); const screenOptions = getRootNavigatorScreenOptions(isSmallScreenWidth, styles, StyleUtils); - return ( - + require('@pages/workspace/WorkspaceProfilePage').default as React.ComponentType} + /> + require('@pages/workspace/card/WorkspaceCardPage').default as React.ComponentType} + /> + require('@pages/workspace/workflows/WorkspaceWorkflowsPage').default as React.ComponentType} + /> + require('@pages/workspace/reimburse/WorkspaceReimbursePage').default as React.ComponentType} + /> + require('@pages/workspace/bills/WorkspaceBillsPage').default as React.ComponentType} + /> + require('@pages/workspace/invoices/WorkspaceInvoicesPage').default as React.ComponentType} + /> + require('@pages/workspace/travel/WorkspaceTravelPage').default as React.ComponentType} + /> + require('@pages/workspace/WorkspaceMembersPage').default as React.ComponentType} + /> + + require('@pages/workspace/accounting/WorkspaceAccountingPage').default as React.ComponentType} + /> + + require('@pages/workspace/categories/WorkspaceCategoriesPage').default as React.ComponentType} + /> + require('@pages/workspace/WorkspaceMoreFeaturesPage').default as React.ComponentType} + /> + require('@pages/workspace/tags/WorkspaceTagsPage').default as React.ComponentType} + /> + require('@pages/workspace/taxes/WorkspaceTaxesPage').default as React.ComponentType} + /> + require('@pages/workspace/distanceRates/PolicyDistanceRatesPage').default as React.ComponentType} /> diff --git a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx index eb19f891ecd5..99e6fb23b8ba 100644 --- a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx @@ -11,11 +11,6 @@ const isAtLeastOneInState = (state: StackState, screenName: string): boolean => function adaptStateIfNecessary(state: StackState) { const isNarrowLayout = getIsNarrowLayout(); const workspaceCentralPane = state.routes.at(-1); - const topmostWorkspaceCentralPaneRoute = workspaceCentralPane?.state?.routes[0]; - - // When a screen from the FullScreenNavigator is opened from the deeplink then params should be passed to SCREENS.WORKSPACE.INITIAL from the variable defined below. - const workspacesCentralPaneParams = - workspaceCentralPane?.params && 'params' in workspaceCentralPane.params ? (workspaceCentralPane.params.params as Record) : undefined; // There should always be WORKSPACE.INITIAL screen in the state to make sure go back works properly if we deeplinkg to a subpage of settings. if (!isAtLeastOneInState(state, SCREENS.WORKSPACE.INITIAL)) { @@ -28,7 +23,7 @@ function adaptStateIfNecessary(state: StackState) { // Unshift the root screen to fill left pane. state.routes.unshift({ name: SCREENS.WORKSPACE.INITIAL, - params: topmostWorkspaceCentralPaneRoute?.params ?? workspacesCentralPaneParams, + params: workspaceCentralPane?.params, }); } } @@ -37,22 +32,15 @@ function adaptStateIfNecessary(state: StackState) { // - WORKSPACE.INITIAL to cover left pane. // - WORKSPACES_CENTRAL_PANE to cover central pane. if (!isNarrowLayout) { - if (!isAtLeastOneInState(state, SCREENS.WORKSPACES_CENTRAL_PANE)) { + if (state.routes.length === 1 && state.routes[0].name === SCREENS.WORKSPACE.INITIAL) { // @ts-expect-error Updating read only property // noinspection JSConstantReassignment state.stale = true; // eslint-disable-line // Push the default settings central pane screen. if (state.stale === true) { state.routes.push({ - name: SCREENS.WORKSPACES_CENTRAL_PANE, - state: { - routes: [ - { - name: SCREENS.WORKSPACE.PROFILE, - params: state.routes[0]?.params, - }, - ], - }, + name: SCREENS.WORKSPACE.PROFILE, + params: state.routes[0]?.params, }); } } diff --git a/src/libs/Navigation/getTopmostWorkspacesCentralPaneName.ts b/src/libs/Navigation/getTopmostWorkspacesCentralPaneName.ts index db11368c1345..ec3159ade059 100644 --- a/src/libs/Navigation/getTopmostWorkspacesCentralPaneName.ts +++ b/src/libs/Navigation/getTopmostWorkspacesCentralPaneName.ts @@ -1,5 +1,4 @@ import type {NavigationState, PartialState} from '@react-navigation/native'; -import SCREENS from '@src/SCREENS'; // Get the name of topmost report in the navigation stack. function getTopmostWorkspacesCentralPaneName(state: NavigationState | PartialState): string | undefined { @@ -7,21 +6,7 @@ function getTopmostWorkspacesCentralPaneName(state: NavigationState | PartialSta return; } - const topmostCentralPane = state.routes.filter((route) => typeof route !== 'number' && 'name' in route && route.name === SCREENS.WORKSPACES_CENTRAL_PANE).at(-1); - - if (!topmostCentralPane) { - return; - } - - if (!!topmostCentralPane.params && 'screen' in topmostCentralPane.params && typeof topmostCentralPane.params.screen === 'string') { - return topmostCentralPane.params.screen; - } - - if (!topmostCentralPane.state) { - return; - } - - return topmostCentralPane.state?.routes.at(-1)?.name; + return state.routes.at(-1)?.name } export default getTopmostWorkspacesCentralPaneName; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 95294b7711b5..468a5ce8c3d9 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -635,49 +635,45 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.INITIAL]: { path: ROUTES.WORKSPACE_INITIAL.route, }, - [SCREENS.WORKSPACES_CENTRAL_PANE]: { - screens: { - [SCREENS.WORKSPACE.PROFILE]: ROUTES.WORKSPACE_PROFILE.route, - [SCREENS.WORKSPACE.CARD]: { - path: ROUTES.WORKSPACE_CARD.route, - }, - [SCREENS.WORKSPACE.WORKFLOWS]: { - path: ROUTES.WORKSPACE_WORKFLOWS.route, - }, - [SCREENS.WORKSPACE.REIMBURSE]: { - path: ROUTES.WORKSPACE_REIMBURSE.route, - }, - [SCREENS.WORKSPACE.BILLS]: { - path: ROUTES.WORKSPACE_BILLS.route, - }, - [SCREENS.WORKSPACE.INVOICES]: { - path: ROUTES.WORKSPACE_INVOICES.route, - }, - [SCREENS.WORKSPACE.TRAVEL]: { - path: ROUTES.WORKSPACE_TRAVEL.route, - }, - [SCREENS.WORKSPACE.MEMBERS]: { - path: ROUTES.WORKSPACE_MEMBERS.route, - }, - [SCREENS.WORKSPACE.ACCOUNTING]: { - path: ROUTES.WORKSPACE_ACCOUNTING.route, - }, - [SCREENS.WORKSPACE.CATEGORIES]: { - path: ROUTES.WORKSPACE_CATEGORIES.route, - }, - [SCREENS.WORKSPACE.MORE_FEATURES]: { - path: ROUTES.WORKSPACE_MORE_FEATURES.route, - }, - [SCREENS.WORKSPACE.TAGS]: { - path: ROUTES.WORKSPACE_TAGS.route, - }, - [SCREENS.WORKSPACE.TAXES]: { - path: ROUTES.WORKSPACE_TAXES.route, - }, - [SCREENS.WORKSPACE.DISTANCE_RATES]: { - path: ROUTES.WORKSPACE_DISTANCE_RATES.route, - }, - }, + [SCREENS.WORKSPACE.PROFILE]: ROUTES.WORKSPACE_PROFILE.route, + [SCREENS.WORKSPACE.CARD]: { + path: ROUTES.WORKSPACE_CARD.route, + }, + [SCREENS.WORKSPACE.WORKFLOWS]: { + path: ROUTES.WORKSPACE_WORKFLOWS.route, + }, + [SCREENS.WORKSPACE.REIMBURSE]: { + path: ROUTES.WORKSPACE_REIMBURSE.route, + }, + [SCREENS.WORKSPACE.BILLS]: { + path: ROUTES.WORKSPACE_BILLS.route, + }, + [SCREENS.WORKSPACE.INVOICES]: { + path: ROUTES.WORKSPACE_INVOICES.route, + }, + [SCREENS.WORKSPACE.TRAVEL]: { + path: ROUTES.WORKSPACE_TRAVEL.route, + }, + [SCREENS.WORKSPACE.MEMBERS]: { + path: ROUTES.WORKSPACE_MEMBERS.route, + }, + [SCREENS.WORKSPACE.ACCOUNTING]: { + path: ROUTES.WORKSPACE_ACCOUNTING.route, + }, + [SCREENS.WORKSPACE.CATEGORIES]: { + path: ROUTES.WORKSPACE_CATEGORIES.route, + }, + [SCREENS.WORKSPACE.MORE_FEATURES]: { + path: ROUTES.WORKSPACE_MORE_FEATURES.route, + }, + [SCREENS.WORKSPACE.TAGS]: { + path: ROUTES.WORKSPACE_TAGS.route, + }, + [SCREENS.WORKSPACE.TAXES]: { + path: ROUTES.WORKSPACE_TAXES.route, + }, + [SCREENS.WORKSPACE.DISTANCE_RATES]: { + path: ROUTES.WORKSPACE_DISTANCE_RATES.route, }, }, }, diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index d7bcfbb68952..52e62604c0a5 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -86,11 +86,9 @@ function createFullScreenNavigator(route?: NavigationPartialRoute; + [SCREENS.WORKSPACE.PROFILE]: { + policyID: string; + }; + [SCREENS.WORKSPACE.CARD]: { + policyID: string; + }; + [SCREENS.WORKSPACE.WORKFLOWS]: { + policyID: string; + }; + [SCREENS.WORKSPACE.WORKFLOWS_APPROVER]: { + policyID: string; + }; + [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: { + policyID: string; + }; + [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: { + policyID: string; + }; + [SCREENS.WORKSPACE.REIMBURSE]: { + policyID: string; + }; + [SCREENS.WORKSPACE.BILLS]: { + policyID: string; + }; + [SCREENS.WORKSPACE.INVOICES]: { + policyID: string; + }; + [SCREENS.WORKSPACE.TRAVEL]: { + policyID: string; + }; + [SCREENS.WORKSPACE.MEMBERS]: { + policyID: string; + }; + [SCREENS.WORKSPACE.ACCOUNTING]: { + policyID: string; + }; + [SCREENS.WORKSPACE.CATEGORIES]: { + policyID: string; + }; + [SCREENS.WORKSPACE.MORE_FEATURES]: { + policyID: string; + }; + [SCREENS.WORKSPACE.TAGS]: { + policyID: string; + tagName: string; + }; + [SCREENS.WORKSPACE.TAXES]: { + policyID: string; + }; + [SCREENS.WORKSPACE.DISTANCE_RATES]: { + policyID: string; + }; + [SCREENS.WORKSPACE.ACCOUNTING]: { + policyID: string; + }; }; type OnboardingModalNavigatorParamList = { From 4b1681c76db57613511ac7bbe4be900bbe7954fd Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 12 Apr 2024 08:44:20 +0200 Subject: [PATCH 085/932] Add a comment to useActiveWorkspaceFromNavigationState --- src/hooks/useActiveWorkspaceFromNavigationState.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hooks/useActiveWorkspaceFromNavigationState.ts b/src/hooks/useActiveWorkspaceFromNavigationState.ts index 8f9da3d9df37..63a6865a516b 100644 --- a/src/hooks/useActiveWorkspaceFromNavigationState.ts +++ b/src/hooks/useActiveWorkspaceFromNavigationState.ts @@ -5,6 +5,7 @@ import type {BottomTabNavigatorParamList} from '@libs/Navigation/types'; * Get the currently selected policy ID stored in the navigation state. This hook should only be called only from screens in BottomTab. */ function useActiveWorkspaceFromNavigationState() { + // The last policyID value is always stored in the last route in BottomTabNavigator. const activeWorkpsaceID = useNavigationState((state) => state.routes.at(-1)?.params?.policyID); return activeWorkpsaceID; From 01aafb06eecf79d97444b3e57d41905136723a65 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 12 Apr 2024 14:58:36 +0700 Subject: [PATCH 086/932] modify fetch logic --- src/libs/NetworkConnection.ts | 17 +++++++++++------ .../checkInternetReachability/index.android.ts | 10 +--------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index c2b348cc0f6f..86253b0cca78 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -96,15 +96,20 @@ function subscribeToBackendAndInternetReachability(): () => void { }) .then((isBackendReachable: boolean) => { if (isBackendReachable) { - return Promise.resolve(true); + NetworkActions.setIsBackendReachable(true); + return; } - return checkInternetReachability().then((isInternetReachable: boolean) => { - NetworkActions.setIsOffline(!isInternetReachable); - return Promise.resolve(false); + checkInternetReachability().then((isInternetReachable: boolean) => { + setOfflineStatus(!isInternetReachable); + NetworkActions.setIsBackendReachable(false); }); }) - .then(NetworkActions.setIsBackendReachable) - .catch(() => NetworkActions.setIsBackendReachable(false)); + .catch(() => { + checkInternetReachability().then((isInternetReachable: boolean) => { + setOfflineStatus(!isInternetReachable); + NetworkActions.setIsBackendReachable(false); + }); + }); }, CONST.NETWORK.BACKEND_CHECK_INTERVAL_MS); return () => { diff --git a/src/libs/checkInternetReachability/index.android.ts b/src/libs/checkInternetReachability/index.android.ts index 2dfb015dc9ed..9e1a0170d3e4 100644 --- a/src/libs/checkInternetReachability/index.android.ts +++ b/src/libs/checkInternetReachability/index.android.ts @@ -11,14 +11,6 @@ export default function checkInternetReachability(): InternetReachabilityCheck { method: 'GET', cache: 'no-cache', }) - .then((response) => { - if (!response.ok) { - return Promise.resolve(false); - } - return response - .json() - .then((json) => Promise.resolve(json.jsonCode === 204)) - .catch(() => Promise.resolve(false)); - }) + .then((response) => Promise.resolve(response.status === 204)) .catch(() => Promise.resolve(false)); } From c851a49ecc1b83c3bc642b331d866c78d48804b8 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 12 Apr 2024 15:08:23 +0700 Subject: [PATCH 087/932] modify comment --- src/libs/checkInternetReachability/index.android.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/checkInternetReachability/index.android.ts b/src/libs/checkInternetReachability/index.android.ts index 9e1a0170d3e4..da46623c3e05 100644 --- a/src/libs/checkInternetReachability/index.android.ts +++ b/src/libs/checkInternetReachability/index.android.ts @@ -3,10 +3,9 @@ import type InternetReachabilityCheck from './types'; /** * Although Android supports internet reachability check, it only does on initiating the connection. - * We need to implement a test for a highly-available endpoint to cover the case internet is lost during connection. + * We need to implement a test for a highly-available endpoint in case of lost internet after initiation. */ export default function checkInternetReachability(): InternetReachabilityCheck { - // Using the API url ensures reachability is tested over internet return fetch(CONST.GOOGLE_CLOUD_URL, { method: 'GET', cache: 'no-cache', From bc8db3bb0d71df4415b80ebe1a79da7368f084cd Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 12 Apr 2024 11:22:09 +0200 Subject: [PATCH 088/932] Refactor FullScreenNavigator --- .../Navigators/FullScreenNavigator.tsx | 99 +++++-------------- .../CustomFullScreenRouter.tsx | 2 +- src/libs/Navigation/getTopmostRouteName.ts | 12 +++ .../getTopmostWorkspacesCentralPaneName.ts | 12 --- src/pages/workspace/WorkspaceInitialPage.tsx | 4 +- 5 files changed, 42 insertions(+), 87 deletions(-) create mode 100644 src/libs/Navigation/getTopmostRouteName.ts delete mode 100644 src/libs/Navigation/getTopmostWorkspacesCentralPaneName.ts diff --git a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx index 065e11495a01..42bca6e2de4c 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx @@ -5,12 +5,32 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import createCustomFullScreenNavigator from '@libs/Navigation/AppNavigator/createCustomFullScreenNavigator'; import getRootNavigatorScreenOptions from '@libs/Navigation/AppNavigator/getRootNavigatorScreenOptions'; +import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; const loadWorkspaceInitialPage = () => require('../../../../pages/workspace/WorkspaceInitialPage').default as React.ComponentType; const RootStack = createCustomFullScreenNavigator(); +type Screens = Partial React.ComponentType>>; + +const workspacesScreens = { + [SCREENS.WORKSPACE.PROFILE]: () => require('../../../../pages/workspace/WorkspaceProfilePage').default as React.ComponentType, + [SCREENS.WORKSPACE.CARD]: () => require('../../../../pages/workspace/card/WorkspaceCardPage').default as React.ComponentType, + [SCREENS.WORKSPACE.WORKFLOWS]: () => require('../../../../pages/workspace/workflows/WorkspaceWorkflowsPage').default as React.ComponentType, + [SCREENS.WORKSPACE.REIMBURSE]: () => require('../../../../pages/workspace/reimburse/WorkspaceReimbursePage').default as React.ComponentType, + [SCREENS.WORKSPACE.BILLS]: () => require('../../../../pages/workspace/bills/WorkspaceBillsPage').default as React.ComponentType, + [SCREENS.WORKSPACE.INVOICES]: () => require('../../../../pages/workspace/invoices/WorkspaceInvoicesPage').default as React.ComponentType, + [SCREENS.WORKSPACE.TRAVEL]: () => require('../../../../pages/workspace/travel/WorkspaceTravelPage').default as React.ComponentType, + [SCREENS.WORKSPACE.MEMBERS]: () => require('../../../../pages/workspace/WorkspaceMembersPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING]: () => require('../../../../pages/workspace/accounting/WorkspaceAccountingPage').default as React.ComponentType, + [SCREENS.WORKSPACE.CATEGORIES]: () => require('../../../../pages/workspace/categories/WorkspaceCategoriesPage').default as React.ComponentType, + [SCREENS.WORKSPACE.MORE_FEATURES]: () => require('../../../../pages/workspace/WorkspaceMoreFeaturesPage').default as React.ComponentType, + [SCREENS.WORKSPACE.TAGS]: () => require('../../../../pages/workspace/tags/WorkspaceTagsPage').default as React.ComponentType, + [SCREENS.WORKSPACE.TAXES]: () => require('../../../../pages/workspace/taxes/WorkspaceTaxesPage').default as React.ComponentType, + [SCREENS.WORKSPACE.DISTANCE_RATES]: () => require('../../../../pages/workspace/distanceRates/PolicyDistanceRatesPage').default as React.ComponentType, +} satisfies Screens; + function FullScreenNavigator() { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -24,78 +44,13 @@ function FullScreenNavigator() { options={screenOptions.homeScreen} getComponent={loadWorkspaceInitialPage} /> - require('@pages/workspace/WorkspaceProfilePage').default as React.ComponentType} - /> - require('@pages/workspace/card/WorkspaceCardPage').default as React.ComponentType} - /> - require('@pages/workspace/workflows/WorkspaceWorkflowsPage').default as React.ComponentType} - /> - require('@pages/workspace/reimburse/WorkspaceReimbursePage').default as React.ComponentType} - /> - require('@pages/workspace/bills/WorkspaceBillsPage').default as React.ComponentType} - /> - require('@pages/workspace/invoices/WorkspaceInvoicesPage').default as React.ComponentType} - /> - require('@pages/workspace/travel/WorkspaceTravelPage').default as React.ComponentType} - /> - require('@pages/workspace/WorkspaceMembersPage').default as React.ComponentType} - /> - - require('@pages/workspace/accounting/WorkspaceAccountingPage').default as React.ComponentType} - /> - - require('@pages/workspace/categories/WorkspaceCategoriesPage').default as React.ComponentType} - /> - require('@pages/workspace/WorkspaceMoreFeaturesPage').default as React.ComponentType} - /> - require('@pages/workspace/tags/WorkspaceTagsPage').default as React.ComponentType} - /> - require('@pages/workspace/taxes/WorkspaceTaxesPage').default as React.ComponentType} - /> - require('@pages/workspace/distanceRates/PolicyDistanceRatesPage').default as React.ComponentType} - /> + {Object.entries(workspacesScreens).map(([screenName, componentGetter]) => ( + + ))} ); diff --git a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx index 99e6fb23b8ba..27e976d9be0c 100644 --- a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx @@ -30,7 +30,7 @@ function adaptStateIfNecessary(state: StackState) { // If the screen is wide, there should be at least two screens inside: // - WORKSPACE.INITIAL to cover left pane. - // - WORKSPACES_CENTRAL_PANE to cover central pane. + // - WORKSPACE.PROFILE (first workspace settings screen) to cover central pane. if (!isNarrowLayout) { if (state.routes.length === 1 && state.routes[0].name === SCREENS.WORKSPACE.INITIAL) { // @ts-expect-error Updating read only property diff --git a/src/libs/Navigation/getTopmostRouteName.ts b/src/libs/Navigation/getTopmostRouteName.ts new file mode 100644 index 000000000000..7ae3afaf2cc9 --- /dev/null +++ b/src/libs/Navigation/getTopmostRouteName.ts @@ -0,0 +1,12 @@ +import type {NavigationState, PartialState} from '@react-navigation/native'; + +// Get the name of topmost route in the navigation stack. +function getTopmostRouteName(state: NavigationState | PartialState): string | undefined { + if (!state) { + return; + } + + return state.routes.at(-1)?.name; +} + +export default getTopmostRouteName; diff --git a/src/libs/Navigation/getTopmostWorkspacesCentralPaneName.ts b/src/libs/Navigation/getTopmostWorkspacesCentralPaneName.ts deleted file mode 100644 index ec3159ade059..000000000000 --- a/src/libs/Navigation/getTopmostWorkspacesCentralPaneName.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type {NavigationState, PartialState} from '@react-navigation/native'; - -// Get the name of topmost report in the navigation stack. -function getTopmostWorkspacesCentralPaneName(state: NavigationState | PartialState): string | undefined { - if (!state) { - return; - } - - return state.routes.at(-1)?.name -} - -export default getTopmostWorkspacesCentralPaneName; diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index a6a131f5372c..f1c215447e4e 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -19,7 +19,7 @@ import usePrevious from '@hooks/usePrevious'; import useSingleExecution from '@hooks/useSingleExecution'; import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; -import getTopmostWorkspacesCentralPaneName from '@libs/Navigation/getTopmostWorkspacesCentralPaneName'; +import getTopmostRouteName from '@libs/Navigation/getTopmostRouteName'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; @@ -68,7 +68,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r const hasPolicyCreationError = !!(policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD && policy.errors); const waitForNavigate = useWaitForNavigation(); const {singleExecution, isExecuting} = useSingleExecution(); - const activeRoute = useNavigationState(getTopmostWorkspacesCentralPaneName); + const activeRoute = useNavigationState(getTopmostRouteName); const {translate} = useLocalize(); const {canUseAccountingIntegrations} = usePermissions(); From a451aaf0ea7d7322f4439d78c0701cfae66bf3be Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 12 Apr 2024 11:42:16 +0200 Subject: [PATCH 089/932] Refactor styles in FullScreenNavigator --- .../AppNavigator/Navigators/FullScreenNavigator.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx index 42bca6e2de4c..3b2d64db9778 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx @@ -7,6 +7,7 @@ import createCustomFullScreenNavigator from '@libs/Navigation/AppNavigator/creat import getRootNavigatorScreenOptions from '@libs/Navigation/AppNavigator/getRootNavigatorScreenOptions'; import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; +import useModalScreenOptions from '@libs/Navigation/AppNavigator/ModalStackNavigators/useModalScreenOptions'; const loadWorkspaceInitialPage = () => require('../../../../pages/workspace/WorkspaceInitialPage').default as React.ComponentType; @@ -36,9 +37,11 @@ function FullScreenNavigator() { const StyleUtils = useStyleUtils(); const {isSmallScreenWidth} = useWindowDimensions(); const screenOptions = getRootNavigatorScreenOptions(isSmallScreenWidth, styles, StyleUtils); + const workspaceScreenOptions = useModalScreenOptions((screenStyles) => ({cardStyle: screenStyles.navigationScreenCardStyle, headerShown: false})); + return ( - + Date: Fri, 12 Apr 2024 11:42:44 +0200 Subject: [PATCH 090/932] Remove WorkspaceSettingsModalStackNavigator --- .../WorkspaceSettingsModalStackNavigator.tsx | 89 ------------------- .../ModalStackNavigators/index.tsx | 2 - 2 files changed, 91 deletions(-) delete mode 100644 src/libs/Navigation/AppNavigator/ModalStackNavigators/WorkspaceSettingsModalStackNavigator.tsx diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/WorkspaceSettingsModalStackNavigator.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/WorkspaceSettingsModalStackNavigator.tsx deleted file mode 100644 index 2dce4247c7ae..000000000000 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/WorkspaceSettingsModalStackNavigator.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import {createStackNavigator} from '@react-navigation/stack'; -import React from 'react'; -import SCREENS from '@src/SCREENS'; -import useModalScreenOptions from './useModalScreenOptions'; - -const StackNavigator = createStackNavigator(); - -function WorkspaceSettingsModalStackNavigator() { - const screenOptions = useModalScreenOptions((styles) => ({cardStyle: styles.navigationScreenCardStyle, headerShown: false})); - - return ( - - require('@pages/workspace/WorkspaceProfilePage').default as React.ComponentType} - /> - require('@pages/workspace/card/WorkspaceCardPage').default as React.ComponentType} - /> - require('@pages/workspace/workflows/WorkspaceWorkflowsPage').default as React.ComponentType} - /> - require('@pages/workspace/reimburse/WorkspaceReimbursePage').default as React.ComponentType} - /> - require('@pages/workspace/bills/WorkspaceBillsPage').default as React.ComponentType} - /> - require('@pages/workspace/invoices/WorkspaceInvoicesPage').default as React.ComponentType} - /> - require('@pages/workspace/travel/WorkspaceTravelPage').default as React.ComponentType} - /> - require('@pages/workspace/WorkspaceMembersPage').default as React.ComponentType} - /> - - require('@pages/workspace/accounting/WorkspaceAccountingPage').default as React.ComponentType} - /> - - require('@pages/workspace/categories/WorkspaceCategoriesPage').default as React.ComponentType} - /> - require('@pages/workspace/WorkspaceMoreFeaturesPage').default as React.ComponentType} - /> - require('@pages/workspace/tags/WorkspaceTagsPage').default as React.ComponentType} - /> - require('@pages/workspace/taxes/WorkspaceTaxesPage').default as React.ComponentType} - /> - require('@pages/workspace/distanceRates/PolicyDistanceRatesPage').default as React.ComponentType} - /> - - ); -} - -export default WorkspaceSettingsModalStackNavigator; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index c251f8143631..e8a5f5d17cb5 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -35,7 +35,6 @@ import type {ThemeStyles} from '@styles/index'; import type {Screen} from '@src/SCREENS'; import SCREENS from '@src/SCREENS'; import useModalScreenOptions from './useModalScreenOptions'; -import WorkspaceSettingsModalStackNavigator from './WorkspaceSettingsModalStackNavigator'; type Screens = Partial React.ComponentType>>; @@ -345,5 +344,4 @@ export { TaskModalStackNavigator, WalletStatementStackNavigator, ProcessMoneyRequestHoldStackNavigator, - WorkspaceSettingsModalStackNavigator, }; From 0578de4d75897be641adddb942c578f4fff13c23 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 12 Apr 2024 12:48:05 +0200 Subject: [PATCH 091/932] Remove wrapper for lhp screens --- src/SCREENS.ts | 3 --- .../ModalStackNavigators/index.tsx | 18 +++--------------- .../Navigators/FullScreenNavigator.tsx | 2 +- .../Navigators/LeftModalNavigator.tsx | 8 +++++--- src/libs/Navigation/linkingConfig/config.ts | 12 ++---------- src/libs/Navigation/types.ts | 16 +++------------- src/pages/SearchPage/index.tsx | 2 +- src/styles/theme/themes/dark.ts | 2 +- src/styles/theme/themes/light.ts | 2 +- tests/perf-test/SearchPage.perf-test.tsx | 2 +- 10 files changed, 18 insertions(+), 49 deletions(-) diff --git a/src/SCREENS.ts b/src/SCREENS.ts index b3c2012e90d2..7b653da0ea29 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -98,9 +98,6 @@ const SCREENS = { SEARCH: 'Search', WORKSPACE_SWITCHER: 'WorkspaceSwitcher', }, - WORKSPACE_SWITCHER: { - ROOT: 'WorkspaceSwitcher_Root', - }, RIGHT_MODAL: { SETTINGS: 'Settings', NEW_CHAT: 'NewChat', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index e8a5f5d17cb5..94298d24f37e 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -22,14 +22,12 @@ import type { ReportSettingsNavigatorParamList, RoomInviteNavigatorParamList, RoomMembersNavigatorParamList, - SearchNavigatorParamList, SettingsNavigatorParamList, SignInNavigatorParamList, SplitDetailsNavigatorParamList, TaskDetailsNavigatorParamList, TeachersUniteNavigatorParamList, WalletStatementNavigatorParamList, - WorkspaceSwitcherNavigatorParamList, } from '@navigation/types'; import type {ThemeStyles} from '@styles/index'; import type {Screen} from '@src/SCREENS'; @@ -142,10 +140,6 @@ const RoomInviteModalStackNavigator = createModalStackNavigator require('../../../../pages/RoomInvitePage').default as React.ComponentType, }); -const SearchModalStackNavigator = createModalStackNavigator({ - [SCREENS.SEARCH_ROOT]: () => require('../../../../pages/SearchPage').default as React.ComponentType, -}); - const NewChatModalStackNavigator = createModalStackNavigator({ [SCREENS.NEW_CHAT.ROOT]: () => require('../../../../pages/NewChatSelectorPage').default as React.ComponentType, [SCREENS.NEW_CHAT.NEW_CHAT_CONFIRM]: () => require('../../../../pages/NewChatConfirmPage').default as React.ComponentType, @@ -173,10 +167,6 @@ const NewTeachersUniteNavigator = createModalStackNavigator require('../../../../pages/TeachersUnite/ImTeacherPage').default as React.ComponentType, }); -const WorkspaceSwitcherModalStackNavigator = createModalStackNavigator({ - [SCREENS.WORKSPACE_SWITCHER.ROOT]: () => require('../../../../pages/WorkspaceSwitcherPage').default as React.ComponentType, -}); - const SettingsModalStackNavigator = createModalStackNavigator({ [SCREENS.SETTINGS.SHARE_CODE]: () => require('../../../../pages/ShareCodePage').default as React.ComponentType, [SCREENS.SETTINGS.PROFILE.PRONOUNS]: () => require('../../../../pages/settings/Profile/PronounsPage').default as React.ComponentType, @@ -318,7 +308,6 @@ const ProcessMoneyRequestHoldStackNavigator = createModalStackNavigator({ export { AddPersonalBankAccountModalStackNavigator, DetailsModalStackNavigator, - OnboardEngagementModalStackNavigator, EditRequestStackNavigator, EnablePaymentsStackNavigator, FlagCommentStackNavigator, @@ -326,22 +315,21 @@ export { NewChatModalStackNavigator, NewTaskModalStackNavigator, NewTeachersUniteNavigator, + OnboardEngagementModalStackNavigator, PrivateNotesModalStackNavigator, + ProcessMoneyRequestHoldStackNavigator, ProfileModalStackNavigator, ReferralModalStackNavigator, - WorkspaceSwitcherModalStackNavigator, ReimbursementAccountModalStackNavigator, + ReportDescriptionModalStackNavigator, ReportDetailsModalStackNavigator, ReportParticipantsModalStackNavigator, ReportSettingsModalStackNavigator, - ReportDescriptionModalStackNavigator, RoomInviteModalStackNavigator, RoomMembersModalStackNavigator, - SearchModalStackNavigator, SettingsModalStackNavigator, SignInModalStackNavigator, SplitDetailsModalStackNavigator, TaskModalStackNavigator, WalletStatementStackNavigator, - ProcessMoneyRequestHoldStackNavigator, }; diff --git a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx index 3b2d64db9778..f6fdba422fae 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx @@ -5,9 +5,9 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import createCustomFullScreenNavigator from '@libs/Navigation/AppNavigator/createCustomFullScreenNavigator'; import getRootNavigatorScreenOptions from '@libs/Navigation/AppNavigator/getRootNavigatorScreenOptions'; +import useModalScreenOptions from '@libs/Navigation/AppNavigator/ModalStackNavigators/useModalScreenOptions'; import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; -import useModalScreenOptions from '@libs/Navigation/AppNavigator/ModalStackNavigators/useModalScreenOptions'; const loadWorkspaceInitialPage = () => require('../../../../pages/workspace/WorkspaceInitialPage').default as React.ComponentType; diff --git a/src/libs/Navigation/AppNavigator/Navigators/LeftModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/LeftModalNavigator.tsx index 8f76d8fbdd7b..f83f0d7f0d59 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/LeftModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/LeftModalNavigator.tsx @@ -6,7 +6,6 @@ import NoDropZone from '@components/DragAndDrop/NoDropZone'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import ModalNavigatorScreenOptions from '@libs/Navigation/AppNavigator/ModalNavigatorScreenOptions'; -import * as ModalStackNavigators from '@libs/Navigation/AppNavigator/ModalStackNavigators'; import type {AuthScreensParamList, LeftModalNavigatorParamList} from '@libs/Navigation/types'; import type NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; @@ -14,6 +13,9 @@ import Overlay from './Overlay'; type LeftModalNavigatorProps = StackScreenProps; +const loadSearchPage = () => require('../../../../pages/SearchPage').default as React.ComponentType; +const loadWorkspaceSwitcherPage = () => require('../../../../pages/WorkspaceSwitcherPage').default as React.ComponentType; + const Stack = createStackNavigator(); function LeftModalNavigator({navigation}: LeftModalNavigatorProps) { @@ -33,11 +35,11 @@ function LeftModalNavigator({navigation}: LeftModalNavigatorProps) { diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 468a5ce8c3d9..3e14d0f01ac1 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -67,17 +67,9 @@ const config: LinkingOptions['config'] = { [SCREENS.NOT_FOUND]: '*', [NAVIGATORS.LEFT_MODAL_NAVIGATOR]: { screens: { - [SCREENS.LEFT_MODAL.SEARCH]: { - screens: { - [SCREENS.SEARCH_ROOT]: ROUTES.SEARCH, - }, - }, + [SCREENS.LEFT_MODAL.SEARCH]: ROUTES.SEARCH, [SCREENS.LEFT_MODAL.WORKSPACE_SWITCHER]: { - screens: { - [SCREENS.WORKSPACE_SWITCHER.ROOT]: { - path: ROUTES.WORKSPACE_SWITCHER, - }, - }, + path: ROUTES.WORKSPACE_SWITCHER, }, }, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index c51be87b4191..c8587b2b84c6 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -62,10 +62,6 @@ type CentralPaneNavigatorParamList = { [SCREENS.SETTINGS.SAVE_THE_WORLD]: undefined; }; -type WorkspaceSwitcherNavigatorParamList = { - [SCREENS.WORKSPACE_SWITCHER.ROOT]: undefined; -}; - type BackToParams = { backTo?: Routes; }; @@ -294,10 +290,6 @@ type NewChatNavigatorParamList = { [SCREENS.NEW_CHAT.ROOT]: undefined; }; -type SearchNavigatorParamList = { - [SCREENS.SEARCH_ROOT]: undefined; -}; - type DetailsNavigatorParamList = { [SCREENS.DETAILS_ROOT]: { login: string; @@ -568,8 +560,8 @@ type PrivateNotesNavigatorParamList = { }; type LeftModalNavigatorParamList = { - [SCREENS.LEFT_MODAL.SEARCH]: NavigatorScreenParams; - [SCREENS.LEFT_MODAL.WORKSPACE_SWITCHER]: NavigatorScreenParams; + [SCREENS.LEFT_MODAL.SEARCH]: undefined; + [SCREENS.LEFT_MODAL.WORKSPACE_SWITCHER]: undefined; }; type RightModalNavigatorParamList = { @@ -798,7 +790,7 @@ type AuthScreensParamList = SharedScreensParamList & { }; }; -type RootStackParamList = PublicScreensParamList & AuthScreensParamList & SearchNavigatorParamList; +type RootStackParamList = PublicScreensParamList & AuthScreensParamList & LeftModalNavigatorParamList; type BottomTabName = keyof BottomTabNavigatorParamList; @@ -842,7 +834,6 @@ export type { ParticipantsNavigatorParamList, RoomMembersNavigatorParamList, RoomInviteNavigatorParamList, - SearchNavigatorParamList, NewChatNavigatorParamList, NewTaskNavigatorParamList, TeachersUniteNavigatorParamList, @@ -857,7 +848,6 @@ export type { ReferralDetailsNavigatorParamList, ReimbursementAccountNavigatorParamList, State, - WorkspaceSwitcherNavigatorParamList, OnboardEngagementNavigatorParamList, SwitchPolicyIDParams, FullScreenNavigatorParamList, diff --git a/src/pages/SearchPage/index.tsx b/src/pages/SearchPage/index.tsx index 7d2a5bfecbb8..99c2b084aec0 100644 --- a/src/pages/SearchPage/index.tsx +++ b/src/pages/SearchPage/index.tsx @@ -35,7 +35,7 @@ type SearchPageOnyxProps = { isSearchingForReports: OnyxEntry; }; -type SearchPageProps = SearchPageOnyxProps & StackScreenProps; +type SearchPageProps = SearchPageOnyxProps & StackScreenProps; type Options = OptionsListUtils.Options & {headerMessage: string}; diff --git a/src/styles/theme/themes/dark.ts b/src/styles/theme/themes/dark.ts index e43341f3e8a9..2c3e36a584a2 100644 --- a/src/styles/theme/themes/dark.ts +++ b/src/styles/theme/themes/dark.ts @@ -128,7 +128,7 @@ const darkTheme = { backgroundColor: colors.productDark100, statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, }, - [SCREENS.WORKSPACE_SWITCHER.ROOT]: { + [SCREENS.LEFT_MODAL.WORKSPACE_SWITCHER]: { backgroundColor: colors.productDark100, statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, }, diff --git a/src/styles/theme/themes/light.ts b/src/styles/theme/themes/light.ts index 6444cb9d4073..716deb17893c 100644 --- a/src/styles/theme/themes/light.ts +++ b/src/styles/theme/themes/light.ts @@ -128,7 +128,7 @@ const lightTheme = { backgroundColor: colors.productLight100, statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, }, - [SCREENS.WORKSPACE_SWITCHER.ROOT]: { + [SCREENS.LEFT_MODAL.WORKSPACE_SWITCHER]: { backgroundColor: colors.productLight100, statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, }, diff --git a/tests/perf-test/SearchPage.perf-test.tsx b/tests/perf-test/SearchPage.perf-test.tsx index ea759a1201b2..29bb016230e7 100644 --- a/tests/perf-test/SearchPage.perf-test.tsx +++ b/tests/perf-test/SearchPage.perf-test.tsx @@ -116,7 +116,7 @@ afterEach(() => { PusherHelper.teardown(); }); -type SearchPageProps = StackScreenProps & { +type SearchPageProps = StackScreenProps & { betas: OnyxEntry; reports: OnyxCollection; isSearchingForReports: OnyxEntry; From 1142478d389bcafdbb5868e826c2c47110c4abd4 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Fri, 12 Apr 2024 17:08:49 +0300 Subject: [PATCH 092/932] fix light box offline indicator --- src/components/Lightbox/index.tsx | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx index 86a52c2baf6c..909c4d942e28 100644 --- a/src/components/Lightbox/index.tsx +++ b/src/components/Lightbox/index.tsx @@ -2,12 +2,14 @@ import React, {useCallback, useContext, useEffect, useMemo, useState} from 'reac import type {LayoutChangeEvent, StyleProp, ViewStyle} from 'react-native'; import {ActivityIndicator, PixelRatio, StyleSheet, View} from 'react-native'; import {useSharedValue} from 'react-native-reanimated'; +import AttachmentOfflineIndicator from '@components/AttachmentOfflineIndicator'; import AttachmentCarouselPagerContext from '@components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext'; import Image from '@components/Image'; import type {ImageOnLoadEvent} from '@components/Image/types'; import MultiGestureCanvas, {DEFAULT_ZOOM_RANGE} from '@components/MultiGestureCanvas'; import type {CanvasSize, ContentSize, OnScaleChangedCallback, ZoomRange} from '@components/MultiGestureCanvas/types'; import {getCanvasFitScale} from '@components/MultiGestureCanvas/utils'; +import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import NUMBER_OF_CONCURRENT_LIGHTBOXES from './numberOfConcurrentLightboxes'; @@ -47,6 +49,7 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan * we need to create a shared value that can be used in the render function. */ const isPagerScrollingFallback = useSharedValue(false); + const {isOffline} = useNetwork(); const attachmentCarouselPagerContext = useContext(AttachmentCarouselPagerContext); const { @@ -219,9 +222,9 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan style={[contentSize ?? styles.invisibleImage]} isAuthTokenRequired={isAuthTokenRequired} onError={onError} - onLoad={updateContentSize} - onLoadEnd={() => { + onLoad={(e) => { setLightboxImageLoaded(true); + updateContentSize(e); }} /> @@ -236,21 +239,23 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan resizeMode="contain" style={[fallbackSize ?? styles.invisibleImage]} isAuthTokenRequired={isAuthTokenRequired} - onLoad={updateContentSize} - onLoadEnd={() => setFallbackImageLoaded(true)} + onLoad={(e) => { + setFallbackImageLoaded(true); + updateContentSize(e); + }} /> )} - - {/* Show activity indicator while the lightbox is still loading the image. */} - {isLoading && ( - - )} )} + {/* Show activity or offline indicator (based on the connection status) while the lightbox is still loading the image. */} + {isLoading && !isOffline && ( + + )} + {isLoading && } ); } From df8edb102940b5e5c1d51437fd0c49b5cfff5c3f Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Fri, 12 Apr 2024 22:39:25 +0300 Subject: [PATCH 093/932] fix cached image case offline indicator --- src/components/Lightbox/index.tsx | 64 +++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx index 909c4d942e28..a6b30f1e6815 100644 --- a/src/components/Lightbox/index.tsx +++ b/src/components/Lightbox/index.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useContext, useEffect, useMemo, useState} from 'react'; +import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; import type {LayoutChangeEvent, StyleProp, ViewStyle} from 'react-native'; import {ActivityIndicator, PixelRatio, StyleSheet, View} from 'react-native'; import {useSharedValue} from 'react-native-reanimated'; @@ -133,10 +133,12 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan const indexOutOfRange = page > activePage + indexCanvasOffset || page < activePage - indexCanvasOffset; return !indexOutOfRange; }, [activePage, hasSiblingCarouselItems, page]); - const [isLightboxImageLoaded, setLightboxImageLoaded] = useState(false); + const [isLightboxImageLoading, setLightboxImageLoading] = useState(false); + const isLightboxImageLoaded = useRef(false); const [isFallbackVisible, setFallbackVisible] = useState(!isLightboxVisible); - const [isFallbackImageLoaded, setFallbackImageLoaded] = useState(false); + const [isFallbackImageLoading, setFallbackImageLoading] = useState(false); + const isFallbackImageLoaded = useRef(false); const fallbackSize = useMemo(() => { if (!hasSiblingCarouselItems || !contentSize || isCanvasLoading) { return undefined; @@ -154,18 +156,19 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan // until the fallback gets hidden so that we don't see two overlapping images at the same time. // If there the Lightbox is not used within a carousel, we don't need to hide the Lightbox, // because it's only going to be rendered after the fallback image is hidden. - const shouldShowLightbox = isLightboxImageLoaded && !isFallbackVisible; + const shouldShowLightbox = isLightboxImageLoaded.current && !isFallbackVisible; - const isFallbackStillLoading = isFallbackVisible && !isFallbackImageLoaded; - const isLightboxStillLoading = isLightboxVisible && !isLightboxImageLoaded; - const isLoading = isActive && (isCanvasLoading || isFallbackStillLoading || isLightboxStillLoading); + const isFallbackStillLoading = isFallbackVisible && isFallbackImageLoading; + const isLightboxStillLoading = isLightboxVisible && isLightboxImageLoading; + const isLoading = isActive && (isFallbackStillLoading || isLightboxStillLoading); // Resets the lightbox when it becomes inactive useEffect(() => { if (isLightboxVisible) { return; } - setLightboxImageLoaded(false); + isLightboxImageLoaded.current = false; + setLightboxImageLoading(false); setContentSize(undefined); }, [isLightboxVisible, setContentSize]); @@ -177,9 +180,10 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan } // When the carousel item is active and the lightbox has finished loading, we want to hide the fallback image - if (isActive && isFallbackVisible && isLightboxVisible && isLightboxImageLoaded) { + if (isActive && isFallbackVisible && isLightboxVisible && isLightboxImageLoaded.current) { setFallbackVisible(false); - setFallbackImageLoaded(false); + setFallbackImageLoading(false); + isFallbackImageLoaded.current = false; return; } @@ -187,7 +191,7 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan if (!isActive && !isLightboxVisible) { setFallbackVisible(true); } - }, [hasSiblingCarouselItems, isActive, isFallbackVisible, isLightboxImageLoaded, isLightboxVisible]); + }, [hasSiblingCarouselItems, isActive, isFallbackVisible, isLightboxImageLoading, isLightboxVisible]); const scaleChange = useCallback( (scale: number) => { @@ -223,9 +227,18 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan isAuthTokenRequired={isAuthTokenRequired} onError={onError} onLoad={(e) => { - setLightboxImageLoaded(true); + isLightboxImageLoaded.current = true; + setLightboxImageLoading(false); updateContentSize(e); }} + onLoadStart={() => { + setTimeout(() => { + if (isLightboxImageLoaded.current) { + return; + } + setLightboxImageLoading(true); + }, 200); + }} /> @@ -240,22 +253,31 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan style={[fallbackSize ?? styles.invisibleImage]} isAuthTokenRequired={isAuthTokenRequired} onLoad={(e) => { - setFallbackImageLoaded(true); + setFallbackImageLoading(false); + isFallbackImageLoaded.current = true; updateContentSize(e); }} + onLoadStart={() => { + setTimeout(() => { + if (isFallbackImageLoaded.current) { + return; + } + setFallbackImageLoading(true); + }, 200); + }} /> )} + {/* Show activity or offline indicator (based on the connection status) while the lightbox is still loading the image. */} + {isLoading && !isOffline && ( + + )} + {isLoading && } )} - {/* Show activity or offline indicator (based on the connection status) while the lightbox is still loading the image. */} - {isLoading && !isOffline && ( - - )} - {isLoading && } ); } From ae46fdf8eb05d8ed6084cba1c66b521379b275d0 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Mon, 15 Apr 2024 11:23:57 +0200 Subject: [PATCH 094/932] Add warning to useActiveWorkspaceFromNavigationState --- src/hooks/useActiveWorkspaceFromNavigationState.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/hooks/useActiveWorkspaceFromNavigationState.ts b/src/hooks/useActiveWorkspaceFromNavigationState.ts index 63a6865a516b..5b117816d40f 100644 --- a/src/hooks/useActiveWorkspaceFromNavigationState.ts +++ b/src/hooks/useActiveWorkspaceFromNavigationState.ts @@ -1,12 +1,21 @@ import {useNavigationState} from '@react-navigation/native'; +import Log from '@libs/Log'; import type {BottomTabNavigatorParamList} from '@libs/Navigation/types'; +import SCREENS from '@src/SCREENS'; /** * Get the currently selected policy ID stored in the navigation state. This hook should only be called only from screens in BottomTab. */ function useActiveWorkspaceFromNavigationState() { // The last policyID value is always stored in the last route in BottomTabNavigator. - const activeWorkpsaceID = useNavigationState((state) => state.routes.at(-1)?.params?.policyID); + const activeWorkpsaceID = useNavigationState((state) => { + // SCREENS.HOME is a screen located in the BottomTabNavigator, if it's not in state.routeNames it means that this hook was called from a screen in another navigator. + if (!state.routeNames.includes(SCREENS.HOME)) { + Log.warn('useActiveWorkspaceFromNavigationState should be called only from BottomTab screens'); + } + + return state.routes.at(-1)?.params?.policyID; + }); return activeWorkpsaceID; } From fea7ada105b4d1cbc82e9ff5ebf723e9b40c07b9 Mon Sep 17 00:00:00 2001 From: BrtqKr Date: Mon, 15 Apr 2024 11:56:37 +0200 Subject: [PATCH 095/932] fix typography --- src/components/MoneyRequestHeader.tsx | 8 ++++---- .../MoneyRequestPreview/MoneyRequestPreviewContent.tsx | 8 ++++++-- src/components/ReportActionItem/ReportPreview.tsx | 8 ++++++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 5ea46f339d46..7557799472e1 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -188,8 +188,8 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, title={ } @@ -202,8 +202,8 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, title={ } diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index 08d693dd109a..63950e3fe8d0 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -313,7 +313,9 @@ function MoneyRequestPreviewContent({ width={variables.iconSizeExtraSmall} fill={theme.textSupporting} /> - {translate('iou.receiptScanInProgress')} + + {translate('iou.receiptScanInProgress')} + )} {isPending && ( @@ -324,7 +326,9 @@ function MoneyRequestPreviewContent({ width={variables.iconSizeExtraSmall} fill={theme.textSupporting} /> - {translate('iou.transactionPending')} + + {translate('iou.transactionPending')} + )} diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 231b1176423b..2b5034708dac 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -330,7 +330,9 @@ function ReportPreview({ width={variables.iconSizeExtraSmall} fill={theme.textSupporting} /> - {translate('iou.receiptScanInProgress')} + + {translate('iou.receiptScanInProgress')} + )} {shouldShowPendingSubtitle && ( @@ -341,7 +343,9 @@ function ReportPreview({ width={variables.iconSizeExtraSmall} fill={theme.textSupporting} /> - {translate('iou.transactionPending')} + + {translate('iou.transactionPending')} + )} From f0835125dda095f8088b9c5df5d75141658e6118 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Mon, 15 Apr 2024 13:39:56 +0300 Subject: [PATCH 096/932] fix android loading indicator case --- src/components/Lightbox/index.tsx | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx index a6b30f1e6815..e35dc1703b36 100644 --- a/src/components/Lightbox/index.tsx +++ b/src/components/Lightbox/index.tsx @@ -201,6 +201,19 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan [onScaleChangedContext, onScaleChangedProp], ); + useEffect(() => { + // To avoid showing loading or offline indicator for cached images we set loading + // states after a 200 ms delay based on whether the image is loaded or not by then. + setTimeout(() => { + if (!isFallbackImageLoaded.current) { + setFallbackImageLoading(true); + } + if (!isLightboxImageLoaded.current) { + setLightboxImageLoading(true); + } + }, 200); + }, []); + return ( { - setTimeout(() => { - if (isLightboxImageLoaded.current) { - return; - } - setLightboxImageLoading(true); - }, 200); - }} /> @@ -257,14 +262,6 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan isFallbackImageLoaded.current = true; updateContentSize(e); }} - onLoadStart={() => { - setTimeout(() => { - if (isFallbackImageLoaded.current) { - return; - } - setFallbackImageLoading(true); - }, 200); - }} /> )} From 36ccc936df58fd041f097c2e2fe4290ab339d0af Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Mon, 15 Apr 2024 14:09:27 +0300 Subject: [PATCH 097/932] minor fix --- src/components/Lightbox/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx index e35dc1703b36..42467569a904 100644 --- a/src/components/Lightbox/index.tsx +++ b/src/components/Lightbox/index.tsx @@ -168,7 +168,7 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan return; } isLightboxImageLoaded.current = false; - setLightboxImageLoading(false); + setLightboxImageLoading(true); setContentSize(undefined); }, [isLightboxVisible, setContentSize]); From dd4ae04abcefd6e6768ca036bbf32744a02c493c Mon Sep 17 00:00:00 2001 From: BrtqKr Date: Mon, 15 Apr 2024 17:16:42 +0200 Subject: [PATCH 098/932] style fixes --- src/components/MoneyRequestHeader.tsx | 4 ++-- src/components/MoneyRequestHeaderStatusBar.tsx | 2 +- .../MoneyRequestPreviewContent.tsx | 8 ++------ src/components/ReportActionItem/ReportPreview.tsx | 12 ++++-------- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 7557799472e1..c50913bc2d31 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -190,7 +190,7 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, src={Expensicons.CreditCardHourglass} height={variables.iconSizeSmall} width={variables.iconSizeSmall} - fill={theme.textSupporting} + fill={theme.icon} /> } description={translate('iou.transactionPendingDescription')} @@ -204,7 +204,7 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, src={Expensicons.ReceiptScan} height={variables.iconSizeSmall} width={variables.iconSizeSmall} - fill={theme.textSupporting} + fill={theme.icon} /> } description={translate('iou.receiptScanInProgressDescription')} diff --git a/src/components/MoneyRequestHeaderStatusBar.tsx b/src/components/MoneyRequestHeaderStatusBar.tsx index d21e66ba39eb..0052768a4cf0 100644 --- a/src/components/MoneyRequestHeaderStatusBar.tsx +++ b/src/components/MoneyRequestHeaderStatusBar.tsx @@ -25,7 +25,7 @@ function MoneyRequestHeaderStatusBar({title, description, shouldShowBorderBottom {title} ) : ( - {title} + {title} )} {description} diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index 63950e3fe8d0..c234eb749653 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -313,9 +313,7 @@ function MoneyRequestPreviewContent({ width={variables.iconSizeExtraSmall} fill={theme.textSupporting} /> - - {translate('iou.receiptScanInProgress')} - + {translate('iou.receiptScanInProgress')} )} {isPending && ( @@ -326,9 +324,7 @@ function MoneyRequestPreviewContent({ width={variables.iconSizeExtraSmall} fill={theme.textSupporting} /> - - {translate('iou.transactionPending')} - + {translate('iou.transactionPending')} )} diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 2b5034708dac..766680fd378b 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -328,11 +328,9 @@ function ReportPreview({ src={Expensicons.ReceiptScan} height={variables.iconSizeExtraSmall} width={variables.iconSizeExtraSmall} - fill={theme.textSupporting} + fill={theme.icon} /> - - {translate('iou.receiptScanInProgress')} - + {translate('iou.receiptScanInProgress')} )} {shouldShowPendingSubtitle && ( @@ -341,11 +339,9 @@ function ReportPreview({ src={Expensicons.CreditCardHourglass} height={variables.iconSizeExtraSmall} width={variables.iconSizeExtraSmall} - fill={theme.textSupporting} + fill={theme.icon} /> - - {translate('iou.transactionPending')} - + {translate('iou.transactionPending')} )} From 75a772eb376f562b9ef16c3ea1b36801344785e5 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Mon, 15 Apr 2024 20:38:35 +0300 Subject: [PATCH 099/932] fix receipt offline indicator case --- src/components/AttachmentModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index 7d13524b78df..3938c2de8559 100644 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -463,7 +463,7 @@ function AttachmentModal({ } const context = useMemo( () => ({ - pagerItems: [], + pagerItems: [{source: sourceForAttachmentView, index: 0, isActive: true}], activePage: 0, pagerRef: undefined, isPagerScrolling: nope, @@ -472,7 +472,7 @@ function AttachmentModal({ onScaleChanged: () => {}, onSwipeDown: closeModal, }), - [closeModal, nope], + [closeModal, nope, sourceForAttachmentView], ); return ( From bd00d41ed94ef5ab1ffc1bb31b4a312cab204351 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Mon, 15 Apr 2024 22:40:59 +0300 Subject: [PATCH 100/932] minor fix --- src/components/Lightbox/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx index 42467569a904..bf5067e81bbb 100644 --- a/src/components/Lightbox/index.tsx +++ b/src/components/Lightbox/index.tsx @@ -168,7 +168,6 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan return; } isLightboxImageLoaded.current = false; - setLightboxImageLoading(true); setContentSize(undefined); }, [isLightboxVisible, setContentSize]); From 92a626574ba95b9dd448df13967533aa41318b6c Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Mon, 15 Apr 2024 22:01:51 +0100 Subject: [PATCH 101/932] Participants onyx migration --- src/libs/migrateOnyx.ts | 2 + src/libs/migrations/Participants.ts | 60 +++++++++++++++++++++++++++++ src/types/onyx/Report.ts | 2 - 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 src/libs/migrations/Participants.ts diff --git a/src/libs/migrateOnyx.ts b/src/libs/migrateOnyx.ts index 412a8e00f052..674b5b8242dc 100644 --- a/src/libs/migrateOnyx.ts +++ b/src/libs/migrateOnyx.ts @@ -2,6 +2,7 @@ import Log from './Log'; import CheckForPreviousReportActionID from './migrations/CheckForPreviousReportActionID'; import KeyReportActionsDraftByReportActionID from './migrations/KeyReportActionsDraftByReportActionID'; import NVPMigration from './migrations/NVPMigration'; +import Participants from './migrations/Participants'; import PronounsMigration from './migrations/PronounsMigration'; import RemoveEmptyReportActionsDrafts from './migrations/RemoveEmptyReportActionsDrafts'; import RenameReceiptFilename from './migrations/RenameReceiptFilename'; @@ -21,6 +22,7 @@ export default function () { RemoveEmptyReportActionsDrafts, NVPMigration, PronounsMigration, + Participants, ]; // Reduce all promises down to a single promise. All promises run in a linear fashion, waiting for the diff --git a/src/libs/migrations/Participants.ts b/src/libs/migrations/Participants.ts new file mode 100644 index 000000000000..0e0cd8722348 --- /dev/null +++ b/src/libs/migrations/Participants.ts @@ -0,0 +1,60 @@ +import Onyx from 'react-native-onyx'; +import type {NullishDeep, OnyxCollection} from 'react-native-onyx'; +import Log from '@libs/Log'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Report} from '@src/types/onyx'; +import type {Participant, Participants} from '@src/types/onyx/Report'; + +type ReportKey = `${typeof ONYXKEYS.COLLECTION.REPORT}${string}`; +type OldReport = Report & {participantAccountIDs?: number[]; visibleChatMemberAccountIDs?: number[]}; +type OldReportCollection = Record>; + +function getReports(): Promise> { + return new Promise((resolve) => { + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (reports) => { + Onyx.disconnect(connectionID); + return resolve(reports); + }, + }); + }); +} + +export default function (): Promise { + return getReports().then((reports) => { + if (!reports) { + Log.info('[Migrate Onyx] Skipped Participants migration because there are no reports'); + return; + } + + const collection = Object.entries(reports).reduce((reportsCollection, [onyxKey, report]) => { + // If we have participantAccountIDs then this report is eligible for migration + if (report?.participantAccountIDs) { + const visibleParticipants = new Set(report.visibleChatMemberAccountIDs); + const participants = report.participantAccountIDs.reduce((reportParticipants, accountID) => { + const participant: Participant = { + hidden: !visibleParticipants.has(accountID), + }; + + // eslint-disable-next-line no-param-reassign + reportParticipants[accountID] = participant; + return reportParticipants; + }, {}); + + // eslint-disable-next-line no-param-reassign + reportsCollection[onyxKey as ReportKey] = { + participants, + participantAccountIDs: null, + visibleChatMemberAccountIDs: null, + }; + } + + return reportsCollection; + }, {}); + + // eslint-disable-next-line rulesdir/prefer-actions-set-data + return Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, collection).then(() => Log.info('[Migrate Onyx] Ran migration Participants successfully')); + }); +} diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index fe3eec6dc11e..b271255b06a6 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -140,8 +140,6 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback< ownerAccountID?: number; ownerEmail?: string; participants?: Participants; - participantAccountIDs?: number[]; - visibleChatMemberAccountIDs?: number[]; total?: number; unheldTotal?: number; currency?: string; From 02b2315b9e91a1b96800045c82d23bedb018b164 Mon Sep 17 00:00:00 2001 From: ntdiary <2471314@gmail.com> Date: Tue, 16 Apr 2024 22:56:51 +0800 Subject: [PATCH 102/932] clean up mobile safari code --- src/libs/updateMultilineInputRange/index.ts | 11 ++---- .../report/ReportActionItemMessageEdit.tsx | 35 ++++--------------- 2 files changed, 9 insertions(+), 37 deletions(-) diff --git a/src/libs/updateMultilineInputRange/index.ts b/src/libs/updateMultilineInputRange/index.ts index f5d71c5e2038..d0bc1c306dac 100644 --- a/src/libs/updateMultilineInputRange/index.ts +++ b/src/libs/updateMultilineInputRange/index.ts @@ -1,4 +1,3 @@ -import * as Browser from '@libs/Browser'; import type UpdateMultilineInputRange from './types'; /** @@ -17,14 +16,10 @@ const updateMultilineInputRange: UpdateMultilineInputRange = (input, shouldAutoF if ('value' in input && input.value && input.setSelectionRange) { const length = input.value.length; - - // For mobile Safari, updating the selection prop on an unfocused input will cause it to automatically gain focus - // and subsequent programmatic focus shifts (e.g., modal focus trap) to show the blue frame (:focus-visible style), - // so we need to ensure that it is only updated after focus. - const shouldSetSelection = !(Browser.isMobileSafari() && !shouldAutoFocus); - if (shouldSetSelection) { - input.setSelectionRange(length, length); + if (!shouldAutoFocus) { + return; } + input.setSelectionRange(length, length); // eslint-disable-next-line no-param-reassign input.scrollTop = input.scrollHeight; } diff --git a/src/pages/home/report/ReportActionItemMessageEdit.tsx b/src/pages/home/report/ReportActionItemMessageEdit.tsx index fc3c92434fc4..8920f789e167 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.tsx +++ b/src/pages/home/report/ReportActionItemMessageEdit.tsx @@ -22,7 +22,6 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import * as Browser from '@libs/Browser'; import * as ComposerUtils from '@libs/ComposerUtils'; import * as EmojiUtils from '@libs/EmojiUtils'; import focusComposerWithDelay from '@libs/focusComposerWithDelay'; @@ -68,7 +67,6 @@ type ReportActionItemMessageEditProps = { const emojiButtonID = 'emojiButton'; const messageEditInput = 'messageEditInput'; -const isMobileSafari = Browser.isMobileSafari(); const shouldUseForcedSelectionRange = shouldUseEmojiPickerSelection(); function ReportActionItemMessageEdit( @@ -84,14 +82,6 @@ function ReportActionItemMessageEdit( const {isSmallScreenWidth} = useWindowDimensions(); const prevDraftMessage = usePrevious(draftMessage); - const getInitialSelection = () => { - if (isMobileSafari) { - return {start: 0, end: 0}; - } - - const length = draftMessage.length; - return {start: length, end: length}; - }; const emojisPresentBefore = useRef([]); const [draft, setDraft] = useState(() => { if (draftMessage) { @@ -99,7 +89,7 @@ function ReportActionItemMessageEdit( } return draftMessage; }); - const [selection, setSelection] = useState(getInitialSelection); + const [selection, setSelection] = useState({start: 0, end: 0}); const [isFocused, setIsFocused] = useState(false); const {hasExceededMaxCommentLength, validateCommentMaxLength} = useHandleExceedMaxCommentLength(); const [modal, setModal] = useState({ @@ -171,23 +161,10 @@ function ReportActionItemMessageEdit( ); useEffect(() => { - // For mobile Safari, updating the selection prop on an unfocused input will cause it to automatically gain focus - // and subsequent programmatic focus shifts (e.g., modal focus trap) to show the blue frame (:focus-visible style), - // so we need to ensure that it is only updated after focus. - if (isMobileSafari) { - setDraft((prevDraft) => { - setSelection({ - start: prevDraft.length, - end: prevDraft.length, - }); - return prevDraft; - }); - - // Scroll content of textInputRef to bottom - if (textInputRef.current) { - textInputRef.current.scrollTop = textInputRef.current.scrollHeight; - } - } + setSelection({ + start: draftMessage.length, + end: draftMessage.length, + }); return () => { InputFocus.callback(() => setIsFocused(false)); @@ -208,7 +185,7 @@ function ReportActionItemMessageEdit( // Show the main composer when the focused message is deleted from another client // to prevent the main composer stays hidden until we swtich to another chat. setShouldShowComposeInputKeyboardAware(true); - }; + } // eslint-disable-next-line react-hooks/exhaustive-deps -- this cleanup needs to be called only on unmount }, [action.reportActionID]); From a743ffcb12f76928915760d1524fc9eb036489b6 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 17 Apr 2024 15:10:05 +0700 Subject: [PATCH 103/932] fix empty chat displayed in focus mode --- src/libs/ReportUtils.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f31b4a780c5a..5b3f0e66ffd8 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4470,11 +4470,23 @@ function buildOptimisticMoneyRequestEntities( return [createdActionForChat, createdActionForIOUReport, iouAction, transactionThread, createdActionForTransactionThread]; } +// Check if the report is empty report +function isEmptyReport(report: OnyxEntry): boolean { + if (!report) { + return true; + } + const lastVisibleMessage = ReportActionsUtils.getLastVisibleMessage(report.reportID); + return !report.lastMessageText && !report.lastMessageTranslationKey && !lastVisibleMessage.lastMessageText && !lastVisibleMessage.lastMessageTranslationKey; +} + function isUnread(report: OnyxEntry): boolean { if (!report) { return false; } + if (isEmptyReport(report)) { + return false; + } // lastVisibleActionCreated and lastReadTime are both datetime strings and can be compared directly const lastVisibleActionCreated = report.lastVisibleActionCreated ?? ''; const lastReadTime = report.lastReadTime ?? ''; @@ -4678,8 +4690,8 @@ function shouldReportBeInOptionList({ if (hasDraftComment || requiresAttentionFromCurrentUser(report)) { return true; } - const lastVisibleMessage = ReportActionsUtils.getLastVisibleMessage(report.reportID); - const isEmptyChat = !report.lastMessageText && !report.lastMessageTranslationKey && !lastVisibleMessage.lastMessageText && !lastVisibleMessage.lastMessageTranslationKey; + + const isEmptyChat = isEmptyReport(report); const canHideReport = shouldHideReport(report, currentReportId); // Include reports if they are pinned From 787ad3b011378172c0bc70d454d962a6a8676e36 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 17 Apr 2024 15:58:57 +0700 Subject: [PATCH 104/932] fix jest test --- tests/unit/SidebarOrderTest.ts | 16 +++++++----- tests/unit/SidebarTest.ts | 2 ++ tests/unit/UnreadIndicatorUpdaterTest.ts | 32 ++++++++++++++++++++---- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/tests/unit/SidebarOrderTest.ts b/tests/unit/SidebarOrderTest.ts index 2758d43fb81e..0b8ec5b1385f 100644 --- a/tests/unit/SidebarOrderTest.ts +++ b/tests/unit/SidebarOrderTest.ts @@ -861,10 +861,10 @@ describe('Sidebar', () => { it('alphabetizes chats', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); - const report1 = LHNTestUtils.getFakeReport([1, 2], 3, true); - const report2 = LHNTestUtils.getFakeReport([3, 4], 2, true); - const report3 = LHNTestUtils.getFakeReport([5, 6], 1, true); - const report4 = LHNTestUtils.getFakeReport([7, 8], 0, true); + const report1 = {...LHNTestUtils.getFakeReport([1, 2], 3, true), lastMessageText: 'test'}; + const report2 = {...LHNTestUtils.getFakeReport([3, 4], 2, true), lastMessageText: 'test'}; + const report3 = {...LHNTestUtils.getFakeReport([5, 6], 1, true), lastMessageText: 'test'}; + const report4 = {...LHNTestUtils.getFakeReport([7, 8], 0, true), lastMessageText: 'test'}; const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, @@ -918,9 +918,13 @@ describe('Sidebar', () => { chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, + lastMessageText: 'test', }; - const report2 = LHNTestUtils.getFakeReport([3, 4], 2, true); - const report3 = LHNTestUtils.getFakeReport([5, 6], 1, true); + const report2 = { + ...LHNTestUtils.getFakeReport([3, 4], 2, true), + lastMessageText: 'test', + }; + const report3 = {...LHNTestUtils.getFakeReport([5, 6], 1, true), lastMessageText: 'test'}; // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS]; diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index 23ea0d377634..75f8fa256c57 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -42,6 +42,7 @@ describe('Sidebar', () => { chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, + lastMessageText: 'test', }; const action = { @@ -94,6 +95,7 @@ describe('Sidebar', () => { chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, + lastMessageText: 'test', }; const action = { ...LHNTestUtils.getFakeReportAction('email1@test.com', 3), diff --git a/tests/unit/UnreadIndicatorUpdaterTest.ts b/tests/unit/UnreadIndicatorUpdaterTest.ts index a5f58b57793a..22141eee791d 100644 --- a/tests/unit/UnreadIndicatorUpdaterTest.ts +++ b/tests/unit/UnreadIndicatorUpdaterTest.ts @@ -6,9 +6,23 @@ describe('UnreadIndicatorUpdaterTest', () => { describe('should return correct number of unread reports', () => { it('given last read time < last visible action created', () => { const reportsToBeUsed = { - 1: {reportID: '1', reportName: 'test', type: CONST.REPORT.TYPE.EXPENSE, lastReadTime: '2023-07-08 07:15:44.030', lastVisibleActionCreated: '2023-08-08 07:15:44.030'}, - 2: {reportID: '2', reportName: 'test', type: CONST.REPORT.TYPE.TASK, lastReadTime: '2023-02-05 09:12:05.000', lastVisibleActionCreated: '2023-02-06 07:15:44.030'}, - 3: {reportID: '3', reportName: 'test', type: CONST.REPORT.TYPE.TASK}, + 1: { + reportID: '1', + reportName: 'test', + type: CONST.REPORT.TYPE.EXPENSE, + lastReadTime: '2023-07-08 07:15:44.030', + lastVisibleActionCreated: '2023-08-08 07:15:44.030', + lastMessageText: 'test', + }, + 2: { + reportID: '2', + reportName: 'test', + type: CONST.REPORT.TYPE.TASK, + lastReadTime: '2023-02-05 09:12:05.000', + lastVisibleActionCreated: '2023-02-06 07:15:44.030', + lastMessageText: 'test', + }, + 3: {reportID: '3', reportName: 'test', type: CONST.REPORT.TYPE.TASK, lastMessageText: 'test'}, }; expect(getUnreadReportsForUnreadIndicator(reportsToBeUsed, '3').length).toBe(2); }); @@ -31,9 +45,17 @@ describe('UnreadIndicatorUpdaterTest', () => { notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, lastReadTime: '2023-07-08 07:15:44.030', lastVisibleActionCreated: '2023-08-08 07:15:44.030', + lastMessageText: 'test', + }, + 2: { + reportID: '2', + reportName: 'test', + type: CONST.REPORT.TYPE.TASK, + lastReadTime: '2023-02-05 09:12:05.000', + lastVisibleActionCreated: '2023-02-06 07:15:44.030', + lastMessageText: 'test', }, - 2: {reportID: '2', reportName: 'test', type: CONST.REPORT.TYPE.TASK, lastReadTime: '2023-02-05 09:12:05.000', lastVisibleActionCreated: '2023-02-06 07:15:44.030'}, - 3: {reportID: '3', reportName: 'test', type: CONST.REPORT.TYPE.TASK}, + 3: {reportID: '3', reportName: 'test', type: CONST.REPORT.TYPE.TASK, lastMessageText: 'test'}, }; expect(getUnreadReportsForUnreadIndicator(reportsToBeUsed, '3').length).toBe(1); }); From 68d480ddc07f37d0644c9bfe4d42f62abf195093 Mon Sep 17 00:00:00 2001 From: smelaa Date: Wed, 17 Apr 2024 14:31:32 +0200 Subject: [PATCH 105/932] Display rter violation in MoneyRequestHeader & ReportPreview --- src/components/MoneyRequestHeader.tsx | 66 ++++++++++++------- .../ReportActionItem/ReportPreview.tsx | 21 +++++- src/languages/en.ts | 2 + src/languages/es.ts | 2 + src/libs/TransactionUtils.ts | 24 ++++++- src/types/onyx/TransactionViolation.ts | 1 + 6 files changed, 88 insertions(+), 28 deletions(-) diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 2f80cf3c6e59..a7511b44f049 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -1,7 +1,7 @@ import React, {useCallback, useEffect, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -16,7 +16,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, ReportActions, Session, Transaction} from '@src/types/onyx'; +import type {Policy, Report, ReportAction, ReportActions, Session, Transaction, TransactionViolations} from '@src/types/onyx'; import type {OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import ConfirmModal from './ConfirmModal'; import HeaderWithBackButton from './HeaderWithBackButton'; @@ -37,6 +37,9 @@ type MoneyRequestHeaderOnyxProps = { /** All the data for the transaction */ transaction: OnyxEntry; + /** The violations of the transaction */ + transactionViolations: OnyxCollection; + /** All report actions */ // eslint-disable-next-line react/no-unused-prop-types parentReportActions: OnyxEntry; @@ -56,7 +59,7 @@ type MoneyRequestHeaderProps = MoneyRequestHeaderOnyxProps & { parentReportAction: OnyxEntry; }; -function MoneyRequestHeader({session, parentReport, report, parentReportAction, transaction, shownHoldUseExplanation = false, policy}: MoneyRequestHeaderProps) { +function MoneyRequestHeader({session, parentReport, report, parentReportAction, transactionViolations, transaction, shownHoldUseExplanation = false, policy}: MoneyRequestHeaderProps) { const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); @@ -86,9 +89,6 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, setIsDeleteModalVisible(false); }, [parentReport?.reportID, parentReportAction, setIsDeleteModalVisible]); - const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction); - const isPending = TransactionUtils.isExpensifyCardTransaction(transaction) && TransactionUtils.isPending(transaction); - const isDeletedParentAction = ReportActionsUtils.isDeletedAction(parentReportAction); const canHoldOrUnholdRequest = !isSettled && !isApproved && !isDeletedParentAction; @@ -106,6 +106,21 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, } }; + const getPendingType = () => { + if (TransactionUtils.isExpensifyCardTransaction(transaction) && TransactionUtils.isPending(transaction)) { + return {pendingType: 'PENDING', pendingTitle: translate('iou.pending'), pendingDescription: translate('iou.transactionPendingText')}; + } + if (TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction)) { + return {pendingType: 'SCANNING', pendingTitle: ReceiptScan, pendingDescription: translate('iou.receiptScanInProgressDescription')}; + } + if (TransactionUtils.hasPendingRTERViolation(TransactionUtils.getTransactionViolations(transaction?.transactionID ?? '', transactionViolations))) { + return {pendingType: 'RTER', pendingTitle: Expensicons.Hourglass, pendingDescription: translate('iou.pendingMatchWithCreditCardDescription')}; + } + return {}; + }; + + const {pendingType, pendingTitle, pendingDescription} = getPendingType(); + useEffect(() => { if (canDeleteRequest) { return; @@ -170,7 +185,7 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, <> Navigation.goBack(undefined, false, true)} /> - {isPending && ( - - )} - {isScanning && ( + {pendingType && ( + typeof pendingTitle === 'string' ? ( + pendingTitle + ) : ( + + ) } - description={translate('iou.receiptScanInProgressDescription')} + description={pendingDescription} shouldShowBorderBottom /> )} @@ -230,7 +242,7 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, MoneyRequestHeader.displayName = 'MoneyRequestHeader'; -const MoneyRequestHeaderWithTransaction = withOnyx>({ +const MoneyRequestHeaderWithTransaction = withOnyx>({ transaction: { key: ({report, parentReportActions}) => { const parentReportAction = (report.parentReportActionID && parentReportActions ? parentReportActions[report.parentReportActionID] : {}) as ReportAction & OriginalMessageIOU; @@ -241,9 +253,15 @@ const MoneyRequestHeaderWithTransaction = withOnyx, Omit>({ +export default withOnyx< + Omit, + Omit +>({ session: { key: ONYXKEYS.SESSION, }, diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 190343e48abd..98f4cae7dee4 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -26,6 +26,7 @@ import * as ReportActionUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; +import variables from '@styles/variables'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -140,6 +141,8 @@ function ReportPreview({ const hasErrors = hasMissingSmartscanFields || (canUseViolations && ReportUtils.hasViolations(iouReportID, transactionViolations)) || ReportUtils.hasActionsWithErrors(iouReportID); const lastThreeTransactionsWithReceipts = transactionsWithReceipts.slice(-3); const lastThreeReceipts = lastThreeTransactionsWithReceipts.map((transaction) => ReceiptUtils.getThumbnailAndImageURIs(transaction)); + const showRTERViolationMessage = + allTransactions.length === 1 && TransactionUtils.hasPendingUI(allTransactions[0], TransactionUtils.getTransactionViolations(allTransactions[0].transactionID, transactionViolations)); let formattedMerchant = numberOfRequests === 1 ? TransactionUtils.getMerchant(allTransactions[0]) : null; const formattedDescription = numberOfRequests === 1 ? TransactionUtils.getDescription(allTransactions[0]) : null; @@ -148,7 +151,7 @@ function ReportPreview({ formattedMerchant = null; } - const shouldShowSubmitButton = isOpenExpenseReport && reimbursableSpend !== 0; + const shouldShowSubmitButton = isOpenExpenseReport && reimbursableSpend !== 0 && !showRTERViolationMessage; const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(iouReport); // The submit button should be success green colour only if the user is submitter and the policy does not have Scheduled Submit turned on @@ -235,6 +238,9 @@ function ReportPreview({ const parsedSubtitle = new ExpensiMark().replace(formattedDescription ?? moneyRequestComment); return {isSupportTextHtml: !!parsedSubtitle, supportText: parsedSubtitle ? `${parsedSubtitle}` : ''}; } + // if (true) { + // return {isSupportTextHtml: false, supportText: 'ABC'}; + // } return { isSupportTextHtml: false, supportText: translate('iou.requestCount', { @@ -318,6 +324,19 @@ function ReportPreview({ )} + {showRTERViolationMessage && ( + + + + {translate('iou.pendingMatchWithCreditCard')} + + + )} {shouldShowSettlementButton && ( diff --git a/src/languages/en.ts b/src/languages/en.ts index 314f4f990a49..5667b3fc83e7 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -633,6 +633,8 @@ export default { canceled: 'Canceled', posted: 'Posted', deleteReceipt: 'Delete receipt', + pendingMatchWithCreditCard: 'Receipt pending match with credit card.', + pendingMatchWithCreditCardDescription: 'Receipt pending match with credit card. Marks as cash to ignore and request payment.', routePending: 'Route pending...', receiptScanning: 'Receipt scanning…', receiptScanInProgress: 'Receipt scan in progress.', diff --git a/src/languages/es.ts b/src/languages/es.ts index 387f2b4c1841..4366d61425aa 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -629,6 +629,8 @@ export default { canceled: 'Canceló', posted: 'Contabilizado', deleteReceipt: 'Eliminar recibo', + pendingMatchWithCreditCard: 'Recibo pendiente de conciliar con la tarjeta de crédito.', + pendingMatchWithCreditCardDescription: 'Recibo pendiente de conciliar con tarjeta de crédito. Marcar como efectivo para ignorar y solicitar pago.', routePending: 'Ruta pendiente...', receiptScanning: 'Escaneo en curso…', receiptScanInProgress: 'Escaneo en curso…', diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 89b89ff7b584..a54fa66191a4 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -5,7 +5,7 @@ import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {RecentWaypoint, Report, TaxRate, TaxRates, TaxRatesWithDefault, Transaction, TransactionViolation} from '@src/types/onyx'; +import type {RecentWaypoint, Report, TaxRate, TaxRates, TaxRatesWithDefault, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; import type {Comment, Receipt, TransactionChanges, TransactionPendingFieldsKey, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {IOURequestType} from './actions/IOU'; @@ -514,6 +514,22 @@ function hasMissingSmartscanFields(transaction: OnyxEntry): boolean return Boolean(transaction && !isDistanceRequest(transaction) && !isReceiptBeingScanned(transaction) && areRequiredFieldsEmpty(transaction)); } +/** + * Check if there is pending rter violation in transactionViolations. + */ +function hasPendingRTERViolation(transactionViolations?: TransactionViolations | null): boolean { + return Boolean( + transactionViolations?.some((transactionViolation: TransactionViolation) => transactionViolation.name === CONST.VIOLATIONS.RTER && transactionViolation.data?.pendingPattern), + ); +} + +/** + * Check if the transaction is pending or has a pending rter violation. + */ +function hasPendingUI(transaction: OnyxEntry, transactionViolations?: TransactionViolations | null): boolean { + return Boolean(isReceiptBeingScanned(transaction) || isPending(transaction) || (transaction && hasPendingRTERViolation(transactionViolations))); +} + /** * Check if the transaction has a defined route */ @@ -605,13 +621,13 @@ function isOnHold(transaction: OnyxEntry): boolean { /** * Checks if any violations for the provided transaction are of type 'violation' */ -function hasViolation(transactionID: string, transactionViolations: OnyxCollection): boolean { +function hasViolation(transactionID: string, transactionViolations: OnyxCollection): boolean { return Boolean( transactionViolations?.[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS + transactionID]?.some((violation: TransactionViolation) => violation.type === CONST.VIOLATION_TYPES.VIOLATION), ); } -function getTransactionViolations(transactionID: string, transactionViolations: OnyxCollection): TransactionViolation[] | null { +function getTransactionViolations(transactionID: string, transactionViolations: OnyxCollection | null): TransactionViolations | null { return transactionViolations?.[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS + transactionID] ?? null; } @@ -698,6 +714,8 @@ export { isCreatedMissing, areRequiredFieldsEmpty, hasMissingSmartscanFields, + hasPendingRTERViolation, + hasPendingUI, getWaypointIndex, waypointHasValidAddress, getRecentTransactions, diff --git a/src/types/onyx/TransactionViolation.ts b/src/types/onyx/TransactionViolation.ts index 28de4582bd5e..ab2037ff336a 100644 --- a/src/types/onyx/TransactionViolation.ts +++ b/src/types/onyx/TransactionViolation.ts @@ -29,6 +29,7 @@ type TransactionViolation = { tagListIndex?: number; tagListName?: string; errorIndexes?: number[]; + pendingPattern?: boolean; }; }; From 3409d9cfcfb71aefbfa985ac0834f7b4115ba88b Mon Sep 17 00:00:00 2001 From: smelaa Date: Wed, 17 Apr 2024 16:36:57 +0200 Subject: [PATCH 106/932] Display rter violations in MoneyReportHeader --- src/components/MoneyReportHeader.tsx | 29 +++++++++++++++++++++++++--- src/libs/TransactionUtils.ts | 24 ++++++++++++++++++----- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 14227d6a2051..b68cbd464137 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -3,6 +3,7 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as CurrencyUtils from '@libs/CurrencyUtils'; @@ -10,6 +11,8 @@ import * as HeaderUtils from '@libs/HeaderUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import * as TransactionUtils from '@libs/TransactionUtils'; +import variables from '@styles/variables'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -20,8 +23,10 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import Button from './Button'; import ConfirmModal from './ConfirmModal'; import HeaderWithBackButton from './HeaderWithBackButton'; +import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import MoneyReportHeaderStatusBar from './MoneyReportHeaderStatusBar'; +import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar'; import ProcessMoneyReportHoldMenu from './ProcessMoneyReportHoldMenu'; import SettlementButton from './SettlementButton'; @@ -56,6 +61,7 @@ type MoneyReportHeaderProps = MoneyReportHeaderOnyxProps & { function MoneyReportHeader({session, policy, chatReport, nextStep, report: moneyRequestReport, transactionThreadReport, reportActions}: MoneyReportHeaderProps) { const styles = useThemeStyles(); + const theme = useTheme(); const [isDeleteRequestModalVisible, setIsDeleteRequestModalVisible] = useState(false); const {translate} = useLocalize(); const {windowWidth, isSmallScreenWidth} = useWindowDimensions(); @@ -83,6 +89,9 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money const isDraft = ReportUtils.isOpenExpenseReport(moneyRequestReport); const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false); + const transactionIDs = TransactionUtils.getAllReportTransactions(chatReport?.reportID).map((transaction) => transaction.transactionID); + const haveAllPendingRTERViolation = TransactionUtils.haveAllPendingRTERViolation(transactionIDs); + const cancelPayment = useCallback(() => { if (!chatReport) { return; @@ -97,9 +106,9 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money const shouldDisableApproveButton = shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(moneyRequestReport); - const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; + const shouldShowSettlementButton = (shouldShowPayButton || shouldShowApproveButton) && !haveAllPendingRTERViolation; - const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0; + const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0 && !haveAllPendingRTERViolation; const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; const shouldShowNextStep = !ReportUtils.isClosedExpenseReportWithNoExpenses(moneyRequestReport) && isFromPaidPolicy && !!nextStep?.message?.length; @@ -188,7 +197,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money shouldShowBackButton={isSmallScreenWidth} onBackButtonPress={() => Navigation.goBack(undefined, false, true)} // Shows border if no buttons or next steps are showing below the header - shouldShowBorderBottom={!(shouldShowAnyButton && isSmallScreenWidth) && !(shouldShowNextStep && !isSmallScreenWidth)} + shouldShowBorderBottom={!(shouldShowAnyButton && isSmallScreenWidth) && !(shouldShowNextStep && !isSmallScreenWidth) && !haveAllPendingRTERViolation} shouldShowThreeDotsButton threeDotsMenuItems={threeDotsMenuItems} threeDotsAnchorPosition={styles.threeDotsPopoverOffsetNoCloseButton(windowWidth)} @@ -226,6 +235,20 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money )} + {haveAllPendingRTERViolation && ( + + } + description={translate('iou.pendingMatchWithCreditCardDescription')} + shouldShowBorderBottom + /> + )} {shouldShowSettlementButton && isSmallScreenWidth && ( diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index a54fa66191a4..df21a992a047 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -16,7 +16,6 @@ import * as NumberUtils from './NumberUtils'; import {getCleanedTagName} from './PolicyUtils'; let allTransactions: OnyxCollection = {}; - Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, waitForCollectionCallback: true, @@ -28,6 +27,13 @@ Onyx.connect({ }, }); +let allTransactionViolations: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, + waitForCollectionCallback: true, + callback: (value) => (allTransactionViolations = value), +}); + let allReports: OnyxCollection; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, @@ -514,6 +520,10 @@ function hasMissingSmartscanFields(transaction: OnyxEntry): boolean return Boolean(transaction && !isDistanceRequest(transaction) && !isReceiptBeingScanned(transaction) && areRequiredFieldsEmpty(transaction)); } +function getTransactionViolations(transactionID: string, transactionViolations: OnyxCollection | null): TransactionViolations | null { + return transactionViolations?.[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS + transactionID] ?? null; +} + /** * Check if there is pending rter violation in transactionViolations. */ @@ -523,6 +533,13 @@ function hasPendingRTERViolation(transactionViolations?: TransactionViolations | ); } +/** + * Check if there is pending rter violation in transactionViolations. + */ +function haveAllPendingRTERViolation(transactionIds: string[]): boolean { + return transactionIds.map((transactionId) => hasPendingRTERViolation(getTransactionViolations(transactionId, allTransactionViolations))).every((value) => value); +} + /** * Check if the transaction is pending or has a pending rter violation. */ @@ -627,10 +644,6 @@ function hasViolation(transactionID: string, transactionViolations: OnyxCollecti ); } -function getTransactionViolations(transactionID: string, transactionViolations: OnyxCollection | null): TransactionViolations | null { - return transactionViolations?.[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS + transactionID] ?? null; -} - /** * this is the formulae to calculate tax */ @@ -715,6 +728,7 @@ export { areRequiredFieldsEmpty, hasMissingSmartscanFields, hasPendingRTERViolation, + haveAllPendingRTERViolation, hasPendingUI, getWaypointIndex, waypointHasValidAddress, From ed168c1a2206d839926655ad4bc7d3334ebdc510 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 17 Apr 2024 22:50:28 +0200 Subject: [PATCH 107/932] feat: setup Terms and Fees step --- src/languages/en.ts | 4 + src/languages/es.ts | 4 + .../TermsAndFees/TermsAndFees.tsx | 58 ++++++++++ .../TermsAndFees/substeps/FeesStep.tsx | 24 ++++ .../TermsAndFees/substeps/TermsStep.tsx | 106 ++++++++++++++++++ 5 files changed, 196 insertions(+) create mode 100644 src/pages/EnablePayments/TermsAndFees/TermsAndFees.tsx create mode 100644 src/pages/EnablePayments/TermsAndFees/substeps/FeesStep.tsx create mode 100644 src/pages/EnablePayments/TermsAndFees/substeps/TermsStep.tsx diff --git a/src/languages/en.ts b/src/languages/en.ts index fda7acc309bd..2aab5f00c830 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1564,6 +1564,7 @@ export default { }, termsStep: { headerTitle: 'Terms and fees', + headerTitleRefactor: 'Fees and terms', haveReadAndAgree: 'I have read and agree to receive ', electronicDisclosures: 'electronic disclosures', agreeToThe: 'I agree to the', @@ -1574,6 +1575,9 @@ export default { noOverdraftOrCredit: 'No overdraft/credit feature.', electronicFundsWithdrawal: 'Electronic funds withdrawal', standard: 'Standard', + takeALookAtSomeFees: 'Take a look at some fees.', + checkPlease: 'Check please.', + agreeToTerms: 'Agree to the terms and you’ll be good to go!', shortTermsForm: { expensifyPaymentsAccount: ({walletProgram}: WalletProgramParams) => `The Expensify Wallet is issued by ${walletProgram}.`, perPurchase: 'Per purchase', diff --git a/src/languages/es.ts b/src/languages/es.ts index 46a8d051bdfe..acdf9978de0b 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1585,6 +1585,7 @@ export default { }, termsStep: { headerTitle: 'Condiciones y tarifas', + headerTitleRefactor: 'Tarifas y condiciones', haveReadAndAgree: 'He leído y acepto recibir ', electronicDisclosures: 'divulgaciones electrónicas', agreeToThe: 'Estoy de acuerdo con el ', @@ -1595,6 +1596,9 @@ export default { noOverdraftOrCredit: 'Sin función de sobregiro/crédito', electronicFundsWithdrawal: 'Retiro electrónico de fondos', standard: 'Estándar', + takeALookAtSomeFees: 'Echa un vistazo a algunas tarifas.', + checkPlease: 'Por favor, revisa.', + agreeToTerms: 'Debes aceptar los términos y condiciones para continuar.', shortTermsForm: { expensifyPaymentsAccount: ({walletProgram}: WalletProgramParams) => `La billetera Expensify es emitida por ${walletProgram}.`, perPurchase: 'Por compra', diff --git a/src/pages/EnablePayments/TermsAndFees/TermsAndFees.tsx b/src/pages/EnablePayments/TermsAndFees/TermsAndFees.tsx new file mode 100644 index 000000000000..7e26f652efe8 --- /dev/null +++ b/src/pages/EnablePayments/TermsAndFees/TermsAndFees.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import {View} from 'react-native'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import InteractiveStepSubHeader from '@components/InteractiveStepSubHeader'; +import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import useSubStep from '@hooks/useSubStep'; +import type {SubStepProps} from '@hooks/useSubStep/types'; +import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; +import FeesStep from './substeps/FeesStep'; +import TermsStep from './substeps/TermsStep'; + +const termsAndFeesSubsteps: Array> = [FeesStep, TermsStep]; + +function TermsAndFees() { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + + const submit = () => {}; + const {componentToRender: SubStep, isEditing, screenIndex, nextScreen, prevScreen, moveTo} = useSubStep({bodyContent: termsAndFeesSubsteps, startFrom: 0, onFinished: submit}); + + const handleBackButtonPress = () => { + if (screenIndex === 0) { + return; + } + prevScreen(); + }; + + return ( + + + + + + + + ); +} + +TermsAndFees.displayName = 'TermsAndFees'; + +export default TermsAndFees; diff --git a/src/pages/EnablePayments/TermsAndFees/substeps/FeesStep.tsx b/src/pages/EnablePayments/TermsAndFees/substeps/FeesStep.tsx new file mode 100644 index 000000000000..78ff371b747c --- /dev/null +++ b/src/pages/EnablePayments/TermsAndFees/substeps/FeesStep.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import {useOnyx} from 'react-native-onyx'; +import ScrollView from '@components/ScrollView'; +import useThemeStyles from '@hooks/useThemeStyles'; +import LongTermsForm from '@pages/EnablePayments/TermsPage/LongTermsForm'; +import ShortTermsForm from '@pages/EnablePayments/TermsPage/ShortTermsForm'; +import ONYXKEYS from '@src/ONYXKEYS'; + +function FeesStep() { + const styles = useThemeStyles(); + const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET); + + return ( + + + + + ); +} + +export default FeesStep; diff --git a/src/pages/EnablePayments/TermsAndFees/substeps/TermsStep.tsx b/src/pages/EnablePayments/TermsAndFees/substeps/TermsStep.tsx new file mode 100644 index 000000000000..e77bf7db4817 --- /dev/null +++ b/src/pages/EnablePayments/TermsAndFees/substeps/TermsStep.tsx @@ -0,0 +1,106 @@ +import React, {useEffect, useState} from 'react'; +import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; +import CheckboxWithLabel from '@components/CheckboxWithLabel'; +import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; +import ScrollView from '@components/ScrollView'; +import Text from '@components/Text'; +import TextLink from '@components/TextLink'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as ErrorUtils from '@libs/ErrorUtils'; +import * as BankAccounts from '@userActions/BankAccounts'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +function HaveReadAndAgreeLabel() { + const {translate} = useLocalize(); + + return ( + + {`${translate('termsStep.haveReadAndAgree')}`} + {`${translate('termsStep.electronicDisclosures')}.`} + + ); +} + +function AgreeToTheLabel() { + const {translate} = useLocalize(); + + return ( + + {`${translate('termsStep.agreeToThe')} `} + {`${translate('common.privacy')} `} + {`${translate('common.and')} `} + {`${translate('termsStep.walletAgreement')}.`} + + ); +} + +function TermsStep() { + const styles = useThemeStyles(); + const [hasAcceptedDisclosure, setHasAcceptedDisclosure] = useState(false); + const [hasAcceptedPrivacyPolicyAndWalletAgreement, setHasAcceptedPrivacyPolicyAndWalletAgreement] = useState(false); + const [error, setError] = useState(false); + const {translate} = useLocalize(); + + const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS); + + const errorMessage = error ? 'common.error.acceptTerms' : ErrorUtils.getLatestErrorMessage(walletTerms ?? {}) ?? ''; + + const toggleDisclosure = () => { + setHasAcceptedDisclosure(!hasAcceptedDisclosure); + }; + + const togglePrivacyPolicy = () => { + setHasAcceptedPrivacyPolicyAndWalletAgreement(!hasAcceptedPrivacyPolicyAndWalletAgreement); + }; + + /** clear error */ + useEffect(() => { + if (!hasAcceptedDisclosure || !hasAcceptedPrivacyPolicyAndWalletAgreement) { + return; + } + + setError(false); + }, [hasAcceptedDisclosure, hasAcceptedPrivacyPolicyAndWalletAgreement]); + + return ( + + {translate('termsStep.checkPlease')} + {translate('termsStep.agreeToTerms')} + + + { + if (!hasAcceptedDisclosure || !hasAcceptedPrivacyPolicyAndWalletAgreement) { + setError(true); + return; + } + + setError(false); + BankAccounts.acceptWalletTerms({ + hasAcceptedTerms: hasAcceptedDisclosure && hasAcceptedPrivacyPolicyAndWalletAgreement, + reportID: walletTerms?.chatReportID ?? '', + }); + }} + message={errorMessage} + isAlertVisible={error || Boolean(errorMessage)} + isLoading={!!walletTerms?.isLoading} + containerStyles={[styles.mh0, styles.mv4]} + /> + + ); +} + +export default TermsStep; From 7574fc9acbe20a012b7d92a90ff1523d20b373d6 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 18 Apr 2024 13:19:08 +0100 Subject: [PATCH 108/932] add taxAmount param to updateMoneyRequestAmountAndCurrency --- src/libs/actions/IOU.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 83caa65e1d77..43cc73df9c97 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4449,6 +4449,7 @@ type UpdateMoneyRequestAmountAndCurrencyParams = { transactionThreadReportID: string; currency: string; amount: number; + taxAmount?: number; policy?: OnyxEntry; policyTagList?: OnyxEntry; policyCategories?: OnyxEntry; @@ -4460,6 +4461,7 @@ function updateMoneyRequestAmountAndCurrency({ transactionThreadReportID, currency, amount, + taxAmount, policy, policyTagList, policyCategories, @@ -4467,6 +4469,7 @@ function updateMoneyRequestAmountAndCurrency({ const transactionChanges = { amount, currency, + ...(taxAmount && {taxAmount}), }; const {params, onyxData} = getUpdateMoneyRequestParams( transactionID, From 1ed12dfe94efdb4cf61a0cf865e98b2dd08f71ee Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 18 Apr 2024 13:19:58 +0100 Subject: [PATCH 109/932] should optimistically update tax amount when updating expense amount --- .../iou/request/step/IOURequestStepAmount.tsx | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index cb8e51120f01..7b56049811fc 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -16,7 +16,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {Transaction} from '@src/types/onyx'; +import type {Policy, TaxRatesWithDefault, Transaction} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; @@ -33,6 +33,9 @@ type IOURequestStepAmountOnyxProps = { /** The draft transaction object being modified in Onyx */ draftTransaction: OnyxEntry; + + /** The policy which the user has access to and which the report is tied to */ + policy: OnyxEntry; }; type IOURequestStepAmountProps = IOURequestStepAmountOnyxProps & @@ -40,7 +43,15 @@ type IOURequestStepAmountProps = IOURequestStepAmountOnyxProps & /** The transaction object being modified in Onyx */ transaction: OnyxEntry; }; - +function getTaxAmount(transaction: OnyxEntry, taxRates: TaxRatesWithDefault | undefined, newAmount: number) { + if (!transaction?.amount) { + return; + } + const transactionTaxCode = transaction?.taxCode ?? ''; + const defaultTaxValue = taxRates?.defaultValue; + const taxPercentage = (transactionTaxCode ? taxRates?.taxes[transactionTaxCode]?.value : defaultTaxValue) ?? ''; + return CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, newAmount)); +} function IOURequestStepAmount({ report, route: { @@ -49,6 +60,7 @@ function IOURequestStepAmount({ transaction, splitDraftTransaction, draftTransaction, + policy, }: IOURequestStepAmountProps) { const {translate} = useLocalize(); const textInput = useRef(null); @@ -148,7 +160,9 @@ function IOURequestStepAmount({ return; } - IOU.updateMoneyRequestAmountAndCurrency({transactionID, transactionThreadReportID: reportID, currency, amount: newAmount}); + const taxAmount = getTaxAmount(transaction, policy?.taxRates, newAmount); + + IOU.updateMoneyRequestAmountAndCurrency({transactionID, transactionThreadReportID: reportID, currency, amount: newAmount, taxAmount}); Navigation.dismissModal(); }; @@ -190,6 +204,9 @@ export default withWritableReportOrNotFound( return `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`; }, }, + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, + }, })(IOURequestStepAmount), ), ); From 5275089ec8167f03667de8e53ecef56532a492ee Mon Sep 17 00:00:00 2001 From: smelaa Date: Thu, 18 Apr 2024 14:33:15 +0200 Subject: [PATCH 110/932] Fix util function --- src/libs/TransactionUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 44ccd387e670..3f374d46a6de 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -535,7 +535,8 @@ function hasPendingRTERViolation(transactionViolations?: TransactionViolations | * Check if there is pending rter violation in transactionViolations. */ function haveAllPendingRTERViolation(transactionIds: string[]): boolean { - return transactionIds.map((transactionId) => hasPendingRTERViolation(getTransactionViolations(transactionId, allTransactionViolations))).every((value) => value); + const transactionsWithRTERViolations = transactionIds.map((transactionId) => hasPendingRTERViolation(getTransactionViolations(transactionId, allTransactionViolations))); + return transactionsWithRTERViolations.length !== 0 && transactionsWithRTERViolations.every((value) => value === true); } /** From 1c16b1aeab10ab4e85462dcc9b50cb399d6588f7 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 18 Apr 2024 13:33:58 +0100 Subject: [PATCH 111/932] add UpdateMoneyRequestTaxRateParams types to updateMoneyRequestTaxRate --- src/libs/actions/IOU.ts | 18 ++++++++++-------- .../request/step/IOURequestStepTaxRatePage.tsx | 9 ++++++++- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 43cc73df9c97..04dd151506bb 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2379,15 +2379,17 @@ function updateMoneyRequestTaxAmount( API.write('UpdateMoneyRequestTaxAmount', params, onyxData); } +type UpdateMoneyRequestTaxRateParams = { + transactionID: string; + optimisticReportActionID: string; + taxCode: string; + policy: OnyxEntry; + policyTagList: OnyxEntry; + policyCategories: OnyxEntry; +}; + /** Updates the created tax rate of an expense */ -function updateMoneyRequestTaxRate( - transactionID: string, - optimisticReportActionID: string, - taxCode: string, - policy: OnyxEntry, - policyTagList: OnyxEntry, - policyCategories: OnyxEntry, -) { +function updateMoneyRequestTaxRate({transactionID, optimisticReportActionID, taxCode, policy, policyTagList, policyCategories}: UpdateMoneyRequestTaxRateParams) { const transactionChanges = { taxCode, }; diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx index da3a244a2db2..3e14610652df 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx @@ -74,7 +74,14 @@ function IOURequestStepTaxRatePage({ navigateBack(); return; } - IOU.updateMoneyRequestTaxRate(transaction?.transactionID ?? '', report?.reportID ?? '', newTaxCode, policy, policyTags, policyCategories); + IOU.updateMoneyRequestTaxRate({ + transactionID: transaction?.transactionID ?? '', + optimisticReportActionID: report?.reportID ?? '', + taxCode: newTaxCode, + policy, + policyTagList: policyTags, + policyCategories, + }); navigateBack(); return; } From 8dce58a29da1500c7178168f31affbb669abeb4a Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 18 Apr 2024 13:37:51 +0100 Subject: [PATCH 112/932] add taxAmount to UpdateMoneyRequestTaxRateParams types --- src/libs/actions/IOU.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 04dd151506bb..53fc9e408a06 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2383,15 +2383,17 @@ type UpdateMoneyRequestTaxRateParams = { transactionID: string; optimisticReportActionID: string; taxCode: string; + taxAmount?: number; policy: OnyxEntry; policyTagList: OnyxEntry; policyCategories: OnyxEntry; }; /** Updates the created tax rate of an expense */ -function updateMoneyRequestTaxRate({transactionID, optimisticReportActionID, taxCode, policy, policyTagList, policyCategories}: UpdateMoneyRequestTaxRateParams) { +function updateMoneyRequestTaxRate({transactionID, optimisticReportActionID, taxCode, taxAmount, policy, policyTagList, policyCategories}: UpdateMoneyRequestTaxRateParams) { const transactionChanges = { taxCode, + ...(taxAmount && {taxAmount}), }; const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, policy, policyTagList, policyCategories, true); API.write('UpdateMoneyRequestTaxRate', params, onyxData); From 228c47705cc4a06d8d0a343be222e8e952523039 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 18 Apr 2024 14:36:36 +0100 Subject: [PATCH 113/932] should optimistically update tax amount when updating tax rate --- .../iou/request/step/IOURequestStepTaxRatePage.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx index 3e14610652df..55d43370c477 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx @@ -68,6 +68,12 @@ function IOURequestStepTaxRatePage({ : transactionTaxCode && TransactionUtils.getTaxName(taxRates.taxes, transactionTaxCode)); const updateTaxRates = (taxes: OptionsListUtils.TaxRatesOption) => { + if (!transaction || !taxes.text || !taxRates) { + Navigation.goBack(backTo); + return; + } + const taxAmount = getTaxAmount(taxRates, taxes.text, TransactionUtils.getAmount(transaction, false, true)); + if (isEditing) { const newTaxCode = taxes.data.code; if (newTaxCode === undefined || newTaxCode === TransactionUtils.getTaxCode(transaction)) { @@ -78,6 +84,7 @@ function IOURequestStepTaxRatePage({ transactionID: transaction?.transactionID ?? '', optimisticReportActionID: report?.reportID ?? '', taxCode: newTaxCode, + taxAmount: CurrencyUtils.convertToBackendAmount(taxAmount ?? 0), policy, policyTagList: policyTags, policyCategories, @@ -85,11 +92,7 @@ function IOURequestStepTaxRatePage({ navigateBack(); return; } - if (!transaction || !taxes.text || !taxRates) { - Navigation.goBack(backTo); - return; - } - const taxAmount = getTaxAmount(taxRates, taxes.text, transaction?.amount); + if (taxAmount === undefined) { Navigation.goBack(backTo); return; From a0ac9bac20da8c89b5a5f2e9bbaee1e314e7bb49 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 18 Apr 2024 14:39:33 +0100 Subject: [PATCH 114/932] fix lint --- src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx index 55d43370c477..c310e88dc928 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx @@ -73,7 +73,7 @@ function IOURequestStepTaxRatePage({ return; } const taxAmount = getTaxAmount(taxRates, taxes.text, TransactionUtils.getAmount(transaction, false, true)); - + if (isEditing) { const newTaxCode = taxes.data.code; if (newTaxCode === undefined || newTaxCode === TransactionUtils.getTaxCode(transaction)) { From 046dbc2d40d1c88603b2f6eb2c408b42e0a0b283 Mon Sep 17 00:00:00 2001 From: smelaa Date: Thu, 18 Apr 2024 16:14:20 +0200 Subject: [PATCH 115/932] Add rter violation information to MoneyRequestPreviewContent header --- .../MoneyRequestPreviewContent.tsx | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index 9a7549b9aac6..a3a8bfd131c3 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -4,6 +4,7 @@ import lodashSortBy from 'lodash/sortBy'; import React from 'react'; import {View} from 'react-native'; import type {GestureResponderEvent} from 'react-native'; +import type {SvgProps} from 'react-native-svg'; import ConfirmedRoute from '@components/ConfirmedRoute'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -134,7 +135,11 @@ function MoneyRequestPreviewContent({ showContextMenuForReport(event, contextMenuAnchor, reportID, action, checkIfContextMenuActive); }; - const getPreviewHeaderText = (): string => { + const getPreviewHeaderTextAndIcon: () => {headerMessage: string; headerIcon?: React.FC} = () => { + if (TransactionUtils.hasPendingUI(transaction, TransactionUtils.getTransactionViolations(transaction?.transactionID ?? '', transactionViolations))) { + return {headerMessage: translate('iou.pendingMatchWithCreditCard'), headerIcon: Expensicons.Hourglass}; + } + let message = translate('iou.cash'); if (isDistanceRequest) { @@ -149,13 +154,13 @@ function MoneyRequestPreviewContent({ message = translate('iou.card'); if (TransactionUtils.isPending(transaction)) { message += ` ${CONST.DOT_SEPARATOR} ${translate('iou.pending')}`; - return message; + return {headerMessage: message}; } } if (isSettled && !iouReport?.isCancelledIOU && !isPartialHold) { message += ` ${CONST.DOT_SEPARATOR} ${getSettledMessage()}`; - return message; + return {headerMessage: message}; } if (shouldShowRBR && transaction) { @@ -166,7 +171,8 @@ function MoneyRequestPreviewContent({ const isTooLong = violationsCount > 1 || violationMessage.length > 15; const hasViolationsAndFieldErrors = violationsCount > 0 && hasFieldErrors; - return `${message} ${CONST.DOT_SEPARATOR} ${isTooLong || hasViolationsAndFieldErrors ? translate('violations.reviewRequired') : violationMessage}`; + message = `${message} ${CONST.DOT_SEPARATOR} ${isTooLong || hasViolationsAndFieldErrors ? translate('violations.reviewRequired') : violationMessage}`; + return {headerMessage: message, headerIcon: undefined}; } const isMerchantMissing = TransactionUtils.isMerchantMissing(transaction); @@ -189,9 +195,11 @@ function MoneyRequestPreviewContent({ } else if (!(isSettled && !isSettlementOrApprovalPartial) && isOnHold) { message += ` ${CONST.DOT_SEPARATOR} ${translate('iou.hold')}`; } - return message; + return {headerMessage: message}; }; + const {headerMessage, headerIcon} = getPreviewHeaderTextAndIcon(); + const getDisplayAmountText = (): string => { if (isScanning) { return translate('iou.receiptScanning'); @@ -249,7 +257,15 @@ function MoneyRequestPreviewContent({ - {getPreviewHeaderText()} + {headerIcon && ( + + )} + {headerMessage} {!isSettled && shouldShowRBR && ( Date: Thu, 18 Apr 2024 18:48:35 +0200 Subject: [PATCH 116/932] feat: rename terms and feex, fix styling, create temporary test route --- src/ROUTES.ts | 2 + src/SCREENS.ts | 2 + .../ModalStackNavigators/index.tsx | 2 + src/libs/Navigation/linkingConfig/config.ts | 5 +++ .../FeesAndTerms.tsx} | 8 ++-- .../FeesAndTerms/substeps/FeesStep.tsx | 37 +++++++++++++++++++ .../substeps/TermsStep.tsx | 32 ++++++++-------- .../TermsAndFees/substeps/FeesStep.tsx | 24 ------------ .../TermsPage/ShortTermsForm.tsx | 16 ++++---- src/styles/index.ts | 5 ++- 10 files changed, 80 insertions(+), 53 deletions(-) rename src/pages/EnablePayments/{TermsAndFees/TermsAndFees.tsx => FeesAndTerms/FeesAndTerms.tsx} (93%) create mode 100644 src/pages/EnablePayments/FeesAndTerms/substeps/FeesStep.tsx rename src/pages/EnablePayments/{TermsAndFees => FeesAndTerms}/substeps/TermsStep.tsx (79%) delete mode 100644 src/pages/EnablePayments/TermsAndFees/substeps/FeesStep.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 46f2e2fef049..2ed8be54f408 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -119,6 +119,8 @@ const ROUTES = { SETTINGS_ADD_BANK_ACCOUNT: 'settings/wallet/add-bank-account', SETTINGS_ADD_BANK_ACCOUNT_REFACTOR: 'settings/wallet/add-bank-account-refactor', SETTINGS_ENABLE_PAYMENTS: 'settings/wallet/enable-payments', + // TODO: Added temporarily for testing purposes, remove after refactor - https://github.com/Expensify/App/issues/36648 + SETTINGS_ENABLE_PAYMENTS_TEMPORARY_TERMS: 'settings/wallet/enable-payments-temporary-terms', SETTINGS_WALLET_CARD_DIGITAL_DETAILS_UPDATE_ADDRESS: { route: 'settings/wallet/card/:domain/digital-details/update-address', getRoute: (domain: string) => `settings/wallet/card/${domain}/digital-details/update-address` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index acbb4b507b65..cf5723c43d31 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -84,6 +84,8 @@ const SCREENS = { TRANSFER_BALANCE: 'Settings_Wallet_Transfer_Balance', CHOOSE_TRANSFER_ACCOUNT: 'Settings_Wallet_Choose_Transfer_Account', ENABLE_PAYMENTS: 'Settings_Wallet_EnablePayments', + // TODO: Added temporarily for testing purposes, remove after refactor - https://github.com/Expensify/App/issues/36648 + ENABLE_PAYMENTS_TEMPORARY_TERMS: 'Settings_Wallet_EnablePayments_Temporary_Terms', CARD_ACTIVATE: 'Settings_Wallet_Card_Activate', REPORT_VIRTUAL_CARD_FRAUD: 'Settings_Wallet_ReportVirtualCardFraud', CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS: 'Settings_Wallet_Cards_Digital_Details_Update_Address', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index a596acf0a3ac..711e911fe9f1 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -215,6 +215,8 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/settings/Wallet/TransferBalancePage').default as React.ComponentType, [SCREENS.SETTINGS.WALLET.CHOOSE_TRANSFER_ACCOUNT]: () => require('../../../../pages/settings/Wallet/ChooseTransferAccountPage').default as React.ComponentType, [SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS]: () => require('../../../../pages/EnablePayments/EnablePaymentsPage').default as React.ComponentType, + // TODO: Added temporarily for testing purposes, remove after refactor - https://github.com/Expensify/App/issues/36648 + [SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS_TEMPORARY_TERMS]: () => require('../../../../pages/EnablePayments/FeesAndTerms/FeesAndTerms').default as React.ComponentType, [SCREENS.SETTINGS.ADD_DEBIT_CARD]: () => require('../../../../pages/settings/Wallet/AddDebitCardPage').default as React.ComponentType, [SCREENS.SETTINGS.ADD_BANK_ACCOUNT]: () => require('../../../../pages/AddPersonalBankAccountPage').default as React.ComponentType, [SCREENS.SETTINGS.ADD_BANK_ACCOUNT_REFACTOR]: () => require('../../../../pages/EnablePayments/AddBankAccount/AddBankAccount').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 05b7190fa181..c19ce019a621 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -156,6 +156,11 @@ const config: LinkingOptions['config'] = { path: ROUTES.SETTINGS_ENABLE_PAYMENTS, exact: true, }, + // TODO: Added temporarily for testing purposes, remove after refactor - https://github.com/Expensify/App/issues/36648 + [SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS_TEMPORARY_TERMS]: { + path: ROUTES.SETTINGS_ENABLE_PAYMENTS_TEMPORARY_TERMS, + exact: true, + }, [SCREENS.SETTINGS.WALLET.TRANSFER_BALANCE]: { path: ROUTES.SETTINGS_WALLET_TRANSFER_BALANCE, exact: true, diff --git a/src/pages/EnablePayments/TermsAndFees/TermsAndFees.tsx b/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx similarity index 93% rename from src/pages/EnablePayments/TermsAndFees/TermsAndFees.tsx rename to src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx index 7e26f652efe8..76955e860f13 100644 --- a/src/pages/EnablePayments/TermsAndFees/TermsAndFees.tsx +++ b/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx @@ -13,7 +13,7 @@ import TermsStep from './substeps/TermsStep'; const termsAndFeesSubsteps: Array> = [FeesStep, TermsStep]; -function TermsAndFees() { +function FeesAndTerms() { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -29,7 +29,7 @@ function TermsAndFees() { return ( + {translate('termsStep.takeALookAtSomeFees')} + + + +