import { useApolloClient } from "@apollo/client"; import * as MediaLibrary from "expo-media-library"; import _ from "lodash"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { ActivityIndicator, Alert, Modal, ScrollView, StyleSheet, Text, View, } from "react-native"; import { ProgressBar } from "react-native-paper"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { logImEXEvent } from "../../firebase/firebase.analytics"; import { selectCurrentCameraJobId, selectDeleteAfterUpload, } from "../../redux/app/app.selectors"; import { selectBodyshop, selectCurrentUser, } from "../../redux/user/user.selectors"; import { formatBytes } from "../../util/document-upload.utility"; import { handleLocalUpload } from "../../util/local-document-upload.utility"; import Toast from "react-native-toast-message"; const mapStateToProps = createStructuredSelector({ currentUser: selectCurrentUser, bodyshop: selectBodyshop, selectedCameraJobId: selectCurrentCameraJobId, deleteAfterUpload: selectDeleteAfterUpload, }); export default connect(mapStateToProps, null)(UploadProgress); export function UploadProgress({ currentUser, bodyshop, selectedCameraJobId, deleteAfterUpload, uploads, forceRerender, }) { const [progress, setProgress] = useState({ loading: false, uploadInProgress: false, speed: 0, files: {}, //uri is the key, value is progress }); let filesToDelete = []; const client = useApolloClient(); const { t } = useTranslation(); useEffect(() => { //Set the state of uploads to do. if (uploads) onDone(uploads); }, [uploads]); //if (!uploads) return null; function handleOnSuccess(id, asset, { duration }) { //If it's not in production, show a toast with the time. Toast.show({ type: "success", text1: `${duration} - Upload completed for ${asset.filename}.`, text2: duration, }); logImEXEvent("imexmobile_successful_upload"); filesToDelete.push(asset); setProgress((progress) => ({ ...progress, action: t("mediabrowser.labels.converting"), files: { ...progress.files, [id]: { ...progress.files[id], action: t("mediabrowser.labels.converting"), }, }, // }); })); } function handleOnProgress(uri, percent, loaded) { setProgress((progress) => ({ ...progress, speed: loaded - progress.files[uri].loaded, action: percent === 1 ? t("mediabrowser.labels.converting") : t("mediabrowser.labels.uploading"), files: { ...progress.files, [uri]: { ...progress.files[uri], percent, action: percent === 1 ? t("mediabrowser.labels.converting") : t("mediabrowser.labels.uploading"), loaded: loaded, }, }, })); } function handleOnError(id, error) { logImEXEvent("imexmobile_upload_documents_error"); Toast.show({ type: "error", text1: "Unable to upload documents.", text2: (error && error.message) || JSON.stringify(error), }); setProgress((progress) => ({ ...progress, action: t("mediabrowser.labels.converting"), files: { ...progress.files, [id]: { ...progress.files[id], percent: 1, action: t("mediabrowser.labels.converting"), }, }, // }); })); } const onDone = async (data) => { //Validate to make sure the totals for the file sizes do not exceed the total on the job. setProgress({ files: _.keyBy(data, "id"), loading: true, uploadInProgress: true, }); //Sequentially await the proms. // for (const file of data) { // await CreateUploadProm(file); // } for (var i = 0; i < data.length + 4; i = i + 4) { let proms = []; if (data[i]) { proms.push(CreateUploadProm(data[i])); } if (data[i + 1]) { proms.push(CreateUploadProm(data[i + 1])); } if (data[i + 2]) { proms.push(CreateUploadProm(data[i + 2])); } if (data[i + 3]) { proms.push(CreateUploadProm(data[i + 3])); } await Promise.all(proms); } if (deleteAfterUpload) { try { const a = await MediaLibrary.getAssetsAsync(); const res = await Promise.all( filesToDelete.map((f) => { return MediaLibrary.removeAssetsFromAlbumAsync(f, f.albumId); }) ); const deleteResult = await MediaLibrary.deleteAssetsAsync( filesToDelete.map((f) => f.id) ); } catch (error) { console.log("Unable to delete picture.", error); } } filesToDelete = []; setProgress({ loading: false, speed: 0, action: null, uploadInProgress: false, files: {}, //uri is the key, value is progress }); forceRerender(); }; const CreateUploadProm = async (p) => { let filename; filename = p.filename || p.uri.split("/").pop(); await handleLocalUpload({ ev: { filename, mediaId: p.id, onError: (error) => handleOnError(p.id, error), onProgress: ({ percent, loaded }) => handleOnProgress(p.id, percent, loaded), onSuccess: ({ duration }) => handleOnSuccess(p.id, p, { duration }), file: p, }, context: { bodyshop: bodyshop, jobid: selectedCameraJobId !== "temp" ? selectedCameraJobId : "temporary", }, }); //Set the state to mark that it's done. setProgress((progress) => ({ ...progress, action: null, speed: 0, files: { ...progress.files, [p.id]: { ...progress.files[p.id], action: null, }, }, })); }; return ( { Alert.alert("Modal has been closed."); }} > {progress.loading && } {progress.action && ( {`${progress.action} ${ (progress.speed !== 0 || !progress.speed) && `- ${formatBytes(progress.speed)}/sec` }`} )} {Object.keys(progress.files).map((key) => ( {progress.files[key].filename} ))} ); } const styles = StyleSheet.create({ modal: { flex: 1, marginTop: 50, marginBottom: 60, marginLeft: 20, marginRight: 20, backgroundColor: "white", borderRadius: 20, padding: 18, shadowColor: "#000", shadowOffset: { width: 0, height: 2, }, shadowOpacity: 0.25, shadowRadius: 4, elevation: 5, }, centeredView: { flex: 1, // justifyContent: "center", // alignItems: "center", marginTop: 22, }, progressItem: { display: "flex", flexDirection: "row", alignItems: "center", marginBottom: 12, marginLeft: 12, marginRight: 12, }, progressText: { flex: 1, }, progressBarContainer: { flex: 3, marginLeft: 12, marginRight: 12, }, });