diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 295bdb13ecc1..dcf8c59b59a4 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1,4 +1,22 @@ { + "connectedSites": { + "message": "Connected Sites" + }, + "connectedSitesDescription": { + "message": "$1 is connected to these sites. They can view your account address." + }, + "connectManually": { + "message": "Manually connect to current site" + }, + "disconnect": { + "message": "Disconnect" + }, + "disconnectSite": { + "message": "Disconnect $1?" + }, + "disconnectAccountConfirmationDescription": { + "message": "Are you sure you want to disconnect? You may lose site functionality." + }, "migrateSai": { "message": "A message from Maker: The new Multi-Collateral Dai token has been released. Your old tokens are now called Sai. Please upgrade your Sai tokens to the new Dai." }, @@ -20,16 +38,9 @@ "chartOnlyAvailableEth": { "message": "Chart only available on Ethereum networks." }, - "connectedSites": { - "message": "Connected Sites" - }, "connectingWithMetaMask": { "message": "Connecting With MetaMask..." }, - "connectTo": { - "message": "Connect to $1", - "description": "$1 is the name/origin of a site/dapp that the user can connect to metamask" - }, "chooseAnAcount": { "message": "Choose an account" }, @@ -408,24 +419,12 @@ "details": { "message": "Details" }, - "disconnectAccount": { - "message": "Disconnect account" - }, "disconnectAll": { "message": "Disconnect All" }, "disconnectAllModalDescription": { "message": "Are you sure? You will be disconnected from all sites on all accounts." }, - "disconnectAccountModalDescription": { - "message": "Are you sure? Your account (\"$1\") will be disconnected from this site." - }, - "disconnectAccountQuestion": { - "message": "Disconnect account?" - }, - "disconnectFromThisAccount": { - "message": "Disconnect from this account?" - }, "disconnectAllAccountsQuestion": { "message": "Disconnect all accounts?" }, @@ -435,10 +434,6 @@ "directDepositEtherExplainer": { "message": "If you already have some Ether, the quickest way to get Ether in your new wallet by direct deposit." }, - "domainLastConnect": { - "message": "Last Connected: $1", - "description": "$1 is the date at which the user was last connected to a given domain" - }, "done": { "message": "Done" }, @@ -499,10 +494,6 @@ "endOfFlowMessage10": { "message": "All Done" }, - "extensionId": { - "message": "Extension ID: $1", - "description": "$1 is a string of random letters that are the id of another extension connecting to MetaMask" - }, "externalExtension": { "message": "External Extension" }, diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json index d89e2cd50700..dbe92e98888f 100644 --- a/app/_locales/it/messages.json +++ b/app/_locales/it/messages.json @@ -26,10 +26,6 @@ "connectingWithMetaMask": { "message": "Connettendo con MetaMask..." }, - "connectTo": { - "message": "Collegati a $1", - "description": "$1 is the name/origin of a site/dapp that the user can connect to metamask" - }, "chooseAnAcount": { "message": "Scegli un account" }, @@ -408,24 +404,12 @@ "details": { "message": "Dettagli" }, - "disconnectAccount": { - "message": "Disconnetti account" - }, "disconnectAll": { "message": "Disconnetti Tutti" }, "disconnectAllModalDescription": { "message": "Sei sicuro? Sarai disconnesso da tutti i siti su tutti gli account." }, - "disconnectAccountModalDescription": { - "message": "Sei sicuro? Il tuo account (\"$1\") sarà disconnesso da questo sito." - }, - "disconnectAccountQuestion": { - "message": "Disconnettere account?" - }, - "disconnectFromThisAccount": { - "message": "Disconnettersi da questo account?" - }, "disconnectAllAccountsQuestion": { "message": "Disconnettere tutti gli account?" }, @@ -435,10 +419,6 @@ "directDepositEtherExplainer": { "message": "Se possiedi già degli Ether, questa è la via più veloce per aggiungere Ether al tuo portafoglio con un deposito diretto." }, - "domainLastConnect": { - "message": "Ultima Connessione: $1", - "description": "$1 is the date at which the user was last connected to a given domain" - }, "done": { "message": "Finito" }, @@ -499,10 +479,6 @@ "endOfFlowMessage10": { "message": "Tutto Fatto" }, - "extensionId": { - "message": "ID Estensione: $1", - "description": "$1 is a string of random letters that are the id of another extension connecting to MetaMask" - }, "externalExtension": { "message": "Estensione Esterna" }, diff --git a/test/e2e/permissions.spec.js b/test/e2e/permissions.spec.js index 8bf97b4fbf51..5ba6a0da357b 100644 --- a/test/e2e/permissions.spec.js +++ b/test/e2e/permissions.spec.js @@ -137,16 +137,8 @@ describe('MetaMask', function () { await driver.findElement(By.xpath(`//h2[contains(text(), 'Connected Sites')]`)) - const domains = await driver.findClickableElements(By.css('.connected-sites-list__domain')) + const domains = await driver.findClickableElements(By.css('.connected-sites__domain-name')) assert.equal(domains.length, 1) - - const domainName = await driver.findElement(By.css('.connected-sites-list__domain-name')) - assert.equal(await domainName.getText(), 'E2E Test Dapp') - - await domains[0].click() - - const permissionDescription = await driver.findElement(By.css('.connected-sites-list__permission-description')) - assert.equal(await permissionDescription.getText(), `View the addresses of the user's chosen accounts.`) }) it('can get accounts within the dapp', async function () { @@ -158,29 +150,5 @@ describe('MetaMask', function () { const getAccountsResult = await driver.findElement(By.css('#getAccountsResult')) assert.equal((await getAccountsResult.getText()).toLowerCase(), publicAddress.toLowerCase()) }) - - it('can disconnect all accounts', async function () { - await driver.switchToWindow(extension) - - await driver.clickElement(By.xpath(`//button[contains(text(), 'Disconnect All')]`)) - await driver.clickElement(By.css('.popover-header__close')) - - const disconnectModal = await driver.findElement(By.css('span .modal')) - - await driver.clickElement(By.css('.disconnect-all-modal .btn-danger')) - - await driver.wait(until.stalenessOf(disconnectModal)) - await driver.delay(regularDelayMs) - }) - - it('can no longer get accounts within the dapp', async function () { - await driver.switchToWindow(dapp) - await driver.delay(regularDelayMs) - - await driver.clickElement(By.xpath(`//button[contains(text(), 'eth_accounts')]`)) - - const getAccountsResult = await driver.findElement(By.css('#getAccountsResult')) - assert.equal(await getAccountsResult.getText(), 'Not able to get accounts') - }) }) }) diff --git a/ui/app/components/app/connected-sites-list/connected-sites-list.component.js b/ui/app/components/app/connected-sites-list/connected-sites-list.component.js index 19cba3b2e937..67d5b6a9552f 100644 --- a/ui/app/components/app/connected-sites-list/connected-sites-list.component.js +++ b/ui/app/components/app/connected-sites-list/connected-sites-list.component.js @@ -1,7 +1,5 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import classnames from 'classnames' -import Button from '../../ui/button' import IconWithFallBack from '../../ui/icon-with-fallback' export default class ConnectedSitesList extends Component { @@ -10,148 +8,40 @@ export default class ConnectedSitesList extends Component { } static propTypes = { - renderableDomains: PropTypes.arrayOf(PropTypes.shape({ + connectedDomains: PropTypes.arrayOf(PropTypes.shape({ name: PropTypes.string, icon: PropTypes.string, key: PropTypes.string, - lastConnectedTime: PropTypes.string, - permissionDescriptions: PropTypes.array, })).isRequired, - domains: PropTypes.object, - showDisconnectAccountModal: PropTypes.func.isRequired, - showDisconnectAllModal: PropTypes.func.isRequired, - tabToConnect: PropTypes.object, - legacyExposeAccounts: PropTypes.func.isRequired, - selectedAddress: PropTypes.string.isRequired, - getOpenMetamaskTabsIds: PropTypes.func.isRequired, - } - - state = { - expandedDomain: '', - } - - UNSAFE_componentWillMount () { - const { getOpenMetamaskTabsIds } = this.props - getOpenMetamaskTabsIds() - } - - handleDomainItemClick (domainKey) { - if (this.state.expandedDomain === domainKey) { - this.setState({ expandedDomain: '' }) - } else { - this.setState({ expandedDomain: domainKey }) - } + onDisconnectSite: PropTypes.func.isRequired, } render () { - const { - renderableDomains, - domains, - showDisconnectAccountModal, - showDisconnectAllModal, - tabToConnect, - legacyExposeAccounts, - selectedAddress, - } = this.props - const { expandedDomain } = this.state + const { connectedDomains, onDisconnectSite } = this.props const { t } = this.context return ( -
- { - renderableDomains.map((domain, domainIndex) => { - const domainIsExpanded = expandedDomain === domain.key - return ( -
-
this.handleDomainItemClick(domain.key) }> -
- - -
-
-
- { domain.extensionId ? t('externalExtension') : domain.name } -
-
- { domain.lastConnectedTime - ? ( -
- { t('domainLastConnect', [domain.lastConnectedTime]) } -
- ) - : null - } - {domainIsExpanded - ? ( -
- { domain.extensionId ? t('extensionId', [domain.extensionId]) : domain.secondaryName } -
- ) - : null - } -
-
-
- { domainIsExpanded ? : } -
-
- { domainIsExpanded - ? ( -
-
- { - domain.permissionDescriptions.map((description, pdIndex) => { - return ( -
- -
- { description } -
-
- ) - }) - } -
-
showDisconnectAccountModal(domain.key, domains[domain.key]) } - > - { t('disconnectAccount') } -
-
- ) - : null +
+ { connectedDomains.map((domain) => ( +
+
+ + + { + domain.extensionId + ? t('externalExtension') + : domain.key } -
- ) - }) - } -
-
- + +
+ onDisconnectSite(domain.key, domain.name)} + />
- { tabToConnect - ? ( -
- -
- ) - : null - } -
-
+ )) } + ) } } diff --git a/ui/app/components/app/connected-sites-list/connected-sites-list.container.js b/ui/app/components/app/connected-sites-list/connected-sites-list.container.js index 490c3fc021d4..5f50608681c7 100644 --- a/ui/app/components/app/connected-sites-list/connected-sites-list.container.js +++ b/ui/app/components/app/connected-sites-list/connected-sites-list.container.js @@ -1,54 +1,11 @@ import { connect } from 'react-redux' - import ConnectedSitesList from './connected-sites-list.component' -import { - showModal, - legacyExposeAccounts, - getOpenMetamaskTabsIds, -} from '../../../store/actions' -import { - getRenderablePermissionsDomains, - getPermissionsDomains, - getSelectedAddress, - getPermittedAccountsForCurrentTab, -} from '../../../selectors/selectors' -import { getOriginFromUrl } from '../../../helpers/utils/util' +import { getRenderablePermissionsDomains } from '../../../selectors/selectors' const mapStateToProps = (state) => { - const { openMetaMaskTabs } = state.appState - const { title, url, id } = state.activeTab - const permittedAccounts = getPermittedAccountsForCurrentTab(state) - - let tabToConnect - - if (url && permittedAccounts.length === 0 && !openMetaMaskTabs[id]) { - tabToConnect = { - title, - origin: getOriginFromUrl(url), - } - } - - return { - domains: getPermissionsDomains(state), - renderableDomains: getRenderablePermissionsDomains(state), - tabToConnect, - selectedAddress: getSelectedAddress(state), - } -} - -const mapDispatchToProps = (dispatch) => { return { - showDisconnectAccountModal: (domainKey, domain) => { - dispatch(showModal({ name: 'DISCONNECT_ACCOUNT', domainKey, domain })) - }, - showDisconnectAllModal: () => { - dispatch(showModal({ name: 'DISCONNECT_ALL' })) - }, - legacyExposeAccounts: (origin, account) => { - dispatch(legacyExposeAccounts(origin, [account])) - }, - getOpenMetamaskTabsIds: () => dispatch(getOpenMetamaskTabsIds()), + connectedDomains: getRenderablePermissionsDomains(state), } } -export default connect(mapStateToProps, mapDispatchToProps)(ConnectedSitesList) +export default connect(mapStateToProps)(ConnectedSitesList) diff --git a/ui/app/components/app/connected-sites-list/index.scss b/ui/app/components/app/connected-sites-list/index.scss index c2d98d2aa0c1..a06a361e595d 100644 --- a/ui/app/components/app/connected-sites-list/index.scss +++ b/ui/app/components/app/connected-sites-list/index.scss @@ -1,125 +1,37 @@ -.connected-sites-list { - font-family: Roboto; - font-style: normal; - font-weight: normal; - - &__domain, &__domain--expanded { - border-bottom: 1px solid #c4c4c4; - } - - &__domain { - cursor: pointer; - } - - &__domain--expanded { - background: #FAFBFC; - cursor: initial; - } - - &__domain-item { +.connected-sites { + &__content-rows { display: flex; - justify-content: space-between; + flex-direction: column; align-items: center; - padding: 16px; - } - - &__domain-item-info-container { - display: flex; + border-top: 1px solid #D2D8DD; } - &__identicon-container { - position: relative; + &__content-row { display: flex; - justify-content: center; + width: 100%; + flex-direction: row; + justify-content: space-between; align-items: center; - height: 32px; - width: 32px; - } - - &__identicon-border { - height: 32px; - width: 32px; - border-radius: 50%; - border: 1px solid #F2F3F4; - position: absolute; - background: #FFFFFF; - } - - &__identicon { - width: 24px; - height: 24px; - z-index: 1; - - &--default { - z-index: 1; - color: black; - } + border-bottom: 1px solid #D2D8DD; + padding: 16px 24px; } &__domain-info { display: flex; - flex-direction: column; - margin-left: 16px; - } - - &__domain-names { - display: flex; + flex-direction: row; align-items: center; - } - - &__domain-name { - font-size: 18px; - color: #24292E; - } - - &__domain-origin, &__domain-last-connected { + min-width: 0; font-size: 12px; - color: #6A737D; - } - - &__domain-last-connected { - margin-top: 2px; } - &__expand-arrow { - align-self: flex-start; - margin-top: 6px; - } - - &__permissions { - padding-left: 16px; - padding-bottom: 24px; - } - - &__permission { - display: flex; - align-items: center; - color: #6A737D; - margin-left: 16px; - } - - &__permission-description { - margin-left: 18px; + &__domain-name { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-left: 6px; } - &__disconnect { - margin-top: 24px; - color: #D73A49; + &__trash { cursor: pointer; } - - &__bottom-buttons { - display: flex; - align-items: center; - } - - &__disconnect-all { - padding: 10px; - width: 50%; - } - - &__connect-to { - padding: 10px; - width: 50%; - } -} \ No newline at end of file +} diff --git a/ui/app/components/app/modals/disconnect-account/disconnect-account.component.js b/ui/app/components/app/modals/disconnect-account/disconnect-account.component.js deleted file mode 100644 index 4fe5c7227eff..000000000000 --- a/ui/app/components/app/modals/disconnect-account/disconnect-account.component.js +++ /dev/null @@ -1,52 +0,0 @@ -import React, { PureComponent } from 'react' -import PropTypes from 'prop-types' -import Modal from '../../modal' -import Button from '../../../ui/button' - - -export default class DisconnectAccount extends PureComponent { - static propTypes = { - hideModal: PropTypes.func.isRequired, - disconnectAccount: PropTypes.func.isRequired, - accountLabel: PropTypes.string.isRequired, - } - - static contextTypes = { - t: PropTypes.func, - } - - render () { - const { t } = this.context - const { hideModal, disconnectAccount, accountLabel } = this.props - - return ( - hideModal()} - hideFooter - > -
-
- { t('disconnectAccountModalDescription', [ accountLabel ]) } -
- - -
-
- ) - } -} diff --git a/ui/app/components/app/modals/disconnect-account/disconnect-account.container.js b/ui/app/components/app/modals/disconnect-account/disconnect-account.container.js deleted file mode 100644 index bda50cb4bd19..000000000000 --- a/ui/app/components/app/modals/disconnect-account/disconnect-account.container.js +++ /dev/null @@ -1,44 +0,0 @@ -import { connect } from 'react-redux' -import { compose } from 'redux' -import withModalProps from '../../../../helpers/higher-order-components/with-modal-props' -import DisconnectAccount from './disconnect-account.component' -import { getCurrentAccountWithSendEtherInfo } from '../../../../selectors/selectors' -import { removePermissionsFor } from '../../../../store/actions' - -const mapStateToProps = (state) => { - return { - ...(state.appState.modal.modalState.props || {}), - accountLabel: getCurrentAccountWithSendEtherInfo(state).name, - } -} - -const mapDispatchToProps = (dispatch) => { - return { - disconnectAccount: (domainKey, domain) => { - const permissionMethodNames = domain.permissions.map((perm) => perm.parentCapability) - dispatch(removePermissionsFor({ [domainKey]: permissionMethodNames })) - }, - } -} - -const mergeProps = (stateProps, dispatchProps, ownProps) => { - const { - domainKey, - domain, - } = stateProps - const { - disconnectAccount: dispatchDisconnectAccount, - } = dispatchProps - - return { - ...ownProps, - ...stateProps, - ...dispatchProps, - disconnectAccount: () => dispatchDisconnectAccount(domainKey, domain), - } -} - -export default compose( - withModalProps, - connect(mapStateToProps, mapDispatchToProps, mergeProps) -)(DisconnectAccount) diff --git a/ui/app/components/app/modals/disconnect-account/index.js b/ui/app/components/app/modals/disconnect-account/index.js deleted file mode 100644 index 43bfac9fdcec..000000000000 --- a/ui/app/components/app/modals/disconnect-account/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './disconnect-account.container' diff --git a/ui/app/components/app/modals/disconnect-account/index.scss b/ui/app/components/app/modals/disconnect-account/index.scss deleted file mode 100644 index 861b7cec224e..000000000000 --- a/ui/app/components/app/modals/disconnect-account/index.scss +++ /dev/null @@ -1,25 +0,0 @@ -.disconnect-account-modal { - &__description { - color: #24292E; - margin-bottom: 24px; - } - - &__cancel-button { - border: none; - margin-top: 12px; - } -} - -.disconnect-account-modal-container { - .modal-container__header-text { - @extend %header--18; - } - - .modal-container__content { - padding-bottom: 18px; - - @media screen and (max-width: 575px) { - padding-bottom: 18px; - } - } -} \ No newline at end of file diff --git a/ui/app/components/app/modals/index.scss b/ui/app/components/app/modals/index.scss index dbf47265f3c5..8b2b747ac552 100644 --- a/ui/app/components/app/modals/index.scss +++ b/ui/app/components/app/modals/index.scss @@ -12,8 +12,6 @@ @import './edit-approval-permission/index'; -@import './disconnect-account/index'; - @import './disconnect-all/index'; @import './new-account-modal/index'; diff --git a/ui/app/components/app/modals/modal.js b/ui/app/components/app/modals/modal.js index 313bb7c243bc..6bb84d469da9 100644 --- a/ui/app/components/app/modals/modal.js +++ b/ui/app/components/app/modals/modal.js @@ -29,7 +29,6 @@ import ConfirmDeleteNetwork from './confirm-delete-network' import AddToAddressBookModal from './add-to-addressbook-modal' import EditApprovalPermission from './edit-approval-permission' import NewAccountModal from './new-account-modal' -import DisconnectAccount from './disconnect-account' import DisconnectAll from './disconnect-all' const modalContainerBaseStyle = { @@ -168,33 +167,6 @@ const MODALS = { }, }, - DISCONNECT_ACCOUNT: { - contents: , - mobileModalStyle: { - width: '95%', - top: '10%', - boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', - transform: 'none', - left: '0', - right: '0', - margin: '0 auto', - borderRadius: '10px', - }, - laptopModalStyle: { - width: '375px', - top: '10%', - boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', - transform: 'none', - left: '0', - right: '0', - margin: '0 auto', - borderRadius: '10px', - }, - contentStyle: { - borderRadius: '10px', - }, - }, - DISCONNECT_ALL: { contents: , mobileModalStyle: { diff --git a/ui/app/components/ui/popover/index.scss b/ui/app/components/ui/popover/index.scss index a74db33334e0..c5fd6547c009 100644 --- a/ui/app/components/ui/popover/index.scss +++ b/ui/app/components/ui/popover/index.scss @@ -10,6 +10,8 @@ background: #C4C4C4; } + display: flex; + flex-direction: column; position: absolute; width: 328px; height: 564px; @@ -20,24 +22,48 @@ } &-header { - &__heading { + display: flex; + padding: 24px; + flex-direction: column; + + &__title { + display: flex; + align-items: center; + justify-content: space-between; + @extend %font; font-weight: bold; font-size: 18px; line-height: 25px; - text-align: center; - padding: 20px 0; - border-bottom: 1px solid #DDDEE9; + padding-bottom: 8px; + + h2 { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + button { + margin-right: 24px; + } + } + } + + &__subtitle { + @extend %font; + font-weight: normal; + font-size: 14px; + line-height: 20px; } - &__close { - position: absolute; - top: 13px; - right: 6px; - margin: 10px; + &__button { background: none; + font-size: inherit; padding: 0; } + + i { + cursor: pointer; + } } &-bg { @@ -48,14 +74,10 @@ } &-content { - padding: 0 5px 0 16px; - margin-top: 5px; - margin-bottom: -10px; - margin-right: 5px; - height: calc(100% - 66px - 10px); overflow-y: scroll; position: relative; display: flex; + flex: 1; flex-direction: column; justify-content: flex-start; align-items: stretch; diff --git a/ui/app/components/ui/popover/popover.component.js b/ui/app/components/ui/popover/popover.component.js index 1e7b7fc8cf3d..d0444317364f 100644 --- a/ui/app/components/ui/popover/popover.component.js +++ b/ui/app/components/ui/popover/popover.component.js @@ -1,23 +1,51 @@ -import React, { PureComponent } from 'react' +import React, { PureComponent, useContext } from 'react' import ReactDOM from 'react-dom' import PropTypes from 'prop-types' -import PopoverHeader from './popover.header.component' - -const Popover = ({ title, children, onClose }) => ( -
-
-
- -
- {children} +import { I18nContext } from '../../../contexts/i18n' + +const Popover = ({ title, subtitle, children, onBack, onClose }) => { + const t = useContext(I18nContext) + return ( +
+
+
+
+
+

+ { + onBack + ? ( +

+
+

{subtitle}

+
+
+ {children} +
-
-) + ) +} Popover.propTypes = { title: PropTypes.string.isRequired, + subtitle: PropTypes.string.isRequired, children: PropTypes.node.isRequired, + onBack: PropTypes.func, onClose: PropTypes.func.isRequired, } diff --git a/ui/app/components/ui/popover/popover.header.component.js b/ui/app/components/ui/popover/popover.header.component.js deleted file mode 100644 index d1f6099ae56e..000000000000 --- a/ui/app/components/ui/popover/popover.header.component.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import Close from '../icon/close-icon.component' - -const PopoverHeader = ({ title, onClose }) => ( -
-

{title}

- -
-) - -PopoverHeader.propTypes = { - title: PropTypes.string.isRequired, - onClose: PropTypes.func.isRequired, -} - -export default PopoverHeader diff --git a/ui/app/components/ui/popover/popover.stories.js b/ui/app/components/ui/popover/popover.stories.js index 3edeef126529..45e5da5cc46b 100644 --- a/ui/app/components/ui/popover/popover.stories.js +++ b/ui/app/components/ui/popover/popover.stories.js @@ -10,15 +10,25 @@ const containerStyle = { position: 'relative', } +const mainWrapperStyle = { + padding: '0 24px 24px', +} + export default { title: 'Popover', } export const approve = () => (
- -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Semper eget duis at tellus at urna condimentum. Posuere urna nec tincidunt praesent semper. Arcu dictum varius duis at. A lacus vestibulum sed arcu. Orci porta non pulvinar neque laoreet suspendisse interdum. Pretium fusce id velit ut. Ut consequat semper viverra nam libero justo laoreet sit. In ante metus dictum at tempor commodo ullamcorper a lacus. Posuere morbi leo urna molestie at elementum eu facilisis sed. Libero enim sed faucibus turpis in eu mi bibendum neque. Amet massa vitae tortor condimentum lacinia quis. Pretium viverra suspendisse potenti nullam ac. Pellentesque elit eget gravida cum sociis natoque penatibus. Proin libero nunc consequat interdum varius sit amet. Est ultricies integer quis auctor elit sed vulputate. Ornare arcu odio ut sem nulla pharetra. Eget nullam non nisi est sit. Leo vel fringilla est ullamcorper eget nulla.

-

Mattis pellentesque id nibh tortor id. Commodo sed egestas egestas fringilla phasellus. Semper eget duis at tellus at urna. Tristique nulla aliquet enim tortor at auctor urna nunc. Pellentesque habitant morbi tristique senectus et netus et. Turpis egestas sed tempus urna et pharetra pharetra massa massa. Mi eget mauris pharetra et ultrices neque ornare aenean. Facilisis volutpat est velit egestas dui id ornare arcu odio. Lacus sed turpis tincidunt id aliquet risus feugiat in. Cras tincidunt lobortis feugiat vivamus. Blandit libero volutpat sed cras ornare arcu. Facilisi morbi tempus iaculis urna id volutpat. Risus viverra adipiscing at in tellus. Leo vel orci porta non pulvinar neque. Malesuada fames ac turpis egestas integer. Euismod nisi porta lorem mollis aliquam.

+ +
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Semper eget duis at tellus at urna condimentum. Posuere urna nec tincidunt praesent semper. Arcu dictum varius duis at. A lacus vestibulum sed arcu. Orci porta non pulvinar neque laoreet suspendisse interdum. Pretium fusce id velit ut. Ut consequat semper viverra nam libero justo laoreet sit. In ante metus dictum at tempor commodo ullamcorper a lacus. Posuere morbi leo urna molestie at elementum eu facilisis sed. Libero enim sed faucibus turpis in eu mi bibendum neque. Amet massa vitae tortor condimentum lacinia quis. Pretium viverra suspendisse potenti nullam ac. Pellentesque elit eget gravida cum sociis natoque penatibus. Proin libero nunc consequat interdum varius sit amet. Est ultricies integer quis auctor elit sed vulputate. Ornare arcu odio ut sem nulla pharetra. Eget nullam non nisi est sit. Leo vel fringilla est ullamcorper eget nulla.

+

Mattis pellentesque id nibh tortor id. Commodo sed egestas egestas fringilla phasellus. Semper eget duis at tellus at urna. Tristique nulla aliquet enim tortor at auctor urna nunc. Pellentesque habitant morbi tristique senectus et netus et. Turpis egestas sed tempus urna et pharetra pharetra massa massa. Mi eget mauris pharetra et ultrices neque ornare aenean. Facilisis volutpat est velit egestas dui id ornare arcu odio. Lacus sed turpis tincidunt id aliquet risus feugiat in. Cras tincidunt lobortis feugiat vivamus. Blandit libero volutpat sed cras ornare arcu. Facilisi morbi tempus iaculis urna id volutpat. Risus viverra adipiscing at in tellus. Leo vel orci porta non pulvinar neque. Malesuada fames ac turpis egestas integer. Euismod nisi porta lorem mollis aliquam.

+
) diff --git a/ui/app/pages/connected-sites/connected-sites.component.js b/ui/app/pages/connected-sites/connected-sites.component.js index 4fbad599708b..7dfb769b5d0c 100644 --- a/ui/app/pages/connected-sites/connected-sites.component.js +++ b/ui/app/pages/connected-sites/connected-sites.component.js @@ -1,28 +1,115 @@ import PropTypes from 'prop-types' import React, { Component } from 'react' import ConnectedSitesList from '../../components/app/connected-sites-list' -import { - DEFAULT_ROUTE, -} from '../../helpers/constants/routes' import Popover from '../../components/ui/popover/popover.component' +import { DEFAULT_ROUTE } from '../../helpers/constants/routes' +import Button from '../../components/ui/button' export default class ConnectSites extends Component { + static contextTypes = { + t: PropTypes.func, + } + + static defaultProps = { + tabToConnect: null, + } + static propTypes = { + accountLabel: PropTypes.string.isRequired, + disconnectAccount: PropTypes.func.isRequired, history: PropTypes.object.isRequired, + tabToConnect: PropTypes.object, + legacyExposeAccount: PropTypes.func.isRequired, + getOpenMetamaskTabsIds: PropTypes.func.isRequired, } - static contextTypes = { - t: PropTypes.func, + state = { + sitePendingDisconnect: null, + } + + UNSAFE_componentWillMount () { + const { getOpenMetamaskTabsIds } = this.props + getOpenMetamaskTabsIds() + } + + setSitePendingDisconnect = (domainKey, domainName) => { + this.setState({ + sitePendingDisconnect: { + domainKey, + domainName, + }, + }) + } + + clearSitePendingDisconnect = () => { + this.setState({ + sitePendingDisconnect: null, + }) + } + + disconnect = () => { + const { disconnectAccount } = this.props + const { sitePendingDisconnect } = this.state + + disconnectAccount(sitePendingDisconnect.domainKey) + this.clearSitePendingDisconnect() + } + + renderConnectedSites () { + const { tabToConnect, legacyExposeAccount } = this.props + const { t } = this.context + return ( + <> + + { tabToConnect ? ( + + ) : null } + + ) + } + + renderDisconnectConfirmation () { + const { t } = this.context + return ( +
+ + +
+ ) } render () { - const { - history, - } = this.props + const { accountLabel, history } = this.props + const { t } = this.context + const { sitePendingDisconnect } = this.state return ( - history.push(DEFAULT_ROUTE)}> - - + sitePendingDisconnect + ? ( + history.push(DEFAULT_ROUTE)} + > + {this.renderDisconnectConfirmation()} + + ) + : ( + history.push(DEFAULT_ROUTE)} + > + {this.renderConnectedSites()} + + ) ) } } diff --git a/ui/app/pages/connected-sites/connected-sites.container.js b/ui/app/pages/connected-sites/connected-sites.container.js new file mode 100644 index 000000000000..917dd496299a --- /dev/null +++ b/ui/app/pages/connected-sites/connected-sites.container.js @@ -0,0 +1,62 @@ +import { connect } from 'react-redux' +import ConnectedSites from './connected-sites.component' +import { getOpenMetamaskTabsIds, legacyExposeAccounts, removePermissionsFor } from '../../store/actions' +import { + getCurrentAccountWithSendEtherInfo, + getPermissionsDomains, + getPermittedAccountsForCurrentTab, + getSelectedAddress, +} from '../../selectors/selectors' +import { getOriginFromUrl } from '../../helpers/utils/util' + +const mapStateToProps = (state) => { + const { openMetaMaskTabs } = state.appState + const { title, url, id } = state.activeTab + const permittedAccounts = getPermittedAccountsForCurrentTab(state) + + let tabToConnect + if (url && permittedAccounts.length === 0 && !openMetaMaskTabs[id]) { + tabToConnect = { + title, + origin: getOriginFromUrl(url), + } + } + + return { + accountLabel: getCurrentAccountWithSendEtherInfo(state).name, + domains: getPermissionsDomains(state), + selectedAddress: getSelectedAddress(state), + tabToConnect, + } +} + +const mapDispatchToProps = (dispatch) => { + return { + getOpenMetamaskTabsIds: () => dispatch(getOpenMetamaskTabsIds()), + disconnectAccount: (domainKey, domain) => { + const permissionMethodNames = domain.permissions.map(({ parentCapability }) => parentCapability) + dispatch(removePermissionsFor({ + [domainKey]: permissionMethodNames, + })) + }, + legacyExposeAccounts: (origin, account) => dispatch(legacyExposeAccounts(origin, [account])), + } +} + +const mergeProps = (stateProps, dispatchProps, ownProps) => { + const { domains, selectedAddress, tabToConnect } = stateProps + const { + disconnectAccount, + legacyExposeAccounts: dispatchLegacyExposeAccounts, + } = dispatchProps + + return { + ...ownProps, + ...stateProps, + ...dispatchProps, + disconnectAccount: (domainKey) => disconnectAccount(domainKey, domains[domainKey]), + legacyExposeAccount: () => dispatchLegacyExposeAccounts(tabToConnect.origin, selectedAddress), + } +} + +export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(ConnectedSites) diff --git a/ui/app/pages/connected-sites/index.js b/ui/app/pages/connected-sites/index.js index 62aa0683fba0..96cf6e67de05 100644 --- a/ui/app/pages/connected-sites/index.js +++ b/ui/app/pages/connected-sites/index.js @@ -1 +1 @@ -export { default } from './connected-sites.component' +export { default } from './connected-sites.container' diff --git a/ui/app/pages/connected-sites/index.scss b/ui/app/pages/connected-sites/index.scss new file mode 100644 index 000000000000..178f4d03621f --- /dev/null +++ b/ui/app/pages/connected-sites/index.scss @@ -0,0 +1,32 @@ +.connected-sites { + &__confirmation { + display: flex; + flex-direction: row; + justify-content: space-between; + margin-top: 30px; + border-top: 1px solid #D2D8DD; + padding: 16px 24px 16px; + + button:first-child { + margin-right: 24px; + } + } + + &__add-site-manually { + position: sticky; + bottom: 0; + background: white; + border-top: 1px solid #D2D8DD; + margin-top: -1px; + padding: 16px 24px 24px 24px; + border-radius: 0 0 10px 10px; + font-size: 14px; + line-height: 20px; + z-index: 1; + + a, a:hover { + cursor: pointer; + color: #037DD6; + } + } +} diff --git a/ui/app/pages/home/home.component.js b/ui/app/pages/home/home.component.js index e646fe2c780b..d83a6e0754ad 100644 --- a/ui/app/pages/home/home.component.js +++ b/ui/app/pages/home/home.component.js @@ -10,7 +10,7 @@ import WalletView from '../../components/app/wallet-view' import TransactionList from '../../components/app/transaction-list' import TransactionViewBalance from '../../components/app/transaction-view-balance' import MenuBar from '../../components/app/menu-bar' -import ConnectedSites from '../connected-sites/connected-sites.component' +import ConnectedSites from '../connected-sites' import { RESTORE_VAULT_ROUTE, diff --git a/ui/app/pages/index.scss b/ui/app/pages/index.scss index 6e2769a77a1d..c5e0c854627e 100644 --- a/ui/app/pages/index.scss +++ b/ui/app/pages/index.scss @@ -10,6 +10,8 @@ @import 'confirm-add-token/index'; +@import 'connected-sites/index'; + @import 'settings/index'; @import 'first-time-flow/index';