Upload improvements for imgproxy.

This commit is contained in:
Patrick Fic
2025-10-31 09:38:51 -07:00
parent ab8703a524
commit beb1145f98
11 changed files with 43 additions and 24 deletions

View File

@@ -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,
}, },
}} }}
> >

View File

@@ -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>

View File

@@ -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}
</> </>
); );

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"

View File

@@ -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,
}, },
]} ]}

View File

@@ -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")} ${

View File

@@ -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]);

View File

@@ -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;
} }

View File

@@ -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.