import React, { useEffect, useRef, useState } from 'react'; import { useHistory, useLocation } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; import { SnapCaveatType, WALLET_SNAP_PERMISSION_KEY, } from '@metamask/rpc-methods'; import classnames from 'classnames'; import Button from '../../../../components/ui/button'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import { Color, FLEX_WRAP, TextColor, TextVariant, } from '../../../../helpers/constants/design-system'; import SnapAuthorship from '../../../../components/app/snaps/snap-authorship'; import Box from '../../../../components/ui/box'; import SnapRemoveWarning from '../../../../components/app/snaps/snap-remove-warning'; import ConnectedSitesList from '../../../../components/app/connected-sites-list'; import { SNAPS_LIST_ROUTE } from '../../../../helpers/constants/routes'; import { removeSnap, removePermissionsFor, updateCaveat, } from '../../../../store/actions'; import { getSnaps, getSubjectsWithSnapPermission, getPermissions, getPermissionSubjects, getTargetSubjectMetadata, } from '../../../../selectors'; import { getSnapName } from '../../../../helpers/utils/util'; import { Text, BUTTON_TYPES } from '../../../../components/component-library'; import SnapPermissionsList from '../../../../components/app/snaps/snap-permissions-list'; import { SnapDelineator } from '../../../../components/app/snaps/snap-delineator'; import { DelineatorType } from '../../../../helpers/constants/flask'; function ViewSnap() { const t = useI18nContext(); const history = useHistory(); const location = useLocation(); const descriptionRef = useRef(null); const { pathname } = location; // The snap ID is in URI-encoded form in the last path segment of the URL. const decodedSnapId = decodeURIComponent(pathname.match(/[^/]+$/u)[0]); const snaps = useSelector(getSnaps); const snap = Object.entries(snaps) .map(([_, snapState]) => snapState) .find((snapState) => snapState.id === decodedSnapId); const [isShowingRemoveWarning, setIsShowingRemoveWarning] = useState(false); const [isDescriptionOpen, setIsDescriptionOpen] = useState(false); const [isOverflowing, setIsOverflowing] = useState(false); useEffect(() => { if (!snap) { history.push(SNAPS_LIST_ROUTE); } }, [history, snap]); useEffect(() => { setIsOverflowing( descriptionRef.current && descriptionRef.current.offsetHeight < descriptionRef.current.scrollHeight, ); }, [descriptionRef]); const connectedSubjects = useSelector((state) => getSubjectsWithSnapPermission(state, snap?.id), ); const permissions = useSelector( (state) => snap && getPermissions(state, snap.id), ); const subjects = useSelector((state) => getPermissionSubjects(state)); const targetSubjectMetadata = useSelector((state) => getTargetSubjectMetadata(state, snap?.id), ); const dispatch = useDispatch(); const onDisconnect = (connectedOrigin, snapId) => { const caveatValue = subjects[connectedOrigin].permissions[WALLET_SNAP_PERMISSION_KEY] .caveats[0].value; const newCaveatValue = { ...caveatValue }; delete newCaveatValue[snapId]; if (Object.keys(newCaveatValue) > 0) { dispatch( updateCaveat( connectedOrigin, WALLET_SNAP_PERMISSION_KEY, SnapCaveatType.SnapIds, newCaveatValue, ), ); } else { dispatch( removePermissionsFor({ [connectedOrigin]: [WALLET_SNAP_PERMISSION_KEY], }), ); } }; if (!snap) { return null; } const snapName = getSnapName(snap.id, targetSubjectMetadata); const shouldDisplayMoreButton = isOverflowing && !isDescriptionOpen; const handleMoreClick = () => { setIsDescriptionOpen(true); }; return ( {snap?.manifest.description} {shouldDisplayMoreButton && ( )} {t('permissions')} {t('connectedSites')} { onDisconnect(origin, snap.id); }} /> {t('removeSnap')} {t('removeSnapDescription')} {isShowingRemoveWarning && ( setIsShowingRemoveWarning(false)} onSubmit={async () => { await dispatch(removeSnap(snap.id)); }} snapName={snapName} /> )} ); } export default ViewSnap;