mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Update Connected Sites modal design (#8262)
This commit is contained in:
parent
2d66e90d07
commit
cb7f81bb42
@ -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"
|
||||
},
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -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 (
|
||||
<div className="connected-sites-list">
|
||||
{
|
||||
renderableDomains.map((domain, domainIndex) => {
|
||||
const domainIsExpanded = expandedDomain === domain.key
|
||||
return (
|
||||
<div
|
||||
className={classnames('connected-sites-list__domain', {
|
||||
'connected-sites-list__domain--expanded': domainIsExpanded,
|
||||
})}
|
||||
key={`connected-domain-${domainIndex}`}
|
||||
>
|
||||
<div className="connected-sites-list__domain-item" onClick={ () => this.handleDomainItemClick(domain.key) }>
|
||||
<div className="connected-sites-list__domain-item-info-container">
|
||||
<IconWithFallBack icon={domain.icon} name={domain.name} />
|
||||
|
||||
<div className="connected-sites-list__domain-info">
|
||||
<div className="connected-sites-list__domain-names">
|
||||
<div className="connected-sites-list__domain-name">
|
||||
{ domain.extensionId ? t('externalExtension') : domain.name }
|
||||
</div>
|
||||
</div>
|
||||
{ domain.lastConnectedTime
|
||||
? (
|
||||
<div className="connected-sites-list__domain-last-connected">
|
||||
{ t('domainLastConnect', [domain.lastConnectedTime]) }
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
{domainIsExpanded
|
||||
? (
|
||||
<div className="connected-sites-list__domain-origin">
|
||||
{ domain.extensionId ? t('extensionId', [domain.extensionId]) : domain.secondaryName }
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className="connected-sites-list__expand-arrow">
|
||||
{ domainIsExpanded ? <i className="fa fa-chevron-up fa-sm" /> : <i className="fa fa-chevron-down fa-sm" /> }
|
||||
</div>
|
||||
</div>
|
||||
{ domainIsExpanded
|
||||
? (
|
||||
<div className="connected-sites-list__permissions">
|
||||
<div className="connected-sites-list__permission-list">
|
||||
{
|
||||
domain.permissionDescriptions.map((description, pdIndex) => {
|
||||
return (
|
||||
<div className="connected-sites-list__permission" key={`permissionDescription-${pdIndex}`}>
|
||||
<i className="fa fa-check-square fa-sm" />
|
||||
<div className="connected-sites-list__permission-description">
|
||||
{ description }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
className="connected-sites-list__disconnect"
|
||||
onClick={ () => showDisconnectAccountModal(domain.key, domains[domain.key]) }
|
||||
>
|
||||
{ t('disconnectAccount') }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
<main className="connected-sites__content-rows">
|
||||
{ connectedDomains.map((domain) => (
|
||||
<div key={domain.key} className="connected-sites__content-row">
|
||||
<div className="connected-sites__domain-info">
|
||||
<IconWithFallBack icon={domain.icon} name={domain.name} />
|
||||
<span className="connected-sites__domain-name" title={domain.extensionId || domain.key}>
|
||||
{
|
||||
domain.extensionId
|
||||
? t('externalExtension')
|
||||
: domain.key
|
||||
}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
<div className="connected-sites-list__bottom-buttons">
|
||||
<div className="connected-sites-list__disconnect-all">
|
||||
<Button onClick={showDisconnectAllModal} type="danger" >
|
||||
{ t('disconnectAll') }
|
||||
</Button>
|
||||
</span>
|
||||
</div>
|
||||
<i
|
||||
className="fas fa-trash-alt connected-sites__trash"
|
||||
title={t('disconnect')}
|
||||
onClick={() => onDisconnectSite(domain.key, domain.name)}
|
||||
/>
|
||||
</div>
|
||||
{ tabToConnect
|
||||
? (
|
||||
<div className="connected-sites-list__connect-to">
|
||||
<Button
|
||||
onClick={() => legacyExposeAccounts(tabToConnect.origin, selectedAddress)}
|
||||
type="primary"
|
||||
>
|
||||
{ t('connectTo', [tabToConnect.title || tabToConnect.origin]) }
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)) }
|
||||
</main>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
connectedDomains: getRenderablePermissionsDomains(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()),
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ConnectedSitesList)
|
||||
export default connect(mapStateToProps)(ConnectedSitesList)
|
||||
|
@ -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;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border-top: 1px solid #D2D8DD;
|
||||
}
|
||||
|
||||
&__content-row {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
&__domain-item-info-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__identicon-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
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;
|
||||
min-width: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&__domain-name {
|
||||
font-size: 18px;
|
||||
color: #24292E;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
&__domain-origin, &__domain-last-connected {
|
||||
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;
|
||||
}
|
||||
|
||||
&__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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 (
|
||||
<Modal
|
||||
headerText={t('disconnectAccountQuestion')}
|
||||
onClose={() => hideModal()}
|
||||
hideFooter
|
||||
>
|
||||
<div className="disconnect-account-modal">
|
||||
<div className="disconnect-account-modal__description">
|
||||
{ t('disconnectAccountModalDescription', [ accountLabel ]) }
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={ () => {
|
||||
disconnectAccount()
|
||||
hideModal()
|
||||
}}
|
||||
>
|
||||
{ t('disconnectFromThisAccount') }
|
||||
</Button>
|
||||
<Button
|
||||
type="secondary"
|
||||
onClick={ () => hideModal() }
|
||||
className="disconnect-account-modal__cancel-button"
|
||||
>
|
||||
{ t('cancel') }
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
@ -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)
|
@ -1 +0,0 @@
|
||||
export { default } from './disconnect-account.container'
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -12,8 +12,6 @@
|
||||
|
||||
@import './edit-approval-permission/index';
|
||||
|
||||
@import './disconnect-account/index';
|
||||
|
||||
@import './disconnect-all/index';
|
||||
|
||||
@import './new-account-modal/index';
|
||||
|
@ -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: <DisconnectAccount />,
|
||||
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: <DisconnectAll />,
|
||||
mobileModalStyle: {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__close {
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
right: 6px;
|
||||
margin: 10px;
|
||||
&__subtitle {
|
||||
@extend %font;
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
&__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;
|
||||
|
@ -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'
|
||||
import { I18nContext } from '../../../contexts/i18n'
|
||||
|
||||
const Popover = ({ title, children, onClose }) => (
|
||||
<div className="popover-container">
|
||||
<div className="popover-bg" onClick={onClose} />
|
||||
<div className="popover-wrap">
|
||||
<PopoverHeader title={title} onClose={onClose} />
|
||||
<div className="popover-content">
|
||||
{children}
|
||||
const Popover = ({ title, subtitle, children, onBack, onClose }) => {
|
||||
const t = useContext(I18nContext)
|
||||
return (
|
||||
<div className="popover-container">
|
||||
<div className="popover-bg" onClick={onClose} />
|
||||
<div className="popover-wrap">
|
||||
<header className="popover-header">
|
||||
<div className="popover-header__title">
|
||||
<h2 title={title}>
|
||||
{
|
||||
onBack
|
||||
? (
|
||||
<button
|
||||
className="fas fa-chevron-left popover-header__button"
|
||||
title={t('back')}
|
||||
onClick={onBack}
|
||||
/>
|
||||
)
|
||||
: null
|
||||
}
|
||||
{title}
|
||||
</h2>
|
||||
<button
|
||||
className="fas fa-times popover-header__button"
|
||||
title={t('close')}
|
||||
onClick={onClose}
|
||||
/>
|
||||
</div>
|
||||
<p className="popover-header__subtitle">{subtitle}</p>
|
||||
</header>
|
||||
<div className="popover-content">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Popover.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
subtitle: PropTypes.string.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
onBack: PropTypes.func,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
|
@ -1,22 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Close from '../icon/close-icon.component'
|
||||
|
||||
const PopoverHeader = ({ title, onClose }) => (
|
||||
<header className="popover-header">
|
||||
<h2 className="popover-header__heading">{title}</h2>
|
||||
<button className="popover-header__close" onClick={onClose}>
|
||||
<Close
|
||||
size={18}
|
||||
color="#4A4A4A"
|
||||
/>
|
||||
</button>
|
||||
</header>
|
||||
)
|
||||
|
||||
PopoverHeader.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default PopoverHeader
|
@ -10,15 +10,25 @@ const containerStyle = {
|
||||
position: 'relative',
|
||||
}
|
||||
|
||||
const mainWrapperStyle = {
|
||||
padding: '0 24px 24px',
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'Popover',
|
||||
}
|
||||
|
||||
export const approve = () => (
|
||||
<div style={containerStyle}>
|
||||
<Popover title={text('title', 'Approve spend limit')} onClose={action('clicked')}>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<Popover
|
||||
title={text('title', 'Approve spend limit')}
|
||||
subtitle={text('subtitle', 'This is the new limit')}
|
||||
onClose={action('clicked')}
|
||||
>
|
||||
<main style={mainWrapperStyle}>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
</main>
|
||||
</Popover>
|
||||
</div>
|
||||
)
|
||||
|
@ -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 propTypes = {
|
||||
history: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
history,
|
||||
} = this.props
|
||||
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,
|
||||
}
|
||||
|
||||
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 (
|
||||
<Popover title={this.context.t('connectedSites')} onClose={() => history.push(DEFAULT_ROUTE)}>
|
||||
<ConnectedSitesList />
|
||||
</Popover>
|
||||
<>
|
||||
<ConnectedSitesList
|
||||
onDisconnectSite={this.setSitePendingDisconnect}
|
||||
/>
|
||||
{ tabToConnect ? (
|
||||
<footer className="connected-sites__add-site-manually">
|
||||
<a onClick={legacyExposeAccount}>{ t('connectManually') }</a>
|
||||
</footer>
|
||||
) : null }
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
renderDisconnectConfirmation () {
|
||||
const { t } = this.context
|
||||
return (
|
||||
<div className="connected-sites__confirmation">
|
||||
<Button type="secondary" onClick={this.clearSitePendingDisconnect}>
|
||||
{ t('cancel') }
|
||||
</Button>
|
||||
<Button type="primary" onClick={this.disconnect}>
|
||||
{ t('disconnect') }
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { accountLabel, history } = this.props
|
||||
const { t } = this.context
|
||||
const { sitePendingDisconnect } = this.state
|
||||
return (
|
||||
sitePendingDisconnect
|
||||
? (
|
||||
<Popover
|
||||
title={t('disconnectSite', [sitePendingDisconnect.domainName])}
|
||||
subtitle={t('disconnectAccountConfirmationDescription')}
|
||||
onClose={() => history.push(DEFAULT_ROUTE)}
|
||||
>
|
||||
{this.renderDisconnectConfirmation()}
|
||||
</Popover>
|
||||
)
|
||||
: (
|
||||
<Popover
|
||||
title={t('connectedSites')}
|
||||
subtitle={t('connectedSitesDescription', [accountLabel])}
|
||||
onClose={() => history.push(DEFAULT_ROUTE)}
|
||||
>
|
||||
{this.renderConnectedSites()}
|
||||
</Popover>
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
62
ui/app/pages/connected-sites/connected-sites.container.js
Normal file
62
ui/app/pages/connected-sites/connected-sites.container.js
Normal file
@ -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)
|
@ -1 +1 @@
|
||||
export { default } from './connected-sites.component'
|
||||
export { default } from './connected-sites.container'
|
||||
|
32
ui/app/pages/connected-sites/index.scss
Normal file
32
ui/app/pages/connected-sites/index.scss
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
@import 'confirm-add-token/index';
|
||||
|
||||
@import 'connected-sites/index';
|
||||
|
||||
@import 'settings/index';
|
||||
|
||||
@import 'first-time-flow/index';
|
||||
|
Loading…
Reference in New Issue
Block a user