IO-3092 Additional comments, email compatibility.

This commit is contained in:
Patrick Fic
2025-02-27 12:15:36 -08:00
parent 38681158c1
commit fa29bd609f
19 changed files with 185 additions and 56 deletions

View File

@@ -51,9 +51,10 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c
setSelectedMedia([]);
}, [setSelectedMedia, conversation]);
//Knowingly taking on the technical debt of poor implementation below.
//Cloudinary will be removed once the migration is completed.
//Knowingly taking on the technical debt of poor implementation below. Done this way to avoid an edge case where no component may be displayed.
//Cloudinary will be removed once the migration is completed.
//If Imageproxy is on, rely only on the LMS selector
//If not on, use the old methods.
const content = (
<div>
{loading && <LoadingSpinner />}
@@ -61,23 +62,37 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c
{selectedMedia.filter((s) => s.isSelected).length >= 10 ? (
<div style={{ color: "red" }}>{t("messaging.labels.maxtenimages")}</div>
) : null}
{Imgproxy.treatment === "on" && !bodyshop.uselocalmediaserver && data && (
<JobsDocumentImgproxyGalleryExternal
jobId={conversation.job_conversations[0].jobid}
externalMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
{Imgproxy.treatment !== "on" && !bodyshop.uselocalmediaserver && data && (
<JobDocumentsGalleryExternal
data={data ? data.documents : []}
externalMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
{Imgproxy.treatment !== "on" && bodyshop.uselocalmediaserver && open && (
<JobDocumentsLocalGalleryExternal
externalMediaState={[selectedMedia, setSelectedMedia]}
jobId={conversation.job_conversations[0] && conversation.job_conversations[0].jobid}
/>
{Imgproxy.treatment === "on" ? (
<>
{!bodyshop.uselocalmediaserver && (
<JobsDocumentImgproxyGalleryExternal
jobId={conversation.job_conversations[0].jobid}
externalMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
{bodyshop.uselocalmediaserver && open && (
<JobDocumentsLocalGalleryExternal
externalMediaState={[selectedMedia, setSelectedMedia]}
jobId={conversation.job_conversations[0] && conversation.job_conversations[0].jobid}
/>
)}
</>
) : (
<>
{!bodyshop.uselocalmediaserver && data && (
<JobDocumentsGalleryExternal
data={data ? data.documents : []}
externalMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
{bodyshop.uselocalmediaserver && open && (
<JobDocumentsLocalGalleryExternal
externalMediaState={[selectedMedia, setSelectedMedia]}
jobId={conversation.job_conversations[0] && conversation.job_conversations[0].jobid}
/>
)}
</>
)}
</div>
);

View File

@@ -74,7 +74,7 @@ export const uploadToS3 = async (
const exif = await exifr.parse(file);
takenat = exif && exif.DateTimeOriginal;
} catch (error) {
console.log("Unable to parse image file for EXIF Data");
console.log("Unable to parse image file for EXIF Data", error.message);
}
}

View File

@@ -10,6 +10,8 @@ import AlertComponent from "../alert/alert.component";
import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component";
import JobsDocumentsLocalGalleryExternalComponent from "../jobs-documents-local-gallery/jobs-documents-local-gallery.external.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import JobsDocumentImgproxyGalleryExternal from "../jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.external.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -23,6 +25,13 @@ export default connect(mapStateToProps, mapDispatchToProps)(EmailDocumentsCompon
export function EmailDocumentsComponent({ emailConfig, form, selectedMediaState, bodyshop }) {
const { t } = useTranslation();
const {
treatments: { Imgproxy }
} = useSplitTreatments({
attributes: {},
names: ["Imgproxy"],
splitKey: bodyshop && bodyshop.imexshopid
});
const [selectedMedia, setSelectedMedia] = selectedMediaState;
const { loading, error, data } = useQuery(GET_DOCUMENTS_BY_JOB, {
@@ -46,17 +55,37 @@ export function EmailDocumentsComponent({ emailConfig, form, selectedMediaState,
10485760 - new Blob([form.getFieldValue("html")]).size ? (
<div style={{ color: "red" }}>{t("general.errors.sizelimit")}</div>
) : null}
{!bodyshop.uselocalmediaserver && data && (
<JobDocumentsGalleryExternal
data={data ? data.documents : []}
externalMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
{bodyshop.uselocalmediaserver && (
<JobsDocumentsLocalGalleryExternalComponent
externalMediaState={[selectedMedia, setSelectedMedia]}
jobId={emailConfig.jobid}
/>
{Imgproxy.treatment === "on" ? (
<>
{!bodyshop.uselocalmediaserver && data && (
<JobsDocumentImgproxyGalleryExternal
jobId={emailConfig.jobid}
externalMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
{bodyshop.uselocalmediaserver && (
<JobsDocumentsLocalGalleryExternalComponent
externalMediaState={[selectedMedia, setSelectedMedia]}
jobId={emailConfig.jobid}
/>
)}
</>
) : (
<>
{!bodyshop.uselocalmediaserver && data && (
<JobDocumentsGalleryExternal
data={data ? data.documents : []}
externalMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
{bodyshop.uselocalmediaserver && (
<JobsDocumentsLocalGalleryExternalComponent
externalMediaState={[selectedMedia, setSelectedMedia]}
jobId={emailConfig.jobid}
/>
)}
</>
)}
</div>
);

View File

@@ -19,7 +19,13 @@ const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsDownloadButton);
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed.
################################################################################################
*/
export function JobsDocumentsDownloadButton({ bodyshop, galleryImages, identifier }) {
const { t } = useTranslation();
const [download, setDownload] = useState(null);

View File

@@ -17,7 +17,13 @@ const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsGalleryReassign);
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed.
################################################################################################
*/
export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages, callback }) {
const { t } = useTranslation();
const [form] = Form.useForm();

View File

@@ -22,7 +22,13 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({});
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed.
################################################################################################
*/
function JobsDocumentsComponent({
bodyshop,
data,

View File

@@ -14,7 +14,13 @@ const mapStateToProps = createStructuredSelector({
});
const mapDispatchToProps = (dispatch) => ({});
export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsContainer);
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed.
################################################################################################
*/
export function JobsDocumentsContainer({
jobId,
billId,

View File

@@ -5,8 +5,13 @@ import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
//Context: currentUserEmail, bodyshop, jobid, invoiceid
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed.
################################################################################################
*/
export default function JobsDocumentsDeleteButton({ galleryImages, deletionCallback }) {
const { t } = useTranslation();
const notification = useNotification();

View File

@@ -3,6 +3,13 @@ import { Gallery } from "react-grid-gallery";
import { useTranslation } from "react-i18next";
import { GenerateSrcUrl, GenerateThumbUrl } from "./job-documents.utility";
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed.
################################################################################################
*/
function JobsDocumentGalleryExternal({
data,

View File

@@ -2,6 +2,14 @@ import { Button, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed.
################################################################################################
*/
export default function JobsDocumentsGallerySelectAllComponent({ galleryImages, setGalleryImages }) {
const { t } = useTranslation();

View File

@@ -17,6 +17,15 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration.
################################################################################################
*/
export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsImgproxyDownloadButton);
export function JobsDocumentsImgproxyDownloadButton({ bodyshop, galleryImages, identifier }) {

View File

@@ -17,7 +17,13 @@ const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsImgproxyGalleryReassign);
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration.
################################################################################################
*/
export function JobsDocumentsImgproxyGalleryReassign({ bodyshop, galleryImages, callback }) {
const { t } = useTranslation();
const [form] = Form.useForm();

View File

@@ -22,7 +22,13 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({});
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration.
################################################################################################
*/
function JobsDocumentsImgproxyComponent({
bodyshop,
data,

View File

@@ -4,6 +4,14 @@ import AlertComponent from "../alert/alert.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import JobDocuments from "./jobs-documents-imgproxy-gallery.component";
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration.
################################################################################################
*/
export default function JobsDocumentsImgproxyContainer({ jobId, billId, documentsList, billsCallback }) {
const { loading, error, data, refetch } = useQuery(GET_DOCUMENTS_BY_JOB, {
variables: { jobId: jobId },

View File

@@ -5,7 +5,15 @@ import { useState } from "react";
import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils.js";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
//Context: currentUserEmail, bodyshop, jobid, invoiceid
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration.
################################################################################################
*/
export default function JobsDocumentsImgproxyDeleteButton({ galleryImages, deletionCallback }) {
const { t } = useTranslation();

View File

@@ -2,7 +2,14 @@ import { useEffect } from "react";
import { Gallery } from "react-grid-gallery";
import { useTranslation } from "react-i18next";
import { fetchImgproxyThumbnails } from "./jobs-documents-imgproxy-gallery.component";
//import { GenerateSrcUrl, GenerateThumbUrl } from "./job-documents-imgproxy.utility";
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration.
################################################################################################
*/
function JobsDocumentImgproxyGalleryExternal({ jobId, externalMediaState }) {
const [galleryImages, setgalleryImages] = externalMediaState;

View File

@@ -1,6 +1,14 @@
import { Button, Space } from "antd";
import { useTranslation } from "react-i18next";
/*
################################################################################################
Developer Note:
Known Technical Debt Item
Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration.
################################################################################################
*/
export default function JobsDocumentsImgproxyGallerySelectAllComponent({ galleryImages, setGalleryImages }) {
const { t } = useTranslation();

View File

@@ -24,9 +24,7 @@ const {
const archiver = require("archiver");
const stream = require("node:stream");
const imgproxyBaseUrl =
// `https://u4gzpp5wm437dnm75qa42tvza40fguqr.lambda-url.ca-central-1.on.aws` || //Direct Lambda function access to bypass CDN.
process.env.IMGPROXY_BASE_URL;
const imgproxyBaseUrl = process.env.IMGPROXY_BASE_URL; // `https://u4gzpp5wm437dnm75qa42tvza40fguqr.lambda-url.ca-central-1.on.aws` //Direct Lambda function access to bypass CDN.
const imgproxyKey = process.env.IMGPROXY_KEY;
const imgproxySalt = process.env.IMGPROXY_SALT;
const imgproxyDestinationBucket = process.env.IMGPROXY_DESTINATION_BUCKET;
@@ -38,17 +36,9 @@ exports.generateSignedUploadUrls = async (req, res) => {
try {
logger.log("imgproxy-upload-start", "DEBUG", req.user?.email, jobid, { filenames, bodyshopid, jobid });
//TODO: Ensure that the user has access to the given bodyshopid.
//This can be done by querying associations, or, maintaining a REDIS cache of user permissions.
const hasAccess = true; //TODO: Ensure this is not hardcoded.
if (!hasAccess) {
res.send(403);
return;
}
const signedUrls = [];
for (const filename of filenames) {
const key = filename; //GenerateKey({ bodyshopid, jobid, filename });
const key = filename;
const client = new S3Client({ region: InstanceRegion() });
const command = new PutObjectCommand({
Bucket: imgproxyDestinationBucket,
@@ -258,7 +248,6 @@ exports.deleteFiles = async (req, res) => {
});
const result = await Promise.all(deleteTransactions);
console.log("*** ~ file: imgprox-media.js:260 ~ exports.deleteFiles ~ result:", result);
const errors = result.filter((d) => d.error);
//Delete only the succesful deletes.

View File

@@ -7,7 +7,7 @@ const {
downloadFiles: downloadFilesImgproxy,
moveFiles,
deleteFiles: deleteFilesImgproxy
} = require("../media/imgprox-media");
} = require("../media/imgproxy-media");
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");