From 25b289b65de3be0a1cf8226787eadb21a5fe8f85 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Wed, 26 Feb 2025 20:56:13 -0800 Subject: [PATCH] IO-3092 add messaging support for imgproxy --- .../chat-media-selector.component.jsx | 24 +++- .../jobs-documents-gallery.container.jsx | 30 +++- ...s-documents-imgproxy-gallery.component.jsx | 134 ++++++++++-------- ...ts-imgproxy-gallery.external.component.jsx | 36 ++--- .../temporary-docs.component.jsx | 14 +- 5 files changed, 136 insertions(+), 102 deletions(-) diff --git a/client/src/components/chat-media-selector/chat-media-selector.component.jsx b/client/src/components/chat-media-selector/chat-media-selector.component.jsx index 0526fde43..cc7a56b73 100644 --- a/client/src/components/chat-media-selector/chat-media-selector.component.jsx +++ b/client/src/components/chat-media-selector/chat-media-selector.component.jsx @@ -1,7 +1,8 @@ import { PictureFilled } from "@ant-design/icons"; import { useQuery } from "@apollo/client"; +import { useSplitTreatments } from "@splitsoftware/splitio-react"; import { Badge, Popover } from "antd"; -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -9,6 +10,7 @@ import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; import AlertComponent from "../alert/alert.component"; import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component"; +import JobsDocumentImgproxyGalleryExternal from "../jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.external.component"; import JobDocumentsLocalGalleryExternal from "../jobs-documents-local-gallery/jobs-documents-local-gallery.external.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component"; @@ -23,6 +25,13 @@ export default connect(mapStateToProps, mapDispatchToProps)(ChatMediaSelector); export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, conversation }) { const { t } = useTranslation(); const [open, setOpen] = useState(false); + const { + treatments: { Imgproxy } + } = useSplitTreatments({ + attributes: {}, + names: ["Imgproxy"], + splitKey: bodyshop && bodyshop.imexshopid + }); const { loading, error, data } = useQuery(GET_DOCUMENTS_BY_JOB, { fetchPolicy: "network-only", @@ -42,6 +51,9 @@ 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. const content = (
{loading && } @@ -49,13 +61,19 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c {selectedMedia.filter((s) => s.isSelected).length >= 10 ? (
{t("messaging.labels.maxtenimages")}
) : null} - {!bodyshop.uselocalmediaserver && data && ( + {Imgproxy.treatment === "on" && !bodyshop.uselocalmediaserver && data && ( + + )} + {Imgproxy.treatment !== "on" && !bodyshop.uselocalmediaserver && data && ( )} - {bodyshop.uselocalmediaserver && open && ( + {Imgproxy.treatment !== "on" && bodyshop.uselocalmediaserver && open && ( ({}); +export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsContainer); -export default function JobsDocumentsContainer({ +export function JobsDocumentsContainer({ jobId, billId, documentsList, billsCallback, refetchOverride, - ignoreSizeLimit + ignoreSizeLimit, + bodyshop }) { - //TODO Add a checker to see whether we should use the img proxy side, or this side. - const useImgProxy = true; + const { + treatments: { Imgproxy } + } = useSplitTreatments({ + attributes: {}, + names: ["Imgproxy"], + splitKey: bodyshop && bodyshop.imexshopid + }); + const { loading, error, data, refetch } = useQuery(GET_DOCUMENTS_BY_JOB, { variables: { jobId: jobId }, fetchPolicy: "network-only", nextFetchPolicy: "network-only", - skip: useImgProxy || !!billId + skip: Imgproxy.treatment === "on" || !!billId }); if (loading) return ; if (error) return ; - if (useImgProxy) { + if (Imgproxy.treatment === "on") { return ( { - const result = await axios.post("/media/imgproxy/thumbnails", { jobid: jobId }); - let documents = result.data.reduce( - (acc, value) => { - if (value.type.startsWith("image")) { - acc.images.push({ - src: value.thumbnailUrl, - fullsize: value.originalUrl, - height: 225, - width: 225, - isSelected: false, - key: value.key, - extension: value.extension, - id: value.id, - type: value.type, - size: value.size, - tags: [{ value: value.type, title: value.type }] - }); - } else { - const fileName = value.key.split("/").pop(); - acc.other.push({ - source: value.originalUrlViaProxyPath, - src: value.thumbnailUrl, - fullsize: value.presignedGetUrl, - tags: [ - { - value: fileName, - title: fileName - }, - - { value: value.type, title: value.type }, - ...(value.bill - ? [ - { - value: value.bill.vendor.name, - title: t("vendors.fields.name") - }, - { value: value.bill.date, title: t("bills.fields.date") }, - { - value: value.bill.invoice_number, - title: t("bills.fields.invoice_number") - } - ] - : []) - ], - height: 225, - width: 225, - isSelected: false, - extension: value.extension, - key: value.key, - id: value.id, - type: value.type, - size: value.size - }); - } - return acc; - }, - { images: [], other: [] } - ); - setgalleryImages(documents); + const fetchThumbnails = () => { + fetchImgproxyThumbnails({ setStateCallback: setgalleryImages, jobId }); }; + useEffect(() => { if (data) { fetchThumbnails(); } - }, [data, setgalleryImages, t]); + }, [data, setgalleryImages]); const hasMediaAccess = HasFeatureAccess({ bodyshop, featureName: "media" }); const hasMobileAccess = HasFeatureAccess({ bodyshop, featureName: "mobile" }); @@ -249,3 +193,69 @@ function JobsDocumentsImgproxyComponent({ } export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsImgproxyComponent); + +export const fetchImgproxyThumbnails = async ({ setStateCallback, jobId, imagesOnly }) => { + const result = await axios.post("/media/imgproxy/thumbnails", { jobid: jobId }); + let documents = result.data.reduce( + (acc, value) => { + if (value.type.startsWith("image")) { + acc.images.push({ + src: value.thumbnailUrl, + fullsize: value.originalUrl, + height: 225, + width: 225, + isSelected: false, + key: value.key, + extension: value.extension, + id: value.id, + type: value.type, + size: value.size, + tags: [{ value: value.type, title: value.type }] + }); + } else { + const fileName = value.key.split("/").pop(); + acc.other.push({ + source: value.originalUrlViaProxyPath, + src: value.thumbnailUrl, + fullsize: value.presignedGetUrl, + tags: [ + { + value: fileName, + title: fileName + }, + + { value: value.type, title: value.type }, + ...(value.bill + ? [ + { + value: value.bill.vendor.name, + title: i18n.t("vendors.fields.name") + }, + { value: value.bill.date, title: i18n.t("bills.fields.date") }, + { + value: value.bill.invoice_number, + title: i18n.t("bills.fields.invoice_number") + } + ] + : []) + ], + height: 225, + width: 225, + isSelected: false, + extension: value.extension, + key: value.key, + id: value.id, + type: value.type, + size: value.size + }); + } + return acc; + }, + { images: [], other: [] } + ); + if (imagesOnly) { + setStateCallback(documents.images); + } else { + setStateCallback(documents); + } +}; diff --git a/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.external.component.jsx b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.external.component.jsx index ed89cc1ca..21c8059f9 100644 --- a/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.external.component.jsx +++ b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.external.component.jsx @@ -1,38 +1,20 @@ import { useEffect } from "react"; import { Gallery } from "react-grid-gallery"; import { useTranslation } from "react-i18next"; -import { GenerateSrcUrl, GenerateThumbUrl } from "./job-documents-imgproxy.utility"; +import { fetchImgproxyThumbnails } from "./jobs-documents-imgproxy-gallery.component"; +//import { GenerateSrcUrl, GenerateThumbUrl } from "./job-documents-imgproxy.utility"; -function JobsDocumentImgproxyGalleryExternal({ - data, - - externalMediaState -}) { +function JobsDocumentImgproxyGalleryExternal({ jobId, externalMediaState }) { const [galleryImages, setgalleryImages] = externalMediaState; const { t } = useTranslation(); - useEffect(() => { - let documents = data.reduce((acc, value) => { - if (value.type.startsWith("image")) { - acc.push({ - fullsize: GenerateSrcUrl(value), - src: GenerateThumbUrl(value), - thumbnailHeight: 225, - thumbnailWidth: 225, - isSelected: false, - key: value.key, - extension: value.extension, - id: value.id, - type: value.type, - tags: [{ value: value.type, title: value.type }], - size: value.size - }); - } + const fetchThumbnails = () => { + fetchImgproxyThumbnails({ setStateCallback: setgalleryImages, jobId, imagesOnly: true }); + }; - return acc; - }, []); - setgalleryImages(documents); - }, [data, setgalleryImages, t]); + useEffect(() => { + fetchThumbnails(); + }, [jobId]); return (
diff --git a/client/src/pages/temporary-docs/temporary-docs.component.jsx b/client/src/pages/temporary-docs/temporary-docs.component.jsx index 95ff5cabe..26948d7a4 100644 --- a/client/src/pages/temporary-docs/temporary-docs.component.jsx +++ b/client/src/pages/temporary-docs/temporary-docs.component.jsx @@ -1,15 +1,15 @@ import { useQuery } from "@apollo/client"; import React from "react"; import AlertComponent from "../../components/alert/alert.component"; -import JobsDocumentsComponent from "../../components/jobs-documents-gallery/jobs-documents-gallery.component"; import JobsDocumentsContainer from "../../components/jobs-documents-gallery/jobs-documents-gallery.container"; import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; import { QUERY_TEMPORARY_DOCS } from "../../graphql/documents.queries"; +import { useSplitTreatments } from "@splitsoftware/splitio-react"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; -import { selectBodyshop } from "../../redux/user/user.selectors"; import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container"; +import { selectBodyshop } from "../../redux/user/user.selectors"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop @@ -20,10 +20,18 @@ const mapDispatchToProps = (dispatch) => ({ export default connect(mapStateToProps, mapDispatchToProps)(TemporaryDocsComponent); export function TemporaryDocsComponent({ bodyshop }) { + const { + treatments: { Imgproxy } + } = useSplitTreatments({ + attributes: {}, + names: ["Imgproxy"], + splitKey: bodyshop && bodyshop.imexshopid + }); + const { loading, error, data, refetch } = useQuery(QUERY_TEMPORARY_DOCS, { fetchPolicy: "network-only", nextFetchPolicy: "network-only", - skip: bodyshop.uselocalmediaserver //TODO: Add skip if imgproxy is enabled. + skip: Imgproxy.treatment === "on" }); if (loading) return ;