Skip to content

Commit

Permalink
feat(#1806): add support for CIP-119 DRep images
Browse files Browse the repository at this point in the history
  • Loading branch information
MSzalowski committed Dec 5, 2024
1 parent 8f32f9d commit a2c1820
Show file tree
Hide file tree
Showing 13 changed files with 124 additions and 37 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ changes.

### Added

-
- Add support for displaying CIP-119 DRep images [Issue 1806](https://github.com/IntersectMBO/govtool/issues/1806)

### Fixed

Expand Down
2 changes: 2 additions & 0 deletions govtool/frontend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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=""
6 changes: 5 additions & 1 deletion govtool/frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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

Expand Down
2 changes: 2 additions & 0 deletions govtool/frontend/Dockerfile.qovery
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 5 additions & 0 deletions govtool/frontend/public/icons/DefaultDRep.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 23 additions & 2 deletions govtool/frontend/src/components/molecules/DataMissingHeader.tsx
Original file line number Diff line number Diff line change
@@ -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) => (
<Box
sx={{
Expand All @@ -27,12 +32,28 @@ export const DataMissingHeader = ({
>
<Box
sx={{
flexDirection: {
sm: "column",
lg: "row",
},
alignItems: {
lg: "center",
},
display: "flex",
alignItems: "center",
}}
>
{isDRep && (
<Avatar
alt="drep-image"
src={image || ICONS.defaultDRepIcon}
sx={{ width: 80, height: 80 }}
data-testid="drep-image"
/>
)}
<Typography
sx={{
...(isDRep && { ml: { lg: 3 } }),
...(isDRep && { mt: { sm: 2, lg: 0 } }),
textOverflow: "ellipsis",
fontWeight: 600,
...(isDataMissing && { color: "errorRed" }),
Expand Down
73 changes: 42 additions & 31 deletions govtool/frontend/src/components/organisms/DRepCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useNavigate } from "react-router-dom";
import { Box, ButtonBase, Divider } from "@mui/material";
import { Box, ButtonBase, Divider, Avatar } from "@mui/material";

import { Button, StatusPill, Typography } from "@atoms";
import { ICONS, PATHS } from "@consts";
Expand All @@ -24,7 +24,7 @@ type DRepCardProps = {
};

export const DRepCard = ({
dRep: { status, type, view, votingPower, givenName, metadataStatus },
dRep: { status, type, view, votingPower, givenName, metadataStatus, image },
isConnected,
isDelegationLoading,
isInProgress,
Expand Down Expand Up @@ -100,36 +100,47 @@ export const DRepCard = ({
containerType: "inline-size",
}}
>
<Box minWidth={0} display="flex" flexDirection="column">
<Typography
sx={{ ellipsisStyles, color: metadataStatus && "errorRed" }}
>
{metadataStatus
? getMetadataDataMissingStatusTranslation(metadataStatus)
: ellipsizeText(givenName ?? "", 25)}
</Typography>
<ButtonBase
data-testid={`${view}-copy-id-button`}
onClick={(e) => {
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",
},
}}
>
<Typography color="primary" variant="body2" sx={ellipsisStyles}>
{view}
<Box flexDirection="row" minWidth={0} display="flex">
<Avatar
alt="drep-image"
src={image || ICONS.defaultDRepIcon}
data-testid="drep-image"
/>
<Box ml={3}>
<Typography
sx={{ ellipsisStyles, color: metadataStatus && "errorRed" }}
>
{metadataStatus
? getMetadataDataMissingStatusTranslation(metadataStatus)
: ellipsizeText(givenName ?? "", 25)}
</Typography>
<img alt="" src={ICONS.copyBlueIcon} />
</ButtonBase>
<ButtonBase
data-testid={`${view}-copy-id-button`}
onClick={(e) => {
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",
},
}}
>
<Typography
color="primary"
variant="body2"
sx={ellipsisStyles}
>
{view}
</Typography>
<img alt="" src={ICONS.copyBlueIcon} />
</ButtonBase>
</Box>
</Box>

<Box sx={{ display: "flex", flex: { xl: 1 }, gap: 3 }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand Down Expand Up @@ -121,7 +121,9 @@ export const DRepDetailsCardHeader = ({
</Box>
)}
<DataMissingHeader
isDRep
title={givenName ?? undefined}
image={image}
isDataMissing={metadataStatus}
titleStyle={{ wordBreak: "break-word", whiteSpace: "wrap" }}
/>
Expand Down
1 change: 1 addition & 0 deletions govtool/frontend/src/consts/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 6 additions & 0 deletions govtool/frontend/src/models/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 & {
Expand All @@ -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";
Expand Down
31 changes: 30 additions & 1 deletion govtool/frontend/src/utils/mapDtoToDrep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const mapDtoToDrep = async (dto: DrepDataDTO): Promise<DRepData> => {
const emptyMetadata = {
paymentAddress: null,
givenName: "",
image: null,
imageUrl: null,
objectives: null,
motivations: null,
qualifications: null,
Expand All @@ -24,6 +24,33 @@ export const mapDtoToDrep = async (dto: DrepDataDTO): Promise<DRepData> => {
// 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<DRepMetadata>({
url: dto.url,
Expand All @@ -36,6 +63,7 @@ export const mapDtoToDrep = async (dto: DrepDataDTO): Promise<DRepData> => {
...validationResponse.metadata,
metadataStatus: validationResponse.status || null,
metadataValid: validationResponse.valid,
image: isIPFSImage ? base64Image : dto.imageUrl,
view,
};
}
Expand All @@ -44,5 +72,6 @@ export const mapDtoToDrep = async (dto: DrepDataDTO): Promise<DRepData> => {
...dto,
...emptyMetadata,
view,
image: isIPFSImage ? base64Image : dto.imageUrl,
};
};
2 changes: 2 additions & 0 deletions govtool/frontend/src/utils/tests/dRep.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const EXAMPLE_DREP: DRepData = {
qualifications: null,
doNotList: false,
isScriptBased: false,
imageUrl: null,
image: null,
};

describe("isSameDRep function", () => {
Expand Down
2 changes: 2 additions & 0 deletions scripts/govtool/frontend.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit a2c1820

Please sign in to comment.