Upload improvements for imgproxy.
This commit is contained in:
@@ -13,9 +13,9 @@ function JobTabLayout() {
|
|||||||
tabBarActiveTintColor: theme.colors.primary,
|
tabBarActiveTintColor: theme.colors.primary,
|
||||||
tabBarPosition: "top",
|
tabBarPosition: "top",
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
animation: "shift",
|
animation: "fade",
|
||||||
tabBarStyle: {
|
tabBarStyle: {
|
||||||
// marginTop: -50
|
marginTop: -48,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Button, Card, Text } from "react-native-paper";
|
|||||||
export default function ErrorDisplay({ errorMessage, error, onDismiss }) {
|
export default function ErrorDisplay({ errorMessage, error, onDismiss }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<Card style={{ margin: 8, backgroundColor: "#ffdddd" }}>
|
<Card mode="outlined" style={{ margin: 8, backgroundColor: "#ffdddd" }}>
|
||||||
<Card.Title title={t("general.labels.error")} titleVariant="titleLarge" />
|
<Card.Title title={t("general.labels.error")} titleVariant="titleLarge" />
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Text>
|
<Text>
|
||||||
|
|||||||
@@ -33,7 +33,14 @@ const ImageLoader = ({ style, source, ...props }) => {
|
|||||||
if (loading)
|
if (loading)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ActivityIndicator style={{ ...style, position: "absolute" }} />
|
<ActivityIndicator
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
position: "absolute",
|
||||||
|
alignSelf: "center",
|
||||||
|
margin: 12,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
{memorizedImage}
|
{memorizedImage}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useGlobalSearchParams } from "expo-router";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ScrollView, useWindowDimensions } from "react-native";
|
import { ScrollView, useWindowDimensions } from "react-native";
|
||||||
import { ActivityIndicator, DataTable } from "react-native-paper";
|
import { ActivityIndicator, DataTable, useTheme } from "react-native-paper";
|
||||||
import ErrorDisplay from "../error/error-display";
|
import ErrorDisplay from "../error/error-display";
|
||||||
|
|
||||||
export default function JobLines() {
|
export default function JobLines() {
|
||||||
@@ -18,7 +18,7 @@ export default function JobLines() {
|
|||||||
});
|
});
|
||||||
const { width, height } = useWindowDimensions();
|
const { width, height } = useWindowDimensions();
|
||||||
const isLandscape = width > height;
|
const isLandscape = width > height;
|
||||||
|
const theme = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const onRefresh = async () => {
|
const onRefresh = async () => {
|
||||||
return refetch();
|
return refetch();
|
||||||
@@ -58,8 +58,13 @@ export default function JobLines() {
|
|||||||
</DataTable.Title>
|
</DataTable.Title>
|
||||||
</DataTable.Header>
|
</DataTable.Header>
|
||||||
|
|
||||||
{job.joblines.map((item) => (
|
{job.joblines.map((item, index) => (
|
||||||
<DataTable.Row key={item.id}>
|
<DataTable.Row
|
||||||
|
key={item.id}
|
||||||
|
style={{
|
||||||
|
backgroundColor: index % 2 === 0 && theme.colors.surface,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<DataTable.Cell style={{ flex: 4 }}>
|
<DataTable.Cell style={{ flex: 4 }}>
|
||||||
{item.line_desc}
|
{item.line_desc}
|
||||||
</DataTable.Cell>
|
</DataTable.Cell>
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ export default function JobNotes() {
|
|||||||
|
|
||||||
const NoteListItem = memo(function NoteListItem({ item }) {
|
const NoteListItem = memo(function NoteListItem({ item }) {
|
||||||
return (
|
return (
|
||||||
<Card style={{ margin: 8 }}>
|
<Card mode="outlined" style={{ margin: 8 }}>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<View style={{ display: "flex", flex: 1 }}>
|
<View style={{ display: "flex", flex: 1 }}>
|
||||||
<Text>{item.text}</Text>
|
<Text>{item.text}</Text>
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ function JobTombstone({ bodyshop }) {
|
|||||||
<RefreshControl refreshing={loading} onRefresh={onRefresh} />
|
<RefreshControl refreshing={loading} onRefresh={onRefresh} />
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Card>
|
<Card mode="outlined">
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={t("jobdetail.labels.jobinfo")}
|
title={t("jobdetail.labels.jobinfo")}
|
||||||
titleVariant="titleLarge"
|
titleVariant="titleLarge"
|
||||||
@@ -132,7 +132,7 @@ function JobTombstone({ bodyshop }) {
|
|||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card>
|
<Card mode="outlined">
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={t("jobdetail.labels.claiminformation")}
|
title={t("jobdetail.labels.claiminformation")}
|
||||||
titleVariant="titleLarge"
|
titleVariant="titleLarge"
|
||||||
@@ -164,7 +164,7 @@ function JobTombstone({ bodyshop }) {
|
|||||||
</View>
|
</View>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card>
|
</Card>
|
||||||
<Card>
|
<Card mode="outlined">
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={t("jobdetail.labels.employeeassignments")}
|
title={t("jobdetail.labels.employeeassignments")}
|
||||||
titleVariant="titleLarge"
|
titleVariant="titleLarge"
|
||||||
@@ -211,7 +211,7 @@ function JobTombstone({ bodyshop }) {
|
|||||||
/>
|
/>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card>
|
</Card>
|
||||||
<Card>
|
<Card mode="outlined">
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={t("jobdetail.labels.dates")}
|
title={t("jobdetail.labels.dates")}
|
||||||
titleVariant="titleLarge"
|
titleVariant="titleLarge"
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ function JobListItemComponent({ openImagePicker, item }) {
|
|||||||
style={[
|
style={[
|
||||||
styles.glassCard,
|
styles.glassCard,
|
||||||
{
|
{
|
||||||
backgroundColor: theme.colors.primaryContainer,
|
backgroundColor: theme.colors.surface,
|
||||||
borderColor: theme.colors.outlineVariant,
|
borderColor: theme.colors.outlineVariant,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ function Tab({ bodyshop, currentUser, signOutStart }) {
|
|||||||
{t("settings.titles.settings")}
|
{t("settings.titles.settings")}
|
||||||
</Text>
|
</Text>
|
||||||
<ScrollView contentContainerStyle={styles.container}>
|
<ScrollView contentContainerStyle={styles.container}>
|
||||||
<Card style={styles.section}>
|
<Card style={styles.section} mode="outlined">
|
||||||
<Card.Title title="Storage" />
|
<Card.Title title="Storage" />
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<List.Section>
|
<List.Section>
|
||||||
@@ -110,7 +110,7 @@ function Tab({ bodyshop, currentUser, signOutStart }) {
|
|||||||
</Card.Actions>
|
</Card.Actions>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card style={styles.section}>
|
<Card style={styles.section} mode="outlined">
|
||||||
<Card.Title title={t("settings.labels.theme")} />
|
<Card.Title title={t("settings.labels.theme")} />
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<List.Section>
|
<List.Section>
|
||||||
@@ -121,7 +121,7 @@ function Tab({ bodyshop, currentUser, signOutStart }) {
|
|||||||
</Card.Content>
|
</Card.Content>
|
||||||
<Card.Actions></Card.Actions>
|
<Card.Actions></Card.Actions>
|
||||||
</Card>
|
</Card>
|
||||||
<Card style={styles.section}>
|
<Card style={styles.section} mode="outlined">
|
||||||
<Card.Title title="Notifications" />
|
<Card.Title title="Notifications" />
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<List.Section>
|
<List.Section>
|
||||||
@@ -157,7 +157,7 @@ function Tab({ bodyshop, currentUser, signOutStart }) {
|
|||||||
</Card.Actions>
|
</Card.Actions>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card style={styles.section}>
|
<Card style={styles.section} mode="outlined">
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Text style={styles.paragraph}>
|
<Text style={styles.paragraph}>
|
||||||
{`${t("settings.labels.signedinshop")} ${
|
{`${t("settings.labels.signedinshop")} ${
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export function UploadProgress({
|
|||||||
const total = Object.keys(photoUploadProgress).length;
|
const total = Object.keys(photoUploadProgress).length;
|
||||||
if (total === 0) return 0;
|
if (total === 0) return 0;
|
||||||
const completed = Object.values(photoUploadProgress).filter(
|
const completed = Object.values(photoUploadProgress).filter(
|
||||||
(p) => p.progress === 100
|
(p) => p.progress === 1
|
||||||
).length;
|
).length;
|
||||||
return completed / total;
|
return completed / total;
|
||||||
}, [photoUploadProgress]);
|
}, [photoUploadProgress]);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const photosReducer = (state = INITIAL_STATE, action) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
progress: { ...state.progress, [action.payload.assetId]: { ...state.progress[action.payload.assetId], progress: 100, status: 'completed', endTime: new Date() } },
|
progress: { ...state.progress, [action.payload.assetId]: { ...state.progress[action.payload.assetId], progress: 1, status: 'completed', endTime: new Date() } },
|
||||||
cancelTasks: remainingTasks
|
cancelTasks: remainingTasks
|
||||||
};
|
};
|
||||||
case PhotosActionTypes.MEDIA_UPLOAD_PROGRESS_UPDATE_BULK:
|
case PhotosActionTypes.MEDIA_UPLOAD_PROGRESS_UPDATE_BULK:
|
||||||
@@ -53,7 +53,7 @@ const photosReducer = (state = INITIAL_STATE, action) => {
|
|||||||
uploadInProgress: false,
|
uploadInProgress: false,
|
||||||
uploadError: null,
|
uploadError: null,
|
||||||
photos: [],
|
photos: [],
|
||||||
progress: {},
|
// progress: {},
|
||||||
cancelTasks: {}
|
cancelTasks: {}
|
||||||
};
|
};
|
||||||
case PhotosActionTypes.CLEAR_UPLOAD_ERROR:
|
case PhotosActionTypes.CLEAR_UPLOAD_ERROR:
|
||||||
@@ -83,6 +83,12 @@ const photosReducer = (state = INITIAL_STATE, action) => {
|
|||||||
...state,
|
...state,
|
||||||
cancelTriggered: true,
|
cancelTriggered: true,
|
||||||
};
|
};
|
||||||
|
case PhotosActionTypes.DELETE_MEDIA_SUCCESS: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
progress: {}, //Clear it here so we only delete photos that were successful.
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ function* uploadToLocalMediaServer(photos, bodyshop, jobid) {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Uncaught error", error);
|
console.log("Uncaught error", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function* uploadToImageProxy(photo, photoBlob, extension, key, bodyshop, jobid) {
|
function* uploadToImageProxy(photo, photoBlob, extension, key, bodyshop, jobid) {
|
||||||
@@ -297,7 +298,6 @@ function* uploadToImageProxy(photo, photoBlob, extension, key, bodyshop, jobid)
|
|||||||
const sent = progressData.totalBytesSent;
|
const sent = progressData.totalBytesSent;
|
||||||
const total = progressData.totalBytesExpectedToSend;
|
const total = progressData.totalBytesExpectedToSend;
|
||||||
const progress = sent / total;
|
const progress = sent / total;
|
||||||
console.log(progress, sent)
|
|
||||||
store.dispatch(mediaUploadProgressOne({ ...photo, progress, loaded: sent }));
|
store.dispatch(mediaUploadProgressOne({ ...photo, progress, loaded: sent }));
|
||||||
// onUpload(Number(progress.toFixed(2)) * 100);
|
// onUpload(Number(progress.toFixed(2)) * 100);
|
||||||
},
|
},
|
||||||
@@ -347,7 +347,7 @@ function* uploadToImageProxy(photo, photoBlob, extension, key, bodyshop, jobid)
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
console.log("Upload and record creation successful for", photo.uri);
|
|
||||||
} else {
|
} else {
|
||||||
console.log("Error uploading to Cloud", uploadResult);
|
console.log("Error uploading to Cloud", uploadResult);
|
||||||
throw new Error(`Cloud upload failed: ${uploadResult}`);
|
throw new Error(`Cloud upload failed: ${uploadResult}`);
|
||||||
@@ -393,8 +393,9 @@ function* mediaUploadCompletedAction({ payload: photos }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const progress = yield select((state) => state.photos.progress);
|
||||||
// Handle the completion of media uploads
|
// Handle the completion of media uploads
|
||||||
const filesToDelete = [...photos]
|
const filesToDelete = Object.keys(progress).filter((key) => progress[key].status === 'completed').map((key) => progress[key]);
|
||||||
|
|
||||||
if (Platform.OS === "android") {
|
if (Platform.OS === "android") {
|
||||||
//Create a new asset with the first file to delete.
|
//Create a new asset with the first file to delete.
|
||||||
|
|||||||
Reference in New Issue
Block a user