Files
imexmobile/components/job-documents/job-documents.jsx
2025-11-24 10:25:24 -08:00

165 lines
5.0 KiB
JavaScript

import cleanAxios from "@/util/CleanAxios";
import axios from "axios";
import { useGlobalSearchParams } from "expo-router";
import React, { useCallback, useEffect, useState } from "react";
import { FlatList, RefreshControl, TouchableOpacity, View } from "react-native";
import ImageView from "react-native-image-viewing";
import { ActivityIndicator, Text } from "react-native-paper";
import { SafeAreaView } from "react-native-safe-area-context";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import env from "../../env";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { DetermineFileType } from "../../util/document-upload.utility";
import ErrorDisplay from "../error/error-display";
import ImageLoader from "./image-loader";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobDocumentsComponent);
export function JobDocumentsComponent({ bodyshop }) {
const [previewVisible, setPreviewVisible] = useState(false);
const [fullphotos, setFullPhotos] = useState([]);
const [imgIndex, setImgIndex] = useState(0);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const { jobId } = useGlobalSearchParams();
const isLms = bodyshop.uselocalmediaserver;
const onRefresh = async () => {
return getPhotos();
};
const getPhotos = useCallback(async () => {
setError(null);
setLoading(true);
try {
if (!isLms) {
const result = await axios.post(
`${env.API_URL}/media/imgproxy/thumbnails`,
{
jobid: jobId,
}
);
setFullPhotos(
result.data.map((doc, idx) => {
return {
id: idx,
videoUrl:
DetermineFileType(doc.type) === "video" &&
doc.originalUrlViaProxyPath,
source:
DetermineFileType(doc.type) === "video"
? { uri: doc.thumbnailUrl }
: { uri: doc.originalUrl },
url:
DetermineFileType(doc.type) === "video"
? doc.thumbnailUrl
: doc.originalUrl,
uri:
DetermineFileType(doc.type) === "video"
? doc.originalUrlViaProxyPath
: doc.originalUrl,
thumbUrl: doc.thumbnailUrl,
};
})
);
} else {
let localmediaserverhttp = bodyshop.localmediaserverhttp.trim();
if (localmediaserverhttp.endsWith("/")) {
localmediaserverhttp = localmediaserverhttp.slice(0, -1);
}
const imagesFetch = await cleanAxios.post(
`${localmediaserverhttp}/jobs/list`,
{
jobid: jobId,
},
{ headers: { ims_token: bodyshop.localmediatoken } }
);
const normalizedImages = imagesFetch.data
.filter((d) => d.type?.mime?.startsWith("image"))
.map((d, idx) => {
return {
...d,
// src: `${localmediaserverhttp}/${d.src}`,
uri: `${localmediaserverhttp}${d.src}`,
thumbUrl: `${localmediaserverhttp}${d.thumbnail}`,
id: idx,
};
});
setFullPhotos(normalizedImages);
}
} catch (error) {
setError(error.message || "Unknown error fetching photos.");
}
setLoading(false);
}, [isLms, jobId, bodyshop]);
useEffect(() => {
getPhotos();
}, [getPhotos]);
if (loading) return <ActivityIndicator style={{ flex: 1 }} size="large" />;
if (error) {
return <ErrorDisplay message={JSON.stringify(error)} />;
}
return (
<View style={{ flex: 1 }}>
<FlatList
refreshControl={
<RefreshControl refreshing={loading} onRefresh={onRefresh} />
}
data={fullphotos}
ListFooterComponent={
<Text style={{ textAlign: "center", padding: 8 }}>{`${
fullphotos.length
} document${fullphotos.length === 1 ? "" : "s"}`}</Text>
}
numColumns={4}
style={{ flex: 1 }}
keyExtractor={(item) => item.id}
renderItem={(object) => (
<TouchableOpacity
style={{ flex: 1 / 4, aspectRatio: 1, margin: 4 }}
onPress={async () => {
setImgIndex(object.index);
setPreviewVisible(true);
}}
>
<ImageLoader
style={{ flex: 1 }}
resizeMode="cover"
source={{
uri: object.item.thumbUrl,
aspectRatio: 1,
}}
/>
</TouchableOpacity>
)}
/>
<SafeAreaView>
<ImageView
onRequestClose={() => setPreviewVisible(false)}
visible={previewVisible}
images={fullphotos}
imageIndex={imgIndex}
swipeToCloseEnabled={true}
/>
</SafeAreaView>
</View>
);
}