Skip to content

Commit

Permalink
Merge pull request #36531 from rojiphil/34429-fix-upload-receipt-pdf-…
Browse files Browse the repository at this point in the history
…with-space

fix receipt upload error when file name contain spaces
  • Loading branch information
AndrewGable authored Feb 27, 2024
2 parents e4c2ee2 + bed6a98 commit 2b0f145
Show file tree
Hide file tree
Showing 7 changed files with 19 additions and 11 deletions.
7 changes: 4 additions & 3 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
}
Expand Down Expand Up @@ -4193,6 +4193,7 @@ function navigateToStartStepIfScanFileCannotBeRead(
iouType: ValueOf<typeof CONST.IOU.TYPE>,
transactionID: string,
reportID: string,
receiptType: string,
) {
if (!receiptFilename || !receiptPath) {
return;
Expand All @@ -4206,7 +4207,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 */
Expand Down
6 changes: 4 additions & 2 deletions src/libs/fileDownload/FileUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/libs/fileDownload/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type GetImageResolution = (url: File | Asset) => Promise<ImageResolution>;
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<File | void>;
type ReadFileAsync = (path: string, fileName: string, onSuccess: (file: File) => void, onFailure: (error?: unknown) => void, fileType?: string) => Promise<File | void>;

type AttachmentDetails = {
previewSourceURL: null | string;
Expand Down
5 changes: 3 additions & 2 deletions src/pages/iou/request/step/IOURequestStepConfirmation.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions src/pages/iou/request/step/IOURequestStepParticipants.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/types/onyx/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type Receipt = {
source?: ReceiptSource;
filename?: string;
state?: ValueOf<typeof CONST.IOU.RECEIPT_STATE>;
type?: string;
};

type Route = {
Expand Down

0 comments on commit 2b0f145

Please sign in to comment.