diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index f39728e7d31c..905b647d0d20 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -328,9 +328,9 @@ function setMoneyRequestParticipants_temporaryForRefactor(transactionID: string, Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants}); } -function setMoneyRequestReceipt(transactionID: string, source: string, filename: string, isDraft: boolean) { +function setMoneyRequestReceipt(transactionID: string, source: string, filename: string, isDraft: boolean, type?: string) { Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, { - receipt: {source}, + receipt: {source, type: type ?? ''}, filename, }); } @@ -4066,6 +4066,7 @@ function navigateToStartStepIfScanFileCannotBeRead( iouType: ValueOf, transactionID: string, reportID: string, + receiptType: string, ) { if (!receiptFilename || !receiptPath) { return; @@ -4079,7 +4080,7 @@ function navigateToStartStepIfScanFileCannotBeRead( } IOUUtils.navigateToStartMoneyRequestStep(requestType, iouType, transactionID, reportID); }; - FileUtils.readFileAsync(receiptPath, receiptFilename, onSuccess, onFailure); + FileUtils.readFileAsync(receiptPath, receiptFilename, onSuccess, onFailure, receiptType); } /** Save the preferred payment method for a policy */ diff --git a/src/libs/fileDownload/FileUtils.ts b/src/libs/fileDownload/FileUtils.ts index 055abf140e64..ad0e08b990d3 100644 --- a/src/libs/fileDownload/FileUtils.ts +++ b/src/libs/fileDownload/FileUtils.ts @@ -159,7 +159,7 @@ function appendTimeToFileName(fileName: string): string { * @param path - the blob url of the locally uploaded file * @param fileName - name of the file to read */ -const readFileAsync: ReadFileAsync = (path, fileName, onSuccess, onFailure = () => {}) => +const readFileAsync: ReadFileAsync = (path, fileName, onSuccess, onFailure = () => {}, fileType = '') => new Promise((resolve) => { if (!path) { resolve(); @@ -176,7 +176,9 @@ const readFileAsync: ReadFileAsync = (path, fileName, onSuccess, onFailure = () } res.blob() .then((blob) => { - const file = new File([blob], cleanFileName(fileName), {type: blob.type}); + // On Android devices, fetching blob for a file with name containing spaces fails to retrieve the type of file. + // In this case, let us fallback on fileType provided by the caller of this function. + const file = new File([blob], cleanFileName(fileName), {type: blob.type || fileType}); file.source = path; // For some reason, the File object on iOS does not have a uri property // so images aren't uploaded correctly to the backend diff --git a/src/libs/fileDownload/types.ts b/src/libs/fileDownload/types.ts index 6d92bddd5816..f09ce495795b 100644 --- a/src/libs/fileDownload/types.ts +++ b/src/libs/fileDownload/types.ts @@ -8,7 +8,7 @@ type GetImageResolution = (url: File | Asset) => Promise; type ExtensionAndFileName = {fileName: string; fileExtension: string}; type SplitExtensionFromFileName = (fileName: string) => ExtensionAndFileName; -type ReadFileAsync = (path: string, fileName: string, onSuccess: (file: File) => void, onFailure: (error?: unknown) => void) => Promise; +type ReadFileAsync = (path: string, fileName: string, onSuccess: (file: File) => void, onFailure: (error?: unknown) => void, fileType?: string) => Promise; type AttachmentDetails = { previewSourceURL: null | string; diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index 98bb6851d0de..95267d3a0807 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -92,6 +92,7 @@ function IOURequestStepConfirmation({ const [receiptFile, setReceiptFile] = useState(); const receiptFilename = lodashGet(transaction, 'filename'); const receiptPath = lodashGet(transaction, 'receipt.source'); + const receiptType = lodashGet(transaction, 'receipt.type'); const transactionTaxCode = transaction.taxRate && transaction.taxRate.keyForList; const transactionTaxAmount = transaction.taxAmount; const requestType = TransactionUtils.getRequestType(transaction); @@ -179,8 +180,8 @@ function IOURequestStepConfirmation({ setReceiptFile(receipt); }; - IOU.navigateToStartStepIfScanFileCannotBeRead(receiptFilename, receiptPath, onSuccess, requestType, iouType, transactionID, reportID); - }, [receiptPath, receiptFilename, requestType, iouType, transactionID, reportID]); + IOU.navigateToStartStepIfScanFileCannotBeRead(receiptFilename, receiptPath, onSuccess, requestType, iouType, transactionID, reportID, receiptType); + }, [receiptType, receiptPath, receiptFilename, requestType, iouType, transactionID, reportID]); useEffect(() => { const policyExpenseChat = _.find(participants, (participant) => participant.isPolicyExpenseChat); diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 5f1b22cab128..29719313563e 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -42,13 +42,14 @@ function IOURequestStepParticipants({ const headerTitle = translate(TransactionUtils.getHeaderTitleTranslationKey(transaction)); const receiptFilename = lodashGet(transaction, 'filename'); const receiptPath = lodashGet(transaction, 'receipt.source'); + const receiptType = lodashGet(transaction, 'receipt.type'); // When the component mounts, if there is a receipt, see if the image can be read from the disk. If not, redirect the user to the starting step of the flow. // This is because until the request is saved, the receipt file is only stored in the browsers memory as a blob:// and if the browser is refreshed, then // the image ceases to exist. The best way for the user to recover from this is to start over from the start of the request process. useEffect(() => { - IOU.navigateToStartStepIfScanFileCannotBeRead(receiptFilename, receiptPath, () => {}, iouRequestType, iouType, transactionID, reportID); - }, [receiptPath, receiptFilename, iouRequestType, iouType, transactionID, reportID]); + IOU.navigateToStartStepIfScanFileCannotBeRead(receiptFilename, receiptPath, () => {}, iouRequestType, iouType, transactionID, reportID, receiptType); + }, [receiptType, receiptPath, receiptFilename, iouRequestType, iouType, transactionID, reportID]); const addParticipant = useCallback( (val) => { diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.js b/src/pages/iou/request/step/IOURequestStepScan/index.native.js index b23420b5ef69..181d8edc22f2 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.js +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.js @@ -211,7 +211,9 @@ function IOURequestStepScan({ } // Store the receipt on the transaction object in Onyx - IOU.setMoneyRequestReceipt(transactionID, file.uri, file.name, action !== CONST.IOU.ACTION.EDIT); + // 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 + IOU.setMoneyRequestReceipt(transactionID, file.uri, file.name, action !== CONST.IOU.ACTION.EDIT, file.type); if (action === CONST.IOU.ACTION.EDIT) { updateScanAndNavigate(file, file.uri); diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index ee6a7f2d0b4c..29bc6e3f922b 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -48,6 +48,7 @@ type Receipt = { source?: ReceiptSource; filename?: string; state?: ValueOf; + type?: string; }; type Route = {