diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30c8aea4c..48280e697 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,7 +12,7 @@ changes.
### Added
--
+- Add support for displaying CIP-119 DRep images [Issue 1806](https://github.com/IntersectMBO/govtool/issues/1806)
### Fixed
diff --git a/govtool/frontend/.env.example b/govtool/frontend/.env.example
index 5fbb1a97f..2489d65a0 100644
--- a/govtool/frontend/.env.example
+++ b/govtool/frontend/.env.example
@@ -8,3 +8,5 @@ VITE_IS_DEV=true
VITE_USERSNAP_SPACE_API_KEY=""
VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED='true'
VITE_PDF_API_URL=""
+VITE_IPFS_GATEWAY=""
+VITE_IPFS_PROJECT_ID=""
\ No newline at end of file
diff --git a/govtool/frontend/Dockerfile b/govtool/frontend/Dockerfile
index 5186b390e..9a12dba06 100644
--- a/govtool/frontend/Dockerfile
+++ b/govtool/frontend/Dockerfile
@@ -10,6 +10,8 @@ ARG NPMRC_TOKEN
ARG VITE_USERSNAP_SPACE_API_KEY
ARG VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED='true'
ARG VITE_PDF_API_URL
+ARG VITE_IPFS_GATEWAY
+ARG VITE_IPFS_PROJECT_ID
# Ensure all required build arguments are set
RUN \
@@ -21,7 +23,9 @@ RUN \
: "${VITE_SENTRY_DSN:?Build argument VITE_SENTRY_DSN is not set}" && \
: "${NPMRC_TOKEN:?Build argument NPMRC_TOKEN is not set}" && \
: "${VITE_USERSNAP_SPACE_API_KEY:?Build argument VITE_USERSNAP_SPACE_API_KEY is not set}" && \
- : "${VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED:?Build argument VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED is not set}"
+ : "${VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED:?Build argument VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED is not set}" && \
+ : "${VITE_IPFS_GATEWAY:?Build argument VITE_IPFS_GATEWAY is not set}" && \
+ : "${VITE_IPFS_PROJECT_ID:?Build argument VITE_IPFS_PROJECT_ID is not set}"
ENV NODE_OPTIONS=--max_old_space_size=8192
diff --git a/govtool/frontend/Dockerfile.qovery b/govtool/frontend/Dockerfile.qovery
index ddc6c1288..f9b562ade 100644
--- a/govtool/frontend/Dockerfile.qovery
+++ b/govtool/frontend/Dockerfile.qovery
@@ -10,6 +10,8 @@ ARG NPMRC_TOKEN
ARG VITE_USERSNAP_SPACE_API_KEY
ARG VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED='true'
ARG VITE_PDF_API_URL
+ARG VITE_IPFS_GATEWAY
+ARG VITE_IPFS_PROJECT_ID
ENV NODE_OPTIONS=--max_old_space_size=8192
diff --git a/govtool/frontend/public/icons/DefaultDRep.svg b/govtool/frontend/public/icons/DefaultDRep.svg
new file mode 100644
index 000000000..00557579b
--- /dev/null
+++ b/govtool/frontend/public/icons/DefaultDRep.svg
@@ -0,0 +1,5 @@
+
diff --git a/govtool/frontend/src/components/molecules/DataMissingHeader.tsx b/govtool/frontend/src/components/molecules/DataMissingHeader.tsx
index 6267d8742..ef6e278b4 100644
--- a/govtool/frontend/src/components/molecules/DataMissingHeader.tsx
+++ b/govtool/frontend/src/components/molecules/DataMissingHeader.tsx
@@ -1,19 +1,24 @@
-import { Box, SxProps } from "@mui/material";
+import { Avatar, Box, SxProps } from "@mui/material";
import { Typography } from "@atoms";
import { MetadataValidationStatus } from "@models";
import { getMetadataDataMissingStatusTranslation } from "@/utils";
+import { ICONS } from "@/consts";
type DataMissingHeaderProps = {
isDataMissing: MetadataValidationStatus | null;
title?: string;
titleStyle?: SxProps;
+ isDRep?: boolean;
+ image?: string | null;
};
export const DataMissingHeader = ({
title,
isDataMissing,
titleStyle,
+ isDRep,
+ image,
}: DataMissingHeaderProps) => (
+ {isDRep && (
+
+ )}
-
-
- {metadataStatus
- ? getMetadataDataMissingStatusTranslation(metadataStatus)
- : ellipsizeText(givenName ?? "", 25)}
-
- {
- navigator.clipboard.writeText(view);
- addSuccessAlert(t("alerts.copiedToClipboard"));
- e.stopPropagation();
- }}
- sx={{
- gap: 1,
- width: "250px",
- maxWidth: "100%",
- "&:hover": {
- opacity: 0.6,
- transition: "opacity 0.3s",
- },
- }}
- >
-
- {view}
+
+
+
+
+ {metadataStatus
+ ? getMetadataDataMissingStatusTranslation(metadataStatus)
+ : ellipsizeText(givenName ?? "", 25)}
-
-
+ {
+ navigator.clipboard.writeText(view);
+ addSuccessAlert(t("alerts.copiedToClipboard"));
+ e.stopPropagation();
+ }}
+ sx={{
+ gap: 1,
+ width: "250px",
+ maxWidth: "100%",
+ "&:hover": {
+ opacity: 0.6,
+ transition: "opacity 0.3s",
+ },
+ }}
+ >
+
+ {view}
+
+
+
+
diff --git a/govtool/frontend/src/components/organisms/DRepDetailsCardHeader.tsx b/govtool/frontend/src/components/organisms/DRepDetailsCardHeader.tsx
index 77092b30f..ecbb61eba 100644
--- a/govtool/frontend/src/components/organisms/DRepDetailsCardHeader.tsx
+++ b/govtool/frontend/src/components/organisms/DRepDetailsCardHeader.tsx
@@ -32,7 +32,7 @@ export const DRepDetailsCardHeader = ({
const { votingPower: myVotingPower } =
useGetAdaHolderVotingPowerQuery(stakeKey);
- const { givenName, metadataStatus } = dRepData;
+ const { givenName, metadataStatus, image } = dRepData;
const navigateToEditDRep = () => {
navigate(PATHS.editDrepMetadata, {
@@ -121,7 +121,9 @@ export const DRepDetailsCardHeader = ({
)}
diff --git a/govtool/frontend/src/consts/icons.ts b/govtool/frontend/src/consts/icons.ts
index bd70a2b06..4d8672516 100644
--- a/govtool/frontend/src/consts/icons.ts
+++ b/govtool/frontend/src/consts/icons.ts
@@ -13,6 +13,7 @@ export const ICONS = {
copyWhiteIcon: "/icons/CopyWhite.svg",
dashboardActiveIcon: "/icons/DashboardActive.svg",
dashboardIcon: "/icons/Dashboard.svg",
+ defaultDRepIcon: "/icons/DefaultDRep.svg",
download: "/icons/Download.svg",
drawerIcon: "/icons/DrawerIcon.svg",
dRepDirectoryActiveIcon: "/icons/DRepDirectoryActive.svg",
diff --git a/govtool/frontend/src/models/api.ts b/govtool/frontend/src/models/api.ts
index fff33a0c0..32ef7bcc5 100644
--- a/govtool/frontend/src/models/api.ts
+++ b/govtool/frontend/src/models/api.ts
@@ -144,6 +144,9 @@ export type DrepDataDTO = {
url?: string;
view: string;
votingPower?: number;
+ imageUrl: string | null;
+ // either base64 for IPFS image or URL for regular image
+ image: string | null;
};
export type DRepData = DrepDataDTO & {
@@ -156,6 +159,9 @@ export type DRepData = DrepDataDTO & {
doNotList: boolean;
metadataStatus: MetadataValidationStatus | null;
metadataValid: boolean;
+ imageUrl: string | null;
+ // either base64 for IPFS image or URL for regular image
+ image: string | null;
};
export type Vote = "yes" | "no" | "abstain";
diff --git a/govtool/frontend/src/utils/mapDtoToDrep.ts b/govtool/frontend/src/utils/mapDtoToDrep.ts
index 419973c14..4ab65d9fa 100644
--- a/govtool/frontend/src/utils/mapDtoToDrep.ts
+++ b/govtool/frontend/src/utils/mapDtoToDrep.ts
@@ -11,7 +11,7 @@ export const mapDtoToDrep = async (dto: DrepDataDTO): Promise => {
const emptyMetadata = {
paymentAddress: null,
givenName: "",
- image: null,
+ imageUrl: null,
objectives: null,
motivations: null,
qualifications: null,
@@ -24,6 +24,33 @@ export const mapDtoToDrep = async (dto: DrepDataDTO): Promise => {
// DBSync contains wrong representation of DRep view for script based DReps
const view = fixViewForScriptBasedDRep(dto.view, dto.isScriptBased);
+ // We need to prefetch the image, for the IPFS support
+ let base64Image = null;
+ const isIPFSImage = dto.imageUrl?.startsWith("ipfs://") || false;
+ if (dto.imageUrl) {
+ fetch(
+ isIPFSImage
+ ? `${process.env.VITE_IPFS_GATEWAY}/${dto.imageUrl?.slice(7)}`
+ : dto.imageUrl,
+ isIPFSImage
+ ? {
+ headers: { project_id: import.meta.env.VITE_IPFS_PROJECT_ID },
+ }
+ : {},
+ )
+ .then(async (res) => {
+ const blob = await res.blob();
+ const reader = new FileReader();
+ reader.readAsDataURL(blob);
+ reader.onloadend = () => {
+ base64Image = reader.result;
+ };
+ })
+ .catch((e) => {
+ console.error("Fetching the DRep image failed, reason: ", e);
+ });
+ }
+
if (dto.metadataHash && dto.url) {
const validationResponse = await postValidate({
url: dto.url,
@@ -36,6 +63,7 @@ export const mapDtoToDrep = async (dto: DrepDataDTO): Promise => {
...validationResponse.metadata,
metadataStatus: validationResponse.status || null,
metadataValid: validationResponse.valid,
+ image: isIPFSImage ? base64Image : dto.imageUrl,
view,
};
}
@@ -44,5 +72,6 @@ export const mapDtoToDrep = async (dto: DrepDataDTO): Promise => {
...dto,
...emptyMetadata,
view,
+ image: isIPFSImage ? base64Image : dto.imageUrl,
};
};
diff --git a/govtool/frontend/src/utils/tests/dRep.test.ts b/govtool/frontend/src/utils/tests/dRep.test.ts
index 104bfe019..66f41fe63 100644
--- a/govtool/frontend/src/utils/tests/dRep.test.ts
+++ b/govtool/frontend/src/utils/tests/dRep.test.ts
@@ -24,6 +24,8 @@ const EXAMPLE_DREP: DRepData = {
qualifications: null,
doNotList: false,
isScriptBased: false,
+ imageUrl: null,
+ image: null,
};
describe("isSameDRep function", () => {
diff --git a/scripts/govtool/frontend.mk b/scripts/govtool/frontend.mk
index 64f5516e8..910cbd030 100644
--- a/scripts/govtool/frontend.mk
+++ b/scripts/govtool/frontend.mk
@@ -25,6 +25,8 @@ build-frontend: docker-login
--build-arg VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED="$${IS_PROPOSAL_DISCUSSION_FORUM_ENABLED}" \
--build-arg NPMRC_TOKEN="$${NPMRC_TOKEN}" \
--build-arg VITE_PDF_API_URL="$${PDF_API_URL}" \
+ --build-arg VITE_IPFS_GATEWAY="$${IPFS_GATEWAY}" \
+ --build-arg VITE_IPFS_PROJECT_ID="$${IPFS_PROJECT_ID}" \
$(root_dir)/govtool/frontend
.PHONY: push-frontend