Skip to content

Commit

Permalink
Merge pull request #2636 from IntersectMBO/fix/2633-cip-119-drep-imag…
Browse files Browse the repository at this point in the history
…es-base64-encoded-image-is-not-supported

feat(#2633): add support for base64 encoded images
  • Loading branch information
MSzalowski authored Jan 15, 2025
2 parents 1e78a63 + 2915a8d commit 5973428
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 46 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 base64 encoded images [Issue 2633](https://github.com/IntersectMBO/govtool/issues/2633)

### Fixed

Expand Down
99 changes: 55 additions & 44 deletions govtool/frontend/src/components/molecules/DataMissingHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { Avatar, Box, SxProps } from "@mui/material";

import { Typography } from "@atoms";
import { MetadataValidationStatus } from "@models";
import { getMetadataDataMissingStatusTranslation } from "@/utils";
import {
getBase64ImageDetails,
getMetadataDataMissingStatusTranslation,
} from "@/utils";
import { ICONS } from "@/consts";

type DataMissingHeaderProps = {
Expand All @@ -19,54 +22,62 @@ export const DataMissingHeader = ({
titleStyle,
isDRep,
image,
}: DataMissingHeaderProps) => (
<Box
sx={{
display: "grid",
gridTemplateColumns: "1fr auto",
gap: 2,
alignItems: "center",
mb: 2,
}}
data-testid="governance-action-details-card-header"
>
}: DataMissingHeaderProps) => {
const base64Image = getBase64ImageDetails(image ?? "");

return (
<Box
sx={{
flexDirection: {
xxs: "column",
md: "row",
},
alignItems: {
md: "center",
},
display: "flex",
display: "grid",
gridTemplateColumns: "1fr auto",
gap: 2,
alignItems: "center",
mb: 2,
}}
data-testid="governance-action-details-card-header"
>
{isDRep && (
<Avatar
alt="drep-image"
src={image || ICONS.defaultDRepIcon}
sx={{ width: 80, height: 80 }}
data-testid="drep-image"
/>
)}
<Typography
<Box
sx={{
...(isDRep && { ml: { md: 3 } }),
...(isDRep && { mt: { xxs: 2, md: 0 } }),
textOverflow: "ellipsis",
fontWeight: 600,
...(isDataMissing && { color: "errorRed" }),
...titleStyle,
flexDirection: {
xxs: "column",
md: "row",
},
alignItems: {
md: "center",
},
display: "flex",
}}
variant="title2"
>
{(isDataMissing &&
getMetadataDataMissingStatusTranslation(
isDataMissing as MetadataValidationStatus,
)) ||
title}
</Typography>
{isDRep && (
<Avatar
alt="drep-image"
src={
(base64Image.isValidBase64Image
? `${base64Image.base64Prefix}${image}`
: image) || ICONS.defaultDRepIcon
}
sx={{ width: 80, height: 80 }}
data-testid="drep-image"
/>
)}
<Typography
sx={{
...(isDRep && { ml: { md: 3 } }),
...(isDRep && { mt: { xxs: 2, md: 0 } }),
textOverflow: "ellipsis",
fontWeight: 600,
...(isDataMissing && { color: "errorRed" }),
...titleStyle,
}}
variant="title2"
>
{(isDataMissing &&
getMetadataDataMissingStatusTranslation(
isDataMissing as MetadataValidationStatus,
)) ||
title}
</Typography>
</Box>
</Box>
</Box>
);
);
};
9 changes: 8 additions & 1 deletion govtool/frontend/src/components/organisms/DRepCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
correctDRepDirectoryFormat,
ellipsizeText,
encodeCIP129Identifier,
getBase64ImageDetails,
getMetadataDataMissingStatusTranslation,
} from "@utils";

Expand Down Expand Up @@ -64,6 +65,8 @@ export const DRepCard = ({
bech32Prefix: isScriptBased ? "drep_script" : "drep",
});

const base64Image = getBase64ImageDetails(image ?? "");

return (
<Card
{...(isMe && {
Expand Down Expand Up @@ -119,7 +122,11 @@ export const DRepCard = ({
<Box flexDirection="row" minWidth={0} display="flex">
<Avatar
alt="drep-image"
src={image || ICONS.defaultDRepIcon}
src={
(base64Image.isValidBase64Image
? `${base64Image.base64Prefix}${image}`
: image) || ICONS.defaultDRepIcon
}
data-testid="drep-image"
/>
<Box
Expand Down
68 changes: 68 additions & 0 deletions govtool/frontend/src/utils/getBase64ImageDetails.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Checks if a given string is a base64-encoded image or SVG and returns its details.
*
* @param str - The string to check.
* @returns An object with the MIME type, base64 prefix, and validity flag.
*/
export function getBase64ImageDetails(str: string): {
type: string | null;
base64Prefix: string | null;
isValidBase64Image: boolean;
} {
if (!str || typeof str !== "string") {
return {
type: null,
base64Prefix: null,
isValidBase64Image: false,
};
}

try {
const decoded = atob(str);

if (decoded.trim().startsWith("<?xml") || decoded.includes("<svg")) {
return {
type: "svg+xml",
base64Prefix: "data:image/svg+xml;base64,",
isValidBase64Image: true,
};
}

const magicNumbers = decoded.slice(0, 4);
let type: string | null = null;

switch (magicNumbers) {
case "\x89PNG":
type = "png";
break;
case "\xFF\xD8\xFF":
type = "jpeg";
break;
case "GIF8":
type = "gif";
break;
default:
type = null;
}

if (type) {
return {
type,
base64Prefix: `data:image/${type};base64,`,
isValidBase64Image: true,
};
}
} catch {
return {
type: null,
base64Prefix: null,
isValidBase64Image: false,
};
}

return {
type: null,
base64Prefix: null,
isValidBase64Image: false,
};
}
1 change: 1 addition & 0 deletions govtool/frontend/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ export * from "./setProtocolParameterUpdate";
export * from "./testIdFromLabel";
export * from "./uniqBy";
export * from "./wait";
export * from "./getBase64ImageDetails";
Loading

0 comments on commit 5973428

Please sign in to comment.