159 lines
4.9 KiB
JavaScript
159 lines
4.9 KiB
JavaScript
//import "tui-image-editor/dist/tui-image-editor.css";
|
|
import axios from "axios";
|
|
import { Result } from "antd";
|
|
import * as markerjs2 from "markerjs2";
|
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { connect } from "react-redux";
|
|
import { createStructuredSelector } from "reselect";
|
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
|
import { handleUpload } from "../documents-upload-imgproxy/documents-upload-imgproxy.utility.js";
|
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
currentUser: selectCurrentUser,
|
|
bodyshop: selectBodyshop
|
|
});
|
|
const mapDispatchToProps = () => ({
|
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
|
});
|
|
|
|
export function DocumentEditorComponent({ currentUser, bodyshop, document }) {
|
|
const imgRef = useRef(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [uploaded, setuploaded] = useState(false);
|
|
const [imageUrl, setImageUrl] = useState(null);
|
|
const [imageLoaded, setImageLoaded] = useState(false);
|
|
const [imageLoading, setImageLoading] = useState(true);
|
|
const markerArea = useRef(null);
|
|
const { t } = useTranslation();
|
|
const notification = useNotification();
|
|
|
|
const triggerUpload = useCallback(
|
|
async (dataUrl) => {
|
|
setLoading(true);
|
|
handleUpload(
|
|
{
|
|
filename: `${document.key.split("/").pop()}-${Date.now()}.jpg`,
|
|
file: await b64toBlob(dataUrl),
|
|
onSuccess: () => {
|
|
setLoading(false);
|
|
setuploaded(true);
|
|
},
|
|
onError: () => setLoading(false)
|
|
},
|
|
{
|
|
bodyshop: bodyshop,
|
|
uploaded_by: currentUser.email,
|
|
jobId: document.jobid,
|
|
//billId: billId,
|
|
tagsArray: ["edited"]
|
|
//callback: callbackAfterUpload,
|
|
},
|
|
notification
|
|
);
|
|
},
|
|
[bodyshop, currentUser, document, notification]
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (imgRef.current !== null && imageLoaded && !markerArea.current) {
|
|
// create a marker.js MarkerArea
|
|
markerArea.current = new markerjs2.MarkerArea(imgRef.current);
|
|
|
|
// attach an event handler to assign annotated image back to our image element
|
|
markerArea.current.addEventListener("close", () => {
|
|
// NO OP
|
|
});
|
|
|
|
markerArea.current.addEventListener("render", (event) => {
|
|
const dataUrl = event.dataUrl;
|
|
imgRef.current.src = dataUrl;
|
|
markerArea.current.close();
|
|
triggerUpload(dataUrl);
|
|
});
|
|
// launch marker.js
|
|
|
|
markerArea.current.renderAtNaturalSize = true;
|
|
markerArea.current.renderImageType = "image/jpeg";
|
|
markerArea.current.renderImageQuality = 1;
|
|
//markerArea.current.settings.displayMode = "inline";
|
|
markerArea.current.show();
|
|
}
|
|
}, [triggerUpload, imageLoaded]);
|
|
|
|
useEffect(() => {
|
|
if (!document?.id) return;
|
|
const controller = new AbortController();
|
|
|
|
const loadImage = async () => {
|
|
setImageLoaded(false);
|
|
setImageLoading(true);
|
|
try {
|
|
const response = await axios.post(
|
|
"/media/imgproxy/original",
|
|
{ documentId: document.id },
|
|
{
|
|
responseType: "blob",
|
|
signal: controller.signal
|
|
}
|
|
);
|
|
const blobUrl = URL.createObjectURL(response.data);
|
|
setImageUrl((prevUrl) => {
|
|
if (prevUrl) URL.revokeObjectURL(prevUrl);
|
|
return blobUrl;
|
|
});
|
|
} catch (error) {
|
|
if (axios.isCancel?.(error) || error.name === "CanceledError") {
|
|
// request was aborted — safe to ignore
|
|
return;
|
|
}
|
|
console.error("Failed to fetch original image blob", error);
|
|
} finally {
|
|
setImageLoading(false);
|
|
}
|
|
};
|
|
loadImage();
|
|
return () => {
|
|
controller.abort();
|
|
};
|
|
}, [document]);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (imageUrl) {
|
|
URL.revokeObjectURL(imageUrl);
|
|
}
|
|
};
|
|
}, [imageUrl]);
|
|
|
|
async function b64toBlob(url) {
|
|
const res = await fetch(url);
|
|
return await res.blob();
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{!loading && !uploaded && imageUrl && (
|
|
<img
|
|
ref={imgRef}
|
|
src={imageUrl}
|
|
alt="sample"
|
|
onLoad={() => setImageLoaded(true)}
|
|
onError={(error) => {
|
|
console.error("Failed to load original image", error);
|
|
}}
|
|
style={{ maxWidth: "90vw", maxHeight: "90vh" }}
|
|
/>
|
|
)}
|
|
{(loading || imageLoading || !imageLoaded) && !uploaded && (
|
|
<LoadingSpinner message={t("documents.labels.uploading")} />
|
|
)}
|
|
{uploaded && <Result status="success" title={t("documents.successes.edituploaded")} />}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(DocumentEditorComponent);
|