IO-3092 Move upload to use expo upload instead of axios for proper status reporting.

This commit is contained in:
Patrick Fic
2025-02-28 10:59:35 -08:00
parent ca93c0900c
commit b2869b9fac
2 changed files with 86 additions and 42 deletions

View File

@@ -1,5 +1,5 @@
import * as MediaLibrary from 'expo-media-library'; import * as MediaLibrary from "expo-media-library";
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from "react";
import { import {
ActivityIndicator, ActivityIndicator,
@@ -8,20 +8,20 @@ import {
StyleSheet, StyleSheet,
Text, Text,
View, View,
} from 'react-native'; } from "react-native";
import { ProgressBar } from 'react-native-paper'; import { ProgressBar } from "react-native-paper";
import Toast from 'react-native-toast-message'; import Toast from "react-native-toast-message";
import { connect } from 'react-redux'; import { connect } from "react-redux";
import { createStructuredSelector } from 'reselect'; import { createStructuredSelector } from "reselect";
import { logImEXEvent } from '../../firebase/firebase.analytics'; import { logImEXEvent } from "../../firebase/firebase.analytics";
import { import {
selectCurrentCameraJobId, selectCurrentCameraJobId,
selectDeleteAfterUpload, selectDeleteAfterUpload,
} from '../../redux/app/app.selectors'; } from "../../redux/app/app.selectors";
import * as Sentry from '@sentry/react-native'; import * as Sentry from "@sentry/react-native";
import { formatBytes } from '../../util/document-upload.utility'; import { formatBytes } from "../../util/document-upload.utility";
import { handleLocalUpload } from '../../util/local-document-upload.utility'; import { handleLocalUpload } from "../../util/local-document-upload.utility";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
selectedCameraJobId: selectCurrentCameraJobId, selectedCameraJobId: selectCurrentCameraJobId,
@@ -54,7 +54,7 @@ export function UploadProgress({
async function handleOnSuccess({ duration, data }) { async function handleOnSuccess({ duration, data }) {
//If it's not in production, show a toast with the time. //If it's not in production, show a toast with the time.
Toast.show({ Toast.show({
type: 'success', type: "success",
text1: ` Upload completed in ${duration}.`, text1: ` Upload completed in ${duration}.`,
// //
// text2: duration, // text2: duration,
@@ -64,7 +64,7 @@ export function UploadProgress({
await MediaLibrary.deleteAssetsAsync(data); await MediaLibrary.deleteAssetsAsync(data);
const album = await MediaLibrary.createAlbumAsync( const album = await MediaLibrary.createAlbumAsync(
'ImEX Mobile Deleted', "ImEX Mobile Deleted",
data.pop(), data.pop(),
false false
); );
@@ -78,12 +78,12 @@ export function UploadProgress({
} }
const deleteResult = await MediaLibrary.deleteAlbumsAsync(album); const deleteResult = await MediaLibrary.deleteAlbumsAsync(album);
} catch (error) { } catch (error) {
console.log('Unable to delete picture.', error); console.log("Unable to delete picture.", error);
Sentry.Native.captureException(error); Sentry.Native.captureException(error);
} }
} }
logImEXEvent('imexmobile_successful_upload'); logImEXEvent("imexmobile_successful_upload");
forceRerender(); forceRerender();
setProgress({ ...progress, speed: 0, percent: 1, uploadInProgress: false }); setProgress({ ...progress, speed: 0, percent: 1, uploadInProgress: false });
} }
@@ -98,10 +98,10 @@ export function UploadProgress({
} }
function handleOnError({ assetid, error }) { function handleOnError({ assetid, error }) {
logImEXEvent('imexmobile_upload_documents_error'); logImEXEvent("imexmobile_upload_documents_error");
Toast.show({ Toast.show({
type: 'error', type: "error",
text1: 'Unable to upload documents.', text1: "Unable to upload documents.",
text2: error, text2: error,
autoHide: false, autoHide: false,
}); });
@@ -130,7 +130,7 @@ export function UploadProgress({
onSuccess: ({ duration }) => handleOnSuccess({ duration, data }), onSuccess: ({ duration }) => handleOnSuccess({ duration, data }),
context: { context: {
jobid: jobid:
selectedCameraJobId !== 'temp' ? selectedCameraJobId : 'temporary', selectedCameraJobId !== "temp" ? selectedCameraJobId : "temporary",
}, },
}); });
}; };
@@ -138,41 +138,41 @@ export function UploadProgress({
return ( return (
<Modal <Modal
visible={progress.uploadInProgress} visible={progress.uploadInProgress}
animationType='slide' animationType="slide"
transparent={true} transparent={true}
onRequestClose={() => { onRequestClose={() => {
Alert.alert('Cancel?', 'Do you want to abort the upload?', [ Alert.alert("Cancel?", "Do you want to abort the upload?", [
{ {
text: 'Yes', text: "Yes",
onPress: () => { onPress: () => {
setUploads(null); setUploads(null);
setProgress(null); setProgress(null);
}, },
}, },
{ text: 'No' }, { text: "No" },
]); ]);
}} }}
> >
<View style={styles.modalContainer}> <View style={styles.modalContainer}>
<View style={styles.modal}> <View style={styles.modal}>
<ActivityIndicator style={{ alignSelf: 'center', marginTop: 16 }} /> <ActivityIndicator style={{ alignSelf: "center", marginTop: 16 }} />
<ProgressBar <ProgressBar
progress={progress.percent} progress={progress.percent}
style={{ alignSelf: 'center', marginTop: 16 }} style={{ alignSelf: "center", marginTop: 16 }}
color={progress.percent === 1 ? 'green' : 'blue'} color={progress.percent === 1 ? "green" : "blue"}
/> />
<Text style={{ alignSelf: 'center', marginTop: 16 }}>{`${formatBytes( <Text style={{ alignSelf: "center", marginTop: 16 }}>{`${formatBytes(
progress.speed progress.speed
)}/sec`}</Text> )}/sec`}</Text>
<Text <Text
style={{ alignSelf: 'center', marginTop: 16 }} style={{ alignSelf: "center", marginTop: 16 }}
>{`Avg. ${formatBytes( >{`Avg. ${formatBytes(
progress.loaded / ((new Date() - progress.start) / 1000) progress.loaded / ((new Date() - progress.start) / 1000)
)}/sec`}</Text> )}/sec`}</Text>
<Text <Text
style={{ alignSelf: 'center', marginTop: 16 }} style={{ alignSelf: "center", marginTop: 16 }}
>{`Total Uploaded ${formatBytes(progress.loaded)}`}</Text> >{`Total Uploaded ${formatBytes(progress.loaded)}`}</Text>
<Text style={{ alignSelf: 'center', marginTop: 16 }}>{`Duration ${( <Text style={{ alignSelf: "center", marginTop: 16 }}>{`Duration ${(
(new Date() - progress.start) / (new Date() - progress.start) /
1000 1000
).toFixed(1)} sec`}</Text> ).toFixed(1)} sec`}</Text>
@@ -183,19 +183,19 @@ export function UploadProgress({
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
modalContainer: { modalContainer: {
display: 'flex', display: "flex",
flex: 1, flex: 1,
justifyContent: 'center', justifyContent: "center",
}, },
modal: { modal: {
// flex: 1, // flex: 1,
display: 'flex', display: "flex",
marginLeft: 20, marginLeft: 20,
marginRight: 20, marginRight: 20,
backgroundColor: 'white', backgroundColor: "white",
borderRadius: 20, borderRadius: 20,
padding: 18, padding: 18,
shadowColor: '#000', shadowColor: "#000",
shadowOffset: { shadowOffset: {
width: 0, width: 0,
height: 2, height: 2,

View File

@@ -6,6 +6,8 @@ import { client } from "../graphql/client";
import { INSERT_NEW_DOCUMENT } from "../graphql/documents.queries"; import { INSERT_NEW_DOCUMENT } from "../graphql/documents.queries";
import { axiosAuthInterceptorId } from "./CleanAxios"; import { axiosAuthInterceptorId } from "./CleanAxios";
import { splitClient } from "../components/screen-main/screen-main.component"; import { splitClient } from "../components/screen-main/screen-main.component";
import * as FileSystem from "expo-file-system";
//Context: currentUserEmail, bodyshop, jobid, invoiceid //Context: currentUserEmail, bodyshop, jobid, invoiceid
//Required to prevent headers from getting set and rejected from Cloudinary. //Required to prevent headers from getting set and rejected from Cloudinary.
@@ -143,15 +145,57 @@ export const uploadToImgproxy = async (
}); });
try { try {
const s3UploadResponse = await cleanAxios.put( // const s3UploadResponse = await cleanAxios.put(
// preSignedUploadUrlToS3,
// file,
// options
// );
const task = FileSystem.createUploadTask(
preSignedUploadUrlToS3, preSignedUploadUrlToS3,
file, imageData.localUri,
options {
httpMethod: "PUT",
//uploadType: FileSystem.FileSystemUploadType.MULTIPART,
//mimeType: fileType,
//headers: {},
// parameters: {...OTHER PARAMS IN REQUEST},
},
(progressData) => {
const sent = progressData.totalBytesSent;
const total = progressData.totalBytesExpectedToSend;
const progress = sent / total;
if (onProgress)
onProgress({
percent: Number(progress.toFixed(2)) * 100,
loaded: sent,
});
// onUpload(Number(progress.toFixed(2)) * 100);
}
); );
const request = await task.uploadAsync();
if (request.status !== 200) {
if (onError) onError(request.status);
return {
success: false,
error: JSON.stringify(documentInsert.errors),
message: request.body,
status: request.status,
mediaId,
};
}
//debugger;
} catch (error) { } catch (error) {
console.log("Error uploading to S3", error.message, error.stack); console.log("Error uploading to S3", error.message, error.stack);
if (onError) onError(error.message);
return {
success: false,
error: error.message,
stack: error.stack,
mediaId,
};
Sentry.Native.captureException(error); Sentry.Native.captureException(error);
} }