From 751f5405728ead6073fb27dbd83fc31e66d25aa0 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Wed, 26 Jun 2024 20:46:20 +0000 Subject: [PATCH 01/46] Update version to 9.0.2-1 (cherry picked from commit ba6e2bed34d389d1ade3534e21acaac526c49fa4) --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index fac54600f021..6f0e3f485bb4 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009000200 - versionName "9.0.2-0" + versionCode 1009000201 + versionName "9.0.2-1" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index a2e274eafc4d..210467d50dc2 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.2.0 + 9.0.2.1 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index e1f9960caa92..d4a072073554 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.2.0 + 9.0.2.1 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 7d83d9f3d273..96010dc3eba1 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.2 CFBundleVersion - 9.0.2.0 + 9.0.2.1 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index f4fb672b527f..1e86632761e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.2-0", + "version": "9.0.2-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.2-0", + "version": "9.0.2-1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 14328e61bed8..a16b15ae8387 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.2-0", + "version": "9.0.2-1", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From d7542be695589e3aa18da9cd6941117da64f73ec Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 26 Jun 2024 14:25:40 -0600 Subject: [PATCH 02/46] Merge pull request #44444 from software-mansion-labs/fix/link-to-without-central-pane Fix navigating between CentralPane screens (cherry picked from commit f757005dad95d4d1e86290891fea0708c659400c) --- src/libs/Navigation/linkTo/index.ts | 16 ++++++++++------ src/libs/NavigationUtils.ts | 6 +++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/libs/Navigation/linkTo/index.ts b/src/libs/Navigation/linkTo/index.ts index 90e52d02163c..3c4608d6b5de 100644 --- a/src/libs/Navigation/linkTo/index.ts +++ b/src/libs/Navigation/linkTo/index.ts @@ -72,17 +72,21 @@ export default function linkTo(navigation: NavigationContainerRef | undefined, (value) => value === undefined), - omitBy(actionParams?.params as Record | undefined, (value) => value === undefined), + omitBy(targetParams as Record | undefined, (value) => value === undefined), ); // If this action is navigating to the report screen and the top most navigator is different from the one we want to navigate - PUSH the new screen to the top of the stack by default @@ -110,8 +114,8 @@ export default function linkTo(navigation: NavigationContainerRef).policyIDs = policyID; + if (targetName === SCREENS.SEARCH.CENTRAL_PANE && targetParams && policyID) { + (targetParams as Record).policyIDs = policyID; } // If the type is UP, we deeplinked into one of the RHP flows and we want to replace the current screen with the previous one in the flow diff --git a/src/libs/NavigationUtils.ts b/src/libs/NavigationUtils.ts index f0442e4995d2..4fdc03c3d334 100644 --- a/src/libs/NavigationUtils.ts +++ b/src/libs/NavigationUtils.ts @@ -1,7 +1,7 @@ import SCREENS from '@src/SCREENS'; import type {CentralPaneName} from './Navigation/types'; -const CENTRAL_PANE_SCREEN_NAMES = [ +const CENTRAL_PANE_SCREEN_NAMES = new Set([ SCREENS.SETTINGS.WORKSPACES, SCREENS.SETTINGS.PREFERENCES.ROOT, SCREENS.SETTINGS.SECURITY, @@ -13,14 +13,14 @@ const CENTRAL_PANE_SCREEN_NAMES = [ SCREENS.SETTINGS.SUBSCRIPTION.ROOT, SCREENS.SEARCH.CENTRAL_PANE, SCREENS.REPORT, -]; +]); function isCentralPaneName(screen: string | undefined): screen is CentralPaneName { if (!screen) { return false; } - return CENTRAL_PANE_SCREEN_NAMES.includes(screen as CentralPaneName); + return CENTRAL_PANE_SCREEN_NAMES.has(screen as CentralPaneName); } export default isCentralPaneName; From 98e53737515ff98c0fe2141dfe0c9e38eba3870b Mon Sep 17 00:00:00 2001 From: OSBotify Date: Wed, 26 Jun 2024 22:22:24 +0000 Subject: [PATCH 03/46] Update version to 9.0.2-2 (cherry picked from commit f1f472a74f9dbc5934894d362f9fd0ff4260a57c) --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 6f0e3f485bb4..e701a1fdcc0b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009000201 - versionName "9.0.2-1" + versionCode 1009000202 + versionName "9.0.2-2" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 210467d50dc2..72a45bcad449 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.2.1 + 9.0.2.2 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index d4a072073554..bf91562a8e62 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.2.1 + 9.0.2.2 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 96010dc3eba1..a71819c07514 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.2 CFBundleVersion - 9.0.2.1 + 9.0.2.2 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 1e86632761e1..108c12ab4c5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.2-1", + "version": "9.0.2-2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.2-1", + "version": "9.0.2-2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index a16b15ae8387..928ec6713ecf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.2-1", + "version": "9.0.2-2", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 20f13e8396d48a65e3365258a7dcfe55724b7ea0 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Wed, 26 Jun 2024 15:17:47 -0700 Subject: [PATCH 04/46] Merge pull request #44505 from Expensify/yuwen-fixSplitSelection [CP Staging] Fix selection for splits when there is a search term present (cherry picked from commit 47fafdb5c82a366344da7d9f03b40ab206a147eb) --- .../MoneyRequestParticipantsSelector.tsx | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index 46c0d10e08ac..58e69485c1b3 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -34,8 +34,9 @@ type MoneyRequestParticipantsSelectorProps = { /** Callback to add participants in MoneyRequestModal */ onParticipantsAdded: (value: Participant[]) => void; + /** Selected participants from MoneyRequestModal with login */ - participants?: Participant[]; + participants?: Participant[] | typeof CONST.EMPTY_ARRAY; /** The type of IOU report, i.e. split, request, send, track */ iouType: IOUType; @@ -47,7 +48,7 @@ type MoneyRequestParticipantsSelectorProps = { action: IOUAction; }; -function MoneyRequestParticipantsSelector({participants = [], onFinish, onParticipantsAdded, iouType, iouRequestType, action}: MoneyRequestParticipantsSelectorProps) { +function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onFinish, onParticipantsAdded, iouType, iouRequestType, action}: MoneyRequestParticipantsSelectorProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); @@ -94,7 +95,7 @@ function MoneyRequestParticipantsSelector({participants = [], onFinish, onPartic options.personalDetails, betas, '', - participants, + participants as Participant[], CONST.EXPENSIFY_EMAILS, // If we are using this component in the "Submit expense" flow then we pass the includeOwnedWorkspaceChats argument so that the current user @@ -153,7 +154,7 @@ function MoneyRequestParticipantsSelector({participants = [], onFinish, onPartic const newOptions = OptionsListUtils.filterOptions(defaultOptions, debouncedSearchTerm, { betas, - selectedOptions: participants, + selectedOptions: participants as Participant[], excludeLogins: CONST.EXPENSIFY_EMAILS, maxRecentReportsToShow: CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW, }); @@ -378,6 +379,17 @@ function MoneyRequestParticipantsSelector({participants = [], onFinish, onPartic onFinish, ]); + const onSelectRow = useCallback( + (item: Participant) => { + if (isIOUSplit) { + addParticipantToSelection(item); + return; + } + addSingleParticipant(item); + }, + [isIOUSplit, addParticipantToSelection, addSingleParticipant], + ); + return ( (isIOUSplit ? addParticipantToSelection(item) : addSingleParticipant(item))} + onSelectRow={onSelectRow} shouldDebounceRowSelect footerContent={footerContent} headerMessage={header} From d6c5cc5989095442321dd5dac0a9ce2dea78a14d Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 27 Jun 2024 00:24:30 +0000 Subject: [PATCH 05/46] Update version to 9.0.2-3 (cherry picked from commit 050641a6451e75d13723e46062086e3db3c2ad35) --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index e701a1fdcc0b..ea7e6bdfce45 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009000202 - versionName "9.0.2-2" + versionCode 1009000203 + versionName "9.0.2-3" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 72a45bcad449..1653edce72b6 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.2.2 + 9.0.2.3 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index bf91562a8e62..8341a5d96c13 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.2.2 + 9.0.2.3 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index a71819c07514..d9fcba7e3c9d 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.2 CFBundleVersion - 9.0.2.2 + 9.0.2.3 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 108c12ab4c5f..0066dac558c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.2-2", + "version": "9.0.2-3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.2-2", + "version": "9.0.2-3", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 928ec6713ecf..3aada9ac811f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.2-2", + "version": "9.0.2-3", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 75d4ce99e4b5717be70239312913efab15b50c22 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 26 Jun 2024 19:57:14 -0300 Subject: [PATCH 06/46] Merge pull request #44508 from etCoderDysto/fixHold fix: Hold option appears for invoice (cherry picked from commit 6ea15ed1b1515ddd832e8b1bae93bc86a7cb15e3) --- src/components/MoneyReportHeader.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 64c0de7d7eb2..355aa0538f0b 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -249,14 +249,15 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const isHoldCreator = ReportUtils.isHoldCreator(transaction, moneyRequestReport?.reportID) && isRequestIOU; const isTrackExpenseReport = ReportUtils.isTrackExpenseReport(moneyRequestReport); const canModifyStatus = !isTrackExpenseReport && (isPolicyAdmin || isActionOwner || isApprover); - if (isOnHold && (isHoldCreator || (!isRequestIOU && canModifyStatus))) { + const isInvoiceReport = ReportUtils.isInvoiceReport(moneyRequestReport); + if (isOnHold && (isHoldCreator || (!isRequestIOU && canModifyStatus)) && !isInvoiceReport) { threeDotsMenuItems.push({ icon: Expensicons.Stopwatch, text: translate('iou.unholdExpense'), onSelected: () => changeMoneyRequestStatus(), }); } - if (!isOnHold && (isRequestIOU || canModifyStatus) && !isScanning) { + if (!isOnHold && (isRequestIOU || canModifyStatus) && !isScanning && !isInvoiceReport) { threeDotsMenuItems.push({ icon: Expensicons.Stopwatch, text: translate('iou.hold'), From 7076b54b651c4bb60ea00d0c912d8554dd8a9e35 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 27 Jun 2024 12:31:38 +0700 Subject: [PATCH 07/46] Change big image preview to thumbnail --- src/components/AttachmentModal.tsx | 4 + .../AttachmentCarousel/extractAttachments.ts | 5 +- .../Attachments/AttachmentView/index.tsx | 146 ++++++++++++++++-- 3 files changed, 138 insertions(+), 17 deletions(-) diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index 19bad0fbdf5a..cb8538bac52c 100644 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -65,6 +65,8 @@ type ImagePickerResponse = { type FileObject = Partial; +// type FileObject = Partial & Partial ; + type ChildrenProps = { displayFileInModal: (data: FileObject) => void; show: () => void; @@ -238,6 +240,7 @@ function AttachmentModal({ */ const downloadAttachment = useCallback(() => { let sourceURL = sourceState; + console.log('[wildebug] sourceURL', sourceURL) if (isAuthTokenRequiredState && typeof sourceURL === 'string') { sourceURL = addEncryptedAuthTokenToURL(sourceURL); } @@ -530,6 +533,7 @@ function AttachmentModal({ fallbackSource={fallbackSource} isUsedInAttachmentModal transactionID={transaction?.transactionID} + downloadAttachment={() => downloadAttachment()} /> ) diff --git a/src/components/Attachments/AttachmentCarousel/extractAttachments.ts b/src/components/Attachments/AttachmentCarousel/extractAttachments.ts index 1e9c67cf84ac..c5725ccba6db 100644 --- a/src/components/Attachments/AttachmentCarousel/extractAttachments.ts +++ b/src/components/Attachments/AttachmentCarousel/extractAttachments.ts @@ -59,6 +59,9 @@ function extractAttachments( uniqueSources.add(source); let fileName = attribs[CONST.ATTACHMENT_ORIGINAL_FILENAME_ATTRIBUTE] || FileUtils.getFileName(`${source}`); + const width = (attribs['data-expensify-width'] && parseInt(attribs['data-expensify-width'], 10)) || undefined; + const height = (attribs['data-expensify-height'] && parseInt(attribs['data-expensify-height'], 10)) || undefined; + // Public image URLs might lack a file extension in the source URL, without an extension our // AttachmentView fails to recognize them as images and renders fallback content instead. // We apply this small hack to add an image extension and ensure AttachmentView renders the image. @@ -73,7 +76,7 @@ function extractAttachments( reportActionID: attribs['data-id'], source, isAuthTokenRequired: !!expensifySource, - file: {name: fileName}, + file: {name: fileName, width, height}, isReceipt: false, hasBeenFlagged: attribs['data-flagged'] === 'true', }); diff --git a/src/components/Attachments/AttachmentView/index.tsx b/src/components/Attachments/AttachmentView/index.tsx index a7409e57f846..abc64bab926d 100644 --- a/src/components/Attachments/AttachmentView/index.tsx +++ b/src/components/Attachments/AttachmentView/index.tsx @@ -2,6 +2,7 @@ import {Str} from 'expensify-common'; import React, {memo, useEffect, useState} from 'react'; import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; +import Text from '@components/Text'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import type {Attachment, AttachmentSource} from '@components/Attachments/types'; @@ -26,9 +27,24 @@ import AttachmentViewImage from './AttachmentViewImage'; import AttachmentViewPdf from './AttachmentViewPdf'; import AttachmentViewVideo from './AttachmentViewVideo'; import DefaultAttachmentView from './DefaultAttachmentView'; +import getImageResolution from '@libs/fileDownload/getImageResolution'; +import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import ThumbnailImage from '@components/ThumbnailImage'; +import TextLink from '@components/TextLink'; +import Hoverable from '@components/Hoverable'; +import CONST from '@src/CONST'; +import fileDownload from '@libs/fileDownload'; +import * as Download from '@userActions/Download'; +import { ImageSource } from '@rnmapbox/maps'; +import type {Download as OnyxDownload} from '@src/types/onyx'; +import * as FileUtils from '@libs/fileDownload/FileUtils'; + + type AttachmentViewOnyxProps = { transaction: OnyxEntry; + /** If a file download is happening */ + download: OnyxEntry; }; type AttachmentViewProps = AttachmentViewOnyxProps & @@ -70,6 +86,8 @@ type AttachmentViewProps = AttachmentViewOnyxProps & /** Whether the attachment is used as a chat attachment */ isUsedAsChatAttachment?: boolean; + + downloadAttachment?: () => void; }; function AttachmentView({ @@ -92,6 +110,8 @@ function AttachmentView({ isHovered, duration, isUsedAsChatAttachment, + downloadAttachment, + download, }: AttachmentViewProps) { const {translate} = useLocalize(); const {updateCurrentlyPlayingURL} = usePlaybackContext(); @@ -99,8 +119,11 @@ function AttachmentView({ const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const [loadComplete, setLoadComplete] = useState(false); + const [isResolutionValid, setIsResolutionValid] = useState(false); + const [isCalculatingDimension, setIsCalculatingDimension] = useState(true); const [hasPDFFailedToLoad, setHasPDFFailedToLoad] = useState(false); const isVideo = (typeof source === 'string' && Str.isVideo(source)) || (file?.name && Str.isVideo(file.name)); + const { isOffline } = useNetwork(); useEffect(() => { if (!isFocused && !(file && isUsedInAttachmentModal)) { @@ -113,6 +136,47 @@ function AttachmentView({ useNetwork({onReconnect: () => setImageError(false)}); + const isFileHaveDimension = (file: FileObject | undefined): Promise => { + if(!file) { + return Promise.resolve(false); + } + + if ('width' in file && 'height' in file) { + return Promise.resolve(true); + } else { + return getImageResolution(file) + .then(({ width, height }) => { + file.width = width; + file.height = height; + return Promise.resolve(true); + + return true; + }) + .catch(error => { + console.error('Failed to get image resolution:', error); + return Promise.resolve(false); + + return false; + }); + } + }; + + + useEffect(() => { + setIsCalculatingDimension(true); + isFileHaveDimension(file) + .then(isDimensionAvailable => { + const isValid = file && isDimensionAvailable && (file?.height ?? 0) <= 8000 && (file?.width ?? 0) <= 8000; + setIsResolutionValid(isValid ?? false); + setIsCalculatingDimension(false); + }) + .catch(error => { + console.error('Failed to get image resolution:', error); + setIsCalculatingDimension(false); + }); + }, [file]); + + // Handles case where source is a component (ex: SVG) or a number // Number may represent a SVG or an image if (typeof source === 'function' || (maybeIcon && typeof source === 'number')) { @@ -196,21 +260,65 @@ function AttachmentView({ // 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' || (typeof source === 'string' && Str.isImage(source)); - if (isImage || (file?.name && Str.isImage(file.name))) { - if (imageError) { - // AttachmentViewImage can't handle icon fallbacks, so we need to handle it here - if (typeof fallbackSource === 'number' || typeof fallbackSource === 'function') { - return ( - - ); - } + + const isSourceImage = typeof source === 'number' || (typeof source === 'string' && Str.isImage(source)); + const isFileNameImage = file?.name && Str.isImage(file.name); + const isFileImage = isSourceImage || isFileNameImage; + + const isFileNameVideo = file?.name && Str.isVideo(file.name); + const isFileVideo = isVideo || isFileNameVideo; + + if (isFileImage && isCalculatingDimension) { + return + } + + if (isFileImage) { + if (imageError && (typeof fallbackSource === 'number' || typeof fallbackSource === 'function')) { + return ( + + ); + } + const imageSource = imageError && fallbackSource ? (fallbackSource as string) : (source as string) + + if (!isResolutionValid) { + const isLocalFile = FileUtils.isLocalFile(imageSource); + const sourceURLWithAuth = isLocalFile ? imageSource : addEncryptedAuthTokenToURL(imageSource); + const sourceID = (imageSource.match(CONST.REGEX.ATTACHMENT_ID) ?? [])[1]; + const styles = useThemeStyles(); + + const isDownloading = download?.isDownloading ?? false; + + return ( + + + This image has been resized for previewing. + { + // if (isOffline) { + if (isDownloading || isOffline) { + return; + } + + Download.setDownload(sourceID, true); + fileDownload(sourceURLWithAuth, file?.name, '').then(() => Download.setDownload(sourceID, false)); + }} + > Download + for full resolution. + + ) } return ( @@ -219,7 +327,7 @@ function AttachmentView({ file={file} isAuthTokenRequired={isAuthTokenRequired} loadComplete={loadComplete} - isImage={isImage} + isImage={isFileImage} onPress={onPress} onError={() => { setImageError(true); @@ -256,6 +364,12 @@ export default memo( transaction: { key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, }, + download: { + key: ({source}) => { + const sourceID = (source?.toString().match(CONST.REGEX.ATTACHMENT_ID) ?? [])[1]; + return `${ONYXKEYS.COLLECTION.DOWNLOAD}${sourceID}`; + }, + }, })(AttachmentView), ); From 012de0afed2bb58d9ec14f24a54f31f62b4a80db Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 28 Jun 2024 08:26:24 +0700 Subject: [PATCH 08/46] Move hi Res image caption to attachment modal, change local upload behavior --- src/components/AttachmentModal.tsx | 27 +++++++-- .../AttachmentCarousel/CarouselItem.tsx | 3 +- .../AttachmentCarousel/extractAttachments.ts | 2 + .../Attachments/AttachmentCarousel/types.ts | 3 + .../DefaultAttachmentView/index.tsx | 7 ++- .../Attachments/AttachmentView/index.tsx | 58 ++++++------------- src/components/Attachments/types.ts | 2 + 7 files changed, 55 insertions(+), 47 deletions(-) diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index cb8538bac52c..5bfb0e8a881c 100644 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -1,6 +1,6 @@ import {Str} from 'expensify-common'; import React, {memo, useCallback, useEffect, useMemo, useState} from 'react'; -import {Animated, Keyboard, View} from 'react-native'; +import {Animated, Keyboard, View, Text} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; @@ -43,6 +43,7 @@ import * as Expensicons from './Icon/Expensicons'; import * as Illustrations from './Icon/Illustrations'; import Modal from './Modal'; import SafeAreaConsumer from './SafeAreaConsumer'; +import TextLink from './TextLink'; /** * Modal render prop component that exposes modal launching triggers that can be used @@ -197,6 +198,9 @@ function AttachmentModal({ const {translate} = useLocalize(); const {isOffline} = useNetwork(); + const isDimensionAvailable = file && 'width' in file && 'height' in file; + const isHiResImage = isDimensionAvailable && (file.height ?? 0) > 5000 && (file.width ?? 0) > 5000; + useEffect(() => { setFile(originalFileName ? {name: originalFileName} : undefined); }, [originalFileName]); @@ -540,10 +544,23 @@ function AttachmentModal({ ))} {/* If we have an onConfirm method show a confirmation button */} - {!!onConfirm && !isConfirmButtonDisabled && ( + {((!!onConfirm && !isConfirmButtonDisabled) || isHiResImage) && ( - {({safeAreaPaddingBottomStyle}) => ( - + {({ safeAreaPaddingBottomStyle }) => (<> + {isHiResImage && + This image has been resized for previewing. + { + downloadAttachment(); + }} + > Download + for full resolution. + + + } + + {!!onConfirm && !isConfirmButtonDisabled &&