From e4d85a65888d68b656d1f661224da2284a4e89db Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 11 Mar 2024 12:38:01 +0100 Subject: [PATCH 1/4] Revert "Revert "[New architecture] Replace `@oguzhnatly/react-native-image-manipulator` with `expo-image-manipulator`"" This reverts commit 52491bd5da97c3c5465d650eec8ee4119b584299. --- ios/Podfile.lock | 22 ++- package-lock.json | 27 +++- package.json | 2 +- ...react-native-image-manipulator+1.0.5.patch | 19 --- src/CONST.ts | 6 + src/libs/cropOrRotateImage/getSaveFormat.ts | 17 +++ src/libs/cropOrRotateImage/index.native.ts | 6 +- src/libs/cropOrRotateImage/index.ts | 136 ++---------------- src/libs/cropOrRotateImage/types.ts | 25 ++-- 9 files changed, 90 insertions(+), 170 deletions(-) delete mode 100644 patches/@oguzhnatly+react-native-image-manipulator+1.0.5.patch create mode 100644 src/libs/cropOrRotateImage/getSaveFormat.ts diff --git a/ios/Podfile.lock b/ios/Podfile.lock index c93dfba50f5a..491ec28b59e5 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -35,6 +35,9 @@ PODS: - EXAV (13.10.4): - ExpoModulesCore - ReactCommon/turbomodule/core + - EXImageLoader (4.6.0): + - ExpoModulesCore + - React-Core - Expo (50.0.4): - ExpoModulesCore - ExpoImage (1.10.1): @@ -43,6 +46,9 @@ PODS: - SDWebImageAVIFCoder (~> 0.10.1) - SDWebImageSVGCoder (~> 1.7.0) - SDWebImageWebPCoder (~> 0.13.0) + - ExpoImageManipulator (11.8.0): + - EXImageLoader + - ExpoModulesCore - ExpoModulesCore (1.11.8): - glog - RCT-Folly (= 2022.05.16.00) @@ -1175,8 +1181,6 @@ PODS: - React-Core - react-native-geolocation (3.0.6): - React-Core - - react-native-image-manipulator (1.0.5): - - React - react-native-image-picker (7.0.3): - React-Core - react-native-key-command (1.0.6): @@ -1475,8 +1479,10 @@ DEPENDENCIES: - BVLinearGradient (from `../node_modules/react-native-linear-gradient`) - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - EXAV (from `../node_modules/expo-av/ios`) + - EXImageLoader (from `../node_modules/expo-image-loader/ios`) - Expo (from `../node_modules/expo`) - ExpoImage (from `../node_modules/expo-image/ios`) + - ExpoImageManipulator (from `../node_modules/expo-image-manipulator/ios`) - ExpoModulesCore (from `../node_modules/expo-modules-core`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) @@ -1536,7 +1542,6 @@ DEPENDENCIES: - react-native-config (from `../node_modules/react-native-config`) - react-native-document-picker (from `../node_modules/react-native-document-picker`) - "react-native-geolocation (from `../node_modules/@react-native-community/geolocation`)" - - "react-native-image-manipulator (from `../node_modules/@oguzhnatly/react-native-image-manipulator`)" - react-native-image-picker (from `../node_modules/react-native-image-picker`) - react-native-key-command (from `../node_modules/react-native-key-command`) - react-native-launch-arguments (from `../node_modules/react-native-launch-arguments`) @@ -1658,10 +1663,14 @@ EXTERNAL SOURCES: :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" EXAV: :path: "../node_modules/expo-av/ios" + EXImageLoader: + :path: "../node_modules/expo-image-loader/ios" Expo: :path: "../node_modules/expo" ExpoImage: :path: "../node_modules/expo-image/ios" + ExpoImageManipulator: + :path: "../node_modules/expo-image-manipulator/ios" ExpoModulesCore: :path: "../node_modules/expo-modules-core" FBLazyVector: @@ -1731,8 +1740,6 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-document-picker" react-native-geolocation: :path: "../node_modules/@react-native-community/geolocation" - react-native-image-manipulator: - :path: "../node_modules/@oguzhnatly/react-native-image-manipulator" react-native-image-picker: :path: "../node_modules/react-native-image-picker" react-native-key-command: @@ -1860,8 +1867,10 @@ SPEC CHECKSUMS: CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953 EXAV: 09a4d87fa6b113fbb0ada3aade6799f78271cb44 + EXImageLoader: 55080616b2fe9da19ef8c7f706afd9814e279b6b Expo: 1e3bcf9dd99de57a636127057f6b488f0609681a ExpoImage: 1cdaa65a6a70bb01067e21ad1347ff2d973885f5 + ExpoImageManipulator: c1d7cb865eacd620a35659f3da34c70531f10b59 ExpoModulesCore: 96d1751929ad10622773bb729ab28a8423f0dd0c FBLazyVector: fbc4957d9aa695250b55d879c1d86f79d7e69ab4 FBReactNativeSpec: 86de768f89901ef6ed3207cd686362189d64ac88 @@ -1935,7 +1944,6 @@ SPEC CHECKSUMS: react-native-config: 7cd105e71d903104e8919261480858940a6b9c0e react-native-document-picker: 3599b238843369026201d2ef466df53f77ae0452 react-native-geolocation: 0f7fe8a4c2de477e278b0365cce27d089a8c5903 - react-native-image-manipulator: c48f64221cfcd46e9eec53619c4c0374f3328a56 react-native-image-picker: 2381c008bbb09e72395a2d043c147b11bd1523d9 react-native-key-command: 5af6ee30ff4932f78da6a2109017549042932aa5 react-native-launch-arguments: 5f41e0abf88a15e3c5309b8875d6fd5ac43df49d @@ -1999,7 +2007,7 @@ SPEC CHECKSUMS: SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2 VisionCamera: 0a6794d1974aed5d653d0d0cb900493e2583e35a - Yoga: e64aa65de36c0832d04e8c7bd614396c77a80047 + Yoga: 13c8ef87792450193e117976337b8527b49e8c03 PODFILE CHECKSUM: 0ccbb4f2406893c6e9f266dc1e7470dcd72885d2 diff --git a/package-lock.json b/package-lock.json index 0f11dc0a485a..29f5e23eb40f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,6 @@ "@invertase/react-native-apple-authentication": "^2.2.2", "@kie/act-js": "^2.6.0", "@kie/mock-github": "^1.0.0", - "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#5cdae3d4455b03a04c57f50be3863e2fe6c92c52", "@onfido/react-native-sdk": "10.6.0", "@react-native-async-storage/async-storage": "1.21.0", "@react-native-camera-roll/camera-roll": "7.4.0", @@ -56,6 +55,7 @@ "expo": "^50.0.3", "expo-av": "~13.10.4", "expo-image": "1.10.1", + "expo-image-manipulator": "11.8.0", "fbjs": "^3.0.2", "htmlparser2": "^7.2.0", "idb-keyval": "^6.2.1", @@ -8048,12 +8048,6 @@ "@octokit/openapi-types": "^12.11.0" } }, - "node_modules/@oguzhnatly/react-native-image-manipulator": { - "version": "1.0.5", - "resolved": "git+ssh://git@github.com/Expensify/react-native-image-manipulator.git#5cdae3d4455b03a04c57f50be3863e2fe6c92c52", - "integrity": "sha512-C9Br1BQqm6io6lvYHptlLcOHbzlaqxp9tS35P8Qj3pdiiYRTzU3KPvZ61rQ+ZnZ4FOQ6MwPsKsmB8+6WHkAY6Q==", - "license": "MIT" - }, "node_modules/@onfido/active-video-capture": { "version": "0.28.6", "resolved": "https://registry.npmjs.org/@onfido/active-video-capture/-/active-video-capture-0.28.6.tgz", @@ -31150,6 +31144,25 @@ "expo": "*" } }, + "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==", + "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==", + "dependencies": { + "expo-image-loader": "~4.6.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-keep-awake": { "version": "12.8.2", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-12.8.2.tgz", diff --git a/package.json b/package.json index 46e41b04ab2c..93853d88ba67 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ "@invertase/react-native-apple-authentication": "^2.2.2", "@kie/act-js": "^2.6.0", "@kie/mock-github": "^1.0.0", - "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#5cdae3d4455b03a04c57f50be3863e2fe6c92c52", "@onfido/react-native-sdk": "10.6.0", "@react-native-async-storage/async-storage": "1.21.0", "@react-native-camera-roll/camera-roll": "7.4.0", @@ -105,6 +104,7 @@ "expo": "^50.0.3", "expo-av": "~13.10.4", "expo-image": "1.10.1", + "expo-image-manipulator": "11.8.0", "fbjs": "^3.0.2", "htmlparser2": "^7.2.0", "idb-keyval": "^6.2.1", diff --git a/patches/@oguzhnatly+react-native-image-manipulator+1.0.5.patch b/patches/@oguzhnatly+react-native-image-manipulator+1.0.5.patch deleted file mode 100644 index d5a390daf201..000000000000 --- a/patches/@oguzhnatly+react-native-image-manipulator+1.0.5.patch +++ /dev/null @@ -1,19 +0,0 @@ -diff --git a/node_modules/@oguzhnatly/react-native-image-manipulator/android/build.gradle b/node_modules/@oguzhnatly/react-native-image-manipulator/android/build.gradle -index 3a1a548..fe030bb 100644 ---- a/node_modules/@oguzhnatly/react-native-image-manipulator/android/build.gradle -+++ b/node_modules/@oguzhnatly/react-native-image-manipulator/android/build.gradle -@@ -13,12 +13,12 @@ buildscript { - apply plugin: 'com.android.library' - - android { -- compileSdkVersion 28 -+ compileSdkVersion 34 - buildToolsVersion "28.0.3" - - defaultConfig { - minSdkVersion 16 -- targetSdkVersion 28 -+ targetSdkVersion 34 - versionCode 1 - versionName "1.0" - } diff --git a/src/CONST.ts b/src/CONST.ts index ce2029c78713..645746c38a7d 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1035,6 +1035,12 @@ const CONST = { VIDEO: 'video', }, + IMAGE_FILE_FORMAT: { + PNG: 'image/png', + WEBP: 'image/webp', + JPEG: 'image/jpeg', + }, + FILE_TYPE_REGEX: { // Image MimeTypes allowed by iOS photos app. IMAGE: /\.(jpg|jpeg|png|webp|gif|tiff|bmp|heic|heif)$/, diff --git a/src/libs/cropOrRotateImage/getSaveFormat.ts b/src/libs/cropOrRotateImage/getSaveFormat.ts new file mode 100644 index 000000000000..6d1ff280c5c5 --- /dev/null +++ b/src/libs/cropOrRotateImage/getSaveFormat.ts @@ -0,0 +1,17 @@ +import {SaveFormat} from 'expo-image-manipulator'; +import CONST from '@src/CONST'; + +function getSaveFormat(type: string) { + switch (type) { + case CONST.IMAGE_FILE_FORMAT.PNG: + return SaveFormat.PNG; + case CONST.IMAGE_FILE_FORMAT.WEBP: + return SaveFormat.WEBP; + case CONST.IMAGE_FILE_FORMAT.JPEG: + return SaveFormat.JPEG; + default: + return SaveFormat.JPEG; + } +} + +export default getSaveFormat; diff --git a/src/libs/cropOrRotateImage/index.native.ts b/src/libs/cropOrRotateImage/index.native.ts index c3fed59ba4fa..03c233450435 100644 --- a/src/libs/cropOrRotateImage/index.native.ts +++ b/src/libs/cropOrRotateImage/index.native.ts @@ -1,5 +1,6 @@ -import RNImageManipulator from '@oguzhnatly/react-native-image-manipulator'; +import {manipulateAsync} from 'expo-image-manipulator'; import RNFetchBlob from 'react-native-blob-util'; +import getSaveFormat from './getSaveFormat'; import type {CropOrRotateImage} from './types'; /** @@ -7,7 +8,8 @@ import type {CropOrRotateImage} from './types'; */ const cropOrRotateImage: CropOrRotateImage = (uri, actions, options) => new Promise((resolve) => { - RNImageManipulator.manipulate(uri, actions, options).then((result) => { + const format = getSaveFormat(options.type); + manipulateAsync(uri, actions, {compress: options.compress, format}).then((result) => { RNFetchBlob.fs.stat(result.uri.replace('file://', '')).then(({size}) => { resolve({ ...result, diff --git a/src/libs/cropOrRotateImage/index.ts b/src/libs/cropOrRotateImage/index.ts index ea3d51a465ec..1b669999a1ee 100644 --- a/src/libs/cropOrRotateImage/index.ts +++ b/src/libs/cropOrRotateImage/index.ts @@ -1,127 +1,19 @@ -import type {CropOptions, CropOrRotateImage, CropOrRotateImageOptions} from './types'; - -type SizeFromAngle = { - width: number; - height: number; -}; - -/** - * Calculates a size of canvas after rotation - */ -function sizeFromAngle(width: number, height: number, angle: number): SizeFromAngle { - const radians = (angle * Math.PI) / 180; - let sine = Math.cos(radians); - let cosine = Math.sin(radians); - if (cosine < 0) { - cosine = -cosine; - } - if (sine < 0) { - sine = -sine; - } - return {width: height * cosine + width * sine, height: height * sine + width * cosine}; -} - -/** - * Creates a new rotated canvas - */ -function rotateCanvas(canvas: HTMLCanvasElement, degrees: number): HTMLCanvasElement { - const {width, height} = sizeFromAngle(canvas.width, canvas.height, degrees); - - // We have to create a new canvas because it is not possible to change already drawn - // elements. Transformations such as rotation have to be applied before drawing - const result = document.createElement('canvas'); - result.width = width; - result.height = height; - - const context = result.getContext('2d'); - if (context) { - // In order to rotate image along its center we have to apply next transformation - context.translate(result.width / 2, result.height / 2); - - const radians = (degrees * Math.PI) / 180; - context.rotate(radians); - - context.drawImage(canvas, -canvas.width / 2, -canvas.height / 2, canvas.width, canvas.height); - } - return result; -} - -/** - * Creates new cropped canvas and returns it - */ -function cropCanvas(canvas: HTMLCanvasElement, options: CropOptions) { - let {originX = 0, originY = 0, width = 0, height = 0} = options; - const clamp = (value: number, max: number) => Math.max(0, Math.min(max, value)); - - width = clamp(width, canvas.width); - height = clamp(height, canvas.height); - originX = clamp(originX, canvas.width); - originY = clamp(originY, canvas.height); - - width = Math.min(originX + width, canvas.width) - originX; - height = Math.min(originY + height, canvas.height) - originY; - - const result = document.createElement('canvas'); - result.width = width; - result.height = height; - - const context = result.getContext('2d'); - context?.drawImage(canvas, originX, originY, width, height, 0, 0, width, height); - - return result; -} - -function convertCanvasToFile(canvas: HTMLCanvasElement, options: CropOrRotateImageOptions): Promise { - return new Promise((resolve) => { - canvas.toBlob((blob) => { - if (!blob) { - return; - } - const file = new File([blob], options.name || 'fileName.jpeg', {type: options.type || 'image/jpeg'}); - file.uri = URL.createObjectURL(file); - resolve(file); - }); - }); -} - -/** - * Loads image from specified url - */ -function loadImageAsync(uri: string): Promise { - return new Promise((resolve, reject) => { - const imageSource = new Image(); - imageSource.crossOrigin = 'anonymous'; - const canvas = document.createElement('canvas'); - imageSource.onload = () => { - canvas.width = imageSource.naturalWidth; - canvas.height = imageSource.naturalHeight; - - const context = canvas.getContext('2d'); - context?.drawImage(imageSource, 0, 0, imageSource.naturalWidth, imageSource.naturalHeight); - - resolve(canvas); - }; - imageSource.onerror = () => reject(canvas); - imageSource.src = uri; - }); -} - -/** - * Crops and rotates the image on web - */ +import {manipulateAsync} from 'expo-image-manipulator'; +import getSaveFormat from './getSaveFormat'; +import type {CropOrRotateImage} from './types'; const cropOrRotateImage: CropOrRotateImage = (uri, actions, options) => - loadImageAsync(uri).then((originalCanvas) => { - const resultCanvas = actions.reduce((canvas, action) => { - if (action.crop) { - return cropCanvas(canvas, action.crop); - } - if (action.rotate) { - return rotateCanvas(canvas, action.rotate); - } - return canvas; - }, originalCanvas); - return convertCanvasToFile(resultCanvas, options); + new Promise((resolve) => { + const format = getSaveFormat(options.type); + manipulateAsync(uri, actions, {compress: options.compress, format}).then((result) => { + fetch(result.uri) + .then((res) => res.blob()) + .then((blob) => { + const file = new File([blob], options.name || 'fileName.jpeg', {type: options.type || 'image/jpeg'}); + file.uri = URL.createObjectURL(file); + resolve(file); + }); + }); }); export default cropOrRotateImage; diff --git a/src/libs/cropOrRotateImage/types.ts b/src/libs/cropOrRotateImage/types.ts index f882e4f9bea2..9d9b9ff98080 100644 --- a/src/libs/cropOrRotateImage/types.ts +++ b/src/libs/cropOrRotateImage/types.ts @@ -1,4 +1,4 @@ -import type {RNImageManipulatorResult} from '@oguzhnatly/react-native-image-manipulator'; +import type {ImageResult} from 'expo-image-manipulator'; type CropOrRotateImageOptions = { type: string; @@ -6,20 +6,21 @@ type CropOrRotateImageOptions = { compress: number; }; -type CropOptions = { - originX: number; - originY: number; - width: number; - height: number; +type CropAction = { + crop: { + originX: number; + originY: number; + width: number; + height: number; + }; }; -type Action = { - crop?: CropOptions; - rotate?: number; -}; +type RotateOption = {rotate: number}; + +type Action = CropAction | RotateOption; -type CustomRNImageManipulatorResult = RNImageManipulatorResult & {size: number; type: string; name: string}; +type CustomRNImageManipulatorResult = ImageResult & {size: number; type: string; name: string}; type CropOrRotateImage = (uri: string, actions: Action[], options: CropOrRotateImageOptions) => Promise; -export type {CropOrRotateImage, CropOptions, Action, CropOrRotateImageOptions, CustomRNImageManipulatorResult}; +export type {CustomRNImageManipulatorResult, CropOrRotateImage}; From ce2d2ea6bf0eba7ec6150436f67d45d5c4d5dd28 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 12 Mar 2024 19:44:38 +0100 Subject: [PATCH 2/4] fix crash on the release build --- src/libs/actions/PersonalDetails.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index 5becaee1593c..b58bace9ce67 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -382,7 +382,14 @@ function updateAvatar(file: File | CustomRNImageManipulatorResult) { }, ]; - const parameters: UpdateUserAvatarParams = {file}; + let parameters: UpdateUserAvatarParams = {file}; + + // We need to remove the base64 value from the file object, as it is causing crashes on Release builds. + // More info: https://github.com/Expensify/App/issues/37963#issuecomment-1989260033 + if ('base64' in file) { + const {base64, ...fileWithoutBase64} = file; + parameters = {file: fileWithoutBase64}; + } API.write(WRITE_COMMANDS.UPDATE_USER_AVATAR, parameters, {optimisticData, successData, failureData}); } From eb61a5499e0c727de69beacfbe2f7cefeb6871cb Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 13 Mar 2024 08:46:04 +0100 Subject: [PATCH 3/4] refactor: remove base64 in cropOrRotateImage --- src/libs/actions/PersonalDetails.ts | 9 +-------- src/libs/cropOrRotateImage/index.native.ts | 4 +++- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index b58bace9ce67..5becaee1593c 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -382,14 +382,7 @@ function updateAvatar(file: File | CustomRNImageManipulatorResult) { }, ]; - let parameters: UpdateUserAvatarParams = {file}; - - // We need to remove the base64 value from the file object, as it is causing crashes on Release builds. - // More info: https://github.com/Expensify/App/issues/37963#issuecomment-1989260033 - if ('base64' in file) { - const {base64, ...fileWithoutBase64} = file; - parameters = {file: fileWithoutBase64}; - } + const parameters: UpdateUserAvatarParams = {file}; API.write(WRITE_COMMANDS.UPDATE_USER_AVATAR, parameters, {optimisticData, successData, failureData}); } diff --git a/src/libs/cropOrRotateImage/index.native.ts b/src/libs/cropOrRotateImage/index.native.ts index 03c233450435..f418d4d3358b 100644 --- a/src/libs/cropOrRotateImage/index.native.ts +++ b/src/libs/cropOrRotateImage/index.native.ts @@ -9,7 +9,9 @@ import type {CropOrRotateImage} from './types'; const cropOrRotateImage: CropOrRotateImage = (uri, actions, options) => new Promise((resolve) => { const format = getSaveFormat(options.type); - manipulateAsync(uri, actions, {compress: options.compress, format}).then((result) => { + // We need to remove the base64 value from the result, as it is causing crashes on Release builds. + // More info: https://github.com/Expensify/App/issues/37963#issuecomment-1989260033 + manipulateAsync(uri, actions, {compress: options.compress, format}).then(({base64, ...result}) => { RNFetchBlob.fs.stat(result.uri.replace('file://', '')).then(({size}) => { resolve({ ...result, From 9e7f66668575312528ee26a194c79fa333107d53 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:52:47 +0100 Subject: [PATCH 4/4] add type --- src/libs/cropOrRotateImage/getSaveFormat.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/cropOrRotateImage/getSaveFormat.ts b/src/libs/cropOrRotateImage/getSaveFormat.ts index 6d1ff280c5c5..0ea0b2ef6ffb 100644 --- a/src/libs/cropOrRotateImage/getSaveFormat.ts +++ b/src/libs/cropOrRotateImage/getSaveFormat.ts @@ -1,7 +1,7 @@ import {SaveFormat} from 'expo-image-manipulator'; import CONST from '@src/CONST'; -function getSaveFormat(type: string) { +function getSaveFormat(type: string): SaveFormat { switch (type) { case CONST.IMAGE_FILE_FORMAT.PNG: return SaveFormat.PNG;