From 203b20f573e526b5ab04569a3597594f5924c1af Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 31 Mar 2025 10:00:08 +0200 Subject: [PATCH 1/6] replace deprecated .manipulateAsync with .manipulate --- .../AttachmentPicker/index.native.tsx | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx index 70966a05b918..c1aeb2dba08d 100644 --- a/src/components/AttachmentPicker/index.native.tsx +++ b/src/components/AttachmentPicker/index.native.tsx @@ -1,5 +1,5 @@ import {Str} from 'expensify-common'; -import {manipulateAsync, SaveFormat} from 'expo-image-manipulator'; +import {ImageManipulator, SaveFormat} from 'expo-image-manipulator'; import React, {useCallback, useMemo, useRef, useState} from 'react'; import {Alert, View} from 'react-native'; import RNFetchBlob from 'react-native-blob-util'; @@ -121,7 +121,6 @@ function AttachmentPicker({ const [isVisible, setIsVisible] = useState(false); const StyleUtils = useStyleUtils(); const theme = useTheme(); - const completeAttachmentSelection = useRef<(data: FileObject[]) => void>(() => {}); const onModalHide = useRef<() => void>(); const onCanceled = useRef<() => void>(() => {}); @@ -178,26 +177,33 @@ function AttachmentPicker({ .then((isHEIC) => { // react-native-image-picker incorrectly changes file extension without transcoding the HEIC file, so we are doing it manually if we detect HEIC signature if (isHEIC && targetAssetUri) { - manipulateAsync(targetAssetUri, [], {format: SaveFormat.JPEG}) - .then((manipResult) => { - const uri = manipResult.uri; - const convertedAsset = { - uri, - name: uri - .substring(uri.lastIndexOf('/') + 1) - .split('?') - .at(0), - type: 'image/jpeg', - width: manipResult.width, - height: manipResult.height, - }; - - return resolve([convertedAsset]); - }) - .catch((err) => reject(err)); - } else { - return resolve(response.assets); + const manipulateContext = ImageManipulator.manipulate(targetAssetUri); + + manipulateContext.renderAsync().then((image) => + image + .saveAsync({format: SaveFormat.JPEG}) + .then((result) => { + manipulateContext.release(); + image.release(); + return result; + }) + .then((manipResult) => { + const uri = manipResult.uri; + const convertedAsset = { + uri, + name: uri + .substring(uri.lastIndexOf('/') + 1) + .split('?') + .at(0), + type: 'image/jpeg', + width: manipResult.width, + height: manipResult.height, + }; + return resolve([convertedAsset]); + }), + ); } + return resolve(response.assets); }) .catch((err) => reject(err)); } else { From 7bad6a432bcbef26e1b0d556b5d64c2f769b6256 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 31 Mar 2025 14:36:07 +0200 Subject: [PATCH 2/6] add loader when picked an image --- .../request/step/IOURequestStepScan/index.native.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index e4cff715f9a3..cf026a94b96c 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -203,8 +203,12 @@ function IOURequestStepScan({ return () => { subscription.remove(); + + if (isLoadingReceipt) { + setIsLoadingReceipt(false); + } }; - }, []), + }, [isLoadingReceipt]), ); const validateReceipt = (file: FileObject) => { @@ -547,13 +551,10 @@ function IOURequestStepScan({ return; } - // With the image size > 24MB, we use manipulateAsync to resize the image. - // It takes a long time so we should display a loading indicator while the resize image progresses. - if (Str.isImage(originalFile.name ?? '') && (originalFile?.size ?? 0) > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) { + if (Str.isImage(originalFile.name ?? '')) { setIsLoadingReceipt(true); } resizeImageIfNeeded(originalFile).then((file) => { - setIsLoadingReceipt(false); // Store the receipt on the transaction object in Onyx // On Android devices, fetching blob for a file with name containing spaces fails to retrieve the type of file. // So, let us also save the file type in receipt for later use during blob fetch From 3a7096fef8deb5e939ad7f658fe512ed126ae468 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Tue, 1 Apr 2025 10:57:43 +0200 Subject: [PATCH 3/6] add loader when picking an image --- src/components/AttachmentPicker/index.native.tsx | 4 +++- src/components/AttachmentPicker/types.ts | 3 +++ .../iou/request/step/IOURequestStepScan/index.native.tsx | 7 ++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx index c1aeb2dba08d..1aba9e8e757b 100644 --- a/src/components/AttachmentPicker/index.native.tsx +++ b/src/components/AttachmentPicker/index.native.tsx @@ -116,6 +116,7 @@ function AttachmentPicker({ shouldValidateImage = true, shouldHideGalleryOption = false, fileLimit = 1, + onOpenPicker, }: AttachmentPickerProps) { const styles = useThemeStyles(); const [isVisible, setIsVisible] = useState(false); @@ -394,6 +395,7 @@ function AttachmentPicker({ */ const selectItem = useCallback( (item: Item) => { + onOpenPicker?.(); /* setTimeout delays execution to the frame after the modal closes * without this on iOS closing the modal closes the gallery/camera as well */ onModalHide.current = () => { @@ -406,7 +408,7 @@ function AttachmentPicker({ }; close(); }, - [pickAttachment], + [pickAttachment, onOpenPicker], ); useKeyboardShortcut( diff --git a/src/components/AttachmentPicker/types.ts b/src/components/AttachmentPicker/types.ts index 1e2e65761527..1958a94ac883 100644 --- a/src/components/AttachmentPicker/types.ts +++ b/src/components/AttachmentPicker/types.ts @@ -55,6 +55,9 @@ type AttachmentPickerProps = { /** Whether to allow multiple files to be selected. */ fileLimit?: number; + + /** A callback that will be called when the picker is opened. */ + onOpenPicker?: () => void; }; export default AttachmentPickerProps; diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index cf026a94b96c..4483070285cf 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -550,10 +550,6 @@ function IOURequestStepScan({ setPdfFile(originalFile); return; } - - if (Str.isImage(originalFile.name ?? '')) { - setIsLoadingReceipt(true); - } resizeImageIfNeeded(originalFile).then((file) => { // Store the receipt on the transaction object in Onyx // On Android devices, fetching blob for a file with name containing spaces fails to retrieve the type of file. @@ -783,7 +779,7 @@ function IOURequestStepScan({ - + setIsLoadingReceipt(true)}> {({openPicker}) => ( { openPicker({ onPicked: (data) => setReceiptAndNavigate(data.at(0) ?? {}), + onCanceled: () => setIsLoadingReceipt(false), }); }} > From 921d1f8ec01a154fa8d8adc137d95eaf41779ef2 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Tue, 1 Apr 2025 11:24:30 +0200 Subject: [PATCH 4/6] fix lint-changed job --- src/components/AttachmentPicker/index.native.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx index 1aba9e8e757b..88efdca45a87 100644 --- a/src/components/AttachmentPicker/index.native.tsx +++ b/src/components/AttachmentPicker/index.native.tsx @@ -19,7 +19,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as FileUtils from '@libs/fileDownload/FileUtils'; +import {cleanFileName, showCameraPermissionsAlert, verifyFileFormat} from '@libs/fileDownload/FileUtils'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import type IconAsset from '@src/types/utils/IconAsset'; @@ -85,7 +85,7 @@ const getDocumentPickerOptions = (type: string, fileLimit: number): DocumentPick const getDataForUpload = (fileData: FileResponse): Promise => { const fileName = fileData.name || 'chat_attachment'; const fileResult: FileObject = { - name: FileUtils.cleanFileName(fileName), + name: cleanFileName(fileName), type: fileData.type, width: fileData.width, height: fileData.height, @@ -156,7 +156,7 @@ function AttachmentPicker({ if (response.errorCode) { switch (response.errorCode) { case 'permission': - FileUtils.showCameraPermissionsAlert(); + showCameraPermissionsAlert(); return resolve(); default: showGeneralAlert(); @@ -174,7 +174,7 @@ function AttachmentPicker({ } if (targetAsset?.type?.startsWith('image')) { - FileUtils.verifyFileFormat({fileUri: targetAssetUri, formatSignatures: CONST.HEIC_SIGNATURES}) + verifyFileFormat({fileUri: targetAssetUri, formatSignatures: CONST.HEIC_SIGNATURES}) .then((isHEIC) => { // react-native-image-picker incorrectly changes file extension without transcoding the HEIC file, so we are doing it manually if we detect HEIC signature if (isHEIC && targetAssetUri) { From 4acd6914394ea425925dc2daad51c2c9101bb5b8 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 2 Apr 2025 08:17:52 +0200 Subject: [PATCH 5/6] Revert "add loader when picking an image" This reverts commit 3a7096fef8deb5e939ad7f658fe512ed126ae468. --- src/components/AttachmentPicker/index.native.tsx | 4 +--- src/components/AttachmentPicker/types.ts | 3 --- .../step/IOURequestStepScan/index.native.tsx | 16 +++++++++------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx index 88efdca45a87..b6a72a3ad508 100644 --- a/src/components/AttachmentPicker/index.native.tsx +++ b/src/components/AttachmentPicker/index.native.tsx @@ -116,7 +116,6 @@ function AttachmentPicker({ shouldValidateImage = true, shouldHideGalleryOption = false, fileLimit = 1, - onOpenPicker, }: AttachmentPickerProps) { const styles = useThemeStyles(); const [isVisible, setIsVisible] = useState(false); @@ -395,7 +394,6 @@ function AttachmentPicker({ */ const selectItem = useCallback( (item: Item) => { - onOpenPicker?.(); /* setTimeout delays execution to the frame after the modal closes * without this on iOS closing the modal closes the gallery/camera as well */ onModalHide.current = () => { @@ -408,7 +406,7 @@ function AttachmentPicker({ }; close(); }, - [pickAttachment, onOpenPicker], + [pickAttachment], ); useKeyboardShortcut( diff --git a/src/components/AttachmentPicker/types.ts b/src/components/AttachmentPicker/types.ts index 1958a94ac883..1e2e65761527 100644 --- a/src/components/AttachmentPicker/types.ts +++ b/src/components/AttachmentPicker/types.ts @@ -55,9 +55,6 @@ type AttachmentPickerProps = { /** Whether to allow multiple files to be selected. */ fileLimit?: number; - - /** A callback that will be called when the picker is opened. */ - onOpenPicker?: () => void; }; export default AttachmentPickerProps; diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index aa9442d6bd2b..8d902955e764 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -203,12 +203,8 @@ function IOURequestStepScan({ return () => { subscription.remove(); - - if (isLoadingReceipt) { - setIsLoadingReceipt(false); - } }; - }, [isLoadingReceipt]), + }, []), ); const validateReceipt = (file: FileObject) => { @@ -554,7 +550,14 @@ function IOURequestStepScan({ setPdfFile(originalFile); return; } + + // With the image size > 24MB, we use manipulateAsync to resize the image. + // It takes a long time so we should display a loading indicator while the resize image progresses. + if (Str.isImage(originalFile.name ?? '') && (originalFile?.size ?? 0) > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) { + setIsLoadingReceipt(true); + } resizeImageIfNeeded(originalFile).then((file) => { + setIsLoadingReceipt(false); // Store the receipt on the transaction object in Onyx // On Android devices, fetching blob for a file with name containing spaces fails to retrieve the type of file. // So, let us also save the file type in receipt for later use during blob fetch @@ -783,7 +786,7 @@ function IOURequestStepScan({ - setIsLoadingReceipt(true)}> + {({openPicker}) => ( { openPicker({ onPicked: (data) => setReceiptAndNavigate(data.at(0) ?? {}), - onCanceled: () => setIsLoadingReceipt(false), }); }} > From 215a62f62f78e7e9f08f5f3aa4ec83d2013efb61 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 2 Apr 2025 14:07:42 +0200 Subject: [PATCH 6/6] release manipulate context when error --- src/components/AttachmentPicker/index.native.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx index b6a72a3ad508..6aa65e1f4977 100644 --- a/src/components/AttachmentPicker/index.native.tsx +++ b/src/components/AttachmentPicker/index.native.tsx @@ -200,6 +200,10 @@ function AttachmentPicker({ height: manipResult.height, }; return resolve([convertedAsset]); + }) + .catch(() => { + manipulateContext?.release(); + resolve(response.assets ?? []); }), ); }