mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
[FLASK] Rework Snaps headers and footers (#19442)
* Add new snap header and footer to snap install * Add new snap header and footer to snap result and snap update * Fix loading state * Fix lint * Add required scrolling * Adjust avatar component * Apply new headers and footers to snaps confirmations * Rename previous SnapAuthorship component to SnapAuthorshipExpanded * Fix lint * Fix font weight * Fix fencing * Fix a test * Fix lint after rebase * Fix E2E * Fix locale lint * Fix another E2E * Fix test ID * Address PR comments * Better scroll button centering * Address design comments * Fix unit test * Fix E2Es
This commit is contained in:
parent
546d8349e7
commit
789779f4d5
7
app/_locales/en/messages.json
generated
7
app/_locales/en/messages.json
generated
@ -1371,6 +1371,9 @@
|
|||||||
"message": "enable $1",
|
"message": "enable $1",
|
||||||
"description": "$1 is a token symbol, e.g. ETH"
|
"description": "$1 is a token symbol, e.g. ETH"
|
||||||
},
|
},
|
||||||
|
"enabled": {
|
||||||
|
"message": "Enabled"
|
||||||
|
},
|
||||||
"encryptionPublicKeyNotice": {
|
"encryptionPublicKeyNotice": {
|
||||||
"message": "$1 would like your public encryption key. By consenting, this site will be able to compose encrypted messages to you.",
|
"message": "$1 would like your public encryption key. By consenting, this site will be able to compose encrypted messages to you.",
|
||||||
"description": "$1 is the web3 site name"
|
"description": "$1 is the web3 site name"
|
||||||
@ -3777,10 +3780,6 @@
|
|||||||
"message": "Install snap"
|
"message": "Install snap"
|
||||||
},
|
},
|
||||||
"snapInstallRequest": {
|
"snapInstallRequest": {
|
||||||
"message": "$1 wants to install $2. Make sure you trust the authors before you proceed.",
|
|
||||||
"description": "$1 is the dApp origin requesting the snap and $2 is the snap name"
|
|
||||||
},
|
|
||||||
"snapInstallRequestsPermission": {
|
|
||||||
"message": "Installing $1 gives it the following permissions. Only continue if you trust $1.",
|
"message": "Installing $1 gives it the following permissions. Only continue if you trust $1.",
|
||||||
"description": "$1 is the snap name."
|
"description": "$1 is the snap name."
|
||||||
},
|
},
|
||||||
|
@ -165,7 +165,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"frequentRpcListDetail": [],
|
"frequentRpcListDetail": [],
|
||||||
"subjectMetadata": {},
|
"subjectMetadata": {
|
||||||
|
"npm:@metamask/test-snap-bip44": {
|
||||||
|
"name": "@metamask/test-snap-bip44",
|
||||||
|
"version": "1.2.3",
|
||||||
|
"subjectType": "snap"
|
||||||
|
}
|
||||||
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"test": {
|
"test": {
|
||||||
"id": "test",
|
"id": "test",
|
||||||
|
@ -54,6 +54,8 @@ describe('Test Snap bip-32', function () {
|
|||||||
|
|
||||||
await driver.waitForSelector({ text: 'Install' });
|
await driver.waitForSelector({ text: 'Install' });
|
||||||
|
|
||||||
|
await driver.clickElement('[data-testid="snap-install-scroll"]');
|
||||||
|
|
||||||
await driver.clickElement({
|
await driver.clickElement({
|
||||||
text: 'Install',
|
text: 'Install',
|
||||||
tag: 'button',
|
tag: 'button',
|
||||||
|
@ -90,7 +90,7 @@ describe('Test Snap Cronjob', function () {
|
|||||||
|
|
||||||
// try to click on the Ok button and pass test if it works
|
// try to click on the Ok button and pass test if it works
|
||||||
await driver.clickElement({
|
await driver.clickElement({
|
||||||
text: 'Ok',
|
text: 'OK',
|
||||||
tag: 'button',
|
tag: 'button',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -95,7 +95,7 @@ describe('Test Snap Dialog', function () {
|
|||||||
|
|
||||||
// click ok button
|
// click ok button
|
||||||
await driver.clickElement({
|
await driver.clickElement({
|
||||||
text: 'Ok',
|
text: 'OK',
|
||||||
tag: 'button',
|
tag: 'button',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ describe('Test Snap networkAccess', function () {
|
|||||||
|
|
||||||
// click ok button
|
// click ok button
|
||||||
await driver.clickElement({
|
await driver.clickElement({
|
||||||
text: 'Ok',
|
text: 'OK',
|
||||||
tag: 'button',
|
tag: 'button',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -55,6 +55,8 @@ describe('Test Snap RPC', function () {
|
|||||||
|
|
||||||
await driver.waitForSelector({ text: 'Install' });
|
await driver.waitForSelector({ text: 'Install' });
|
||||||
|
|
||||||
|
await driver.clickElement('[data-testid="snap-install-scroll"]');
|
||||||
|
|
||||||
await driver.clickElement({
|
await driver.clickElement({
|
||||||
text: 'Install',
|
text: 'Install',
|
||||||
tag: 'button',
|
tag: 'button',
|
||||||
|
@ -55,6 +55,8 @@ describe('Test Snap update', function () {
|
|||||||
|
|
||||||
await driver.waitForSelector({ text: 'Install' });
|
await driver.waitForSelector({ text: 'Install' });
|
||||||
|
|
||||||
|
await driver.clickElement('[data-testid="snap-install-scroll"]');
|
||||||
|
|
||||||
await driver.clickElement({
|
await driver.clickElement({
|
||||||
text: 'Install',
|
text: 'Install',
|
||||||
tag: 'button',
|
tag: 'button',
|
||||||
@ -105,6 +107,8 @@ describe('Test Snap update', function () {
|
|||||||
|
|
||||||
await driver.waitForSelector({ text: 'Update' });
|
await driver.waitForSelector({ text: 'Update' });
|
||||||
|
|
||||||
|
await driver.clickElement('[data-testid="snap-update-scroll"]');
|
||||||
|
|
||||||
await driver.clickElement({
|
await driver.clickElement({
|
||||||
text: 'Update',
|
text: 'Update',
|
||||||
tag: 'button',
|
tag: 'button',
|
||||||
|
@ -7,17 +7,8 @@ import {
|
|||||||
FLEX_DIRECTION,
|
FLEX_DIRECTION,
|
||||||
JustifyContent,
|
JustifyContent,
|
||||||
} from '../../../helpers/constants/design-system';
|
} from '../../../helpers/constants/design-system';
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
|
||||||
import SnapAuthorship from '../snaps/snap-authorship';
|
|
||||||
///: END:ONLY_INCLUDE_IN
|
|
||||||
|
|
||||||
export default class PermissionsConnectHeader extends Component {
|
export default class PermissionsConnectHeader extends Component {
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
|
||||||
static contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
};
|
|
||||||
///: END:ONLY_INCLUDE_IN
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
iconUrl: PropTypes.string,
|
iconUrl: PropTypes.string,
|
||||||
@ -28,10 +19,6 @@ export default class PermissionsConnectHeader extends Component {
|
|||||||
headerText: PropTypes.string,
|
headerText: PropTypes.string,
|
||||||
leftIcon: PropTypes.node,
|
leftIcon: PropTypes.node,
|
||||||
rightIcon: PropTypes.node,
|
rightIcon: PropTypes.node,
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
|
||||||
snapVersion: PropTypes.string,
|
|
||||||
isSnapInstallOrUpdate: PropTypes.bool,
|
|
||||||
///: END:ONLY_INCLUDE_IN
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
@ -42,22 +29,7 @@ export default class PermissionsConnectHeader extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderHeaderIcon() {
|
renderHeaderIcon() {
|
||||||
const {
|
const { iconUrl, iconName, siteOrigin, leftIcon, rightIcon } = this.props;
|
||||||
iconUrl,
|
|
||||||
iconName,
|
|
||||||
siteOrigin,
|
|
||||||
leftIcon,
|
|
||||||
rightIcon,
|
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
|
||||||
isSnapInstallOrUpdate,
|
|
||||||
///: END:ONLY_INCLUDE_IN
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
|
||||||
if (isSnapInstallOrUpdate) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
///: END:ONLY_INCLUDE_IN
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="permissions-connect-header__icon">
|
<div className="permissions-connect-header__icon">
|
||||||
@ -75,16 +47,7 @@ export default class PermissionsConnectHeader extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { boxProps, className, headerTitle, headerText } = this.props;
|
||||||
boxProps,
|
|
||||||
className,
|
|
||||||
headerTitle,
|
|
||||||
headerText,
|
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
|
||||||
siteOrigin,
|
|
||||||
isSnapInstallOrUpdate,
|
|
||||||
///: END:ONLY_INCLUDE_IN
|
|
||||||
} = this.props;
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
className={classnames('permissions-connect-header', className)}
|
className={classnames('permissions-connect-header', className)}
|
||||||
@ -94,11 +57,6 @@ export default class PermissionsConnectHeader extends Component {
|
|||||||
>
|
>
|
||||||
{this.renderHeaderIcon()}
|
{this.renderHeaderIcon()}
|
||||||
<div className="permissions-connect-header__title">{headerTitle}</div>
|
<div className="permissions-connect-header__title">{headerTitle}</div>
|
||||||
{
|
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
|
||||||
isSnapInstallOrUpdate && <SnapAuthorship snapId={siteOrigin} />
|
|
||||||
///: END:ONLY_INCLUDE_IN
|
|
||||||
}
|
|
||||||
<div className="permissions-connect-header__subtitle">{headerText}</div>
|
<div className="permissions-connect-header__subtitle">{headerText}</div>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
export { default } from './snap-authorship-expanded';
|
@ -17,6 +17,7 @@ import {
|
|||||||
BorderStyle,
|
BorderStyle,
|
||||||
Color,
|
Color,
|
||||||
BorderRadius,
|
BorderRadius,
|
||||||
|
FontWeight,
|
||||||
} from '../../../../helpers/constants/design-system';
|
} from '../../../../helpers/constants/design-system';
|
||||||
import {
|
import {
|
||||||
formatDate,
|
formatDate,
|
||||||
@ -34,7 +35,7 @@ import { disableSnap, enableSnap } from '../../../../store/actions';
|
|||||||
import { useOriginMetadata } from '../../../../hooks/useOriginMetadata';
|
import { useOriginMetadata } from '../../../../hooks/useOriginMetadata';
|
||||||
import SnapVersion from '../snap-version/snap-version';
|
import SnapVersion from '../snap-version/snap-version';
|
||||||
|
|
||||||
const SnapAuthorship = ({ snapId, className, expanded = false, snap }) => {
|
const SnapAuthorshipExpanded = ({ snapId, className, snap }) => {
|
||||||
const t = useI18nContext();
|
const t = useI18nContext();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
@ -55,7 +56,6 @@ const SnapAuthorship = ({ snapId, className, expanded = false, snap }) => {
|
|||||||
|
|
||||||
const friendlyName = snapId && getSnapName(snapId, subjectMetadata);
|
const friendlyName = snapId && getSnapName(snapId, subjectMetadata);
|
||||||
|
|
||||||
// Expanded data
|
|
||||||
const versionHistory = snap?.versionHistory ?? [];
|
const versionHistory = snap?.versionHistory ?? [];
|
||||||
const installInfo = versionHistory.length
|
const installInfo = versionHistory.length
|
||||||
? versionHistory[versionHistory.length - 1]
|
? versionHistory[versionHistory.length - 1]
|
||||||
@ -72,33 +72,35 @@ const SnapAuthorship = ({ snapId, className, expanded = false, snap }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
className={classnames('snaps-authorship', className)}
|
className={classnames('snaps-authorship-expanded', className)}
|
||||||
backgroundColor={BackgroundColor.backgroundDefault}
|
backgroundColor={BackgroundColor.backgroundDefault}
|
||||||
borderColor={BorderColor.borderDefault}
|
borderColor={BorderColor.borderDefault}
|
||||||
borderWidth={1}
|
borderWidth={1}
|
||||||
width={BLOCK_SIZES.FULL}
|
width={BLOCK_SIZES.FULL}
|
||||||
borderRadius={expanded ? BorderRadius.LG : BorderRadius.pill}
|
borderRadius={BorderRadius.LG}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
alignItems={AlignItems.center}
|
alignItems={AlignItems.center}
|
||||||
display={DISPLAY.FLEX}
|
display={DISPLAY.FLEX}
|
||||||
width={BLOCK_SIZES.FULL}
|
width={BLOCK_SIZES.FULL}
|
||||||
paddingLeft={expanded ? 4 : 2}
|
paddingLeft={4}
|
||||||
paddingRight={expanded ? 4 : 2}
|
paddingRight={4}
|
||||||
paddingTop={expanded ? 3 : 1}
|
paddingTop={3}
|
||||||
paddingBottom={expanded ? 3 : 1}
|
paddingBottom={3}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<SnapAvatar snapId={snapId} />
|
<SnapAvatar snapId={snapId} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
marginLeft={2}
|
marginLeft={4}
|
||||||
marginRight={expanded ? 0 : 2}
|
marginRight={0}
|
||||||
display={DISPLAY.FLEX}
|
display={DISPLAY.FLEX}
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
style={{ overflow: 'hidden' }}
|
style={{ overflow: 'hidden' }}
|
||||||
>
|
>
|
||||||
<Text ellipsis>{friendlyName}</Text>
|
<Text ellipsis fontWeight={FontWeight.Medium}>
|
||||||
|
{friendlyName}
|
||||||
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
ellipsis
|
ellipsis
|
||||||
variant={TextVariant.bodySm}
|
variant={TextVariant.bodySm}
|
||||||
@ -107,80 +109,77 @@ const SnapAuthorship = ({ snapId, className, expanded = false, snap }) => {
|
|||||||
{packageName}
|
{packageName}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{!expanded && (
|
|
||||||
<Box marginLeft="auto">
|
|
||||||
<SnapVersion version={subjectMetadata?.version} url={url} />
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
{expanded && (
|
<Box flexDirection={FLEX_DIRECTION.COLUMN} width={BLOCK_SIZES.FULL}>
|
||||||
<Box flexDirection={FLEX_DIRECTION.COLUMN} width={BLOCK_SIZES.FULL}>
|
<Box
|
||||||
<Box
|
flexDirection={FLEX_DIRECTION.ROW}
|
||||||
flexDirection={FLEX_DIRECTION.ROW}
|
justifyContent={JustifyContent.spaceBetween}
|
||||||
justifyContent={JustifyContent.spaceBetween}
|
paddingLeft={4}
|
||||||
paddingLeft={4}
|
paddingTop={4}
|
||||||
paddingTop={4}
|
paddingBottom={4}
|
||||||
paddingBottom={4}
|
borderColor={BorderColor.borderDefault}
|
||||||
borderColor={BorderColor.borderDefault}
|
width={BLOCK_SIZES.FULL}
|
||||||
width={BLOCK_SIZES.FULL}
|
style={{
|
||||||
style={{
|
borderLeft: BorderStyle.none,
|
||||||
borderLeft: BorderStyle.none,
|
borderRight: BorderStyle.none,
|
||||||
borderRight: BorderStyle.none,
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<Text variant={TextVariant.bodyMd} fontWeight={FontWeight.Medium}>
|
||||||
<Text variant={TextVariant.bodyMdBold}>{t('enableSnap')}</Text>
|
{t('enabled')}
|
||||||
<Box style={{ maxWidth: '52px' }}>
|
</Text>
|
||||||
<Tooltip interactive position="left" html={t('snapsToggle')}>
|
<Box style={{ maxWidth: '52px' }}>
|
||||||
<ToggleButton value={snap?.enabled} onToggle={onToggle} />
|
<Tooltip interactive position="left" html={t('snapsToggle')}>
|
||||||
</Tooltip>
|
<ToggleButton value={snap?.enabled} onToggle={onToggle} />
|
||||||
</Box>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
</Box>
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
<Box
|
||||||
padding={4}
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
width={BLOCK_SIZES.FULL}
|
padding={4}
|
||||||
>
|
width={BLOCK_SIZES.FULL}
|
||||||
{installOrigin && installInfo && (
|
>
|
||||||
<Box
|
{installOrigin && installInfo && (
|
||||||
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
|
<Box
|
||||||
flexDirection={FLEX_DIRECTION.ROW}
|
flexDirection={FLEX_DIRECTION.ROW}
|
||||||
justifyContent={JustifyContent.spaceBetween}
|
justifyContent={JustifyContent.spaceBetween}
|
||||||
alignItems={AlignItems.center}
|
width={BLOCK_SIZES.FULL}
|
||||||
marginTop={4}
|
|
||||||
>
|
>
|
||||||
<Text variant={TextVariant.bodyMdBold}>{t('version')}</Text>
|
<Text variant={TextVariant.bodyMd} fontWeight={FontWeight.Medium}>
|
||||||
<SnapVersion version={snap?.version} url={url} />
|
{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>
|
||||||
|
)}
|
||||||
|
<Box
|
||||||
|
flexDirection={FLEX_DIRECTION.ROW}
|
||||||
|
justifyContent={JustifyContent.spaceBetween}
|
||||||
|
alignItems={AlignItems.center}
|
||||||
|
marginTop={4}
|
||||||
|
>
|
||||||
|
<Text variant={TextVariant.bodyMd} fontWeight={FontWeight.Medium}>
|
||||||
|
{t('version')}
|
||||||
|
</Text>
|
||||||
|
<SnapVersion version={snap?.version} url={url} />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
SnapAuthorship.propTypes = {
|
SnapAuthorshipExpanded.propTypes = {
|
||||||
/**
|
/**
|
||||||
* The id of the snap
|
* The id of the snap
|
||||||
*/
|
*/
|
||||||
@ -190,13 +189,9 @@ SnapAuthorship.propTypes = {
|
|||||||
*/
|
*/
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
/**
|
/**
|
||||||
* If the authorship component should be expanded
|
* The snap object.
|
||||||
*/
|
|
||||||
expanded: PropTypes.bool,
|
|
||||||
/**
|
|
||||||
* The snap object. Can be undefined if the component is not expanded
|
|
||||||
*/
|
*/
|
||||||
snap: PropTypes.object,
|
snap: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SnapAuthorship;
|
export default SnapAuthorshipExpanded;
|
1
ui/components/app/snaps/snap-authorship-header/index.js
Normal file
1
ui/components/app/snaps/snap-authorship-header/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './snap-authorship-header';
|
@ -0,0 +1,96 @@
|
|||||||
|
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,
|
||||||
|
FLEX_DIRECTION,
|
||||||
|
TextVariant,
|
||||||
|
AlignItems,
|
||||||
|
DISPLAY,
|
||||||
|
BLOCK_SIZES,
|
||||||
|
FontWeight,
|
||||||
|
} from '../../../../helpers/constants/design-system';
|
||||||
|
import {
|
||||||
|
getSnapName,
|
||||||
|
removeSnapIdPrefix,
|
||||||
|
} from '../../../../helpers/utils/util';
|
||||||
|
|
||||||
|
import { Text } from '../../../component-library';
|
||||||
|
import { getTargetSubjectMetadata } from '../../../../selectors';
|
||||||
|
import SnapAvatar from '../snap-avatar';
|
||||||
|
import SnapVersion from '../snap-version/snap-version';
|
||||||
|
|
||||||
|
const SnapAuthorshipHeader = ({ 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-header', className)}
|
||||||
|
backgroundColor={BackgroundColor.backgroundDefault}
|
||||||
|
width={BLOCK_SIZES.FULL}
|
||||||
|
alignItems={AlignItems.center}
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
padding={4}
|
||||||
|
style={{
|
||||||
|
boxShadow: 'var(--shadow-size-lg) var(--color-shadow-default)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box>
|
||||||
|
<SnapAvatar snapId={snapId} />
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
marginLeft={4}
|
||||||
|
marginRight={4}
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
style={{ overflow: 'hidden' }}
|
||||||
|
>
|
||||||
|
<Text ellipsis fontWeight={FontWeight.Medium}>
|
||||||
|
{friendlyName}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
ellipsis
|
||||||
|
variant={TextVariant.bodySm}
|
||||||
|
color={TextColor.textAlternative}
|
||||||
|
>
|
||||||
|
{packageName}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box marginLeft="auto">
|
||||||
|
<SnapVersion version={subjectMetadata?.version} url={url} />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SnapAuthorshipHeader.propTypes = {
|
||||||
|
/**
|
||||||
|
* The id of the snap
|
||||||
|
*/
|
||||||
|
snapId: PropTypes.string,
|
||||||
|
/**
|
||||||
|
* The className of the SnapAuthorship
|
||||||
|
*/
|
||||||
|
className: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SnapAuthorshipHeader;
|
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import SnapAuthorshipHeader from './snap-authorship-header';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/App/Snaps/SnapAuthorshipHeader',
|
||||||
|
|
||||||
|
component: SnapAuthorshipHeader,
|
||||||
|
argTypes: {
|
||||||
|
snapId: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = (args) => <SnapAuthorshipHeader {...args} />;
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default';
|
||||||
|
|
||||||
|
DefaultStory.args = {
|
||||||
|
snapId: 'npm:@metamask/test-snap-bip44',
|
||||||
|
};
|
@ -1 +0,0 @@
|
|||||||
export { default } from './snap-authorship';
|
|
@ -1,21 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import SnapAuthorship from '.';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'Components/App/Snaps/SnapAuthorship',
|
|
||||||
|
|
||||||
component: SnapAuthorship,
|
|
||||||
argTypes: {
|
|
||||||
snapId: {
|
|
||||||
control: 'text',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DefaultStory = (args) => <SnapAuthorship {...args} />;
|
|
||||||
|
|
||||||
DefaultStory.storyName = 'Default';
|
|
||||||
|
|
||||||
DefaultStory.args = {
|
|
||||||
snapId: 'npm:@metamask/test-snap-bip44',
|
|
||||||
};
|
|
@ -9,6 +9,7 @@ import {
|
|||||||
DISPLAY,
|
DISPLAY,
|
||||||
JustifyContent,
|
JustifyContent,
|
||||||
Size,
|
Size,
|
||||||
|
BackgroundColor,
|
||||||
} from '../../../../helpers/constants/design-system';
|
} from '../../../../helpers/constants/design-system';
|
||||||
import { getSnapName } from '../../../../helpers/utils/util';
|
import { getSnapName } from '../../../../helpers/utils/util';
|
||||||
import {
|
import {
|
||||||
@ -39,10 +40,12 @@ const SnapAvatar = ({ snapId, className }) => {
|
|||||||
badge={
|
badge={
|
||||||
<AvatarIcon
|
<AvatarIcon
|
||||||
iconName={IconName.Snaps}
|
iconName={IconName.Snaps}
|
||||||
size={IconSize.Xs}
|
size={IconSize.Sm}
|
||||||
backgroundColor={IconColor.infoDefault}
|
backgroundColor={IconColor.infoDefault}
|
||||||
|
borderColor={BackgroundColor.backgroundDefault}
|
||||||
|
borderWidth={2}
|
||||||
iconProps={{
|
iconProps={{
|
||||||
size: IconSize.Xs,
|
size: IconSize.Sm,
|
||||||
color: IconColor.infoInverse,
|
color: IconColor.infoInverse,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -50,10 +53,10 @@ const SnapAvatar = ({ snapId, className }) => {
|
|||||||
position={BadgeWrapperPosition.bottomRight}
|
position={BadgeWrapperPosition.bottomRight}
|
||||||
>
|
>
|
||||||
{iconUrl ? (
|
{iconUrl ? (
|
||||||
<AvatarFavicon size={Size.MD} src={iconUrl} name={friendlyName} />
|
<AvatarFavicon size={Size.LG} src={iconUrl} name={friendlyName} />
|
||||||
) : (
|
) : (
|
||||||
<AvatarBase
|
<AvatarBase
|
||||||
size={Size.MD}
|
size={Size.LG}
|
||||||
display={DISPLAY.FLEX}
|
display={DISPLAY.FLEX}
|
||||||
alignItems={AlignItems.center}
|
alignItems={AlignItems.center}
|
||||||
justifyContent={JustifyContent.center}
|
justifyContent={JustifyContent.center}
|
||||||
|
@ -47,19 +47,20 @@ export const SnapDelineator = ({
|
|||||||
>
|
>
|
||||||
<AvatarIcon
|
<AvatarIcon
|
||||||
iconName={IconName.Snaps}
|
iconName={IconName.Snaps}
|
||||||
size={IconSize.Xs}
|
size={IconSize.Sm}
|
||||||
backgroundColor={
|
backgroundColor={
|
||||||
isError ? IconColor.errorDefault : IconColor.infoDefault
|
isError ? IconColor.errorDefault : IconColor.infoDefault
|
||||||
}
|
}
|
||||||
margin={1}
|
borderColor={BackgroundColor.backgroundDefault}
|
||||||
|
borderWidth={2}
|
||||||
iconProps={{
|
iconProps={{
|
||||||
size: IconSize.Xs,
|
size: IconSize.Sm,
|
||||||
color: IconColor.infoInverse,
|
color: IconColor.infoInverse,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Text
|
<Text
|
||||||
variant={TextVariant.bodySm}
|
variant={TextVariant.bodySm}
|
||||||
color={isError ? TextColor.errorDefault : TextColor.default}
|
color={isError ? TextColor.errorDefault : TextColor.textAlternative}
|
||||||
className="snap-delineator__header__text"
|
className="snap-delineator__header__text"
|
||||||
marginLeft={1}
|
marginLeft={1}
|
||||||
marginTop={0}
|
marginTop={0}
|
||||||
|
44
ui/hooks/useScrollRequired.js
Normal file
44
ui/hooks/useScrollRequired.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility hook for requiring users to scroll through content.
|
||||||
|
* Returns an object containing state and helpers to accomplish this.
|
||||||
|
*
|
||||||
|
* The hook expects both the `ref` and the `onScroll` handler to be passed to the scrolling element.
|
||||||
|
*
|
||||||
|
* @param dependencies - Any optional hook dependencies for updating the scroll state.
|
||||||
|
* @returns Flags for isScrollable and isScrollToBottom, a ref to use for the scrolling content, a scrollToBottom function and a onScroll handler.
|
||||||
|
*/
|
||||||
|
export const useScrollRequired = (dependencies = []) => {
|
||||||
|
const ref = useRef();
|
||||||
|
const [isScrollableState, setIsScrollable] = useState(false);
|
||||||
|
const [isScrolledToBottomState, setIsScrolledToBottom] = useState(false);
|
||||||
|
|
||||||
|
const update = () => {
|
||||||
|
const isScrollable =
|
||||||
|
ref.current && ref.current.scrollHeight > ref.current.clientHeight;
|
||||||
|
const isScrolledToBottom = isScrollable
|
||||||
|
? Math.round(ref.current.scrollTop) + ref.current.offsetHeight >=
|
||||||
|
ref.current.scrollHeight
|
||||||
|
: true;
|
||||||
|
setIsScrollable(isScrollable);
|
||||||
|
setIsScrolledToBottom(isScrolledToBottom);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(update, [ref, ...dependencies]);
|
||||||
|
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
if (ref.current) {
|
||||||
|
ref.current.scrollTo(0, ref.current.scrollHeight);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
isScrollable: isScrollableState,
|
||||||
|
isScrolledToBottom: isScrolledToBottomState,
|
||||||
|
scrollToBottom,
|
||||||
|
ref,
|
||||||
|
onScroll: debounce(update, 25),
|
||||||
|
};
|
||||||
|
};
|
@ -12,12 +12,14 @@ export default function ConfirmationFooter({
|
|||||||
alerts,
|
alerts,
|
||||||
loading,
|
loading,
|
||||||
submitAlerts,
|
submitAlerts,
|
||||||
|
actionsStyle,
|
||||||
|
style,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="confirmation-footer">
|
<div className="confirmation-footer" style={style}>
|
||||||
{alerts}
|
{alerts}
|
||||||
{submitAlerts}
|
{submitAlerts}
|
||||||
<div className="confirmation-footer__actions">
|
<div className="confirmation-footer__actions" style={actionsStyle}>
|
||||||
{onCancel ? (
|
{onCancel ? (
|
||||||
<Button type="secondary" onClick={onCancel}>
|
<Button type="secondary" onClick={onCancel}>
|
||||||
{cancelText}
|
{cancelText}
|
||||||
@ -47,4 +49,6 @@ ConfirmationFooter.propTypes = {
|
|||||||
loadingText: PropTypes.string,
|
loadingText: PropTypes.string,
|
||||||
loading: PropTypes.bool,
|
loading: PropTypes.bool,
|
||||||
submitAlerts: PropTypes.node,
|
submitAlerts: PropTypes.node,
|
||||||
|
style: PropTypes.object,
|
||||||
|
actionsStyle: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
@ -38,7 +38,7 @@ import Callout from '../../components/ui/callout';
|
|||||||
import SiteOrigin from '../../components/ui/site-origin';
|
import SiteOrigin from '../../components/ui/site-origin';
|
||||||
import { Icon, IconName } from '../../components/component-library';
|
import { Icon, IconName } from '../../components/component-library';
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
import SnapAuthorship from '../../components/app/snaps/snap-authorship';
|
import SnapAuthorshipHeader from '../../components/app/snaps/snap-authorship-header';
|
||||||
import { getSnapName } from '../../helpers/utils/util';
|
import { getSnapName } from '../../helpers/utils/util';
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
import ConfirmationFooter from './components/confirmation-footer';
|
import ConfirmationFooter from './components/confirmation-footer';
|
||||||
@ -372,13 +372,7 @@ export default function ConfirmationPage({
|
|||||||
{
|
{
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
isSnapDialog && (
|
isSnapDialog && (
|
||||||
<Box
|
<SnapAuthorshipHeader snapId={pendingConfirmation?.origin} />
|
||||||
alignItems="center"
|
|
||||||
margin={4}
|
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
|
||||||
>
|
|
||||||
<SnapAuthorship snapId={pendingConfirmation?.origin} />
|
|
||||||
</Box>
|
|
||||||
)
|
)
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
}
|
}
|
||||||
@ -412,6 +406,22 @@ export default function ConfirmationPage({
|
|||||||
</Callout>
|
</Callout>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
|
style={
|
||||||
|
isSnapDialog
|
||||||
|
? {
|
||||||
|
boxShadow: 'var(--shadow-size-lg) var(--color-shadow-default)',
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
actionsStyle={
|
||||||
|
isSnapDialog
|
||||||
|
? {
|
||||||
|
borderTop: 0,
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onCancel={templatedValues.onCancel}
|
onCancel={templatedValues.onCancel}
|
||||||
submitText={templatedValues.submitText}
|
submitText={templatedValues.submitText}
|
||||||
|
@ -14,6 +14,7 @@ function getValues(pendingApproval, t, actions) {
|
|||||||
element: 'Box',
|
element: 'Box',
|
||||||
key: 'snap-dialog-content-wrapper',
|
key: 'snap-dialog-content-wrapper',
|
||||||
props: {
|
props: {
|
||||||
|
marginTop: 4,
|
||||||
marginLeft: 4,
|
marginLeft: 4,
|
||||||
marginRight: 4,
|
marginRight: 4,
|
||||||
},
|
},
|
||||||
@ -29,7 +30,7 @@ function getValues(pendingApproval, t, actions) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
submitText: t('ok'),
|
submitText: t('ok').toUpperCase(),
|
||||||
onSubmit: () => actions.resolvePendingApproval(pendingApproval.id, null),
|
onSubmit: () => actions.resolvePendingApproval(pendingApproval.id, null),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ function getValues(pendingApproval, t, actions) {
|
|||||||
element: 'Box',
|
element: 'Box',
|
||||||
key: 'snap-dialog-content-wrapper',
|
key: 'snap-dialog-content-wrapper',
|
||||||
props: {
|
props: {
|
||||||
|
marginTop: 4,
|
||||||
marginLeft: 4,
|
marginLeft: 4,
|
||||||
marginRight: 4,
|
marginRight: 4,
|
||||||
},
|
},
|
||||||
|
@ -15,6 +15,7 @@ function getValues(pendingApproval, t, actions, _history, setInputState) {
|
|||||||
element: 'Box',
|
element: 'Box',
|
||||||
key: 'snap-dialog-content-wrapper',
|
key: 'snap-dialog-content-wrapper',
|
||||||
props: {
|
props: {
|
||||||
|
marginTop: 4,
|
||||||
marginLeft: 4,
|
marginLeft: 4,
|
||||||
marginRight: 4,
|
marginRight: 4,
|
||||||
},
|
},
|
||||||
|
@ -49,6 +49,7 @@ export default class PermissionConnect extends Component {
|
|||||||
setSnapsInstallPrivacyWarningShownStatus: PropTypes.func.isRequired,
|
setSnapsInstallPrivacyWarningShownStatus: PropTypes.func.isRequired,
|
||||||
snapsInstallPrivacyWarningShown: PropTypes.bool.isRequired,
|
snapsInstallPrivacyWarningShown: PropTypes.bool.isRequired,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
hideTopBar: PropTypes.bool,
|
||||||
totalPages: PropTypes.string.isRequired,
|
totalPages: PropTypes.string.isRequired,
|
||||||
page: PropTypes.string.isRequired,
|
page: PropTypes.string.isRequired,
|
||||||
targetSubjectMetadata: PropTypes.shape({
|
targetSubjectMetadata: PropTypes.shape({
|
||||||
@ -296,6 +297,7 @@ export default class PermissionConnect extends Component {
|
|||||||
permissionsRequestId,
|
permissionsRequestId,
|
||||||
connectPath,
|
connectPath,
|
||||||
confirmPermissionPath,
|
confirmPermissionPath,
|
||||||
|
hideTopBar,
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
snapInstallPath,
|
snapInstallPath,
|
||||||
snapUpdatePath,
|
snapUpdatePath,
|
||||||
@ -318,7 +320,7 @@ export default class PermissionConnect extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="permissions-connect">
|
<div className="permissions-connect">
|
||||||
{this.renderTopBar()}
|
{!hideTopBar && this.renderTopBar()}
|
||||||
{redirecting && permissionsApproved ? (
|
{redirecting && permissionsApproved ? (
|
||||||
<PermissionsRedirect subjectMetadata={targetSubjectMetadata} />
|
<PermissionsRedirect subjectMetadata={targetSubjectMetadata} />
|
||||||
) : (
|
) : (
|
||||||
|
@ -78,8 +78,6 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
const requestType = getRequestType(state, permissionsRequestId);
|
const requestType = getRequestType(state, permissionsRequestId);
|
||||||
|
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
const isSnap = targetSubjectMetadata.subjectType === SubjectType.Snap;
|
|
||||||
|
|
||||||
const requestState = getRequestState(state, permissionsRequestId);
|
const requestState = getRequestState(state, permissionsRequestId);
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
@ -101,11 +99,15 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
const snapInstallPath = `${CONNECT_ROUTE}/${permissionsRequestId}${CONNECT_SNAP_INSTALL_ROUTE}`;
|
const snapInstallPath = `${CONNECT_ROUTE}/${permissionsRequestId}${CONNECT_SNAP_INSTALL_ROUTE}`;
|
||||||
const snapUpdatePath = `${CONNECT_ROUTE}/${permissionsRequestId}${CONNECT_SNAP_UPDATE_ROUTE}`;
|
const snapUpdatePath = `${CONNECT_ROUTE}/${permissionsRequestId}${CONNECT_SNAP_UPDATE_ROUTE}`;
|
||||||
const snapResultPath = `${CONNECT_ROUTE}/${permissionsRequestId}${CONNECT_SNAP_RESULT_ROUTE}`;
|
const snapResultPath = `${CONNECT_ROUTE}/${permissionsRequestId}${CONNECT_SNAP_RESULT_ROUTE}`;
|
||||||
|
const isSnapInstallOrUpdateOrResult =
|
||||||
|
pathname === snapInstallPath ||
|
||||||
|
pathname === snapUpdatePath ||
|
||||||
|
pathname === snapResultPath;
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
let totalPages = 1 + isRequestingAccounts;
|
let totalPages = 1 + isRequestingAccounts;
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
totalPages += isSnap;
|
totalPages += isSnapInstallOrUpdateOrResult;
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
totalPages = totalPages.toString();
|
totalPages = totalPages.toString();
|
||||||
|
|
||||||
@ -115,11 +117,7 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
} else if (pathname === confirmPermissionPath) {
|
} else if (pathname === confirmPermissionPath) {
|
||||||
page = isRequestingAccounts ? '2' : '1';
|
page = isRequestingAccounts ? '2' : '1';
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
} else if (
|
} else if (isSnapInstallOrUpdateOrResult) {
|
||||||
pathname === snapInstallPath ||
|
|
||||||
pathname === snapUpdatePath ||
|
|
||||||
pathname === snapResultPath
|
|
||||||
) {
|
|
||||||
page = isRequestingAccounts ? '3' : '2';
|
page = isRequestingAccounts ? '3' : '2';
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
} else {
|
} else {
|
||||||
@ -134,7 +132,7 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
snapUpdatePath,
|
snapUpdatePath,
|
||||||
snapResultPath,
|
snapResultPath,
|
||||||
requestState,
|
requestState,
|
||||||
isSnap,
|
hideTopBar: isSnapInstallOrUpdateOrResult,
|
||||||
snapsInstallPrivacyWarningShown: getSnapsInstallPrivacyWarningShown(state),
|
snapsInstallPrivacyWarningShown: getSnapsInstallPrivacyWarningShown(state),
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
permissionsRequest,
|
permissionsRequest,
|
||||||
|
@ -2,15 +2,22 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
&__loader-container {
|
&__loader-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__scroll-button {
|
||||||
|
position: absolute;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
.page-container__footer {
|
.page-container__footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 12px;
|
border-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import SnapInstallWarning from '../../../../components/app/snaps/snap-install-wa
|
|||||||
import Box from '../../../../components/ui/box/box';
|
import Box from '../../../../components/ui/box/box';
|
||||||
import {
|
import {
|
||||||
AlignItems,
|
AlignItems,
|
||||||
|
BackgroundColor,
|
||||||
BLOCK_SIZES,
|
BLOCK_SIZES,
|
||||||
BorderStyle,
|
BorderStyle,
|
||||||
FLEX_DIRECTION,
|
FLEX_DIRECTION,
|
||||||
@ -17,11 +18,16 @@ import {
|
|||||||
import { getSnapInstallWarnings } from '../util';
|
import { getSnapInstallWarnings } from '../util';
|
||||||
import PulseLoader from '../../../../components/ui/pulse-loader/pulse-loader';
|
import PulseLoader from '../../../../components/ui/pulse-loader/pulse-loader';
|
||||||
import InstallError from '../../../../components/app/snaps/install-error/install-error';
|
import InstallError from '../../../../components/app/snaps/install-error/install-error';
|
||||||
import SnapAuthorship from '../../../../components/app/snaps/snap-authorship';
|
import SnapAuthorshipHeader from '../../../../components/app/snaps/snap-authorship-header';
|
||||||
import { Text, ValidTag } from '../../../../components/component-library';
|
import {
|
||||||
import { useOriginMetadata } from '../../../../hooks/useOriginMetadata';
|
AvatarIcon,
|
||||||
|
IconName,
|
||||||
|
Text,
|
||||||
|
ValidTag,
|
||||||
|
} from '../../../../components/component-library';
|
||||||
import { getSnapName } from '../../../../helpers/utils/util';
|
import { getSnapName } from '../../../../helpers/utils/util';
|
||||||
import SnapPermissionsList from '../../../../components/app/snaps/snap-permissions-list';
|
import SnapPermissionsList from '../../../../components/app/snaps/snap-permissions-list';
|
||||||
|
import { useScrollRequired } from '../../../../hooks/useScrollRequired';
|
||||||
|
|
||||||
export default function SnapInstall({
|
export default function SnapInstall({
|
||||||
request,
|
request,
|
||||||
@ -33,7 +39,9 @@ export default function SnapInstall({
|
|||||||
const t = useI18nContext();
|
const t = useI18nContext();
|
||||||
|
|
||||||
const [isShowingWarning, setIsShowingWarning] = useState(false);
|
const [isShowingWarning, setIsShowingWarning] = useState(false);
|
||||||
const originMetadata = useOriginMetadata(request.metadata?.dappOrigin) || {};
|
|
||||||
|
const { isScrollable, isScrolledToBottom, scrollToBottom, ref, onScroll } =
|
||||||
|
useScrollRequired([requestState]);
|
||||||
|
|
||||||
const onCancel = useCallback(
|
const onCancel = useCallback(
|
||||||
() => rejectSnapInstall(request.metadata.id),
|
() => rejectSnapInstall(request.metadata.id),
|
||||||
@ -49,13 +57,6 @@ export default function SnapInstall({
|
|||||||
|
|
||||||
const isLoading = requestState.loading;
|
const isLoading = requestState.loading;
|
||||||
|
|
||||||
const hasPermissions =
|
|
||||||
!hasError &&
|
|
||||||
requestState?.permissions &&
|
|
||||||
Object.keys(requestState.permissions).length > 0;
|
|
||||||
|
|
||||||
const isEmpty = !isLoading && !hasError && !hasPermissions;
|
|
||||||
|
|
||||||
const warnings = getSnapInstallWarnings(
|
const warnings = getSnapInstallWarnings(
|
||||||
requestState?.permissions ?? {},
|
requestState?.permissions ?? {},
|
||||||
targetSubjectMetadata,
|
targetSubjectMetadata,
|
||||||
@ -84,25 +85,16 @@ export default function SnapInstall({
|
|||||||
borderStyle={BorderStyle.none}
|
borderStyle={BorderStyle.none}
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
>
|
>
|
||||||
|
<SnapAuthorshipHeader snapId={targetSubjectMetadata.origin} />
|
||||||
<Box
|
<Box
|
||||||
className="snap-install__header"
|
ref={ref}
|
||||||
alignItems={AlignItems.center}
|
onScroll={onScroll}
|
||||||
paddingLeft={4}
|
className="snap-install__content"
|
||||||
paddingRight={4}
|
style={{
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
overflowY: 'auto',
|
||||||
|
flex: !isLoading && '1',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<SnapAuthorship snapId={targetSubjectMetadata.origin} />
|
|
||||||
{!isLoading && !hasError && (
|
|
||||||
<Text
|
|
||||||
variant={TextVariant.headingLg}
|
|
||||||
paddingTop={4}
|
|
||||||
paddingBottom={2}
|
|
||||||
>
|
|
||||||
{t('snapInstall')}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
<Box className="snap-install__content">
|
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<Box
|
<Box
|
||||||
className="snap-install__content__loader-container"
|
className="snap-install__content__loader-container"
|
||||||
@ -116,8 +108,16 @@ export default function SnapInstall({
|
|||||||
{hasError && (
|
{hasError && (
|
||||||
<InstallError error={requestState.error} title={t('requestFailed')} />
|
<InstallError error={requestState.error} title={t('requestFailed')} />
|
||||||
)}
|
)}
|
||||||
{hasPermissions && (
|
{!hasError && !isLoading && (
|
||||||
<>
|
<>
|
||||||
|
<Text
|
||||||
|
variant={TextVariant.headingLg}
|
||||||
|
paddingTop={4}
|
||||||
|
paddingBottom={2}
|
||||||
|
textAlign="center"
|
||||||
|
>
|
||||||
|
{t('snapInstall')}
|
||||||
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
className="snap-install__content__permission-description"
|
className="snap-install__content__permission-description"
|
||||||
paddingBottom={4}
|
paddingBottom={4}
|
||||||
@ -125,7 +125,7 @@ export default function SnapInstall({
|
|||||||
paddingRight={4}
|
paddingRight={4}
|
||||||
textAlign={TEXT_ALIGN.CENTER}
|
textAlign={TEXT_ALIGN.CENTER}
|
||||||
>
|
>
|
||||||
{t('snapInstallRequestsPermission', [
|
{t('snapInstallRequest', [
|
||||||
<Text
|
<Text
|
||||||
as={ValidTag.Span}
|
as={ValidTag.Span}
|
||||||
key="2"
|
key="2"
|
||||||
@ -140,40 +140,38 @@ export default function SnapInstall({
|
|||||||
permissions={requestState.permissions || {}}
|
permissions={requestState.permissions || {}}
|
||||||
targetSubjectMetadata={targetSubjectMetadata}
|
targetSubjectMetadata={targetSubjectMetadata}
|
||||||
/>
|
/>
|
||||||
|
{isScrollable && !isScrolledToBottom ? (
|
||||||
|
<AvatarIcon
|
||||||
|
className="snap-install__scroll-button"
|
||||||
|
data-testid="snap-install-scroll"
|
||||||
|
iconName={IconName.Arrow2Down}
|
||||||
|
backgroundColor={BackgroundColor.infoDefault}
|
||||||
|
color={BackgroundColor.backgroundDefault}
|
||||||
|
onClick={scrollToBottom}
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{isEmpty && (
|
|
||||||
<Box
|
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
|
||||||
height={BLOCK_SIZES.FULL}
|
|
||||||
alignItems={AlignItems.center}
|
|
||||||
justifyContent={JustifyContent.center}
|
|
||||||
>
|
|
||||||
<Text textAlign={TEXT_ALIGN.CENTER}>
|
|
||||||
{t('snapInstallRequest', [
|
|
||||||
<b key="1">{originMetadata?.hostname}</b>,
|
|
||||||
<b key="2">{snapName}</b>,
|
|
||||||
])}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
className="snap-install__footer"
|
className="snap-install__footer"
|
||||||
alignItems={AlignItems.center}
|
alignItems={AlignItems.center}
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
style={{
|
||||||
|
boxShadow: 'var(--shadow-size-lg) var(--color-shadow-default)',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<PageContainerFooter
|
<PageContainerFooter
|
||||||
cancelButtonType="default"
|
cancelButtonType="default"
|
||||||
hideCancel={hasError}
|
hideCancel={hasError}
|
||||||
disabled={isLoading}
|
disabled={
|
||||||
|
isLoading || (!hasError && isScrollable && !isScrolledToBottom)
|
||||||
|
}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
cancelText={t('cancel')}
|
cancelText={t('cancel')}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
submitText={t(
|
submitText={t(hasError ? 'ok' : 'install')}
|
||||||
// eslint-disable-next-line no-nested-ternary
|
|
||||||
hasError ? 'ok' : 'install',
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
{isShowingWarning && (
|
{isShowingWarning && (
|
||||||
|
@ -0,0 +1,122 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
|
import configureStore from '../../../../store/store';
|
||||||
|
import mockState from '../../../../../test/data/mock-state.json';
|
||||||
|
import SnapInstall from '.';
|
||||||
|
|
||||||
|
const store = configureStore(mockState);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Pages/Snaps/SnapInstall',
|
||||||
|
|
||||||
|
component: SnapInstall,
|
||||||
|
argTypes: {},
|
||||||
|
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = (args) => <SnapInstall {...args} />;
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default';
|
||||||
|
|
||||||
|
DefaultStory.args = {
|
||||||
|
request: {
|
||||||
|
metadata: {
|
||||||
|
id: 'foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
requestState: {
|
||||||
|
loading: false,
|
||||||
|
permissions: {
|
||||||
|
'endowment:rpc': {
|
||||||
|
caveats: [
|
||||||
|
{
|
||||||
|
type: 'rpcOrigin',
|
||||||
|
value: {
|
||||||
|
dapps: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
snap_dialog: {},
|
||||||
|
snap_getBip44Entropy: {
|
||||||
|
caveats: [
|
||||||
|
{
|
||||||
|
type: 'permittedCoinTypes',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
coinType: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetSubjectMetadata: {
|
||||||
|
origin: 'npm:@metamask/test-snap-bip44',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LoadingStory = (args) => <SnapInstall {...args} />;
|
||||||
|
|
||||||
|
LoadingStory.storyName = 'Loading';
|
||||||
|
|
||||||
|
LoadingStory.args = {
|
||||||
|
request: {
|
||||||
|
metadata: {
|
||||||
|
id: 'foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
requestState: {
|
||||||
|
loading: true,
|
||||||
|
},
|
||||||
|
targetSubjectMetadata: {
|
||||||
|
origin: 'npm:@metamask/test-snap-bip44',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ScrollingStory = (args) => <SnapInstall {...args} />;
|
||||||
|
|
||||||
|
ScrollingStory.storyName = 'Scrolling';
|
||||||
|
|
||||||
|
ScrollingStory.args = {
|
||||||
|
request: {
|
||||||
|
metadata: {
|
||||||
|
id: 'foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
requestState: {
|
||||||
|
loading: false,
|
||||||
|
permissions: {
|
||||||
|
'endowment:rpc': {
|
||||||
|
caveats: [
|
||||||
|
{
|
||||||
|
type: 'rpcOrigin',
|
||||||
|
value: {
|
||||||
|
dapps: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'endowment:network-access': {},
|
||||||
|
snap_notify: {},
|
||||||
|
snap_dialog: {},
|
||||||
|
snap_getBip44Entropy: {
|
||||||
|
caveats: [
|
||||||
|
{
|
||||||
|
type: 'permittedCoinTypes',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
coinType: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetSubjectMetadata: {
|
||||||
|
origin: 'npm:@metamask/test-snap-bip44',
|
||||||
|
},
|
||||||
|
};
|
@ -1,16 +1,8 @@
|
|||||||
.snap-result {
|
.snap-result {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
||||||
&__header {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
&__loader-container {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-container__footer {
|
.page-container__footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 12px;
|
border-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import {
|
|||||||
} from '../../../../components/component-library';
|
} from '../../../../components/component-library';
|
||||||
import PulseLoader from '../../../../components/ui/pulse-loader/pulse-loader';
|
import PulseLoader from '../../../../components/ui/pulse-loader/pulse-loader';
|
||||||
import InstallError from '../../../../components/app/snaps/install-error/install-error';
|
import InstallError from '../../../../components/app/snaps/install-error/install-error';
|
||||||
import SnapAuthorship from '../../../../components/app/snaps/snap-authorship';
|
import SnapAuthorshipHeader from '../../../../components/app/snaps/snap-authorship-header';
|
||||||
import { getSnapName } from '../../../../helpers/utils/util';
|
import { getSnapName } from '../../../../helpers/utils/util';
|
||||||
|
|
||||||
export default function SnapResult({
|
export default function SnapResult({
|
||||||
@ -140,17 +140,20 @@ export default function SnapResult({
|
|||||||
borderStyle={BorderStyle.none}
|
borderStyle={BorderStyle.none}
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
>
|
>
|
||||||
|
<SnapAuthorshipHeader snapId={targetSubjectMetadata.origin} />
|
||||||
<Box
|
<Box
|
||||||
className="snap-result__header"
|
className="snap-result__content"
|
||||||
paddingLeft={4}
|
paddingLeft={4}
|
||||||
paddingRight={4}
|
paddingRight={4}
|
||||||
alignItems={AlignItems.center}
|
alignItems={AlignItems.center}
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
style={{
|
||||||
|
overflowY: 'auto',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<SnapAuthorship snapId={targetSubjectMetadata.origin} />
|
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<Box
|
<Box
|
||||||
className="snap-result__header__loader-container"
|
className="snap-result__content__loader-container"
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
alignItems={AlignItems.center}
|
alignItems={AlignItems.center}
|
||||||
justifyContent={JustifyContent.center}
|
justifyContent={JustifyContent.center}
|
||||||
@ -167,6 +170,9 @@ export default function SnapResult({
|
|||||||
className="snap-result__footer"
|
className="snap-result__footer"
|
||||||
alignItems={AlignItems.center}
|
alignItems={AlignItems.center}
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
style={{
|
||||||
|
boxShadow: 'var(--shadow-size-lg) var(--color-shadow-default)',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<PageContainerFooter
|
<PageContainerFooter
|
||||||
hideCancel
|
hideCancel
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import SnapResult from '.';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Pages/Snaps/SnapResult',
|
||||||
|
|
||||||
|
component: SnapResult,
|
||||||
|
argTypes: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = (args) => <SnapResult {...args} />;
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default';
|
||||||
|
|
||||||
|
DefaultStory.args = {
|
||||||
|
request: {
|
||||||
|
metadata: {
|
||||||
|
id: 'foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
requestState: {
|
||||||
|
loading: false,
|
||||||
|
},
|
||||||
|
targetSubjectMetadata: {
|
||||||
|
origin: 'npm:@metamask/test-snap-bip44',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LoadingStory = (args) => <SnapResult {...args} />;
|
||||||
|
|
||||||
|
LoadingStory.storyName = 'Loading';
|
||||||
|
|
||||||
|
LoadingStory.args = {
|
||||||
|
request: {
|
||||||
|
metadata: {
|
||||||
|
id: 'foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
requestState: {
|
||||||
|
loading: true,
|
||||||
|
},
|
||||||
|
targetSubjectMetadata: {
|
||||||
|
origin: 'npm:@metamask/test-snap-bip44',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ErrorStory = (args) => <SnapResult {...args} />;
|
||||||
|
|
||||||
|
ErrorStory.storyName = 'Error';
|
||||||
|
|
||||||
|
ErrorStory.args = {
|
||||||
|
request: {
|
||||||
|
metadata: {
|
||||||
|
id: 'foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
requestState: {
|
||||||
|
loading: false,
|
||||||
|
error: 'foo',
|
||||||
|
},
|
||||||
|
targetSubjectMetadata: {
|
||||||
|
origin: 'npm:@metamask/test-snap-bip44',
|
||||||
|
},
|
||||||
|
};
|
@ -2,20 +2,23 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
&__loader-container {
|
&__loader-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__permission-description {
|
&__scroll-button {
|
||||||
border-bottom: 1px solid var(--color-border-default);
|
position: absolute;
|
||||||
}
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 90px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-container__footer {
|
.page-container__footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 12px;
|
border-top: 0;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
|
@ -6,9 +6,11 @@ import SnapInstallWarning from '../../../../components/app/snaps/snap-install-wa
|
|||||||
import Box from '../../../../components/ui/box/box';
|
import Box from '../../../../components/ui/box/box';
|
||||||
import {
|
import {
|
||||||
AlignItems,
|
AlignItems,
|
||||||
|
BackgroundColor,
|
||||||
BLOCK_SIZES,
|
BLOCK_SIZES,
|
||||||
BorderStyle,
|
BorderStyle,
|
||||||
FLEX_DIRECTION,
|
FLEX_DIRECTION,
|
||||||
|
FontWeight,
|
||||||
JustifyContent,
|
JustifyContent,
|
||||||
TextVariant,
|
TextVariant,
|
||||||
TEXT_ALIGN,
|
TEXT_ALIGN,
|
||||||
@ -18,10 +20,16 @@ import UpdateSnapPermissionList from '../../../../components/app/snaps/update-sn
|
|||||||
import { getSnapInstallWarnings } from '../util';
|
import { getSnapInstallWarnings } from '../util';
|
||||||
import PulseLoader from '../../../../components/ui/pulse-loader/pulse-loader';
|
import PulseLoader from '../../../../components/ui/pulse-loader/pulse-loader';
|
||||||
import InstallError from '../../../../components/app/snaps/install-error/install-error';
|
import InstallError from '../../../../components/app/snaps/install-error/install-error';
|
||||||
import SnapAuthorship from '../../../../components/app/snaps/snap-authorship';
|
import SnapAuthorshipHeader from '../../../../components/app/snaps/snap-authorship-header';
|
||||||
import { Text } from '../../../../components/component-library';
|
import {
|
||||||
|
AvatarIcon,
|
||||||
|
IconName,
|
||||||
|
Text,
|
||||||
|
ValidTag,
|
||||||
|
} from '../../../../components/component-library';
|
||||||
import { useOriginMetadata } from '../../../../hooks/useOriginMetadata';
|
import { useOriginMetadata } from '../../../../hooks/useOriginMetadata';
|
||||||
import { getSnapName } from '../../../../helpers/utils/util';
|
import { getSnapName } from '../../../../helpers/utils/util';
|
||||||
|
import { useScrollRequired } from '../../../../hooks/useScrollRequired';
|
||||||
|
|
||||||
export default function SnapUpdate({
|
export default function SnapUpdate({
|
||||||
request,
|
request,
|
||||||
@ -35,6 +43,9 @@ export default function SnapUpdate({
|
|||||||
const [isShowingWarning, setIsShowingWarning] = useState(false);
|
const [isShowingWarning, setIsShowingWarning] = useState(false);
|
||||||
const originMetadata = useOriginMetadata(request.metadata?.dappOrigin) || {};
|
const originMetadata = useOriginMetadata(request.metadata?.dappOrigin) || {};
|
||||||
|
|
||||||
|
const { isScrollable, isScrolledToBottom, scrollToBottom, ref, onScroll } =
|
||||||
|
useScrollRequired([requestState]);
|
||||||
|
|
||||||
const onCancel = useCallback(
|
const onCancel = useCallback(
|
||||||
() => rejectSnapUpdate(request.metadata.id),
|
() => rejectSnapUpdate(request.metadata.id),
|
||||||
[request, rejectSnapUpdate],
|
[request, rejectSnapUpdate],
|
||||||
@ -81,25 +92,26 @@ export default function SnapUpdate({
|
|||||||
borderStyle={BorderStyle.none}
|
borderStyle={BorderStyle.none}
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
>
|
>
|
||||||
|
<SnapAuthorshipHeader snapId={targetSubjectMetadata.origin} />
|
||||||
<Box
|
<Box
|
||||||
className="snap-update__header"
|
ref={ref}
|
||||||
paddingLeft={4}
|
onScroll={onScroll}
|
||||||
paddingRight={4}
|
className="snap-update__content"
|
||||||
alignItems={AlignItems.center}
|
style={{
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
overflowY: 'auto',
|
||||||
|
flex: !isLoading && '1',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<SnapAuthorship snapId={targetSubjectMetadata.origin} />
|
|
||||||
{!isLoading && !hasError && (
|
{!isLoading && !hasError && (
|
||||||
<Text
|
<Text
|
||||||
paddingBottom={4}
|
paddingBottom={4}
|
||||||
paddingTop={4}
|
paddingTop={4}
|
||||||
variant={TextVariant.headingLg}
|
variant={TextVariant.headingLg}
|
||||||
|
textAlign="center"
|
||||||
>
|
>
|
||||||
{t('snapUpdate')}
|
{t('snapUpdate')}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Box>
|
|
||||||
<Box className="snap-update__content">
|
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<Box
|
<Box
|
||||||
className="snap-update__content__loader-container"
|
className="snap-update__content__loader-container"
|
||||||
@ -123,9 +135,30 @@ export default function SnapUpdate({
|
|||||||
textAlign={TEXT_ALIGN.CENTER}
|
textAlign={TEXT_ALIGN.CENTER}
|
||||||
>
|
>
|
||||||
{t('snapUpdateRequest', [
|
{t('snapUpdateRequest', [
|
||||||
<b key="1">{originMetadata?.hostname}</b>,
|
<Text
|
||||||
<b key="2">{snapName}</b>,
|
as={ValidTag.Span}
|
||||||
<b key="3">v{newVersion}</b>,
|
key="1"
|
||||||
|
variant={TextVariant.bodyMd}
|
||||||
|
fontWeight={FontWeight.Medium}
|
||||||
|
>
|
||||||
|
{originMetadata?.hostname}
|
||||||
|
</Text>,
|
||||||
|
<Text
|
||||||
|
as={ValidTag.Span}
|
||||||
|
key="2"
|
||||||
|
variant={TextVariant.bodyMd}
|
||||||
|
fontWeight={FontWeight.Medium}
|
||||||
|
>
|
||||||
|
{snapName}
|
||||||
|
</Text>,
|
||||||
|
<Text
|
||||||
|
as={ValidTag.Span}
|
||||||
|
key="3"
|
||||||
|
variant={TextVariant.bodyMd}
|
||||||
|
fontWeight={FontWeight.Medium}
|
||||||
|
>
|
||||||
|
{newVersion}
|
||||||
|
</Text>,
|
||||||
])}
|
])}
|
||||||
</Text>
|
</Text>
|
||||||
<UpdateSnapPermissionList
|
<UpdateSnapPermissionList
|
||||||
@ -134,6 +167,17 @@ export default function SnapUpdate({
|
|||||||
newPermissions={newPermissions}
|
newPermissions={newPermissions}
|
||||||
targetSubjectMetadata={targetSubjectMetadata}
|
targetSubjectMetadata={targetSubjectMetadata}
|
||||||
/>
|
/>
|
||||||
|
{isScrollable && !isScrolledToBottom ? (
|
||||||
|
<AvatarIcon
|
||||||
|
className="snap-install__scroll-button"
|
||||||
|
data-testid="snap-update-scroll"
|
||||||
|
iconName={IconName.Arrow2Down}
|
||||||
|
backgroundColor={BackgroundColor.infoDefault}
|
||||||
|
color={BackgroundColor.backgroundDefault}
|
||||||
|
onClick={scrollToBottom}
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
@ -141,11 +185,16 @@ export default function SnapUpdate({
|
|||||||
className="snap-update__footer"
|
className="snap-update__footer"
|
||||||
alignItems={AlignItems.center}
|
alignItems={AlignItems.center}
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
style={{
|
||||||
|
boxShadow: 'var(--shadow-size-lg) var(--color-shadow-default)',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<PageContainerFooter
|
<PageContainerFooter
|
||||||
cancelButtonType="default"
|
cancelButtonType="default"
|
||||||
hideCancel={hasError}
|
hideCancel={hasError}
|
||||||
disabled={isLoading}
|
disabled={
|
||||||
|
isLoading || (!hasError && isScrollable && !isScrolledToBottom)
|
||||||
|
}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
cancelText={t('cancel')}
|
cancelText={t('cancel')}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
|
@ -0,0 +1,124 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
|
import configureStore from '../../../../store/store';
|
||||||
|
import mockState from '../../../../../test/data/mock-state.json';
|
||||||
|
import SnapUpdate from '.';
|
||||||
|
|
||||||
|
const store = configureStore(mockState);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Pages/Snaps/SnapUpdate',
|
||||||
|
|
||||||
|
component: SnapUpdate,
|
||||||
|
argTypes: {},
|
||||||
|
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = (args) => <SnapUpdate {...args} />;
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default';
|
||||||
|
|
||||||
|
DefaultStory.args = {
|
||||||
|
request: {
|
||||||
|
metadata: {
|
||||||
|
id: 'foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
requestState: {
|
||||||
|
loading: false,
|
||||||
|
newVersion: '2.0.0',
|
||||||
|
approvedPermissions: {
|
||||||
|
'endowment:rpc': {
|
||||||
|
caveats: [
|
||||||
|
{
|
||||||
|
type: 'rpcOrigin',
|
||||||
|
value: {
|
||||||
|
dapps: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
snap_dialog: {},
|
||||||
|
snap_getBip44Entropy: {
|
||||||
|
caveats: [
|
||||||
|
{
|
||||||
|
type: 'permittedCoinTypes',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
coinType: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetSubjectMetadata: {
|
||||||
|
origin: 'npm:@metamask/test-snap-bip44',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LoadingStory = (args) => <SnapUpdate {...args} />;
|
||||||
|
|
||||||
|
LoadingStory.storyName = 'Loading';
|
||||||
|
|
||||||
|
LoadingStory.args = {
|
||||||
|
request: {
|
||||||
|
metadata: {
|
||||||
|
id: 'foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
requestState: {
|
||||||
|
loading: true,
|
||||||
|
},
|
||||||
|
targetSubjectMetadata: {
|
||||||
|
origin: 'npm:@metamask/test-snap-bip44',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ScrollingStory = (args) => <SnapUpdate {...args} />;
|
||||||
|
|
||||||
|
ScrollingStory.storyName = 'Scrolling';
|
||||||
|
|
||||||
|
ScrollingStory.args = {
|
||||||
|
request: {
|
||||||
|
metadata: {
|
||||||
|
id: 'foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
requestState: {
|
||||||
|
loading: false,
|
||||||
|
newVersion: '2.0.0',
|
||||||
|
approvedPermissions: {
|
||||||
|
'endowment:rpc': {
|
||||||
|
caveats: [
|
||||||
|
{
|
||||||
|
type: 'rpcOrigin',
|
||||||
|
value: {
|
||||||
|
dapps: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'endowment:network-access': {},
|
||||||
|
snap_notify: {},
|
||||||
|
snap_dialog: {},
|
||||||
|
snap_getBip44Entropy: {
|
||||||
|
caveats: [
|
||||||
|
{
|
||||||
|
type: 'permittedCoinTypes',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
coinType: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetSubjectMetadata: {
|
||||||
|
origin: 'npm:@metamask/test-snap-bip44',
|
||||||
|
},
|
||||||
|
};
|
@ -14,7 +14,7 @@ import {
|
|||||||
TextColor,
|
TextColor,
|
||||||
TextVariant,
|
TextVariant,
|
||||||
} from '../../../../helpers/constants/design-system';
|
} from '../../../../helpers/constants/design-system';
|
||||||
import SnapAuthorship from '../../../../components/app/snaps/snap-authorship';
|
import SnapAuthorshipExpanded from '../../../../components/app/snaps/snap-authorship-expanded';
|
||||||
import Box from '../../../../components/ui/box';
|
import Box from '../../../../components/ui/box';
|
||||||
import SnapRemoveWarning from '../../../../components/app/snaps/snap-remove-warning';
|
import SnapRemoveWarning from '../../../../components/app/snaps/snap-remove-warning';
|
||||||
import ConnectedSitesList from '../../../../components/app/connected-sites-list';
|
import ConnectedSitesList from '../../../../components/app/connected-sites-list';
|
||||||
@ -124,7 +124,7 @@ function ViewSnap() {
|
|||||||
paddingLeft={4}
|
paddingLeft={4}
|
||||||
paddingRight={4}
|
paddingRight={4}
|
||||||
>
|
>
|
||||||
<SnapAuthorship snapId={snap.id} snap={snap} expanded />
|
<SnapAuthorshipExpanded snapId={snap.id} snap={snap} />
|
||||||
<Box className="view-snap__description" marginTop={[4, 7]}>
|
<Box className="view-snap__description" marginTop={[4, 7]}>
|
||||||
<SnapDelineator type={DelineatorType.Description} snapName={snapName}>
|
<SnapDelineator type={DelineatorType.Description} snapName={snapName}>
|
||||||
<Box
|
<Box
|
||||||
|
@ -38,9 +38,9 @@ describe('ViewSnap', () => {
|
|||||||
|
|
||||||
// Snap name & Snap authorship component
|
// Snap name & Snap authorship component
|
||||||
expect(getByText('BIP-44 Test Snap')).toBeDefined();
|
expect(getByText('BIP-44 Test Snap')).toBeDefined();
|
||||||
expect(container.getElementsByClassName('snaps-authorship')?.length).toBe(
|
expect(
|
||||||
1,
|
container.getElementsByClassName('snaps-authorship-expanded')?.length,
|
||||||
);
|
).toBe(1);
|
||||||
// Snap description
|
// Snap description
|
||||||
expect(
|
expect(
|
||||||
getByText('An example Snap that signs messages using BLS.'),
|
getByText('An example Snap that signs messages using BLS.'),
|
||||||
@ -48,7 +48,7 @@ describe('ViewSnap', () => {
|
|||||||
// Snap version info
|
// Snap version info
|
||||||
expect(getByText('v5.1.2')).toBeDefined();
|
expect(getByText('v5.1.2')).toBeDefined();
|
||||||
// Enable Snap
|
// Enable Snap
|
||||||
expect(getByText('Enable')).toBeDefined();
|
expect(getByText('Enabled')).toBeDefined();
|
||||||
expect(container.getElementsByClassName('toggle-button')?.length).toBe(1);
|
expect(container.getElementsByClassName('toggle-button')?.length).toBe(1);
|
||||||
// Permissions
|
// Permissions
|
||||||
expect(getByText('Permissions')).toBeDefined();
|
expect(getByText('Permissions')).toBeDefined();
|
||||||
|
Loading…
Reference in New Issue
Block a user