Improve upload progress.

This commit is contained in:
Patrick Fic
2025-10-30 16:34:10 -07:00
parent 162f599e2d
commit 8e63ef0d6d
11 changed files with 186 additions and 137 deletions

View File

@@ -9,7 +9,6 @@ import { createStructuredSelector } from "reselect";
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
//import ErrorDisplay from "../error-display/error-display.component";
import UploadProgress from "../upload-progress/upload-progress";
import JobListItem from "./job-list-item";
const mapStateToProps = createStructuredSelector({
@@ -58,7 +57,6 @@ export function JobListComponent({ bodyshop }) {
>
Jobs
</Text>
<UploadProgress />
<FlatList
refreshControl={
<RefreshControl refreshing={loading} onRefresh={onRefresh} />

View File

@@ -1,8 +1,10 @@
import { useTheme } from "@/hooks";
import { clearUploadError } from "@/redux/photos/photos.actions";
import { formatBytes } from "@/util/uploadUtils";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { StyleSheet, View } from "react-native";
import { ProgressBar, Text } from "react-native-paper";
import { ScrollView, StyleSheet, View } from "react-native";
import { Divider, Modal, Portal, ProgressBar, Text } from "react-native-paper";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
@@ -10,7 +12,6 @@ import {
selectUploadError,
selectUploadProgress,
} from "../../redux/photos/photos.selectors";
import ErrorDisplay from "../error/error-display";
const mapStateToProps = createStructuredSelector({
photos: selectPhotos,
@@ -30,78 +31,94 @@ export function UploadProgress({
clearError,
}) {
const { t } = useTranslation();
if (photos?.length === 0) return null;
if (uploadError)
return <ErrorDisplay error={uploadError} onDismiss={clearError} />;
return (
<View style={styles.modalContainer}>
<View style={styles.modal}>
<Text variant="titleLarge" style={styles.title}>
{t("general.labels.uploadprogress")}
</Text>
const theme = useTheme();
{Object.keys(photoUploadProgress).map((key) => (
<View key={key} style={styles.progressItem}>
<Text
style={styles.progressText}
numberOfLines={1}
ellipsizeMode="tail"
>
{photoUploadProgress[key].fileName}
</Text>
<View style={styles.progressBarContainer}>
<ProgressBar
progress={photoUploadProgress[key].progress}
style={styles.progress}
color={
photoUploadProgress[key].progress === 1 ? "green" : "blue"
}
/>
<View
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
const completion = useMemo(() => {
const total = Object.keys(photoUploadProgress).length;
if (total === 0) return 0;
const completed = Object.values(photoUploadProgress).filter(
(p) => p.progress === 100
).length;
return completed / total;
}, [photoUploadProgress]);
return (
<Portal>
<Modal
visible={photos?.length > 0}
style={styles.modalOuter} // add
contentContainerStyle={[
styles.modalContainer,
{ backgroundColor: theme.colors.elevation.level1 },
]}
>
<ScrollView style={styles.modalFill}>
<Text variant="titleLarge" style={styles.title}>
{t("general.labels.upload")}
</Text>
<Text variant="labelLarge">
{`${t("general.labels.uploadprogress")} ${Math.round(
completion * 100
)}%`}
</Text>
<ProgressBar
progress={completion}
style={styles.progress}
color={completion === 1 ? "green" : "blue"}
/>
<Divider style={{ marginVertical: 12 }} />
{Object.keys(photoUploadProgress).map((key) => (
<View key={key} style={styles.progressItem}>
<Text
style={styles.progressText}
numberOfLines={1}
ellipsizeMode="tail"
>
<Text>{`${formatBytes(
photoUploadProgress[key].loaded /
(((photoUploadProgress[key].endTime || new Date()) -
photoUploadProgress[key].startTime) /
1000)
)}/sec`}</Text>
{photoUploadProgress[key].fileName}
</Text>
<View style={styles.progressBarContainer}>
<ProgressBar
progress={(photoUploadProgress[key].progress || 0) / 100}
style={styles.progress}
color={
photoUploadProgress[key].progress === 100 ? "green" : "blue"
}
/>
<View style={styles.speedRow}>
<Text>{`${formatBytes(
photoUploadProgress[key].loaded /
(((photoUploadProgress[key].endTime || new Date()) -
photoUploadProgress[key].startTime) /
1000)
)}/sec`}</Text>
</View>
</View>
</View>
</View>
))}
</View>
</View>
))}
</ScrollView>
</Modal>
</Portal>
);
}
const styles = StyleSheet.create({
modalContainer: {
display: "flex",
// flex: 1,
marginTop: 14,
marginBottom: 14,
justifyContent: "center",
modalOuter: {
flex: 1, // ensure outer container can grow,
paddingHorizontal: 24,
paddingVertical: 72,
},
modal: {
//flex: 1,
display: "flex",
marginLeft: 12,
marginRight: 12,
//backgroundColor: theme.colors.elevation.level3,
borderRadius: 20,
paddingTop: 12,
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
modalContainer: {
width: "100%",
height: "50%", // force full area (important for iOS)
padding: 24,
justifyContent: "center",
borderRadius: 24,
},
modalFill: {
flex: 1,
},
speedRow: {
flexDirection: "row",
alignItems: "center",
},
title: {
alignSelf: "center",
@@ -110,11 +127,6 @@ const styles = StyleSheet.create({
paddingLeft: 12,
paddingRight: 12,
},
centeredView: {
justifyContent: "center",
alignItems: "center",
marginTop: 22,
},
progressItem: {
display: "flex",
flexDirection: "row",