From 22251e6f10b7e87f870b10b48f9c48e57c57580e Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Wed, 14 Feb 2024 21:45:40 +0530 Subject: [PATCH 1/3] fix receipt upload error when file name contain spaces --- src/libs/actions/IOU.ts | 7 ++++--- src/libs/fileDownload/FileUtils.ts | 6 ++++-- src/libs/fileDownload/types.ts | 2 +- src/pages/iou/request/step/IOURequestStepConfirmation.js | 5 +++-- src/pages/iou/request/step/IOURequestStepParticipants.js | 5 +++-- .../iou/request/step/IOURequestStepScan/index.native.js | 4 +++- src/types/onyx/Transaction.ts | 1 + 7 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 7fca6614f1a1..3473cc567c32 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -324,9 +324,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, }); } @@ -3824,6 +3824,7 @@ function navigateToStartStepIfScanFileCannotBeRead( iouType: ValueOf, transactionID: string, reportID: string, + receiptType: string, ) { if (!receiptFilename || !receiptPath) { return; @@ -3837,7 +3838,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..d0ecd7c51e6a 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 ? 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..9a62e775a6ea 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 cf4e6a461bc1..d533131c2ede 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); @@ -171,8 +172,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 b559346a48de..c4fb2af36002 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -47,6 +47,7 @@ type Receipt = { source?: ReceiptSource; filename?: string; state?: ValueOf; + type?: string; }; type Route = { From 33abc9ebf648de613a5f1481366cf38b74bf9533 Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Fri, 23 Feb 2024 00:11:22 +0530 Subject: [PATCH 2/3] minor refactor Co-authored-by: Situ Chandra Shil <108292595+situchan@users.noreply.github.com> --- src/libs/fileDownload/FileUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/fileDownload/FileUtils.ts b/src/libs/fileDownload/FileUtils.ts index d0ecd7c51e6a..ad0e08b990d3 100644 --- a/src/libs/fileDownload/FileUtils.ts +++ b/src/libs/fileDownload/FileUtils.ts @@ -178,7 +178,7 @@ const readFileAsync: ReadFileAsync = (path, fileName, onSuccess, onFailure = () .then((blob) => { // 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 ? blob.type : fileType}); + 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 From bed6a9801f76b642b27c0f45a8a663684ff7dd91 Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Fri, 23 Feb 2024 00:18:40 +0530 Subject: [PATCH 3/3] default param fix --- src/libs/fileDownload/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/fileDownload/types.ts b/src/libs/fileDownload/types.ts index 9a62e775a6ea..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, fileType: string) => Promise; +type ReadFileAsync = (path: string, fileName: string, onSuccess: (file: File) => void, onFailure: (error?: unknown) => void, fileType?: string) => Promise; type AttachmentDetails = { previewSourceURL: null | string;