Skip to content

Commit

Permalink
Add image component (#1783)
Browse files Browse the repository at this point in the history
Add image component for showing images as part of snaps custom UI. Also
adds support for this feature in the snaps simulator. This PR will need
follow-up work in the extension.

Progresses MetaMask/MetaMask-planning#1345

---------

Co-authored-by: MetaMask Bot <metamaskbot@users.noreply.github.com>
  • Loading branch information
FrederikBolding and metamaskbot authored Oct 4, 2023
1 parent eeab196 commit 50ce7fd
Show file tree
Hide file tree
Showing 37 changed files with 356 additions and 99 deletions.
2 changes: 1 addition & 1 deletion packages/examples/packages/bip32/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "IA416y2exwrsMSudEVq9klDtqzEdIhYI5Otkio++1IM=",
"shasum": "MPZ9ur9CDq3vW/16po2PcMSAE/0wWl4sPgjGBwqCW7g=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/bip44/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "C8bg0hT0mwRb8J7+Jd/0pD7alzy5kjp3eNVdjoUC6u8=",
"shasum": "H7vpcjHBrK0/AdGj9SAxC4gnp9l6zxDYtP+V/vFX9IU=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/cronjobs/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "rvezXlgNZfZl22VCm9paR8ZQBkEW6MMhsjHJsZ88mgU=",
"shasum": "Igjisz3YyW+UXN7ZbJK64iGPOTGYfCQyZe2YJSs14e0=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/dialogs/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "y43f6bGv72tTmRzIiKO8PaVMHJRyCwlNjTqGhQ3hpNs=",
"shasum": "PiQKZ+XJWHflCsFTqI+bfOC2lv51ReaYh55NBKV7sqY=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/ethers-js/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "QY3seNbkU81kQ25hpBICjcMUK41iI0tYUlwqsiWbVCo=",
"shasum": "jxgfHQY46aYV6rUX6/ZdwrFD8A7HFltx7xQLZnRrrbc=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/get-entropy/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "SqG/RM+24nIsLCl7+HucvmAYhMQ805I8XGVwHSNSyeo=",
"shasum": "WvilWKhHZPbr31IFi6GP5yw77sFlJpFELjvS5qN0QvM=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/get-locale/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "n2m2+BjngNocIOW80DpOnTmZfmkK5+i4koXaXrU4Zpo=",
"shasum": "BYde5IQ3I0Y6sCow1IGm4mAyksC5fdP3DHnOXZFzxDw=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "XuzaWZsiAH+Upc+RGI1x1WzFhn23jl3sDwa3xEDNUws=",
"shasum": "qQMuTp5zbcAAVNMENTQCvo5Rj825ohur3Uwld8Nxgs0=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "TszJ60KeknYWFD5tY/w/PMOk+cA5eeeaAkzyYbPqwYw=",
"shasum": "WR+0BkxjQYQxm3GObO6S+C0v2vZYUZmL0l+TQEcZGCs=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/manage-state/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "8PzB9xNqIXiDhbqzk96oslU2V/zzNaxU1KLzLIpuZl8=",
"shasum": "3l7IqA82IUC9I+tMEwQSSRKZKoTtQ3pgRwX8kyLz+MM=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "6oziSobjFA9vgdwOwRRhLzYRcRNnfo3oxYFh1rIoxfg=",
"shasum": "z6nWAwVjLuw07zNx1RrFLPChIrr7U/UlJSFo7JXx4pM=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "8V8IDFAjnJro0x76n9qcAEHMQiWJXGEB33R8PzICy3E=",
"shasum": "qB0yTTvaJe1nh6BTU3IxXZgoMJFIVFDkGjrl12ykEVc=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
3 changes: 3 additions & 0 deletions packages/snaps-simulator/src/assets/icons/image.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions packages/snaps-simulator/src/assets/icons/snap-error.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions packages/snaps-simulator/src/components/Copyable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Flex, Text } from '@chakra-ui/react';
import type { FunctionComponent } from 'react';
import { useEffect, useState } from 'react';

