1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 09:57:02 +01:00

enable disconnecting single or all accounts

This commit is contained in:
Erik Marks 2020-05-01 15:50:03 -07:00
parent d45956b2b7
commit 15958683e5
9 changed files with 136 additions and 60 deletions

View File

@ -16,10 +16,13 @@
"disconnect": {
"message": "Disconnect"
},
"disconnectSite": {
"message": "Disconnect $1?"
"disconnectAllAccounts": {
"message": "Disconnect all accounts"
},
"disconnectSiteConfirmationDescription": {
"disconnectPrompt": {
"message": "Disconnect $1"
},
"disconnectAllAccountsConfirmationDescription": {
"message": "Are you sure you want to disconnect? You may lose site functionality."
},
"dismiss": {

View File

@ -40,7 +40,7 @@
"ganache:start": "./development/run-ganache",
"sentry:publish": "node ./development/sentry-publish.js",
"lint": "eslint . --ext js,json",
"lint:fix": "eslint --ext js,json --fix",
"lint:fix": "eslint . --ext js,json --fix",
"lint:changed": "{ git ls-files --others --exclude-standard ; git diff-index --name-only --diff-filter=d HEAD ; } | grep --regexp='[.]js$' --regexp='[.]json$' | tr '\\n' '\\0' | xargs -0 eslint",
"lint:changed:fix": "{ git ls-files --others --exclude-standard ; git diff-index --name-only --diff-filter=d HEAD ; } | grep --regexp='[.]js$' --regexp='[.]json$' | tr '\\n' '\\0' | xargs -0 eslint --fix",
"lint:shellcheck": "./development/shellcheck.sh",

View File

@ -13,20 +13,20 @@ export default class ConnectedSitesList extends Component {
icon: PropTypes.string,
key: PropTypes.string,
})).isRequired,
onDisconnectSite: PropTypes.func.isRequired,
onDisconnect: PropTypes.func.isRequired,
}
render () {
const { connectedDomains, onDisconnectSite } = this.props
const { connectedDomains, onDisconnect } = this.props
const { t } = this.context
return (
<main className="connected-sites__content-rows">
<main className="connected-sites-list__content-rows">
{ connectedDomains.map((domain) => (
<div key={domain.key} className="connected-sites__content-row">
<div className="connected-sites__domain-info">
<div key={domain.key} className="connected-sites-list__content-row">
<div className="connected-sites-list__domain-info">
<IconWithFallBack icon={domain.icon} name={domain.name} />
<span className="connected-sites__domain-name" title={domain.extensionId || domain.key}>
<span className="connected-sites-list__domain-name" title={domain.extensionId || domain.key}>
{
domain.extensionId
? t('externalExtension')
@ -35,9 +35,9 @@ export default class ConnectedSitesList extends Component {
</span>
</div>
<i
className="fas fa-trash-alt connected-sites__trash"
className="fas fa-trash-alt connected-sites-list__trash"
title={t('disconnect')}
onClick={() => onDisconnectSite(domain.key, domain.name)}
onClick={() => onDisconnect(domain.key, domain.name)}
/>
</div>
)) }

View File

@ -1,4 +1,4 @@
.connected-sites {
.connected-sites-list {
&__content-rows {
display: flex;
flex-direction: column;

View File

@ -61,7 +61,7 @@
&__button {
background: none;
font-size: inherit;
padding: 0;
padding: 0 0 0 10px;
}
i {

View File

@ -4,7 +4,7 @@ import ConnectedSitesList from '../../components/app/connected-sites-list'
import Popover from '../../components/ui/popover/popover.component'
import Button from '../../components/ui/button'
export default class ConnectSites extends Component {
export default class ConnectedSites extends Component {
static contextTypes = {
t: PropTypes.func,
}
@ -17,22 +17,26 @@ export default class ConnectSites extends Component {
accountLabel: PropTypes.string.isRequired,
closePopover: PropTypes.func.isRequired,
connectedDomains: PropTypes.arrayOf(PropTypes.object).isRequired,
disconnectSite: PropTypes.func.isRequired,
tabToConnect: PropTypes.object,
legacyExposeAccount: PropTypes.func.isRequired,
disconnectAllAccounts: PropTypes.func.isRequired,
disconnectAccount: PropTypes.func.isRequired,
getOpenMetamaskTabsIds: PropTypes.func.isRequired,
legacyExposeAccount: PropTypes.func.isRequired,
permittedAccountsByOrigin: PropTypes.objectOf(
PropTypes.arrayOf(PropTypes.string),
).isRequired,
tabToConnect: PropTypes.object,
}
state = {
sitePendingDisconnect: null,
}
UNSAFE_componentWillMount () {
componentDidMount () {
const { getOpenMetamaskTabsIds } = this.props
getOpenMetamaskTabsIds()
}
setSitePendingDisconnect = (domainKey, domainName) => {
setPendingDisconnect = (domainKey, domainName) => {
this.setState({
sitePendingDisconnect: {
domainKey,
@ -41,25 +45,33 @@ export default class ConnectSites extends Component {
})
}
clearSitePendingDisconnect = () => {
clearPendingDisconnect = () => {
this.setState({
sitePendingDisconnect: null,
})
}
disconnect = () => {
const { disconnectSite } = this.props
disconnectAccount = () => {
const { disconnectAccount } = this.props
const { sitePendingDisconnect } = this.state
disconnectSite(sitePendingDisconnect.domainKey)
this.clearSitePendingDisconnect()
disconnectAccount(sitePendingDisconnect.domainKey)
this.clearPendingDisconnect()
}
disconnectAllAccounts = () => {
const { disconnectAllAccounts } = this.props
const { sitePendingDisconnect } = this.state
disconnectAllAccounts(sitePendingDisconnect.domainKey)
this.clearPendingDisconnect()
}
renderConnectedSitesList () {
return (
<ConnectedSitesList
connectedDomains={this.props.connectedDomains}
onDisconnectSite={this.setSitePendingDisconnect}
onDisconnect={this.setPendingDisconnect}
/>
)
}
@ -86,7 +98,12 @@ export default class ConnectSites extends Component {
footer={
tabToConnect
? (
<a onClick={legacyExposeAccount}>{ t('connectManually') }</a>
<div
className="connected-sites__text-button"
onClick={legacyExposeAccount}
>
{t('connectManually')}
</div>
)
: null
}
@ -97,25 +114,47 @@ export default class ConnectSites extends Component {
)
}
renderDisconnectSitePopover () {
renderDisconnectPopover () {
const { closePopover } = this.props
const { closePopover, permittedAccountsByOrigin } = this.props
const { t } = this.context
const { sitePendingDisconnect } = this.state
const { sitePendingDisconnect: { domainKey, domainName } } = this.state
const numPermittedAccounts = permittedAccountsByOrigin[domainKey].length
return (
<Popover
title={t('disconnectSite', [sitePendingDisconnect.domainName])}
subtitle={t('disconnectSiteConfirmationDescription')}
className="connected-sites"
title={t('disconnectPrompt', [domainName])}
subtitle={t('disconnectAllAccountsConfirmationDescription')}
onClose={closePopover}
footer={(
<>
<Button type="secondary" onClick={this.clearSitePendingDisconnect}>
{ t('cancel') }
</Button>
<Button type="primary" onClick={this.disconnect}>
{ t('disconnect') }
</Button>
<div className="connected-sites__footer-row">
<Button type="secondary" onClick={this.clearPendingDisconnect}>
{ t('cancel') }
</Button>
<Button
type="primary"
onClick={this.disconnectAccount}
>
{ t('disconnect') }
</Button>
</div>
<div className="connected-sites__footer-row">
{
numPermittedAccounts > 1
? (
<div
className="connected-sites__text-button"
onClick={this.disconnectAllAccounts}
>
{t('disconnectAllAccounts')}
</div>
)
: null
}
</div>
</>
)}
footerClassName="connected-sites__confirmation"
@ -127,7 +166,7 @@ export default class ConnectSites extends Component {
const { sitePendingDisconnect } = this.state
return (
sitePendingDisconnect
? this.renderDisconnectSitePopover()
? this.renderDisconnectPopover()
: this.renderConnectedSitesPopover()
)
}

View File

@ -4,12 +4,14 @@ import {
getOpenMetamaskTabsIds,
legacyExposeAccounts,
removePermissionsFor,
removePermittedAccount,
} from '../../store/actions'
import {
getConnectedDomainsForSelectedAddress,
getCurrentAccountWithSendEtherInfo,
getOriginOfCurrentTab,
getPermissionsDomains,
getPermittedAccountsForCurrentTab,
getPermittedAccountsByOrigin,
getSelectedAddress,
} from '../../selectors/selectors'
import { DEFAULT_ROUTE } from '../../helpers/constants/routes'
@ -18,11 +20,17 @@ import { getOriginFromUrl } from '../../helpers/utils/util'
const mapStateToProps = (state) => {
const { openMetaMaskTabs } = state.appState
const { title, url, id } = state.activeTab
const permittedAccounts = getPermittedAccountsForCurrentTab(state)
const connectedDomains = getConnectedDomainsForSelectedAddress(state)
const originOfCurrentTab = getOriginOfCurrentTab(state)
const permittedAccountsByOrigin = getPermittedAccountsByOrigin(state)
const selectedAddress = getSelectedAddress(state)
const currentTabHasAccounts = permittedAccountsByOrigin[
originOfCurrentTab
]?.length
let tabToConnect
if (url && permittedAccounts.length === 0 && !openMetaMaskTabs[id]) {
if (url && !currentTabHasAccounts && !openMetaMaskTabs[id]) {
tabToConnect = {
title,
origin: getOriginFromUrl(url),
@ -33,7 +41,8 @@ const mapStateToProps = (state) => {
accountLabel: getCurrentAccountWithSendEtherInfo(state).name,
connectedDomains,
domains: getPermissionsDomains(state),
selectedAddress: getSelectedAddress(state),
permittedAccountsByOrigin,
selectedAddress,
tabToConnect,
}
}
@ -41,7 +50,10 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
getOpenMetamaskTabsIds: () => dispatch(getOpenMetamaskTabsIds()),
disconnectSite: (domainKey, domain) => {
disconnectAccount: (domainKey, address) => {
dispatch(removePermittedAccount(domainKey, address))
},
disconnectAllAccounts: (domainKey, domain) => {
const permissionMethodNames = domain.permissions.map(({ parentCapability }) => parentCapability)
dispatch(removePermissionsFor({
[domainKey]: permissionMethodNames,
@ -53,13 +65,14 @@ const mapDispatchToProps = (dispatch) => {
const mergeProps = (stateProps, dispatchProps, ownProps) => {
const {
connectedDomains,
domains,
selectedAddress,
tabToConnect,
connectedDomains,
} = stateProps
const {
disconnectSite,
disconnectAccount,
disconnectAllAccounts,
legacyExposeAccounts: dispatchLegacyExposeAccounts,
} = dispatchProps
const { history } = ownProps
@ -71,8 +84,14 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
...stateProps,
...dispatchProps,
closePopover,
disconnectSite: (domainKey) => {
disconnectSite(domainKey, domains[domainKey])
disconnectAccount: (domainKey) => {
disconnectAccount(domainKey, selectedAddress)
if (connectedDomains.length === 1) {
closePopover()
}
},
disconnectAllAccounts: (domainKey) => {
disconnectAllAccounts(domainKey, domains[domainKey])
if (connectedDomains.length === 1) {
closePopover()
}

View File

@ -1,22 +1,34 @@
.connected-sites {
h2 {
text-overflow: ellipsis;
}
&__confirmation {
flex-direction: column;
button:first-child {
margin-right: 24px;
}
}
&__add-site-manually {
margin-top: -1px;
&__footer-row {
display: flex;
width: 100%;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
&__footer-row + &__footer-row {
margin-top: 15px;
}
&__text-button {
display: flex;
flex-direction: column;
justify-content: center;
font-size: 14px;
line-height: 20px;
& :only-child {
margin: 0;
}
a, a:hover {
cursor: pointer;
color: #037DD6;
}
color: #2f9ae0;
cursor: pointer;
}
}

View File

@ -14,7 +14,10 @@ import {
import { getPermittedAccountsByOrigin } from './permissions'
export { getPermittedAccounts } from './permissions'
export {
getPermittedAccounts,
getPermittedAccountsByOrigin,
} from './permissions'
export function getNetworkIdentifier (state) {
const { metamask: { provider: { type, nickname, rpcTarget } } } = state