From 4179ce634cab63a03524c3694839eea420d2356c Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 24 Mar 2023 17:16:46 +0100 Subject: [PATCH] [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 --- ui/components/app/app-components.scss | 1 - .../app/flask/snap-authorship/index.js | 1 + .../flask/snap-authorship/snap-authorship.js | 106 ++++++++++++++++++ .../snap-authorship.stories.js} | 8 +- ui/components/app/flask/snap-avatar/index.js | 1 + .../app/flask/snap-avatar/snap-avatar.js | 81 +++++++++++++ .../flask/snap-avatar/snap-avatar.stories.js | 21 ++++ .../app/flask/snaps-authorship-pill/index.js | 1 - .../flask/snaps-authorship-pill/index.scss | 37 ------ .../snaps-authorship-pill.js | 100 ----------------- .../permissions-connect-header.component.js | 7 +- ui/helpers/utils/util.js | 10 +- .../flask/snap-install/snap-install.js | 7 +- .../flask/snap-result/snap-result.js | 7 +- .../flask/snap-update/snap-update.js | 7 +- .../settings/flask/view-snap/view-snap.js | 4 +- 16 files changed, 232 insertions(+), 167 deletions(-) create mode 100644 ui/components/app/flask/snap-authorship/index.js create mode 100644 ui/components/app/flask/snap-authorship/snap-authorship.js rename ui/components/app/flask/{snaps-authorship-pill/snaps-authorship-pill.stories.js => snap-authorship/snap-authorship.stories.js} (52%) create mode 100644 ui/components/app/flask/snap-avatar/index.js create mode 100644 ui/components/app/flask/snap-avatar/snap-avatar.js create mode 100644 ui/components/app/flask/snap-avatar/snap-avatar.stories.js delete mode 100644 ui/components/app/flask/snaps-authorship-pill/index.js delete mode 100644 ui/components/app/flask/snaps-authorship-pill/index.scss delete mode 100644 ui/components/app/flask/snaps-authorship-pill/snaps-authorship-pill.js diff --git a/ui/components/app/app-components.scss b/ui/components/app/app-components.scss index 7cf61bbcf8f0..9659eaad3083 100644 --- a/ui/components/app/app-components.scss +++ b/ui/components/app/app-components.scss @@ -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'; diff --git a/ui/components/app/flask/snap-authorship/index.js b/ui/components/app/flask/snap-authorship/index.js new file mode 100644 index 000000000000..8dc331ebedb2 --- /dev/null +++ b/ui/components/app/flask/snap-authorship/index.js @@ -0,0 +1 @@ +export { default } from './snap-authorship'; diff --git a/ui/components/app/flask/snap-authorship/snap-authorship.js b/ui/components/app/flask/snap-authorship/snap-authorship.js new file mode 100644 index 000000000000..4179c5cbfb6b --- /dev/null +++ b/ui/components/app/flask/snap-authorship/snap-authorship.js @@ -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 ( + + + + + + {friendlyName} + + {packageName} + + + + + ); +}; + +SnapAuthorship.propTypes = { + /** + * The id of the snap + */ + snapId: PropTypes.string, + /** + * The className of the SnapAuthorship + */ + className: PropTypes.string, +}; + +export default SnapAuthorship; diff --git a/ui/components/app/flask/snaps-authorship-pill/snaps-authorship-pill.stories.js b/ui/components/app/flask/snap-authorship/snap-authorship.stories.js similarity index 52% rename from ui/components/app/flask/snaps-authorship-pill/snaps-authorship-pill.stories.js rename to ui/components/app/flask/snap-authorship/snap-authorship.stories.js index 79be8d7c5ba3..0b1910943666 100644 --- a/ui/components/app/flask/snaps-authorship-pill/snaps-authorship-pill.stories.js +++ b/ui/components/app/flask/snap-authorship/snap-authorship.stories.js @@ -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) => ; +export const DefaultStory = (args) => ; DefaultStory.storyName = 'Default'; diff --git a/ui/components/app/flask/snap-avatar/index.js b/ui/components/app/flask/snap-avatar/index.js new file mode 100644 index 000000000000..2c17e12d55b7 --- /dev/null +++ b/ui/components/app/flask/snap-avatar/index.js @@ -0,0 +1 @@ +export { default } from './snap-avatar'; diff --git a/ui/components/app/flask/snap-avatar/snap-avatar.js b/ui/components/app/flask/snap-avatar/snap-avatar.js new file mode 100644 index 000000000000..9a96527ccbc5 --- /dev/null +++ b/ui/components/app/flask/snap-avatar/snap-avatar.js @@ -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 ( + + } + position={BadgeWrapperPosition.bottomRight} + > + {iconUrl ? ( + + ) : ( + + {fallbackIcon} + + )} + + ); +}; + +SnapAvatar.propTypes = { + /** + * The id of the snap + */ + snapId: PropTypes.string, + /** + * The className of the SnapAvatar + */ + className: PropTypes.string, +}; + +export default SnapAvatar; diff --git a/ui/components/app/flask/snap-avatar/snap-avatar.stories.js b/ui/components/app/flask/snap-avatar/snap-avatar.stories.js new file mode 100644 index 000000000000..94b134660b95 --- /dev/null +++ b/ui/components/app/flask/snap-avatar/snap-avatar.stories.js @@ -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) => ; + +DefaultStory.storyName = 'Default'; + +DefaultStory.args = { + snapId: 'npm:@metamask/test-snap-bip44', +}; diff --git a/ui/components/app/flask/snaps-authorship-pill/index.js b/ui/components/app/flask/snaps-authorship-pill/index.js deleted file mode 100644 index 79acafe31ddb..000000000000 --- a/ui/components/app/flask/snaps-authorship-pill/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './snaps-authorship-pill'; diff --git a/ui/components/app/flask/snaps-authorship-pill/index.scss b/ui/components/app/flask/snaps-authorship-pill/index.scss deleted file mode 100644 index a2e0a49e2edf..000000000000 --- a/ui/components/app/flask/snaps-authorship-pill/index.scss +++ /dev/null @@ -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; -} diff --git a/ui/components/app/flask/snaps-authorship-pill/snaps-authorship-pill.js b/ui/components/app/flask/snaps-authorship-pill/snaps-authorship-pill.js deleted file mode 100644 index 826f9c3ff879..000000000000 --- a/ui/components/app/flask/snaps-authorship-pill/snaps-authorship-pill.js +++ /dev/null @@ -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 ( - - - - - } - rightIcon={ - version && ( - - - {t('shorthandVersion', [version])} - - - ) - } - backgroundColor={BackgroundColor.backgroundDefault} - > - - {friendlyName} - - - - ); -}; - -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; diff --git a/ui/components/app/permissions-connect-header/permissions-connect-header.component.js b/ui/components/app/permissions-connect-header/permissions-connect-header.component.js index 43b6d5f7e323..9b7fa112ef89 100644 --- a/ui/components/app/permissions-connect-header/permissions-connect-header.component.js +++ b/ui/components/app/permissions-connect-header/permissions-connect-header.component.js @@ -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 {
{headerTitle}
{ ///: BEGIN:ONLY_INCLUDE_IN(flask) - isSnapInstallOrUpdate && ( - - ) + isSnapInstallOrUpdate && ///: END:ONLY_INCLUDE_IN }
{headerText}
diff --git a/ui/helpers/utils/util.js b/ui/helpers/utils/util.js index 8598260d487a..c82988d7323b 100644 --- a/ui/helpers/utils/util.js +++ b/ui/helpers/utils/util.js @@ -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 /** diff --git a/ui/pages/permissions-connect/flask/snap-install/snap-install.js b/ui/pages/permissions-connect/flask/snap-install/snap-install.js index 2fbc724d794b..6224155b6c39 100644 --- a/ui/pages/permissions-connect/flask/snap-install/snap-install.js +++ b/ui/pages/permissions-connect/flask/snap-install/snap-install.js @@ -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} > - + {!hasError && ( {t('snapInstall')} diff --git a/ui/pages/permissions-connect/flask/snap-result/snap-result.js b/ui/pages/permissions-connect/flask/snap-result/snap-result.js index ffb55e5e1c22..08e7020978fa 100644 --- a/ui/pages/permissions-connect/flask/snap-result/snap-result.js +++ b/ui/pages/permissions-connect/flask/snap-result/snap-result.js @@ -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} > - + {isLoading && ( - + {!hasError && ( {t('snapUpdate')} diff --git a/ui/pages/settings/flask/view-snap/view-snap.js b/ui/pages/settings/flask/view-snap/view-snap.js index 33e7f7001564..42ac59c8c2d0 100644 --- a/ui/pages/settings/flask/view-snap/view-snap.js +++ b/ui/pages/settings/flask/view-snap/view-snap.js @@ -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() { - +