import { Icon } from './Icon';

export type CopyableProps = {
value: string;
};

export const Copyable: FunctionComponent<CopyableProps> = ({ value }) => {
const [copied, setCopied] = useState(false);

const handleClick = () => {
navigator.clipboard
.writeText(value)
.then(() => {
setCopied(true);
})
.catch((error) => {
// eslint-disable-next-line no-console
console.error(error);
});
};

// eslint-disable-next-line consistent-return
useEffect(() => {
if (copied) {
const timeout = setTimeout(() => {
setCopied(false);
}, 2000);

return () => clearTimeout(timeout);
}
}, [copied]);

return (
<Flex
padding="2"
borderRadius="md"
background="background.alternative"
justifyContent="space-between"
marginBottom="1"
wordBreak="break-word"
>
<Text
fontFamily="custom"
color="text.alternative"
fontSize="sm"
lineHeight="157%"
>
{value}
</Text>
<Icon
icon={copied ? 'copied' : 'copy'}
width="15px"
cursor="pointer"
onClick={handleClick}
/>
</Flex>
);
};
65 changes: 50 additions & 15 deletions packages/snaps-simulator/src/components/Delineator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,62 @@ import type { FunctionComponent, ReactNode } from 'react';
import { Icon } from './Icon';

export type DelineatorProps = {
type: DelineatorType;
snapName: string;
children: ReactNode;
};

export enum DelineatorType {
Content = 'content',
Error = 'error',
Insights = 'insights',
}

const getTitle = (type: DelineatorType, snapName: string) => {
switch (type) {
case DelineatorType.Insights:
return `Insights from ${snapName}`;
case DelineatorType.Error:
return `Error from ${snapName}`;
default:
return `Content from ${snapName}`;
}
};

export const Delineator: FunctionComponent<DelineatorProps> = ({
type,
snapName,
children,
}) => (
<Box border="1px solid" borderColor="border.default" borderRadius="md">
<Flex
direction="row"
alignItems="center"
padding="1.5"
borderBottom="1px solid"
}) => {
const isError = type === DelineatorType.Error;
return (
<Box
border="1px solid"
borderColor="border.default"
borderRadius="md"
backgroundColor={isError ? 'error.muted' : undefined}
>
<Icon icon="snap" width="16px" marginRight="1" />
<Text fontFamily="custom" fontSize="xs">
Content from {snapName}
</Text>
</Flex>
<Box padding="3">{children}</Box>
</Box>
);
<Flex
direction="row"
alignItems="center"
padding="1.5"
borderBottom="1px solid"
borderColor="border.default"
>
<Icon
icon={isError ? 'snapError' : 'snap'}
width="16px"
marginRight="1"
/>
<Text
fontFamily="custom"
fontSize="xs"
color={isError ? 'text.error' : undefined}
>
{getTitle(type, snapName)}
</Text>
</Flex>
<Box padding="3">{children}</Box>
</Box>
);
};
10 changes: 10 additions & 0 deletions packages/snaps-simulator/src/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import errorTriangleIcon from '../assets/icons/error-triangle.svg';
import gitHubDarkIcon from '../assets/icons/github-dark.svg';
import gitHubIcon from '../assets/icons/github.svg';
import headingIcon from '../assets/icons/heading.svg';
import imageIcon from '../assets/icons/image.svg';
import insightsIcon from '../assets/icons/insights.svg';
import jsonRpcIcon from '../assets/icons/json-rpc.svg';
import manifestIcon from '../assets/icons/manifest.svg';
Expand All @@ -34,6 +35,7 @@ import playErrorIcon from '../assets/icons/play-error.svg';
import playMutedIcon from '../assets/icons/play-muted.svg';
import playSuccessIcon from '../assets/icons/play-success.svg';
import playIcon from '../assets/icons/play.svg';
import snapErrorIcon from '../assets/icons/snap-error.svg';
import snapIcon from '../assets/icons/snap.svg';
import textBubbleIcon from '../assets/icons/text-bubble.svg';
import textIcon from '../assets/icons/text.svg';
Expand Down Expand Up @@ -101,6 +103,10 @@ const DEFAULT_ICONS = {
alt: 'Snap',
src: snapIcon,
},
snapError: {
alt: 'Snap Error',
src: snapErrorIcon,
},
copy: {
alt: 'Copy',
src: copyIcon,
Expand Down Expand Up @@ -167,6 +173,10 @@ const DEFAULT_ICONS = {
alt: 'UI',
src: uiIcon,
},
image: {
alt: 'Image',
src: imageIcon,
},
};

