1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

[FLASK] Update snap authorship component (#18262)

* Create new snap authorship component

* Add icons and fix overflow issues

* Add empty state + more fixes

* Fix overflow

* Move some code to SnapAvatar

* Fix lint

* Change component name

* Delete forgotten file
This commit is contained in:
Frederik Bolding 2023-03-24 17:16:46 +01:00 committed by GitHub
parent 79b2deb194
commit 4179ce634c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 232 additions and 167 deletions

View File

@ -34,7 +34,6 @@
@import 'edit-gas-fee-popover/network-statistics/status-slider/index';
@import 'edit-gas-fee-popover/edit-gas-tooltip/index';
@import 'flask/experimental-area/index';
@import 'flask/snaps-authorship-pill/index';
@import 'flask/snap-content-footer/index';
@import 'flask/snap-install-warning/index';
@import 'flask/snap-remove-warning/index';

View File

@ -0,0 +1 @@
export { default } from './snap-authorship';

View File

@ -0,0 +1,106 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { getSnapPrefix } from '@metamask/snaps-utils';
import { useSelector } from 'react-redux';
import Box from '../../../ui/box';
import {
BackgroundColor,
TextColor,
IconColor,
FLEX_DIRECTION,
TextVariant,
BorderColor,
AlignItems,
DISPLAY,
BorderRadius,
} from '../../../../helpers/constants/design-system';
import {
getSnapName,
removeSnapIdPrefix,
} from '../../../../helpers/utils/util';
import {
ICON_NAMES,
ICON_SIZES,
Text,
ButtonIcon,
} from '../../../component-library';
import { getTargetSubjectMetadata } from '../../../../selectors';
import SnapAvatar from '../snap-avatar';
const SnapAuthorship = ({ snapId, className }) => {
// We're using optional chaining with snapId, because with the current implementation
// of snap update in the snap controller, we do not have reference to snapId when an
// update request is rejected because the reference comes from the request itself and not subject metadata
// like it is done with snap install
const snapPrefix = snapId && getSnapPrefix(snapId);
const packageName = snapId && removeSnapIdPrefix(snapId);
const isNPM = snapPrefix === 'npm:';
const url = isNPM
? `https://www.npmjs.com/package/${packageName}`
: packageName;
const subjectMetadata = useSelector((state) =>
getTargetSubjectMetadata(state, snapId),
);
const friendlyName = snapId && getSnapName(snapId, subjectMetadata);
return (
<Box
className={classnames('snaps-authorship', className)}
backgroundColor={BackgroundColor.backgroundDefault}
borderColor={BorderColor.borderDefault}
borderWidth={1}
alignItems={AlignItems.center}
paddingLeft={2}
paddingTop={2}
paddingBottom={2}
paddingRight={4}
borderRadius={BorderRadius.pill}
display={DISPLAY.FLEX}
style={{ maxWidth: 'fit-content', width: '100%' }}
>
<Box>
<SnapAvatar snapId={snapId} />
</Box>
<Box
marginLeft={4}
marginRight={2}
display={DISPLAY.FLEX}
flexDirection={FLEX_DIRECTION.COLUMN}
style={{ overflow: 'hidden' }}
>
<Text ellipsis>{friendlyName}</Text>
<Text
ellipsis
variant={TextVariant.bodySm}
color={TextColor.textAlternative}
>
{packageName}
</Text>
</Box>
<ButtonIcon
rel="noopener noreferrer"
target="_blank"
href={url}
iconName={ICON_NAMES.EXPORT}
color={IconColor.infoDefault}
size={ICON_SIZES.MD}
/>
</Box>
);
};
SnapAuthorship.propTypes = {
/**
* The id of the snap
*/
snapId: PropTypes.string,
/**
* The className of the SnapAuthorship
*/
className: PropTypes.string,
};
export default SnapAuthorship;

View File

@ -1,10 +1,10 @@
import React from 'react';
import SnapsAuthorshipPill from '.';
import SnapAuthorship from '.';
export default {
title: 'Components/App/Flask/SnapsAuthorshipPill',
title: 'Components/App/Flask/SnapAuthorship',
component: SnapsAuthorshipPill,
component: SnapAuthorship,
argTypes: {
snapId: {
control: 'text',
@ -12,7 +12,7 @@ export default {
},
};
export const DefaultStory = (args) => <SnapsAuthorshipPill {...args} />;
export const DefaultStory = (args) => <SnapAuthorship {...args} />;
DefaultStory.storyName = 'Default';

View File

@ -0,0 +1 @@
export { default } from './snap-avatar';

View File

@ -0,0 +1,81 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { useSelector } from 'react-redux';
import {
TextColor,
IconColor,
AlignItems,
DISPLAY,
JustifyContent,
Size,
} from '../../../../helpers/constants/design-system';
import { getSnapName } from '../../../../helpers/utils/util';
import {
AvatarFavicon,
BadgeWrapper,
BadgeWrapperPosition,
ICON_NAMES,
ICON_SIZES,
AvatarIcon,
AvatarBase,
} from '../../../component-library';
import { getTargetSubjectMetadata } from '../../../../selectors';
const SnapAvatar = ({ snapId, className }) => {
const subjectMetadata = useSelector((state) =>
getTargetSubjectMetadata(state, snapId),
);
const friendlyName = snapId && getSnapName(snapId, subjectMetadata);
const iconUrl = subjectMetadata?.iconUrl;
const fallbackIcon = friendlyName && friendlyName[0] ? friendlyName[0] : '?';
return (
<BadgeWrapper
className={classnames('snap-avatar', className)}
badge={
<AvatarIcon
iconName={ICON_NAMES.SNAPS}
size={ICON_SIZES.XS}
backgroundColor={IconColor.infoDefault}
iconProps={{
size: ICON_SIZES.XS,
color: IconColor.infoInverse,
}}
/>
}
position={BadgeWrapperPosition.bottomRight}
>
{iconUrl ? (
<AvatarFavicon size={Size.LG} src={iconUrl} />
) : (
<AvatarBase
size={Size.LG}
display={DISPLAY.FLEX}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
color={TextColor.textAlternative}
style={{ borderWidth: '0px' }}
>
{fallbackIcon}
</AvatarBase>
)}
</BadgeWrapper>
);
};
SnapAvatar.propTypes = {
/**
* The id of the snap
*/
snapId: PropTypes.string,
/**
* The className of the SnapAvatar
*/
className: PropTypes.string,
};
export default SnapAvatar;

View File

@ -0,0 +1,21 @@
import React from 'react';
import SnapAvatar from '.';
export default {
title: 'Components/App/Flask/SnapAvatar',
component: SnapAvatar,
argTypes: {
snapId: {
control: 'text',
},
},
};
export const DefaultStory = (args) => <SnapAvatar {...args} />;
DefaultStory.storyName = 'Default';
DefaultStory.args = {
snapId: 'npm:@metamask/test-snap-bip44',
};

View File

@ -1 +0,0 @@
export { default } from './snaps-authorship-pill';

View File

@ -1,37 +0,0 @@
@import "design-system";
.snaps-authorship-pill {
display: inline-block;
.chip {
padding-right: 8px;
margin-top: 4px;
}
.chip__label {
max-width: 168px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
&:hover,
&:focus {
.chip {
background-color: var(--color-background-default-hover);
}
}
}
.snaps-authorship-icon {
color: var(--color-icon-alternative);
}
.snaps-authorship-version {
border-radius: 100px;
line-height: 100%;
}
.snaps-authorship-version > span {
vertical-align: middle;
}

View File

@ -1,100 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { getSnapPrefix } from '@metamask/snaps-utils';
import Chip from '../../../ui/chip';
import Box from '../../../ui/box';
import Typography from '../../../ui/typography';
import {
TypographyVariant,
TEXT_ALIGN,
BackgroundColor,
TextColor,
} from '../../../../helpers/constants/design-system';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import {
getSnapName,
removeSnapIdPrefix,
} from '../../../../helpers/utils/util';
const SnapsAuthorshipPill = ({ snapId, version, className }) => {
// We're using optional chaining with snapId, because with the current implementation
// of snap update in the snap controller, we do not have reference to snapId when an
// update request is rejected because the reference comes from the request itself and not subject metadata
// like it is done with snap install
const snapPrefix = snapId && getSnapPrefix(snapId);
const packageName = snapId && removeSnapIdPrefix(snapId);
const isNPM = snapPrefix === 'npm:';
const url = isNPM
? `https://www.npmjs.com/package/${packageName}`
: packageName;
const icon = isNPM ? 'fab fa-npm fa-lg' : 'fas fa-code';
const t = useI18nContext();
const friendlyName = getSnapName(snapId);
return (
<a
href={url}
target="_blank"
rel="noopener noreferrer"
className={classnames(className, `snaps-authorship-pill`)}
>
<Chip
leftIcon={
<Box paddingLeft={2}>
<i className={`${icon} snaps-authorship-icon`} />
</Box>
}
rightIcon={
version && (
<Box
className="snaps-authorship-version"
backgroundColor={BackgroundColor.primaryDefault}
paddingLeft={2}
paddingRight={2}
>
<Typography
color={TextColor.primaryInverse}
variant={TypographyVariant.H7}
align={TEXT_ALIGN.CENTER}
as="span"
className="version"
>
{t('shorthandVersion', [version])}
</Typography>
</Box>
)
}
backgroundColor={BackgroundColor.backgroundDefault}
>
<Typography
className="chip__label"
variant={TypographyVariant.H7}
as="span"
color={TextColor.textAlternative}
title={friendlyName}
>
{friendlyName}
</Typography>
</Chip>
</a>
);
};
SnapsAuthorshipPill.propTypes = {
/**
* The id of the snap
*/
snapId: PropTypes.string,
/**
* The version of the snap
*/
version: PropTypes.string,
/**
* The className of the SnapsAuthorshipPill
*/
className: PropTypes.string,
};
export default SnapsAuthorshipPill;

View File

@ -8,7 +8,7 @@ import {
JustifyContent,
} from '../../../helpers/constants/design-system';
///: BEGIN:ONLY_INCLUDE_IN(flask)
import SnapsAuthorshipPill from '../flask/snaps-authorship-pill';
import SnapAuthorship from '../flask/snap-authorship';
///: END:ONLY_INCLUDE_IN
export default class PermissionsConnectHeader extends Component {
@ -82,7 +82,6 @@ export default class PermissionsConnectHeader extends Component {
headerText,
///: BEGIN:ONLY_INCLUDE_IN(flask)
siteOrigin,
snapVersion,
isSnapInstallOrUpdate,
///: END:ONLY_INCLUDE_IN
} = this.props;
@ -97,9 +96,7 @@ export default class PermissionsConnectHeader extends Component {
<div className="permissions-connect-header__title">{headerTitle}</div>
{
///: BEGIN:ONLY_INCLUDE_IN(flask)
isSnapInstallOrUpdate && (
<SnapsAuthorshipPill snapId={siteOrigin} version={snapVersion} />
)
isSnapInstallOrUpdate && <SnapAuthorship snapId={siteOrigin} />
///: END:ONLY_INCLUDE_IN
}
<div className="permissions-connect-header__subtitle">{headerText}</div>

View File

@ -558,8 +558,14 @@ export function getSnapDerivationPathName(path, curve) {
export const removeSnapIdPrefix = (snapId) =>
snapId.replace(getSnapPrefix(snapId), '');
export const getSnapName = (snapId) =>
SNAPS_METADATA[snapId]?.name ?? removeSnapIdPrefix(snapId);
export const getSnapName = (snapId, subjectMetadata) => {
if (SNAPS_METADATA[snapId]?.name) {
return SNAPS_METADATA[snapId].name;
}
return subjectMetadata?.name ?? removeSnapIdPrefix(snapId);
};
///: END:ONLY_INCLUDE_IN
/**

View File

@ -17,7 +17,7 @@ import {
import { getSnapInstallWarnings } from '../util';
import PulseLoader from '../../../../components/ui/pulse-loader/pulse-loader';
import InstallError from '../../../../components/app/flask/install-error/install-error';
import SnapsAuthorshipPill from '../../../../components/app/flask/snaps-authorship-pill/snaps-authorship-pill';
import SnapAuthorship from '../../../../components/app/flask/snap-authorship';
import { Text } from '../../../../components/component-library';
import { useOriginMetadata } from '../../../../hooks/useOriginMetadata';
import { getSnapName } from '../../../../helpers/utils/util';
@ -88,10 +88,7 @@ export default function SnapInstall({
alignItems={AlignItems.center}
flexDirection={FLEX_DIRECTION.COLUMN}
>
<SnapsAuthorshipPill
snapId={targetSubjectMetadata.origin}
version={targetSubjectMetadata.version}
/>
<SnapAuthorship snapId={targetSubjectMetadata.origin} />
{!hasError && (
<Text padding={[4, 4, 0, 4]} variant={TextVariant.headingLg}>
{t('snapInstall')}

View File

@ -17,7 +17,7 @@ import {
import { Text } from '../../../../components/component-library';
import PulseLoader from '../../../../components/ui/pulse-loader/pulse-loader';
import InstallError from '../../../../components/app/flask/install-error/install-error';
import SnapsAuthorshipPill from '../../../../components/app/flask/snaps-authorship-pill/snaps-authorship-pill';
import SnapAuthorship from '../../../../components/app/flask/snap-authorship';
import { getSnapName } from '../../../../helpers/utils/util';
export default function SnapResult({
@ -52,10 +52,7 @@ export default function SnapResult({
alignItems={AlignItems.center}
flexDirection={FLEX_DIRECTION.COLUMN}
>
<SnapsAuthorshipPill
snapId={targetSubjectMetadata.origin}
version={targetSubjectMetadata.version}
/>
<SnapAuthorship snapId={targetSubjectMetadata.origin} />
{isLoading && (
<Box
className="loader-container"

View File

@ -18,7 +18,7 @@ import UpdateSnapPermissionList from '../../../../components/app/flask/update-sn
import { getSnapInstallWarnings } from '../util';
import PulseLoader from '../../../../components/ui/pulse-loader/pulse-loader';
import InstallError from '../../../../components/app/flask/install-error/install-error';
import SnapsAuthorshipPill from '../../../../components/app/flask/snaps-authorship-pill/snaps-authorship-pill';
import SnapAuthorship from '../../../../components/app/flask/snap-authorship';
import { Text } from '../../../../components/component-library';
import { useOriginMetadata } from '../../../../hooks/useOriginMetadata';
import { getSnapName } from '../../../../helpers/utils/util';
@ -94,10 +94,7 @@ export default function SnapUpdate({
alignItems={AlignItems.center}
flexDirection={FLEX_DIRECTION.COLUMN}
>
<SnapsAuthorshipPill
snapId={targetSubjectMetadata.origin}
version={requestState.newVersion}
/>
<SnapAuthorship snapId={targetSubjectMetadata.origin} />
{!hasError && (
<Text padding={[4, 4, 0, 4]} variant={TextVariant.headingLg}>
{t('snapUpdate')}

View File

@ -14,7 +14,7 @@ import {
FRACTIONS,
TextColor,
} from '../../../../helpers/constants/design-system';
import SnapsAuthorshipPill from '../../../../components/app/flask/snaps-authorship-pill';
import SnapAuthorship from '../../../../components/app/flask/snap-authorship';
import Box from '../../../../components/ui/box';
import SnapRemoveWarning from '../../../../components/app/flask/snap-remove-warning';
import ToggleButton from '../../../../components/ui/toggle-button';
@ -118,7 +118,7 @@ function ViewSnap() {
</Typography>
<Box className="view-snap__pill-toggle-container">
<Box className="view-snap__pill-container" paddingLeft={2}>
<SnapsAuthorshipPill snapId={snap.id} />
<SnapAuthorship snapId={snap.id} />
</Box>
<Box paddingLeft={4} className="view-snap__toggle-container">
<Tooltip interactive position="bottom" html={t('snapsToggle')}>