1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00
metamask-extension/ui/pages/permissions-connect/snaps/snaps-connect/snaps-connect.js
Hassan Malik ff36e32fb0
[FLASK] Improve snaps connect flow (#19461)
* add todo comments

* add snaps-connect component

* added new messages

* added component scss files to main scss files

* remove dead code and add snap-connect-cell

* update snaps connect

* updated messages and styling

* update messages and css

* update css

* moved snaps privacy warning into snaps connect, moved snaps connect error into snap install

* added story and removed unused import

* fix style linting and move snaps connect error css

* removed unused message

* ran lavamoat policy generation

* fix fencing

* some more css changes

* Fix scrolling and box shadow

* added comment, fixed quote

* Align more with Figma

* Regen LavaMoat policies

* bring back privacy logic to permission page container

* Revert scrolling changes + fix snaps icon

* fix linting, reintroduced dedupe logic and additionally addressed a corner case

* made some fixes

* Fix scrolling with multiple snaps

* add dedupe logic to snaps connect and fix spacing issue

* policy regen

* lint fix

* fix fencing

* replaced with new icon design, trimmed origin urls in certain places

* remove unused imports

* badge icon size

* Revert LM policy changes

* Use SnapAvatar for snaps-connect

* Use InstallError for connection failed

* Delete unused CSS file

* Remove unused CSS

* Use useOriginMetadata

* addressed PR comments

* fix linting errors

* add explicit condition

* fix fencing

* fix some more fencing

* fix util fencing issue

* fix storybook file, prevent null destructuring

* Fix storybook origin URLs

* Fix wrong prop name

---------

Co-authored-by: Frederik Bolding <frederik.bolding@gmail.com>
Co-authored-by: Guillaume Roux <guillaumeroux123@gmail.com>
Co-authored-by: Erik Nilsson <eriks@mail.se>
2023-06-09 10:36:38 -04:00

252 lines
7.6 KiB
JavaScript

import React, { useCallback, useState } from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import Box from '../../../../components/ui/box';
import SiteOrigin from '../../../../components/ui/site-origin';
import {
IconSize,
Text,
ValidTag,
} from '../../../../components/component-library';
import {
FlexDirection,
TextVariant,
JustifyContent,
AlignItems,
TextAlign,
Display,
FontWeight,
BlockSize,
} from '../../../../helpers/constants/design-system';
import { PageContainerFooter } from '../../../../components/ui/page-container';
import SnapConnectCell from '../../../../components/app/snaps/snap-connect-cell/snap-connect-cell';
import { getDedupedSnaps, getSnapName } from '../../../../helpers/utils/util';
import PulseLoader from '../../../../components/ui/pulse-loader/pulse-loader';
import SnapPrivacyWarning from '../../../../components/app/snaps/snap-privacy-warning/snap-privacy-warning';
import {
getPermissions,
getTargetSubjectMetadata,
} from '../../../../selectors';
import SnapAvatar from '../../../../components/app/snaps/snap-avatar/snap-avatar';
import { useOriginMetadata } from '../../../../hooks/useOriginMetadata';
export default function SnapsConnect({
request,
approveConnection,
rejectConnection,
targetSubjectMetadata,
snapsInstallPrivacyWarningShown,
setSnapsInstallPrivacyWarningShownStatus,
}) {
const t = useI18nContext();
const { origin, iconUrl, name } = targetSubjectMetadata;
const [isLoading, setIsLoading] = useState(false);
const [isShowingSnapsPrivacyWarning, setIsShowingSnapsPrivacyWarning] =
useState(!snapsInstallPrivacyWarningShown);
const currentPermissions = useSelector((state) =>
getPermissions(state, request?.metadata?.origin),
);
const onCancel = useCallback(() => {
rejectConnection(request.metadata.id);
}, [request, rejectConnection]);
const onConnect = useCallback(() => {
try {
setIsLoading(true);
approveConnection(request);
} finally {
setIsLoading(false);
}
}, [request, approveConnection]);
const snaps = getDedupedSnaps(request, currentPermissions);
const singularConnectSnapMetadata = useSelector((state) =>
getTargetSubjectMetadata(state, snaps?.[0]),
);
const SnapsConnectContent = () => {
const { hostname: trimmedOrigin } = useOriginMetadata(origin) || {};
if (isLoading) {
return (
<Box
className="snap-connect__loader-container"
flexDirection={FlexDirection.Column}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
<PulseLoader />
</Box>
);
}
if (snaps?.length > 1) {
return (
<Box
className="snaps-connect__content"
flexDirection={FlexDirection.Column}
justifyContent={JustifyContent.center}
alignItems={AlignItems.center}
paddingLeft={4}
paddingRight={4}
paddingTop={8}
width={BlockSize.Full}
style={{ overflowY: 'hidden' }}
>
<Text paddingBottom={2} variant={TextVariant.headingLg}>
{t('connectionRequest')}
</Text>
<Text variant={TextVariant.bodyMd} textAlign={TextAlign.Center}>
{t('multipleSnapConnectionWarning', [
<Text
as={ValidTag.Span}
key="1"
variant={TextVariant.bodyMd}
fontWeight={FontWeight.Medium}
>
{trimmedOrigin}
</Text>,
<Text
as={ValidTag.Span}
key="2"
variant={TextVariant.bodyMd}
fontWeight={FontWeight.Medium}
>
{snaps?.length}
</Text>,
])}
</Text>
<Box
className="snaps-connect__content__snaps-list"
flexDirection={FlexDirection.Column}
display={Display.Flex}
marginTop={4}
width={BlockSize.Full}
style={{ overflowY: 'auto', flex: 1 }}
>
{snaps.map((snap) => (
// TODO(hbmalik88): add in the iconUrl prop when we have access to a snap's icons pre-installation
<SnapConnectCell
key={`snaps-connect-${snap}`}
snapId={snap}
origin={trimmedOrigin}
/>
))}
</Box>
</Box>
);
} else if (snaps?.length === 1) {
const snapId = snaps[0];
const snapName = getSnapName(snapId, singularConnectSnapMetadata);
return (
<Box
className="snaps-connect__content"
flexDirection={FlexDirection.Column}
justifyContent={JustifyContent.center}
alignItems={AlignItems.center}
height={BlockSize.Full}
paddingLeft={4}
paddingRight={4}
>
<Box paddingBottom={2}>
<SnapAvatar
snapId={snaps[0]}
badgeSize={IconSize.Md}
avatarSize={IconSize.Xl}
borderWidth={3}
/>
</Box>
<Text paddingBottom={2} variant={TextVariant.headingLg}>
{t('connectionRequest')}
</Text>
<Text
variant={TextVariant.bodyMd}
textAlign={TextAlign.Center}
padding={[0, 4]}
>
{t('snapConnectionWarning', [
<Text
as={ValidTag.Span}
key="1"
variant={TextVariant.bodyMd}
fontWeight={FontWeight.Medium}
>
{trimmedOrigin}
</Text>,
<Text
as={ValidTag.Span}
key="2"
variant={TextVariant.bodyMd}
fontWeight={FontWeight.Medium}
>
{snapName}
</Text>,
])}
</Text>
</Box>
);
}
return null;
};
return (
<Box
className="page-container snaps-connect"
flexDirection={FlexDirection.Column}
alignItems={AlignItems.center}
>
{isShowingSnapsPrivacyWarning && (
<SnapPrivacyWarning
onAccepted={() => {
setIsShowingSnapsPrivacyWarning(false);
setSnapsInstallPrivacyWarningShownStatus(true);
}}
onCanceled={onCancel}
/>
)}
<Box
className="snaps-connect__header"
flexDirection={FlexDirection.Column}
alignItems={AlignItems.center}
paddingLeft={4}
paddingRight={4}
>
<SiteOrigin
chip
siteOrigin={origin}
title={origin}
iconSrc={iconUrl}
iconName={name}
/>
</Box>
<SnapsConnectContent />
<PageContainerFooter
footerClassName="snaps-connect__footer"
cancelButtonType="default"
hideCancel={false}
disabled={isLoading}
onCancel={onCancel}
cancelText={t('cancel')}
onSubmit={onConnect}
submitText={t('connect')}
/>
</Box>
);
}
SnapsConnect.propTypes = {
request: PropTypes.object.isRequired,
approveConnection: PropTypes.func.isRequired,
rejectConnection: PropTypes.func.isRequired,
targetSubjectMetadata: PropTypes.shape({
extensionId: PropTypes.string,
iconUrl: PropTypes.string,
name: PropTypes.string,
origin: PropTypes.string,
subjectType: PropTypes.string,
}),
snapsInstallPrivacyWarningShown: PropTypes.bool.isRequired,
setSnapsInstallPrivacyWarningShownStatus: PropTypes.func,
};