export type IconName = keyof typeof DEFAULT_ICONS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Component } from '@metamask/snaps-ui';
import type { FunctionComponent } from 'react';

import { Renderer } from '../../features/renderer';
import { Delineator } from '../Delineator';
import { Delineator, DelineatorType } from '../Delineator';
import { Window } from '../Window';

export type AlertDialogProps = {
Expand Down Expand Up @@ -31,7 +31,7 @@ export const AlertDialog: FunctionComponent<AlertDialogProps> = ({
}) => (
<Window snapName={snapName} snapId={snapId}>
<Box margin="4" marginTop="0" flex="1">
<Delineator snapName={snapName}>
<Delineator type={DelineatorType.Content} snapName={snapName}>
<Renderer node={node} />
</Delineator>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Component } from '@metamask/snaps-ui';
import type { FunctionComponent } from 'react';

import { Renderer } from '../../features/renderer';
import { Delineator } from '../Delineator';
import { Delineator, DelineatorType } from '../Delineator';
import { Window } from '../Window';

export type ConfirmationDialogProps = {
Expand Down Expand Up @@ -34,7 +34,7 @@ export const ConfirmationDialog: FunctionComponent<ConfirmationDialogProps> = ({
}) => (
<Window snapName={snapName} snapId={snapId}>
<Box margin="4" marginTop="0" flex="1">
<Delineator snapName={snapName}>
<Delineator type={DelineatorType.Content} snapName={snapName}>
<Renderer node={node} />
</Delineator>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { FunctionComponent } from 'react';
import { useForm } from 'react-hook-form';

import { Renderer } from '../../features/renderer';
import { Delineator } from '../Delineator';
import { Delineator, DelineatorType } from '../Delineator';
import { Window } from '../Window';

export type PromptDialogProps = {
Expand Down Expand Up @@ -58,7 +58,7 @@ export const PromptDialog: FunctionComponent<PromptDialogProps> = ({
return (
<Window snapName={snapName} snapId={snapId}>
<Box margin="4" marginTop="0" flex="1">
<Delineator snapName={snapName}>
<Delineator type={DelineatorType.Content} snapName={snapName}>
<Renderer node={node} />
{/* eslint-disable-next-line @typescript-eslint/no-misused-promises */}
<form onSubmit={handleSubmit(onFormSubmit)} id="prompt-form">
Expand Down
1 change: 1 addition & 0 deletions packages/snaps-simulator/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export * from './Root';
export * from './Link';
export * from './TestConditional';
export * from './Window';
export * from './Copyable';
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';

type ErrorBoundaryProps = {
fallback: React.ReactNode;
children: React.ReactNode;
};

type ErrorBoundaryState = {
hasError: boolean;
};

export class ErrorBoundary extends React.Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
constructor(props: ErrorBoundaryProps) {
super(props);

this.state = { hasError: false };
}

componentDidUpdate(prevProps: ErrorBoundaryProps) {
if (prevProps.children !== this.props.children) {
this.setState({ hasError: false });
}
}

static getDerivedStateFromError() {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}

render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return this.props.fallback;
}

return this.props.children;
}
}
Loading

0 comments on commit 50ce7fd

Please sign in to comment.