mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
[FLASK] Expanded Snap authorship (#18775)
This commit is contained in:
parent
a4a5b28f2e
commit
6126c156ea
31
app/_locales/en/messages.json
generated
31
app/_locales/en/messages.json
generated
@ -1066,6 +1066,10 @@
|
||||
"description": {
|
||||
"message": "Description"
|
||||
},
|
||||
"descriptionFromSnap": {
|
||||
"message": "Description from $1",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"desktopConnectionCriticalErrorDescription": {
|
||||
"message": "This error could be intermittent, so try restarting the extension or disable MetaMask Desktop."
|
||||
},
|
||||
@ -1331,10 +1335,7 @@
|
||||
"message": "Enable smart transactions"
|
||||
},
|
||||
"enableSnap": {
|
||||
"message": "Enable snap"
|
||||
},
|
||||
"enableSnapDescription": {
|
||||
"message": "Your installed snap will only have access to its permissions and run if it’s enabled."
|
||||
"message": "Enable"
|
||||
},
|
||||
"enableToken": {
|
||||
"message": "enable $1",
|
||||
@ -1851,6 +1852,13 @@
|
||||
"install": {
|
||||
"message": "Install"
|
||||
},
|
||||
"installOrigin": {
|
||||
"message": "Install origin"
|
||||
},
|
||||
"installedOn": {
|
||||
"message": "Installed on $1",
|
||||
"description": "$1 is the date when the snap has been installed"
|
||||
},
|
||||
"institutionalFeatures": {
|
||||
"message": "Institutional Features"
|
||||
},
|
||||
@ -2192,6 +2200,9 @@
|
||||
"mmiAuthenticate": {
|
||||
"message": "The page at $1 would like to authorise the following project’s compliance settings in MetaMask Institutional"
|
||||
},
|
||||
"more": {
|
||||
"message": "more"
|
||||
},
|
||||
"moreComingSoon": {
|
||||
"message": "More coming soon..."
|
||||
},
|
||||
@ -3576,6 +3587,10 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "No matching results found."
|
||||
},
|
||||
"shortVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is the version number to show"
|
||||
},
|
||||
"show": {
|
||||
"message": "Show"
|
||||
},
|
||||
@ -4510,7 +4525,6 @@
|
||||
"transactionFailed": {
|
||||
"message": "Transaction Failed"
|
||||
},
|
||||
|
||||
"transactionFee": {
|
||||
"message": "Transaction fee"
|
||||
},
|
||||
@ -4737,6 +4751,9 @@
|
||||
"message": "Verify this token on $1 and make sure this is the token you want to trade.",
|
||||
"description": "Points the user to etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"etherscan\""
|
||||
},
|
||||
"version": {
|
||||
"message": "Version"
|
||||
},
|
||||
"view": {
|
||||
"message": "View"
|
||||
},
|
||||
@ -4870,10 +4887,6 @@
|
||||
"message": "You've added all the popular networks. You can discover more networks $1 Or you can $2",
|
||||
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
|
||||
},
|
||||
"youInstalled": {
|
||||
"message": "You installed",
|
||||
"description": "Part of version description for installed snap"
|
||||
},
|
||||
"youNeedToAllowCameraAccess": {
|
||||
"message": "You need to allow camera access to use this feature."
|
||||
},
|
||||
|
@ -43,6 +43,7 @@
|
||||
@import 'snaps/snap-settings-card/index';
|
||||
@import 'snaps/update-snap-permission-list/index';
|
||||
@import 'snaps/copyable/index';
|
||||
@import 'snaps/snap-version/index';
|
||||
@import 'gas-details-item/index';
|
||||
@import 'gas-details-item/gas-details-item-title/index';
|
||||
@import 'gas-timing/index';
|
||||
|
@ -2,30 +2,42 @@ 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 { useDispatch, useSelector } from 'react-redux';
|
||||
import Box from '../../../ui/box';
|
||||
import {
|
||||
BackgroundColor,
|
||||
TextColor,
|
||||
IconColor,
|
||||
FLEX_DIRECTION,
|
||||
TextVariant,
|
||||
BorderColor,
|
||||
AlignItems,
|
||||
DISPLAY,
|
||||
BorderRadius,
|
||||
BLOCK_SIZES,
|
||||
JustifyContent,
|
||||
BorderStyle,
|
||||
Color,
|
||||
BorderRadius,
|
||||
} from '../../../../helpers/constants/design-system';
|
||||
import {
|
||||
formatDate,
|
||||
getSnapName,
|
||||
removeSnapIdPrefix,
|
||||
} from '../../../../helpers/utils/util';
|
||||
import { ButtonIcon, IconName, Text } from '../../../component-library';
|
||||
|
||||
import { Text, ButtonLink } from '../../../component-library';
|
||||
import { getTargetSubjectMetadata } from '../../../../selectors';
|
||||
import SnapAvatar from '../snap-avatar';
|
||||
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||
import Tooltip from '../../../ui/tooltip/tooltip';
|
||||
import ToggleButton from '../../../ui/toggle-button';
|
||||
import { disableSnap, enableSnap } from '../../../../store/actions';
|
||||
import { useOriginMetadata } from '../../../../hooks/useOriginMetadata';
|
||||
import SnapVersion from '../snap-version/snap-version';
|
||||
|
||||
const SnapAuthorship = ({ snapId, className, expanded = false, snap }) => {
|
||||
const t = useI18nContext();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
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
|
||||
@ -43,27 +55,45 @@ const SnapAuthorship = ({ snapId, className }) => {
|
||||
|
||||
const friendlyName = snapId && getSnapName(snapId, subjectMetadata);
|
||||
|
||||
// Expanded data
|
||||
const versionHistory = snap?.versionHistory ?? [];
|
||||
const installInfo = versionHistory.length
|
||||
? versionHistory[versionHistory.length - 1]
|
||||
: undefined;
|
||||
const installOrigin = useOriginMetadata(installInfo?.origin);
|
||||
|
||||
const onToggle = () => {
|
||||
if (snap?.enabled) {
|
||||
dispatch(disableSnap(snap?.id));
|
||||
} else {
|
||||
dispatch(enableSnap(snap?.id));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
className={classnames('snaps-authorship', className)}
|
||||
backgroundColor={BackgroundColor.backgroundDefault}
|
||||
borderColor={BorderColor.borderDefault}
|
||||
borderWidth={1}
|
||||
width={BLOCK_SIZES.FULL}
|
||||
borderRadius={expanded ? BorderRadius.LG : BorderRadius.pill}
|
||||
>
|
||||
<Box
|
||||
alignItems={AlignItems.center}
|
||||
paddingLeft={2}
|
||||
paddingTop={2}
|
||||
paddingBottom={2}
|
||||
paddingRight={4}
|
||||
borderRadius={BorderRadius.pill}
|
||||
display={DISPLAY.FLEX}
|
||||
width={BLOCK_SIZES.FULL}
|
||||
paddingLeft={expanded ? 4 : 2}
|
||||
paddingRight={expanded ? 4 : 2}
|
||||
paddingTop={expanded ? 3 : 1}
|
||||
paddingBottom={expanded ? 3 : 1}
|
||||
>
|
||||
<Box>
|
||||
<SnapAvatar snapId={snapId} />
|
||||
</Box>
|
||||
<Box
|
||||
marginLeft={4}
|
||||
marginRight={2}
|
||||
marginLeft={2}
|
||||
marginRight={expanded ? 0 : 2}
|
||||
display={DISPLAY.FLEX}
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
style={{ overflow: 'hidden' }}
|
||||
@ -77,14 +107,75 @@ const SnapAuthorship = ({ snapId, className }) => {
|
||||
{packageName}
|
||||
</Text>
|
||||
</Box>
|
||||
<ButtonIcon
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href={url}
|
||||
iconName={IconName.Export}
|
||||
color={IconColor.infoDefault}
|
||||
style={{ marginLeft: 'auto' }}
|
||||
/>
|
||||
{!expanded && (
|
||||
<Box marginLeft="auto">
|
||||
<SnapVersion version={subjectMetadata?.version} url={url} />
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
{expanded && (
|
||||
<Box flexDirection={FLEX_DIRECTION.COLUMN} width={BLOCK_SIZES.FULL}>
|
||||
<Box
|
||||
flexDirection={FLEX_DIRECTION.ROW}
|
||||
justifyContent={JustifyContent.spaceBetween}
|
||||
paddingLeft={4}
|
||||
paddingTop={4}
|
||||
paddingBottom={4}
|
||||
borderColor={BorderColor.borderDefault}
|
||||
width={BLOCK_SIZES.FULL}
|
||||
style={{
|
||||
borderLeft: BorderStyle.none,
|
||||
borderRight: BorderStyle.none,
|
||||
}}
|
||||
>
|
||||
<Text variant={TextVariant.bodyMdBold}>{t('enableSnap')}</Text>
|
||||
<Box style={{ maxWidth: '52px' }}>
|
||||
<Tooltip interactive position="left" html={t('snapsToggle')}>
|
||||
<ToggleButton value={snap?.enabled} onToggle={onToggle} />
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
padding={4}
|
||||
width={BLOCK_SIZES.FULL}
|
||||
>
|
||||
{installOrigin && installInfo && (
|
||||
<Box
|
||||
flexDirection={FLEX_DIRECTION.ROW}
|
||||
justifyContent={JustifyContent.spaceBetween}
|
||||
width={BLOCK_SIZES.FULL}
|
||||
>
|
||||
<Text variant={TextVariant.bodyMdBold}>
|
||||
{t('installOrigin')}
|
||||
</Text>
|
||||
<Box
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
alignItems={AlignItems.flexEnd}
|
||||
>
|
||||
<ButtonLink href={installOrigin.origin} target="_blank">
|
||||
{installOrigin.host}
|
||||
</ButtonLink>
|
||||
<Text color={Color.textMuted}>
|
||||
{t('installedOn', [
|
||||
formatDate(installInfo.date, 'dd MMM yyyy'),
|
||||
])}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
<Box
|
||||
flexDirection={FLEX_DIRECTION.ROW}
|
||||
justifyContent={JustifyContent.spaceBetween}
|
||||
alignItems={AlignItems.center}
|
||||
marginTop={4}
|
||||
>
|
||||
<Text variant={TextVariant.bodyMdBold}>{t('version')}</Text>
|
||||
<SnapVersion version={snap?.version} url={url} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@ -98,6 +189,14 @@ SnapAuthorship.propTypes = {
|
||||
* The className of the SnapAuthorship
|
||||
*/
|
||||
className: PropTypes.string,
|
||||
/**
|
||||
* If the authorship component should be expanded
|
||||
*/
|
||||
expanded: PropTypes.bool,
|
||||
/**
|
||||
* The snap object. Can be undefined if the component is not expanded
|
||||
*/
|
||||
snap: PropTypes.object,
|
||||
};
|
||||
|
||||
export default SnapAuthorship;
|
||||
|
@ -50,10 +50,10 @@ const SnapAvatar = ({ snapId, className }) => {
|
||||
position={BadgeWrapperPosition.bottomRight}
|
||||
>
|
||||
{iconUrl ? (
|
||||
<AvatarFavicon size={Size.LG} src={iconUrl} name={friendlyName} />
|
||||
<AvatarFavicon size={Size.MD} src={iconUrl} name={friendlyName} />
|
||||
) : (
|
||||
<AvatarBase
|
||||
size={Size.LG}
|
||||
size={Size.MD}
|
||||
display={DISPLAY.FLEX}
|
||||
alignItems={AlignItems.center}
|
||||
justifyContent={JustifyContent.center}
|
||||
|
1
ui/components/app/snaps/snap-version/index.js
Normal file
1
ui/components/app/snaps/snap-version/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './snap-version';
|
15
ui/components/app/snaps/snap-version/index.scss
Normal file
15
ui/components/app/snaps/snap-version/index.scss
Normal file
@ -0,0 +1,15 @@
|
||||
.snap-version {
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&__wrapper {
|
||||
&:hover {
|
||||
background-color: var(--color-info-muted);
|
||||
|
||||
& * {
|
||||
color: var(--color-info-default);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
72
ui/components/app/snaps/snap-version/snap-version.js
Normal file
72
ui/components/app/snaps/snap-version/snap-version.js
Normal file
@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
AlignItems,
|
||||
BackgroundColor,
|
||||
BorderRadius,
|
||||
Color,
|
||||
FLEX_DIRECTION,
|
||||
TextVariant,
|
||||
} from '../../../../helpers/constants/design-system';
|
||||
import Box from '../../../ui/box';
|
||||
import {
|
||||
BUTTON_TYPES,
|
||||
Button,
|
||||
Icon,
|
||||
IconName,
|
||||
IconSize,
|
||||
Text,
|
||||
} from '../../../component-library';
|
||||
import Preloader from '../../../ui/icon/preloader/preloader-icon.component';
|
||||
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||
|
||||
const SnapVersion = ({ version, url }) => {
|
||||
const t = useI18nContext();
|
||||
return (
|
||||
<Button
|
||||
type={BUTTON_TYPES.LINK}
|
||||
href={url}
|
||||
target="_blank"
|
||||
className="snap-version"
|
||||
>
|
||||
<Box
|
||||
className="snap-version__wrapper"
|
||||
flexDirection={FLEX_DIRECTION.ROW}
|
||||
alignItems={AlignItems.center}
|
||||
backgroundColor={BackgroundColor.backgroundAlternative}
|
||||
borderRadius={BorderRadius.pill}
|
||||
paddingTop={1}
|
||||
paddingBottom={1}
|
||||
paddingLeft={2}
|
||||
paddingRight={2}
|
||||
>
|
||||
{version ? (
|
||||
<Text color={Color.textAlternative} variant={TextVariant.bodyMd}>
|
||||
{t('shortVersion', [version])}
|
||||
</Text>
|
||||
) : (
|
||||
<Preloader size={18} />
|
||||
)}
|
||||
<Icon
|
||||
name={IconName.Export}
|
||||
color={Color.textAlternative}
|
||||
size={IconSize.Sm}
|
||||
marginLeft={1}
|
||||
/>
|
||||
</Box>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
SnapVersion.propTypes = {
|
||||
/**
|
||||
* The version of the snap
|
||||
*/
|
||||
version: PropTypes.string,
|
||||
/**
|
||||
* The url to the snap package
|
||||
*/
|
||||
url: PropTypes.string,
|
||||
};
|
||||
|
||||
export default SnapVersion;
|
20
ui/components/app/snaps/snap-version/snap-version.stories.js
Normal file
20
ui/components/app/snaps/snap-version/snap-version.stories.js
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import SnapVersion from '.';
|
||||
|
||||
export default {
|
||||
title: 'Components/App/Snaps/SnapVersion',
|
||||
component: SnapVersion,
|
||||
};
|
||||
export const DefaultStory = (args) => <SnapVersion {...args} />;
|
||||
|
||||
DefaultStory.args = {
|
||||
version: '1.4.2',
|
||||
url: 'https://www.npmjs.com/package/@metamask/test-snap-error',
|
||||
};
|
||||
|
||||
export const LoadingStory = (args) => <SnapVersion {...args} />;
|
||||
|
||||
LoadingStory.args = {
|
||||
version: undefined,
|
||||
url: 'https://www.npmjs.com/package/@metamask/test-snap-error',
|
||||
};
|
27
ui/components/app/snaps/snap-version/snap-version.test.js
Normal file
27
ui/components/app/snaps/snap-version/snap-version.test.js
Normal file
@ -0,0 +1,27 @@
|
||||
import * as React from 'react';
|
||||
import { renderWithLocalization } from '../../../../../test/lib/render-helpers';
|
||||
import SnapVersion from './snap-version';
|
||||
|
||||
describe('SnapVersion', () => {
|
||||
const args = {
|
||||
version: '1.4.2',
|
||||
url: 'https://www.npmjs.com/package/@metamask/test-snap-error',
|
||||
};
|
||||
|
||||
it('should render the SnapVersion without crashing and display a version', () => {
|
||||
const { getByText, container } = renderWithLocalization(
|
||||
<SnapVersion {...args} />,
|
||||
);
|
||||
expect(getByText(`v${args.version}`)).toBeDefined();
|
||||
expect(container.firstChild).toHaveAttribute('href', args.url);
|
||||
});
|
||||
|
||||
it('should have a loading state if no version is passed', () => {
|
||||
args.version = undefined;
|
||||
|
||||
const { container } = renderWithLocalization(<SnapVersion {...args} />);
|
||||
|
||||
expect(container.getElementsByClassName('preloader__icon')).toHaveLength(1);
|
||||
expect(container.firstChild).toHaveAttribute('href', args.url);
|
||||
});
|
||||
});
|
@ -3,6 +3,7 @@ export enum DelineatorType {
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
Error = 'error',
|
||||
Insights = 'insights',
|
||||
Description = 'description',
|
||||
}
|
||||
|
||||
export const getDelineatorTitle = (type: DelineatorType) => {
|
||||
@ -11,6 +12,8 @@ export const getDelineatorTitle = (type: DelineatorType) => {
|
||||
return 'errorWithSnap';
|
||||
case DelineatorType.Insights:
|
||||
return 'insightsFromSnap';
|
||||
case DelineatorType.Description:
|
||||
return 'descriptionFromSnap';
|
||||
default:
|
||||
return 'contentFromSnap';
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
.snap-install {
|
||||
box-shadow: none;
|
||||
|
||||
.headers {
|
||||
.content {
|
||||
flex: 1;
|
||||
|
||||
.loader-container {
|
||||
|
@ -84,13 +84,13 @@ export default function SnapInstall({
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
>
|
||||
<Box
|
||||
className="headers"
|
||||
className="header"
|
||||
alignItems={AlignItems.center}
|
||||
paddingLeft={4}
|
||||
paddingRight={4}
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
>
|
||||
<Box paddingLeft={4} paddingRight={4}>
|
||||
<SnapAuthorship snapId={targetSubjectMetadata.origin} />
|
||||
</Box>
|
||||
{!hasError && (
|
||||
<Text
|
||||
variant={TextVariant.headingLg}
|
||||
@ -100,6 +100,8 @@ export default function SnapInstall({
|
||||
{t('snapInstall')}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
<Box className="content">
|
||||
{isLoading && (
|
||||
<Box
|
||||
className="loader-container"
|
||||
@ -116,7 +118,7 @@ export default function SnapInstall({
|
||||
{hasPermissions && (
|
||||
<>
|
||||
<Text
|
||||
className="headers__permission-description"
|
||||
className="content__permission-description"
|
||||
paddingBottom={4}
|
||||
paddingLeft={4}
|
||||
paddingRight={4}
|
||||
|
@ -1,7 +1,7 @@
|
||||
.snap-update {
|
||||
box-shadow: none;
|
||||
|
||||
.headers {
|
||||
.content {
|
||||
flex: 1;
|
||||
|
||||
.loader-container {
|
||||
|
@ -90,7 +90,7 @@ export default function SnapUpdate({
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
>
|
||||
<Box
|
||||
className="headers"
|
||||
className="header"
|
||||
paddingLeft={4}
|
||||
paddingRight={4}
|
||||
alignItems={AlignItems.center}
|
||||
@ -106,6 +106,8 @@ export default function SnapUpdate({
|
||||
{t('snapUpdate')}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
<Box className="content">
|
||||
{isLoading && (
|
||||
<Box
|
||||
className="loader-container"
|
||||
@ -122,8 +124,10 @@ export default function SnapUpdate({
|
||||
{hasPermissions && (
|
||||
<>
|
||||
<Text
|
||||
className="headers__permission-description"
|
||||
className="content__permission-description"
|
||||
paddingBottom={4}
|
||||
paddingLeft={4}
|
||||
paddingRight={4}
|
||||
textAlign={TEXT_ALIGN.CENTER}
|
||||
>
|
||||
{t('snapUpdateRequestsPermission', [
|
||||
|
@ -1,19 +1,36 @@
|
||||
.view-snap {
|
||||
max-width: 475px;
|
||||
|
||||
&__version_info {
|
||||
&__version-number {
|
||||
font-weight: bold;
|
||||
&__description {
|
||||
&__wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
max-height: 5.5rem;
|
||||
|
||||
@include screen-md-min {
|
||||
max-height: 6rem;
|
||||
}
|
||||
|
||||
&__link {
|
||||
vertical-align: top;
|
||||
&.open {
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__enable {
|
||||
&__tooltip_wrapper {
|
||||
max-width: 52px;
|
||||
&__more-button {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: unset;
|
||||
padding: 0;
|
||||
padding-left: 32px;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, var(--color-background-default) 33%);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&__permissions {
|
||||
.permission-cell {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,28 +1,26 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import {
|
||||
SnapCaveatType,
|
||||
WALLET_SNAP_PERMISSION_KEY,
|
||||
} from '@metamask/rpc-methods';
|
||||
import { getSnapPrefix } from '@metamask/snaps-utils';
|
||||
import classnames from 'classnames';
|
||||
import Button from '../../../../components/ui/button';
|
||||
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||
import {
|
||||
Size,
|
||||
Color,
|
||||
FLEX_WRAP,
|
||||
TextColor,
|
||||
TextVariant,
|
||||
} from '../../../../helpers/constants/design-system';
|
||||
import SnapAuthorship from '../../../../components/app/snaps/snap-authorship';
|
||||
import Box from '../../../../components/ui/box';
|
||||
import SnapRemoveWarning from '../../../../components/app/snaps/snap-remove-warning';
|
||||
import ToggleButton from '../../../../components/ui/toggle-button';
|
||||
import ConnectedSitesList from '../../../../components/app/connected-sites-list';
|
||||
import Tooltip from '../../../../components/ui/tooltip';
|
||||
|
||||
import { SNAPS_LIST_ROUTE } from '../../../../helpers/constants/routes';
|
||||
import {
|
||||
disableSnap,
|
||||
enableSnap,
|
||||
removeSnap,
|
||||
removePermissionsFor,
|
||||
updateCaveat,
|
||||
@ -34,18 +32,17 @@ import {
|
||||
getPermissionSubjects,
|
||||
getTargetSubjectMetadata,
|
||||
} from '../../../../selectors';
|
||||
import {
|
||||
formatDate,
|
||||
getSnapName,
|
||||
removeSnapIdPrefix,
|
||||
} from '../../../../helpers/utils/util';
|
||||
import { ButtonLink, Text } from '../../../../components/component-library';
|
||||
import { getSnapName } from '../../../../helpers/utils/util';
|
||||
import { Text, BUTTON_TYPES } from '../../../../components/component-library';
|
||||
import SnapPermissionsList from '../../../../components/app/snaps/snap-permissions-list';
|
||||
import { SnapDelineator } from '../../../../components/app/snaps/snap-delineator';
|
||||
import { DelineatorType } from '../../../../helpers/constants/flask';
|
||||
|
||||
function ViewSnap() {
|
||||
const t = useI18nContext();
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
const descriptionRef = useRef(null);
|
||||
const { pathname } = location;
|
||||
// The snap ID is in URI-encoded form in the last path segment of the URL.
|
||||
const decodedSnapId = decodeURIComponent(pathname.match(/[^/]+$/u)[0]);
|
||||
@ -55,6 +52,8 @@ function ViewSnap() {
|
||||
.find((snapState) => snapState.id === decodedSnapId);
|
||||
|
||||
const [isShowingRemoveWarning, setIsShowingRemoveWarning] = useState(false);
|
||||
const [isDescriptionOpen, setIsDescriptionOpen] = useState(false);
|
||||
const [isOverflowing, setIsOverflowing] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!snap) {
|
||||
@ -62,6 +61,14 @@ function ViewSnap() {
|
||||
}
|
||||
}, [history, snap]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsOverflowing(
|
||||
descriptionRef.current &&
|
||||
descriptionRef.current.offsetHeight <
|
||||
descriptionRef.current.scrollHeight,
|
||||
);
|
||||
}, [descriptionRef]);
|
||||
|
||||
const connectedSubjects = useSelector((state) =>
|
||||
getSubjectsWithSnapPermission(state, snap?.id),
|
||||
);
|
||||
@ -74,14 +81,6 @@ function ViewSnap() {
|
||||
);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const onToggle = () => {
|
||||
if (snap.enabled) {
|
||||
dispatch(disableSnap(snap.id));
|
||||
} else {
|
||||
dispatch(enableSnap(snap.id));
|
||||
}
|
||||
};
|
||||
|
||||
const onDisconnect = (connectedOrigin, snapId) => {
|
||||
const caveatValue =
|
||||
subjects[connectedOrigin].permissions[WALLET_SNAP_PERMISSION_KEY]
|
||||
@ -110,121 +109,51 @@ function ViewSnap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
const versionHistory = snap.versionHistory ?? [];
|
||||
const installInfo = versionHistory.length
|
||||
? versionHistory[versionHistory.length - 1]
|
||||
: undefined;
|
||||
const packageName = snap.id && removeSnapIdPrefix(snap.id);
|
||||
const snapPrefix = snap.id && getSnapPrefix(snap.id);
|
||||
const isNPM = snapPrefix === 'npm:';
|
||||
const url = isNPM
|
||||
? `https://www.npmjs.com/package/${packageName}`
|
||||
: packageName;
|
||||
const snapName = getSnapName(snap.id, targetSubjectMetadata);
|
||||
|
||||
const shouldDisplayMoreButton = isOverflowing && !isDescriptionOpen;
|
||||
const handleMoreClick = () => {
|
||||
setIsDescriptionOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="view-snap"
|
||||
paddingBottom={8}
|
||||
paddingLeft={3}
|
||||
paddingRight={3}
|
||||
paddingBottom={[4, 8]}
|
||||
paddingTop={[4, 8]}
|
||||
paddingLeft={4}
|
||||
paddingRight={4}
|
||||
>
|
||||
<SnapAuthorship snapId={snap.id} snap={snap} expanded />
|
||||
<Box className="view-snap__description" marginTop={[4, 7]}>
|
||||
<SnapDelineator type={DelineatorType.Description} snapName={snapName}>
|
||||
<Box
|
||||
className="view-snap__header"
|
||||
paddingTop={8}
|
||||
marginLeft={4}
|
||||
marginRight={4}
|
||||
className={classnames('view-snap__description__wrapper', {
|
||||
open: isDescriptionOpen,
|
||||
})}
|
||||
ref={descriptionRef}
|
||||
>
|
||||
<SnapAuthorship snapId={snap.id} />
|
||||
</Box>
|
||||
<Box
|
||||
className="view-snap__description"
|
||||
marginTop={4}
|
||||
marginLeft={4}
|
||||
marginRight={4}
|
||||
<Text>{snap?.manifest.description}</Text>
|
||||
{shouldDisplayMoreButton && (
|
||||
<Button
|
||||
className="view-snap__description__more-button"
|
||||
type={BUTTON_TYPES.LINK}
|
||||
onClick={handleMoreClick}
|
||||
>
|
||||
<Text variant={TextVariant.bodyMd} color={TextColor.textDefault}>
|
||||
{snap.manifest.description}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box
|
||||
className="view-snap__version_info"
|
||||
marginTop={2}
|
||||
marginLeft={4}
|
||||
marginRight={4}
|
||||
>
|
||||
<Text variant={TextVariant.bodyMd} color={TextColor.textDefault}>
|
||||
{`${t('youInstalled')} `}
|
||||
<span className="view-snap__version_info__version-number">
|
||||
v{snap.version}
|
||||
</span>
|
||||
{` ${t('ofTextNofM')} `}
|
||||
<ButtonLink
|
||||
size={Size.auto}
|
||||
href={url}
|
||||
target="_blank"
|
||||
className="view-snap__version_info__link"
|
||||
>
|
||||
{packageName}
|
||||
</ButtonLink>
|
||||
{installInfo && ` ${t('from').toLowerCase()} `}
|
||||
{installInfo && (
|
||||
<ButtonLink
|
||||
size={Size.auto}
|
||||
href={installInfo.origin}
|
||||
target="_blank"
|
||||
className="view-snap__version_info__link"
|
||||
>
|
||||
{installInfo.origin}
|
||||
</ButtonLink>
|
||||
<Text color={Color.infoDefault}>{t('more')}</Text>
|
||||
</Button>
|
||||
)}
|
||||
{installInfo &&
|
||||
` ${t('on').toLowerCase()} ${formatDate(
|
||||
installInfo.date,
|
||||
'dd MMM yyyy',
|
||||
)}`}
|
||||
.
|
||||
</Text>
|
||||
</Box>
|
||||
<Box
|
||||
className="view-snap__enable"
|
||||
marginTop={12}
|
||||
marginLeft={4}
|
||||
marginRight={4}
|
||||
>
|
||||
<Text variant={TextVariant.bodyLgMedium}>{t('enableSnap')}</Text>
|
||||
<Text
|
||||
variant={TextVariant.bodyMd}
|
||||
color={TextColor.textDefault}
|
||||
marginBottom={4}
|
||||
>
|
||||
{t('enableSnapDescription')}
|
||||
</Text>
|
||||
<Box className="view-snap__enable__tooltip_wrapper">
|
||||
<Tooltip interactive position="left" html={t('snapsToggle')}>
|
||||
<ToggleButton
|
||||
value={snap.enabled}
|
||||
onToggle={onToggle}
|
||||
className="view-snap__toggle-button"
|
||||
/>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</SnapDelineator>
|
||||
</Box>
|
||||
<Box className="view-snap__permissions" marginTop={12}>
|
||||
<Text variant={TextVariant.bodyLgMedium} marginLeft={4} marginRight={4}>
|
||||
{t('permissions')}
|
||||
</Text>
|
||||
<Text variant={TextVariant.bodyLgMedium}>{t('permissions')}</Text>
|
||||
<SnapPermissionsList
|
||||
permissions={permissions ?? {}}
|
||||
targetSubjectMetadata={targetSubjectMetadata}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
className="view-snap__connected-sites"
|
||||
marginTop={12}
|
||||
marginLeft={4}
|
||||
marginRight={4}
|
||||
>
|
||||
<Box className="view-snap__connected-sites" marginTop={12}>
|
||||
<Text variant={TextVariant.bodyLgMedium} marginBottom={4}>
|
||||
{t('connectedSites')}
|
||||
</Text>
|
||||
@ -235,12 +164,7 @@ function ViewSnap() {
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
className="view-snap__remove"
|
||||
marginTop={12}
|
||||
marginLeft={4}
|
||||
marginRight={4}
|
||||
>
|
||||
<Box className="view-snap__remove" marginTop={12}>
|
||||
<Text variant={TextVariant.bodyLgMedium} color={TextColor.textDefault}>
|
||||
{t('removeSnap')}
|
||||
</Text>
|
||||
@ -253,7 +177,13 @@ function ViewSnap() {
|
||||
type="danger"
|
||||
onClick={() => setIsShowingRemoveWarning(true)}
|
||||
>
|
||||
<Text variant={TextVariant.bodyMd} color={TextColor.errorDefault}>
|
||||
<Text
|
||||
variant={TextVariant.bodyMd}
|
||||
color={TextColor.errorDefault}
|
||||
flexWrap={FLEX_WRAP.NO_WRAP}
|
||||
ellipsis
|
||||
style={{ overflow: 'hidden' }}
|
||||
>
|
||||
{`${t('remove')} ${snapName}`}
|
||||
</Text>
|
||||
</Button>
|
||||
|
@ -48,12 +48,7 @@ describe('ViewSnap', () => {
|
||||
// Snap version info
|
||||
expect(getByText('v5.1.2')).toBeDefined();
|
||||
// Enable Snap
|
||||
expect(getByText('Enable snap')).toBeDefined();
|
||||
expect(
|
||||
getByText(
|
||||
'Your installed snap will only have access to its permissions and run if it’s enabled.',
|
||||
),
|
||||
).toBeDefined();
|
||||
expect(getByText('Enable')).toBeDefined();
|
||||
expect(container.getElementsByClassName('toggle-button')?.length).toBe(1);
|
||||
// Permissions
|
||||
expect(getByText('Permissions')).toBeDefined();
|
||||
|
Loading…
Reference in New Issue
Block a user