import { useApolloClient } from "@apollo/client"; import * as FileSystem from "expo-file-system"; 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 { GET_DOC_SIZE_TOTALS } from "../../graphql/documents.queries"; import { selectCurrentCameraJobId, selectDeleteAfterUpload, } from "../../redux/app/app.selectors"; import { selectBodyshop, selectCurrentUser, } from "../../redux/user/user.selectors"; import { formatBytes, handleUpload } from "../../util/document-upload.utility"; 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 }); 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) { logImEXEvent("imexmobile_successful_upload"); 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(...props) { logImEXEvent("imexmobile_upload_documents_error", { props }); } 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, }); if (selectedCameraJobId !== "temp") { const queryData = await client.query({ query: GET_DOC_SIZE_TOTALS, fetchPolicy: "network-only", variables: { jobId: selectedCameraJobId, }, }); const totalOfUploads = await data.reduce(async (acc, val) => { //Get the size of the file based on URI. const info = await FileSystem.getInfoAsync(val.uri, { size: true }); return (await acc) + info.size; }, 0); if ( bodyshop.jobsizelimit - queryData.data.documents_aggregate.aggregate.sum.size <= totalOfUploads ) { //No more room... abandon ship. setProgress((progress) => ({ ...progress, speed: 0, action: null, loading: false, uploadInProgress: false, })); Alert.alert( t("mediabrowser.labels.storageexceeded_title"), t("mediabrowser.labels.storageexceeded") ); return; } } //Sequentially await the proms. for (const p of data) { let filename; filename = p.filename || p.uri.split("/").pop(); await handleUpload( { filename, mediaId: p.id, onError: handleOnError, onProgress: ({ percent, loaded }) => handleOnProgress(p.id, percent, loaded), onSuccess: () => handleOnSuccess(p.id), }, { bodyshop: bodyshop, jobId: selectedCameraJobId !== "temp" ? selectedCameraJobId : null, uploaded_by: currentUser.email, photo: p, } ); //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, }, }, })); } if (deleteAfterUpload) { try { await MediaLibrary.deleteAssetsAsync(Object.keys(progress.files)); } catch (error) { console.log("Unable to delete picture.", error); } } setProgress({ loading: false, speed: 0, action: null, uploadInProgress: false, files: {}, //uri is the key, value is progress }); forceRerender(); }; console.log("speed", progress.speed, progress.speed !== 0); 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, }, });