import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { isEqual } from 'lodash'; ///: BEGIN:ONLY_INCLUDE_IN(snaps) import { isObject } from '@metamask/utils'; import { SnapCaveatType, WALLET_SNAP_PERMISSION_KEY, } from '@metamask/rpc-methods'; ///: END:ONLY_INCLUDE_IN import { MetaMetricsEventCategory } from '../../../../shared/constants/metametrics'; import { PageContainerFooter } from '../../ui/page-container'; import PermissionsConnectFooter from '../permissions-connect-footer'; ///: BEGIN:ONLY_INCLUDE_IN(snaps) import { RestrictedMethods } from '../../../../shared/constants/permissions'; import SnapPrivacyWarning from '../snaps/snap-privacy-warning'; ///: END:ONLY_INCLUDE_IN import { PermissionPageContainerContent } from '.'; export default class PermissionPageContainer extends Component { static propTypes = { approvePermissionsRequest: PropTypes.func.isRequired, rejectPermissionsRequest: PropTypes.func.isRequired, selectedIdentities: PropTypes.array, allIdentitiesSelected: PropTypes.bool, ///: BEGIN:ONLY_INCLUDE_IN(snaps) currentPermissions: PropTypes.object, snapsInstallPrivacyWarningShown: PropTypes.bool.isRequired, setSnapsInstallPrivacyWarningShownStatus: PropTypes.func, ///: END:ONLY_INCLUDE_IN request: PropTypes.object, requestMetadata: PropTypes.object, targetSubjectMetadata: PropTypes.shape({ name: PropTypes.string, origin: PropTypes.string.isRequired, subjectType: PropTypes.string.isRequired, extensionId: PropTypes.string, iconUrl: PropTypes.string, }), }; static defaultProps = { request: {}, requestMetadata: {}, selectedIdentities: [], allIdentitiesSelected: false, ///: BEGIN:ONLY_INCLUDE_IN(snaps) currentPermissions: {}, ///: END:ONLY_INCLUDE_IN }; static contextTypes = { t: PropTypes.func, trackEvent: PropTypes.func, }; state = { selectedPermissions: this.getRequestedMethodState( this.getRequestedMethodNames(this.props), ), }; componentDidUpdate() { const newMethodNames = this.getRequestedMethodNames(this.props); if (!isEqual(Object.keys(this.state.selectedPermissions), newMethodNames)) { // this should be a new request, so just overwrite this.setState({ selectedPermissions: this.getRequestedMethodState(newMethodNames), }); } } getRequestedMethodState(methodNames) { return methodNames.reduce((acc, methodName) => { ///: BEGIN:ONLY_INCLUDE_IN(snaps) if (methodName === RestrictedMethods.wallet_snap) { acc[methodName] = this.getDedupedSnapPermissions(); return acc; } ///: END:ONLY_INCLUDE_IN acc[methodName] = true; return acc; }, {}); } ///: BEGIN:ONLY_INCLUDE_IN(snaps) getDedupedSnapPermissions() { const permission = this.props.request.permissions[WALLET_SNAP_PERMISSION_KEY]; const requestedSnaps = permission?.caveats[0].value; const currentSnaps = this.props.currentPermissions[WALLET_SNAP_PERMISSION_KEY]?.caveats[0] .value; if (!isObject(currentSnaps)) { return permission; } const requestedSnapKeys = requestedSnaps ? Object.keys(requestedSnaps) : []; const currentSnapKeys = currentSnaps ? Object.keys(currentSnaps) : []; const dedupedCaveats = requestedSnapKeys.reduce((acc, snapId) => { if (!currentSnapKeys.includes(snapId)) { acc[snapId] = {}; } return acc; }, {}); return { ...permission, caveats: [{ type: SnapCaveatType.SnapIds, value: dedupedCaveats }], }; } showSnapsPrivacyWarning() { this.setState({ isShowingSnapsPrivacyWarning: true, }); } ///: END:ONLY_INCLUDE_IN getRequestedMethodNames(props) { return Object.keys(props.request.permissions || {}); } componentDidMount() { this.context.trackEvent({ category: MetaMetricsEventCategory.Auth, event: 'Tab Opened', properties: { action: 'Connect', legacy_event: true, }, }); ///: BEGIN:ONLY_INCLUDE_IN(snaps) if (this.props.request.permissions[WALLET_SNAP_PERMISSION_KEY]) { if (this.props.snapsInstallPrivacyWarningShown === false) { this.showSnapsPrivacyWarning(); } } ///: END:ONLY_INCLUDE_IN } onCancel = () => { const { request, rejectPermissionsRequest } = this.props; rejectPermissionsRequest(request.metadata.id); }; onSubmit = () => { const { request: _request, approvePermissionsRequest, rejectPermissionsRequest, selectedIdentities, } = this.props; const request = { ..._request, permissions: { ..._request.permissions }, approvedAccounts: selectedIdentities.map( (selectedIdentity) => selectedIdentity.address, ), }; Object.keys(this.state.selectedPermissions).forEach((key) => { if (!this.state.selectedPermissions[key]) { delete request.permissions[key]; } }); if (Object.keys(request.permissions).length > 0) { approvePermissionsRequest(request); } else { rejectPermissionsRequest(request.metadata.id); } }; render() { const { requestMetadata, targetSubjectMetadata, selectedIdentities, allIdentitiesSelected, } = this.props; ///: BEGIN:ONLY_INCLUDE_IN(snaps) const setIsShowingSnapsPrivacyWarning = (value) => { this.setState({ isShowingSnapsPrivacyWarning: value, }); }; const confirmSnapsPrivacyWarning = () => { setIsShowingSnapsPrivacyWarning(false); this.props.setSnapsInstallPrivacyWarningShownStatus(true); }; ///: END:ONLY_INCLUDE_IN return (
{ ///: BEGIN:ONLY_INCLUDE_IN(snaps) <> {this.state.isShowingSnapsPrivacyWarning && ( confirmSnapsPrivacyWarning()} onCanceled={() => this.onCancel()} /> )} ///: END:ONLY_INCLUDE_IN }
this.onCancel()} cancelText={this.context.t('cancel')} onSubmit={() => this.onSubmit()} submitText={this.context.t('connect')} buttonSizeLarge={false} />
); } }