From 0c9b2710b7a31f1cdcc3e5c3fd6a2f3e69945555 Mon Sep 17 00:00:00 2001 From: ibolton336 Date: Mon, 2 Oct 2023 12:24:06 -0400 Subject: [PATCH] Move logic to hook and update based on PR suggestions --- .../target-card/hooks/useFetchImageDataUrl.ts | 50 +++++++++++++++++++ .../target-card.css} | 0 .../target-card.tsx} | 34 ++++--------- .../analysis-wizard/set-targets.tsx | 2 +- .../migration-targets/components/dnd/item.tsx | 2 +- 5 files changed, 61 insertions(+), 27 deletions(-) create mode 100644 client/src/app/components/target-card/hooks/useFetchImageDataUrl.ts rename client/src/app/components/{TargetCard.css => target-card/target-card.css} (100%) rename client/src/app/components/{TargetCard.tsx => target-card/target-card.tsx} (86%) diff --git a/client/src/app/components/target-card/hooks/useFetchImageDataUrl.ts b/client/src/app/components/target-card/hooks/useFetchImageDataUrl.ts new file mode 100644 index 0000000000..f381064b38 --- /dev/null +++ b/client/src/app/components/target-card/hooks/useFetchImageDataUrl.ts @@ -0,0 +1,50 @@ +import { useState, useEffect } from "react"; +import axios from "axios"; +import DefaultImage from "@app/images/Icon-Red_Hat-Virtual_server_stack-A-Black-RGB.svg"; +import { Target } from "@app/api/models"; +import { FILES } from "@app/api/rest"; + +const useFetchImageDataUrl = (target: Target) => { + const [imageDataUrl, setImageDataUrl] = useState(null); + + useEffect(() => { + const imagePath = target?.image?.id + ? `${FILES}/${target?.image.id}` + : DefaultImage; + + (async () => { + try { + const response = await axios.get(imagePath, { + headers: { + Accept: "application/octet-stream", + }, + responseType: "arraybuffer", + }); + const contentType = response.headers["content-type"]; + + let imageData; + + if (contentType === "image/svg+xml") { + const text = new TextDecoder().decode(response.data); + imageData = `data:${contentType},${encodeURIComponent(text)}`; + } else { + const base64 = btoa( + new Uint8Array(response.data).reduce( + (data, byte) => data + String.fromCharCode(byte), + "" + ) + ); + imageData = `data:${contentType};base64,${base64}`; + } + + setImageDataUrl(imageData); + } catch (error) { + console.error("There was an issue fetching the image:", error); + } + })(); + }, [target]); + + return imageDataUrl; +}; + +export default useFetchImageDataUrl; diff --git a/client/src/app/components/TargetCard.css b/client/src/app/components/target-card/target-card.css similarity index 100% rename from client/src/app/components/TargetCard.css rename to client/src/app/components/target-card/target-card.css diff --git a/client/src/app/components/TargetCard.tsx b/client/src/app/components/target-card/target-card.tsx similarity index 86% rename from client/src/app/components/TargetCard.tsx rename to client/src/app/components/target-card/target-card.tsx index 8a78a8a972..6914623532 100644 --- a/client/src/app/components/TargetCard.tsx +++ b/client/src/app/components/target-card/target-card.tsx @@ -1,3 +1,4 @@ +import "./target-card.css"; import * as React from "react"; import { EmptyState, @@ -28,11 +29,10 @@ import { GripVerticalIcon } from "@patternfly/react-icons"; import spacing from "@patternfly/react-styles/css/utilities/Spacing/spacing"; import { useTranslation } from "react-i18next"; -import { KebabDropdown } from "./KebabDropdown"; -import DefaultRulesetIcon from "@app/images/Icon-Red_Hat-Virtual_server_stack-A-Black-RGB.svg"; +import { KebabDropdown } from "../KebabDropdown"; +import DefaultImage from "@app/images/Icon-Red_Hat-Virtual_server_stack-A-Black-RGB.svg"; import { Target, TargetLabel } from "@app/api/models"; -import "./TargetCard.css"; -import axios from "axios"; +import useFetchImageDataUrl from "./hooks/useFetchImageDataUrl"; export interface TargetCardProps { item: Target; @@ -68,8 +68,7 @@ export const TargetCard: React.FC = ({ }) => { const { t } = useTranslation(); const [isCardSelected, setCardSelected] = React.useState(cardSelected); - - const [imageDataUrl, setImageDataUrl] = React.useState(null); + const imageDataUrl = useFetchImageDataUrl(target); const prevSelectedLabel = formLabels?.find((formLabel) => { @@ -108,24 +107,6 @@ export const TargetCard: React.FC = ({ } }; - const fetchImageAndSetDataUrl = async (imagePath: string) => { - try { - const imageDataResponse = await axios.get(imagePath, { - headers: { - Accept: "application/octet-stream", - }, - }); - const encodedSvg = encodeURIComponent(imageDataResponse.data); - setImageDataUrl(`data:image/svg+xml,${encodedSvg}`); - } catch (error) { - console.error("There was an issue fetching the image:", error); - } - }; - const imagePath = target?.image?.id - ? `/hub/files/${target?.image.id}` - : DefaultRulesetIcon; - fetchImageAndSetDataUrl(imagePath); - return ( = ({ ( Card logo { + e.currentTarget.src = DefaultImage; + }} /> )} /> diff --git a/client/src/app/pages/applications/analysis-wizard/set-targets.tsx b/client/src/app/pages/applications/analysis-wizard/set-targets.tsx index a5620d3db2..c8cae4a800 100644 --- a/client/src/app/pages/applications/analysis-wizard/set-targets.tsx +++ b/client/src/app/pages/applications/analysis-wizard/set-targets.tsx @@ -10,7 +10,7 @@ import { import { useTranslation } from "react-i18next"; import { useFormContext } from "react-hook-form"; -import { TargetCard } from "@app/components/TargetCard"; +import { TargetCard } from "@app/components/target-card/target-card"; import { AnalysisWizardFormValues } from "./schema"; import { useSetting } from "@app/queries/settings"; import { useFetchTargets } from "@app/queries/targets"; diff --git a/client/src/app/pages/migration-targets/components/dnd/item.tsx b/client/src/app/pages/migration-targets/components/dnd/item.tsx index 30e24f6362..5a21ff52c7 100644 --- a/client/src/app/pages/migration-targets/components/dnd/item.tsx +++ b/client/src/app/pages/migration-targets/components/dnd/item.tsx @@ -1,5 +1,5 @@ import React, { forwardRef } from "react"; -import { TargetCard } from "@app/components/TargetCard"; +import { TargetCard } from "@app/components/target-card/target-card"; import { useFetchTargets } from "@app/queries/targets"; interface ItemProps {