diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index abda8fecd..fb45872eb 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -2927,7 +2927,7 @@
"description": "An extended description for the `snap_getBip32Entropy` permission. $1 is a derivation path (name)"
},
"permission_manageBip44Keys": {
- "message": "Control your \"$1\" accounts and assets.",
+ "message": "Control your $1 accounts and assets.",
"description": "The description for the `snap_getBip44Entropy` permission. $1 is the name of a protocol, e.g. 'Filecoin'."
},
"permission_manageBip44KeysDescription": {
diff --git a/ui/components/app/app-components.scss b/ui/components/app/app-components.scss
index 9659eaad3..196ab6c90 100644
--- a/ui/components/app/app-components.scss
+++ b/ui/components/app/app-components.scss
@@ -59,6 +59,7 @@
@import 'permissions-connect-footer/index';
@import 'permissions-connect-header/index';
@import 'permissions-connect-permission-list/index';
+@import 'permission-cell/index';
@import 'recovery-phrase-reminder/index';
@import 'set-approval-for-all-warning/index';
@import 'step-progress-bar/index.scss';
diff --git a/ui/components/app/flask/update-snap-permission-list/update-snap-permission-list.js b/ui/components/app/flask/update-snap-permission-list/update-snap-permission-list.js
index f787628e9..85f3373d4 100644
--- a/ui/components/app/flask/update-snap-permission-list/update-snap-permission-list.js
+++ b/ui/components/app/flask/update-snap-permission-list/update-snap-permission-list.js
@@ -1,14 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { isFunction } from 'lodash';
-import {
- getRightIcon,
- getWeightedPermissions,
-} from '../../../../helpers/utils/permission';
+import { getWeightedPermissions } from '../../../../helpers/utils/permission';
import { useI18nContext } from '../../../../hooks/useI18nContext';
-import { formatDate } from '../../../../helpers/utils/util';
-import Typography from '../../../ui/typography/typography';
-import { TextColor } from '../../../../helpers/constants/design-system';
+import PermissionCell from '../../permission-cell';
+import Box from '../../../ui/box';
export default function UpdateSnapPermissionList({
approvedPermissions,
@@ -17,52 +12,50 @@ export default function UpdateSnapPermissionList({
}) {
const t = useI18nContext();
- const Permissions = ({ className, permissions, subText }) => {
- return getWeightedPermissions(t, permissions).map((permission) => {
- const { label, permissionName, permissionValue } = permission;
- return (
-
-
-
- {label}
-
- {isFunction(subText)
- ? subText(permissionName, permissionValue)
- : subText}
-
-
- {getRightIcon(permission)}
-
- );
- });
- };
-
return (
-
-
-
{
- const { date } = permissionValue;
- const formattedDate = formatDate(date, 'yyyy-MM-dd');
- return t('approvedOn', [formattedDate]);
- }}
- />
-
-
+
+ {getWeightedPermissions(t, newPermissions).map((permission, index) => {
+ return (
+
+ );
+ })}
+ {getWeightedPermissions(t, approvedPermissions).map(
+ (permission, index) => {
+ return (
+
+ );
+ },
+ )}
+ {getWeightedPermissions(t, revokedPermissions).map(
+ (permission, index) => {
+ return (
+
+ );
+ },
+ )}
+
);
}
diff --git a/ui/components/app/permission-cell/index.js b/ui/components/app/permission-cell/index.js
new file mode 100644
index 000000000..4972ccc53
--- /dev/null
+++ b/ui/components/app/permission-cell/index.js
@@ -0,0 +1 @@
+export { default } from './permission-cell';
diff --git a/ui/components/app/permission-cell/index.scss b/ui/components/app/permission-cell/index.scss
new file mode 100644
index 000000000..95034015d
--- /dev/null
+++ b/ui/components/app/permission-cell/index.scss
@@ -0,0 +1,5 @@
+.permission-cell {
+ &__title-revoked {
+ text-decoration: line-through;
+ }
+}
diff --git a/ui/components/app/permission-cell/permission-cell.js b/ui/components/app/permission-cell/permission-cell.js
new file mode 100644
index 000000000..1090b3958
--- /dev/null
+++ b/ui/components/app/permission-cell/permission-cell.js
@@ -0,0 +1,126 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classnames from 'classnames';
+import Box from '../../ui/box';
+import {
+ AlignItems,
+ Color,
+ IconColor,
+ JustifyContent,
+ Size,
+ TextColor,
+ TextVariant,
+} from '../../../helpers/constants/design-system';
+import {
+ AvatarIcon,
+ Icon,
+ ICON_NAMES,
+ ICON_SIZES,
+ Text,
+} from '../../component-library';
+import { formatDate } from '../../../helpers/utils/util';
+import { useI18nContext } from '../../../hooks/useI18nContext';
+import Tooltip from '../../ui/tooltip';
+
+const PermissionCell = ({
+ title,
+ description,
+ weight,
+ avatarIcon,
+ dateApproved,
+ revoked,
+}) => {
+ const t = useI18nContext();
+
+ let infoIconColor = IconColor.iconMuted;
+ let infoIcon = ICON_NAMES.INFO;
+ let iconColor = Color.primaryDefault;
+ let iconBackgroundColor = Color.primaryMuted;
+
+ if (!revoked && weight === 1) {
+ iconColor = Color.warningDefault;
+ iconBackgroundColor = Color.warningMuted;
+ infoIconColor = IconColor.warningDefault;
+ infoIcon = ICON_NAMES.DANGER;
+ }
+
+ if (dateApproved) {
+ iconColor = Color.iconMuted;
+ iconBackgroundColor = Color.backgroundAlternative;
+ }
+
+ if (revoked) {
+ iconColor = Color.iconMuted;
+ iconBackgroundColor = Color.backgroundAlternative;
+ }
+
+ return (
+
+
+ {typeof avatarIcon === 'string' ? (
+
+ ) : (
+ avatarIcon
+ )}
+
+
+
+ {title}
+
+
+ {!revoked &&
+ (dateApproved
+ ? t('approvedOn', [formatDate(dateApproved, 'yyyy-MM-dd')])
+ : t('permissionRequested'))}
+ {revoked ? t('permissionRevoked') : ''}
+
+
+
+ {description}} position="bottom">
+
+
+
+
+ );
+};
+
+PermissionCell.propTypes = {
+ title: PropTypes.oneOfType([
+ PropTypes.string.isRequired,
+ PropTypes.object.isRequired,
+ ]),
+ description: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
+ weight: PropTypes.number,
+ avatarIcon: PropTypes.any.isRequired,
+ dateApproved: PropTypes.number,
+ revoked: PropTypes.bool,
+};
+
+export default PermissionCell;
diff --git a/ui/components/app/permission-cell/permission-cell.stories.js b/ui/components/app/permission-cell/permission-cell.stories.js
new file mode 100644
index 000000000..986b9d326
--- /dev/null
+++ b/ui/components/app/permission-cell/permission-cell.stories.js
@@ -0,0 +1,22 @@
+import React from 'react';
+import PermissionCell from '.';
+
+export default {
+ title: 'Components/App/PermissionCell',
+
+ component: PermissionCell,
+};
+
+export const DefaultStory = (args) => ;
+
+DefaultStory.storyName = 'Default';
+
+DefaultStory.args = {
+ title: 'Access the Ethereum provider.',
+ description:
+ 'Allow the snap to communicate with MetaMask direct…blockchain and suggest messages and transactions.',
+ weight: 1,
+ avatarIcon: 'ethereum',
+ dateApproved: 1680185432326,
+ revoked: false,
+};
diff --git a/ui/components/app/permission-cell/permission-cell.test.js b/ui/components/app/permission-cell/permission-cell.test.js
new file mode 100644
index 000000000..f4567827f
--- /dev/null
+++ b/ui/components/app/permission-cell/permission-cell.test.js
@@ -0,0 +1,69 @@
+import React from 'react';
+import { screen } from '@testing-library/react';
+import { renderWithProvider } from '../../../../test/jest';
+import PermissionCell from './permission-cell';
+
+describe('Permission Cell', () => {
+ const mockPermissionData = {
+ label: 'Access the Ethereum provider.',
+ description:
+ 'Allow the snap to communicate with MetaMask direct…blockchain and suggest messages and transactions.',
+ weight: 1,
+ leftIcon: 'ethereum',
+ permissionValue: {
+ date: 1680185432326,
+ },
+ permissionName: 'ethereum-provider',
+ };
+
+ it('renders approved permission cell', () => {
+ renderWithProvider(
+ ,
+ );
+ expect(
+ screen.getByText('Access the Ethereum provider.'),
+ ).toBeInTheDocument();
+ expect(screen.getByText('Approved on 2023-03-30')).toBeInTheDocument();
+ });
+
+ it('renders revoked permission cell', () => {
+ renderWithProvider(
+ ,
+ );
+ expect(
+ screen.getByText('Access the Ethereum provider.'),
+ ).toBeInTheDocument();
+ expect(screen.getByText('Revoked in this update')).toBeInTheDocument();
+ });
+
+ it('renders requested permission cell', () => {
+ renderWithProvider(
+ ,
+ );
+ expect(
+ screen.getByText('Access the Ethereum provider.'),
+ ).toBeInTheDocument();
+ expect(screen.getByText('Requested now')).toBeInTheDocument();
+ });
+});
diff --git a/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js b/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js
index 99b554ec5..9e43d0b9d 100644
--- a/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js
+++ b/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js
@@ -1,37 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
-import {
- getRightIcon,
- getWeightedPermissions,
-} from '../../../helpers/utils/permission';
+import { getWeightedPermissions } from '../../../helpers/utils/permission';
import { useI18nContext } from '../../../hooks/useI18nContext';
-
-/**
- * Get one or more permission descriptions for a permission name.
- *
- * @param permission - The permission to render.
- * @param index - The index of the permission.
- * @returns {JSX.Element} A permission description node.
- */
-function getDescriptionNode(permission, index) {
- const { label, leftIcon, permissionName } = permission;
-
- return (
-
- {typeof leftIcon === 'string' ? : leftIcon}
- {label}
- {getRightIcon(permission)}
-
- );
-}
+import PermissionCell from '../permission-cell';
+import Box from '../../ui/box';
export default function PermissionsConnectPermissionList({ permissions }) {
const t = useI18nContext();
return (
-
- {getWeightedPermissions(t, permissions).map(getDescriptionNode)}
-
+
+ {getWeightedPermissions(t, permissions).map((permission, index) => {
+ return (
+
+ );
+ })}
+
);
}
diff --git a/ui/helpers/utils/permission.js b/ui/helpers/utils/permission.js
index 6987b1d7f..2d54064aa 100644
--- a/ui/helpers/utils/permission.js
+++ b/ui/helpers/utils/permission.js
@@ -15,21 +15,13 @@ import {
} from '../../../shared/constants/permissions';
import Tooltip from '../../components/ui/tooltip';
import {
- AvatarIcon,
///: BEGIN:ONLY_INCLUDE_IN(flask)
- Icon,
Text,
///: END:ONLY_INCLUDE_IN
ICON_NAMES,
- ICON_SIZES,
} from '../../components/component-library';
///: BEGIN:ONLY_INCLUDE_IN(flask)
-import {
- IconColor,
- Color,
- FONT_WEIGHT,
- TextVariant,
-} from '../constants/design-system';
+import { Color, FONT_WEIGHT, TextVariant } from '../constants/design-system';
import {
coinTypeToProtocolName,
getSnapDerivationPathName,
@@ -39,63 +31,29 @@ import {
const UNKNOWN_PERMISSION = Symbol('unknown');
-///: BEGIN:ONLY_INCLUDE_IN(flask)
-const RIGHT_WARNING_ICON = (
-
-);
-
-const RIGHT_INFO_ICON = (
-
-);
-///: END:ONLY_INCLUDE_IN
-
-function getLeftIcon(iconName) {
- return (
-
- );
-}
-
export const PERMISSION_DESCRIPTIONS = deepFreeze({
[RestrictedMethods.eth_accounts]: ({ t }) => ({
label: t('permission_ethereumAccounts'),
- leftIcon: getLeftIcon(ICON_NAMES.EYE),
- rightIcon: null,
+ leftIcon: ICON_NAMES.EYE,
weight: 2,
}),
///: BEGIN:ONLY_INCLUDE_IN(flask)
[RestrictedMethods.snap_confirm]: ({ t }) => ({
label: t('permission_customConfirmation'),
description: t('permission_customConfirmationDescription'),
- leftIcon: getLeftIcon(ICON_NAMES.SECURITY_TICK),
- rightIcon: RIGHT_INFO_ICON,
+ leftIcon: ICON_NAMES.SECURITY_TICK,
weight: 3,
}),
[RestrictedMethods.snap_dialog]: ({ t }) => ({
label: t('permission_dialog'),
description: t('permission_dialogDescription'),
- leftIcon: getLeftIcon(ICON_NAMES.MESSAGES),
- rightIcon: RIGHT_INFO_ICON,
+ leftIcon: ICON_NAMES.MESSAGES,
weight: 3,
}),
[RestrictedMethods.snap_notify]: ({ t }) => ({
label: t('permission_notifications'),
description: t('permission_notificationsDescription'),
- leftIcon: getLeftIcon(ICON_NAMES.NOTIFICATION),
- rightIcon: RIGHT_INFO_ICON,
+ leftIcon: ICON_NAMES.NOTIFICATION,
weight: 3,
}),
[RestrictedMethods.snap_getBip32PublicKey]: ({
@@ -105,8 +63,7 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({
}) =>
permissionValue.caveats[0].value.map(({ path, curve }, i) => {
const baseDescription = {
- leftIcon: getLeftIcon(ICON_NAMES.SECURITY_SEARCH),
- rightIcon: RIGHT_WARNING_ICON,
+ leftIcon: ICON_NAMES.SECURITY_SEARCH,
weight: 1,
id: `public-key-access-bip32-${path
.join('-')
@@ -176,8 +133,7 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({
}) =>
permissionValue.caveats[0].value.map(({ path, curve }, i) => {
const baseDescription = {
- leftIcon: getLeftIcon(ICON_NAMES.KEY),
- rightIcon: RIGHT_WARNING_ICON,
+ leftIcon: ICON_NAMES.KEY,
weight: 1,
id: `key-access-bip32-${path
.join('-')
@@ -261,8 +217,7 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({
t('unrecognizedProtocol', [coinType])}
,
]),
- leftIcon: getLeftIcon(ICON_NAMES.KEY),
- rightIcon: RIGHT_WARNING_ICON,
+ leftIcon: ICON_NAMES.KEY,
weight: 1,
id: `key-access-bip44-${coinType}-${i}`,
message: t('snapInstallWarningKeyAccess', [
@@ -284,22 +239,19 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({
[RestrictedMethods.snap_getEntropy]: ({ t }) => ({
label: t('permission_getEntropy'),
description: t('permission_getEntropyDescription'),
- leftIcon: getLeftIcon(ICON_NAMES.SECURITY_KEY),
- rightIcon: RIGHT_INFO_ICON,
+ leftIcon: ICON_NAMES.SECURITY_KEY,
weight: 3,
}),
[RestrictedMethods.snap_manageState]: ({ t }) => ({
label: t('permission_manageState'),
description: t('permission_manageStateDescription'),
- leftIcon: getLeftIcon(ICON_NAMES.ADD_SQUARE),
- rightIcon: RIGHT_INFO_ICON,
+ leftIcon: ICON_NAMES.ADD_SQUARE,
weight: 3,
}),
[RestrictedMethods.wallet_snap]: ({ t, permissionValue }) => {
const snaps = permissionValue.caveats[0].value;
const baseDescription = {
- leftIcon: getLeftIcon(ICON_NAMES.FLASH),
- rightIcon: RIGHT_INFO_ICON,
+ leftIcon: ICON_NAMES.FLASH,
};
return Object.keys(snaps).map((snapId) => {
@@ -326,8 +278,7 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({
[EndowmentPermissions['endowment:network-access']]: ({ t }) => ({
label: t('permission_accessNetwork'),
description: t('permission_accessNetworkDescription'),
- leftIcon: getLeftIcon(ICON_NAMES.GLOBAL),
- rightIcon: RIGHT_INFO_ICON,
+ leftIcon: ICON_NAMES.GLOBAL,
weight: 2,
}),
[EndowmentPermissions['endowment:webassembly']]: ({ t }) => ({
@@ -340,8 +291,7 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({
[EndowmentPermissions['endowment:long-running']]: ({ t }) => ({
label: t('permission_longRunning'),
description: t('permission_longRunningDescription'),
- leftIcon: getLeftIcon(ICON_NAMES.LINK),
- rightIcon: RIGHT_INFO_ICON,
+ leftIcon: ICON_NAMES.LINK,
weight: 3,
}),
[EndowmentPermissions['endowment:transaction-insight']]: ({
@@ -349,8 +299,7 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({
permissionValue,
}) => {
const baseDescription = {
- leftIcon: getLeftIcon(ICON_NAMES.SPEEDOMETER),
- rightIcon: RIGHT_INFO_ICON,
+ leftIcon: ICON_NAMES.SPEEDOMETER,
weight: 3,
};
@@ -371,7 +320,7 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({
...baseDescription,
label: t('permission_transactionInsightOrigin'),
description: t('permission_transactionInsightOriginDescription'),
- leftIcon: getLeftIcon(ICON_NAMES.EXPLORE),
+ leftIcon: ICON_NAMES.EXPLORE,
});
}
@@ -380,8 +329,7 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({
[EndowmentPermissions['endowment:cronjob']]: ({ t }) => ({
label: t('permission_cronjob'),
description: t('permission_cronjobDescription'),
- leftIcon: getLeftIcon(ICON_NAMES.CLOCK),
- rightIcon: RIGHT_INFO_ICON,
+ leftIcon: ICON_NAMES.CLOCK,
weight: 2,
}),
[EndowmentPermissions['endowment:ethereum-provider']]: ({
@@ -390,16 +338,14 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({
}) => ({
label: t('permission_ethereumProvider'),
description: t('permission_ethereumProviderDescription'),
- leftIcon: getLeftIcon(ICON_NAMES.ETHEREUM),
- rightIcon: RIGHT_INFO_ICON,
+ leftIcon: ICON_NAMES.ETHEREUM,
weight: 1,
id: 'ethereum-provider-access',
message: t('ethereumProviderAccess', [targetSubjectMetadata?.origin]),
}),
[EndowmentPermissions['endowment:rpc']]: ({ t, permissionValue }) => {
const baseDescription = {
- leftIcon: getLeftIcon(ICON_NAMES.HIERARCHY),
- rightIcon: RIGHT_INFO_ICON,
+ leftIcon: ICON_NAMES.HIERARCHY,
weight: 2,
};
@@ -427,8 +373,7 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({
///: END:ONLY_INCLUDE_IN
[UNKNOWN_PERMISSION]: ({ t, permissionName }) => ({
label: t('permission_unknown', [permissionName ?? 'undefined']),
- leftIcon: getLeftIcon(ICON_NAMES.QUESTION),
- rightIcon: null,
+ leftIcon: ICON_NAMES.QUESTION,
weight: 4,
}),
});
diff --git a/ui/pages/permissions-connect/flask/snap-install/index.scss b/ui/pages/permissions-connect/flask/snap-install/index.scss
index dc725ce10..654653ab9 100644
--- a/ui/pages/permissions-connect/flask/snap-install/index.scss
+++ b/ui/pages/permissions-connect/flask/snap-install/index.scss
@@ -4,14 +4,6 @@
.headers {
flex: 1;
- .permissions-connect-permission-list {
- padding: 0 24px;
-
- .permission {
- padding: 8px 0;
- }
- }
-
.loader-container {
height: 100%;
}
@@ -21,7 +13,6 @@
}
}
-
.page-container__footer {
width: 100%;
margin-top: 12px;
diff --git a/ui/pages/settings/flask/view-snap/view-snap.js b/ui/pages/settings/flask/view-snap/view-snap.js
index 42ac59c8c..d8f5b6e93 100644
--- a/ui/pages/settings/flask/view-snap/view-snap.js
+++ b/ui/pages/settings/flask/view-snap/view-snap.js
@@ -13,6 +13,7 @@ import {
TEXT_ALIGN,
FRACTIONS,
TextColor,
+ BLOCK_SIZES,
} from '../../../../helpers/constants/design-system';
import SnapAuthorship from '../../../../components/app/flask/snap-authorship';
import Box from '../../../../components/ui/box';
@@ -176,7 +177,7 @@ function ViewSnap() {
>
{t('snapAccess', [snap.manifest.proposedName])}
-
+