From 7d2e1a0b788182616057d492f0a7b9eb7f8352d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kali=C5=84ski?= Date: Tue, 17 Mar 2026 18:38:59 +0100 Subject: [PATCH 1/3] fix: provide image type when setting odometer expense receipt --- src/libs/stitchOdometerImages/index.native.ts | 4 ++-- .../iou/request/step/IOURequestStepDistanceOdometer.tsx | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libs/stitchOdometerImages/index.native.ts b/src/libs/stitchOdometerImages/index.native.ts index ed02089e95ce1..9bfea3d6a4767 100644 --- a/src/libs/stitchOdometerImages/index.native.ts +++ b/src/libs/stitchOdometerImages/index.native.ts @@ -1,4 +1,4 @@ -import {Skia} from '@shopify/react-native-skia'; +import {ImageFormat, Skia} from '@shopify/react-native-skia'; import RNFS from 'react-native-fs'; import Log from '@libs/Log'; import type {FileObject} from '@src/types/utils/Attachment'; @@ -41,7 +41,7 @@ async function stitchOdometerImages(image1: FileObject | string | undefined, ima surface.flush(); snapshot = surface.makeImageSnapshot(); - const base64 = snapshot.encodeToBase64(); + const base64 = snapshot.encodeToBase64(ImageFormat.JPEG); // Delete any previously stitched files before creating a new one try { diff --git a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx index 421d7a07cf129..a94d9a4d35267 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx @@ -394,7 +394,11 @@ function IOURequestStepDistanceOdometer({ (typeof odometerStartImage !== 'string' ? odometerStartImage?.name : odometerStartImage?.split('/').pop()) ?? (typeof odometerEndImage !== 'string' ? odometerEndImage?.name : odometerEndImage?.split('/').pop()) ?? ''; - setMoneyRequestReceipt(transactionID, uri, name, isTransactionDraft); + const type = + stitchedImage?.type ?? + (typeof odometerStartImage !== 'string' ? odometerStartImage?.type : undefined) ?? + (typeof odometerEndImage !== 'string' ? odometerEndImage?.type : undefined); + setMoneyRequestReceipt(transactionID, uri, name, isTransactionDraft, type); } if (isEditing) { From 1b039381dd49e5c3507c8ecd3699c1be2384536f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kali=C5=84ski?= Date: Wed, 18 Mar 2026 13:29:51 +0100 Subject: [PATCH 2/3] fix: fix odometer receipt MIME type on native for single odometer image --- src/libs/stitchOdometerImages/index.native.ts | 2 +- .../step/IOURequestStepDistanceOdometer.tsx | 3 ++- .../index.native.tsx | 20 +++++++++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/libs/stitchOdometerImages/index.native.ts b/src/libs/stitchOdometerImages/index.native.ts index 9bfea3d6a4767..2e86a4bc5c39f 100644 --- a/src/libs/stitchOdometerImages/index.native.ts +++ b/src/libs/stitchOdometerImages/index.native.ts @@ -41,7 +41,7 @@ async function stitchOdometerImages(image1: FileObject | string | undefined, ima surface.flush(); snapshot = surface.makeImageSnapshot(); - const base64 = snapshot.encodeToBase64(ImageFormat.JPEG); + const base64 = snapshot.encodeToBase64(ImageFormat.JPEG, 100); // Delete any previously stitched files before creating a new one try { diff --git a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx index a94d9a4d35267..1e81210cda5dc 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx @@ -397,7 +397,8 @@ function IOURequestStepDistanceOdometer({ const type = stitchedImage?.type ?? (typeof odometerStartImage !== 'string' ? odometerStartImage?.type : undefined) ?? - (typeof odometerEndImage !== 'string' ? odometerEndImage?.type : undefined); + (typeof odometerEndImage !== 'string' ? odometerEndImage?.type : undefined) ?? + 'image/jpeg'; setMoneyRequestReceipt(transactionID, uri, name, isTransactionDraft, type); } diff --git a/src/pages/iou/request/step/IOURequestStepOdometerImage/index.native.tsx b/src/pages/iou/request/step/IOURequestStepOdometerImage/index.native.tsx index cfedd87c414db..d0c9a512fb852 100644 --- a/src/pages/iou/request/step/IOURequestStepOdometerImage/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepOdometerImage/index.native.tsx @@ -183,8 +183,10 @@ function IOURequestStepOdometerImage({ } const file = files.at(0); - const imageUri = (file as {uri?: string}).uri ?? ''; - setMoneyRequestOdometerImage(transactionID, imageType, imageUri, isTransactionDraft); + if (!file) { + return; + } + setMoneyRequestOdometerImage(transactionID, imageType, file, isTransactionDraft); navigateBack(); }; @@ -237,8 +239,18 @@ function IOURequestStepOdometerImage({ .then((photo: PhotoFile) => { const imageObject: ImageObject = {file: photo, filename: photo.path, source: getPhotoSource(photo.path)}; cropImageToAspectRatio(imageObject, viewfinderLayout.current?.width, viewfinderLayout.current?.height, undefined, photo.orientation) - .then(({source}) => { - setMoneyRequestOdometerImage(transactionID, imageType, source, isTransactionDraft); + .then(({file, filename, source}) => { + setMoneyRequestOdometerImage( + transactionID, + imageType, + { + uri: source, + name: filename, + type: (file as FileObject | undefined)?.type ?? 'image/jpeg', + size: (file as FileObject | undefined)?.size, + }, + isTransactionDraft, + ); navigateBack(); }) .catch((error: unknown) => { From b023c9b9750098b296b075fae4e63a97b58be0ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kali=C5=84ski?= Date: Thu, 19 Mar 2026 13:09:00 +0100 Subject: [PATCH 3/3] fix: derive MIME type from URI --- src/CONST/index.ts | 1 + src/libs/fileDownload/FileUtils.ts | 22 +++++++++++++++++++ .../step/IOURequestStepDistanceOdometer.tsx | 5 +++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 27466d9f12abf..87e26142fcab7 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -2294,6 +2294,7 @@ const CONST = { TIF: 'image/tif', TIFF: 'image/tiff', HEIC: 'image/heic', + HEIF: 'image/heif', IMG: 'image/*', PDF: 'application/pdf', MSWORD: 'application/msword', diff --git a/src/libs/fileDownload/FileUtils.ts b/src/libs/fileDownload/FileUtils.ts index 9c6fb7417a196..4c54c3c1f53e7 100644 --- a/src/libs/fileDownload/FileUtils.ts +++ b/src/libs/fileDownload/FileUtils.ts @@ -170,6 +170,27 @@ function getMimeType(fileExtension: string): string { return MIME_TYPES[ext] ?? 'application/octet-stream'; } +/** + * Derives an image MIME type from a file URI or filename. + * Returns undefined for unrecognized extensions so callers can apply their own fallback. + */ +function getMimeTypeFromUri(uri: string): string | undefined { + const {fileExtension} = splitExtensionFromFileName(uri.split('/').pop() ?? uri); + const ext = fileExtension.toLowerCase(); + const IMAGE_EXTENSION_TO_MIME: Record = { + jpg: CONST.SHARE_FILE_MIMETYPE.JPEG, + jpeg: CONST.SHARE_FILE_MIMETYPE.JPEG, + png: CONST.SHARE_FILE_MIMETYPE.PNG, + heic: CONST.SHARE_FILE_MIMETYPE.HEIC, + heif: CONST.SHARE_FILE_MIMETYPE.HEIF, + webp: CONST.SHARE_FILE_MIMETYPE.WEBP, + gif: CONST.SHARE_FILE_MIMETYPE.GIF, + tif: CONST.SHARE_FILE_MIMETYPE.TIF, + tiff: CONST.SHARE_FILE_MIMETYPE.TIFF, + }; + return ext ? IMAGE_EXTENSION_TO_MIME[ext] : undefined; +} + /** * Returns the filename replacing special characters with underscore */ @@ -904,6 +925,7 @@ export { validateReceipt, validateAttachment, normalizeFileObject, + getMimeTypeFromUri, isValidReceiptExtension, getFileValidationErrorText, hasHeicOrHeifExtension, diff --git a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx index 1e81210cda5dc..fa7a76beef3ad 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx @@ -29,6 +29,7 @@ import {setMoneyRequestDistance, setMoneyRequestOdometerReading, setMoneyRequest import {handleMoneyRequestStepDistanceNavigation} from '@libs/actions/IOU/MoneyRequest'; import {setDraftSplitTransaction} from '@libs/actions/IOU/Split'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; +import {getMimeTypeFromUri} from '@libs/fileDownload/FileUtils'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {shouldUseTransactionDraft} from '@libs/IOUUtils'; import Log from '@libs/Log'; @@ -396,8 +397,8 @@ function IOURequestStepDistanceOdometer({ ''; const type = stitchedImage?.type ?? - (typeof odometerStartImage !== 'string' ? odometerStartImage?.type : undefined) ?? - (typeof odometerEndImage !== 'string' ? odometerEndImage?.type : undefined) ?? + (typeof odometerStartImage !== 'string' ? odometerStartImage?.type : getMimeTypeFromUri(odometerStartImage)) ?? + (typeof odometerEndImage !== 'string' ? odometerEndImage?.type : getMimeTypeFromUri(odometerEndImage)) ?? 'image/jpeg'; setMoneyRequestReceipt(transactionID, uri, name, isTransactionDraft, type); }