mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Consolidate connected account alerts (#8802)
* update connected accounts appearance * consolidate account alerts * UnconnectedAccountAlert: use ConnectedAccountsList * move switch account action out of menu in all views Co-authored-by: Mark Stacey <markjstacey@gmail.com>
This commit is contained in:
parent
3f8fa161ca
commit
4dfe4e7463
@ -28,11 +28,8 @@
|
||||
"connectedAccountsEmptyDescription": {
|
||||
"message": "MetaMask is not connected this site. To connect to a Web3 site, find the connect button on their site."
|
||||
},
|
||||
"primary": {
|
||||
"message": "Primary"
|
||||
},
|
||||
"lastActive": {
|
||||
"message": "Last active"
|
||||
"currentAccountNotConnected": {
|
||||
"message": "Your current account is not connected"
|
||||
},
|
||||
"switchToThisAccount": {
|
||||
"message": "Switch to this account"
|
||||
@ -139,6 +136,9 @@
|
||||
"accountSelectionRequired": {
|
||||
"message": "You need to select an account!"
|
||||
},
|
||||
"active": {
|
||||
"message": "Active"
|
||||
},
|
||||
"activity": {
|
||||
"message": "Activity"
|
||||
},
|
||||
@ -187,17 +187,11 @@
|
||||
"alertsSettingsDescription": {
|
||||
"message": "Enable or disable each alert"
|
||||
},
|
||||
"alertSettingsSwitchToConnected": {
|
||||
"message": "Opening popup with an unconnected account selected"
|
||||
},
|
||||
"alertSettingsSwitchToConnectedDescription": {
|
||||
"message": "This alert is shown when you open the popup with an unconnected account selected."
|
||||
},
|
||||
"alertSettingsUnconnectedAccount": {
|
||||
"message": "Switching to an unconnected account"
|
||||
},
|
||||
"alertSettingsUnconnectedAccountDescription": {
|
||||
"message": "This alert is shown in the popup when you switch from a connected account to an unconnected account."
|
||||
"alertSettingsUnconnectedAccount": {
|
||||
"message": "Browsing a website with an unconnected account selected"
|
||||
},
|
||||
"alertSettingsUnconnectedAccountDescription": {
|
||||
"message": "This alert is shown in the popup when you are browsing a connected Web3 site, but the currently selected account is not connected."
|
||||
},
|
||||
"allowOriginSpendToken": {
|
||||
"message": "Allow $1 to spend your $2?",
|
||||
@ -991,9 +985,6 @@
|
||||
"noAlreadyHaveSeed": {
|
||||
"message": "No, I already have a seed phrase"
|
||||
},
|
||||
"notConnected": {
|
||||
"message": "Not connected"
|
||||
},
|
||||
"protectYourKeys": {
|
||||
"message": "Protect Your Keys!"
|
||||
},
|
||||
@ -1478,16 +1469,6 @@
|
||||
"supportCenter": {
|
||||
"message": "Visit our Support Center"
|
||||
},
|
||||
"switchAccounts": {
|
||||
"message": "Switch accounts"
|
||||
},
|
||||
"switchToConnectedAlertMultipleAccountsDescription": {
|
||||
"message": "This account is not connected. Switch to a connected account?"
|
||||
},
|
||||
"switchToConnectedAlertSingleAccountDescription": {
|
||||
"message": "This account is not connected. Switch to a connected account ($1)?",
|
||||
"description": "$1 will be replaced by the name of the connected account"
|
||||
},
|
||||
"symbol": {
|
||||
"message": "Symbol"
|
||||
},
|
||||
@ -1635,10 +1616,7 @@
|
||||
"unapproved": {
|
||||
"message": "Unapproved"
|
||||
},
|
||||
"unconnectedAccountAlertDescription": {
|
||||
"message": "$1 is not connected to $2."
|
||||
},
|
||||
"unconnectedAccountAlertDisableTooltip": {
|
||||
"alertDisableTooltip": {
|
||||
"message": "This can be changed in \"Settings > Alerts\""
|
||||
},
|
||||
"units": {
|
||||
@ -1776,11 +1754,5 @@
|
||||
"encryptionPublicKeyNotice": {
|
||||
"message": "$1 would like your public encryption key. By consenting, this site will be able to compose encrypted messages to you.",
|
||||
"description": "$1 is website or dapp name"
|
||||
},
|
||||
"thisSite": {
|
||||
"message": "this site"
|
||||
},
|
||||
"thisAccount": {
|
||||
"message": "This account"
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import ObservableStore from 'obs-store'
|
||||
*/
|
||||
|
||||
export const ALERT_TYPES = {
|
||||
switchToConnected: 'switchToConnected',
|
||||
unconnectedAccount: 'unconnectedAccount',
|
||||
}
|
||||
|
||||
@ -25,7 +24,7 @@ const defaultState = {
|
||||
},
|
||||
{}
|
||||
),
|
||||
switchToConnectedAlertShown: {},
|
||||
unconnectedAccountAlertShownOrigins: {},
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,7 +43,7 @@ export default class AlertController {
|
||||
defaultState,
|
||||
initState,
|
||||
{
|
||||
switchToConnectedAlertShown: {},
|
||||
unconnectedAccountAlertShownOrigins: {},
|
||||
}
|
||||
)
|
||||
this.store = new ObservableStore(state)
|
||||
@ -54,9 +53,9 @@ export default class AlertController {
|
||||
|
||||
preferencesStore.subscribe(({ selectedAddress }) => {
|
||||
const currentState = this.store.getState()
|
||||
if (currentState.switchToConnectedAlertShown && this.selectedAddress !== selectedAddress) {
|
||||
if (currentState.unconnectedAccountAlertShownOrigins && this.selectedAddress !== selectedAddress) {
|
||||
this.selectedAddress = selectedAddress
|
||||
this.store.updateState({ switchToConnectedAlertShown: {} })
|
||||
this.store.updateState({ unconnectedAccountAlertShownOrigins: {} })
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -72,10 +71,10 @@ export default class AlertController {
|
||||
* Sets the "switch to connected" alert as shown for the given origin
|
||||
* @param {string} origin - The origin the alert has been shown for
|
||||
*/
|
||||
setSwitchToConnectedAlertShown (origin) {
|
||||
let { switchToConnectedAlertShown } = this.store.getState()
|
||||
switchToConnectedAlertShown = { ...switchToConnectedAlertShown }
|
||||
switchToConnectedAlertShown[origin] = true
|
||||
this.store.updateState({ switchToConnectedAlertShown })
|
||||
setUnconnectedAccountAlertShown (origin) {
|
||||
let { unconnectedAccountAlertShownOrigins } = this.store.getState()
|
||||
unconnectedAccountAlertShownOrigins = { ...unconnectedAccountAlertShownOrigins }
|
||||
unconnectedAccountAlertShownOrigins[origin] = true
|
||||
this.store.updateState({ unconnectedAccountAlertShownOrigins })
|
||||
}
|
||||
}
|
||||
|
@ -545,7 +545,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
|
||||
// alert controller
|
||||
setAlertEnabledness: nodeify(alertController.setAlertEnabledness, alertController),
|
||||
setSwitchToConnectedAlertShown: nodeify(this.alertController.setSwitchToConnectedAlertShown, this.alertController),
|
||||
setUnconnectedAccountAlertShown: nodeify(this.alertController.setUnconnectedAccountAlertShown, this.alertController),
|
||||
|
||||
// 3Box
|
||||
setThreeBoxSyncingPermission: nodeify(threeBoxController.setThreeBoxSyncingPermission, threeBoxController),
|
||||
|
@ -522,9 +522,6 @@
|
||||
"priceAndTimeEstimatesLastRetrieved": 1541527901281,
|
||||
"errors": {}
|
||||
},
|
||||
"switchToConnected": {
|
||||
"state": "CLOSED"
|
||||
},
|
||||
"unconnectedAccount": {
|
||||
"state": "CLOSED"
|
||||
},
|
||||
|
@ -473,9 +473,6 @@
|
||||
"priceAndTimeEstimatesLastRetrieved": 1541527901281,
|
||||
"errors": {}
|
||||
},
|
||||
"switchToConnected": {
|
||||
"state": "CLOSED"
|
||||
},
|
||||
"unconnectedAccount": {
|
||||
"state": "CLOSED"
|
||||
},
|
||||
|
@ -1295,9 +1295,6 @@
|
||||
"errors": {}
|
||||
},
|
||||
"confirmTransaction": {},
|
||||
"switchToConnected": {
|
||||
"state": "CLOSED"
|
||||
},
|
||||
"unconnectedAccount": {
|
||||
"state": "CLOSED"
|
||||
}
|
||||
|
@ -2,22 +2,15 @@ import React from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
import UnconnectedAccountAlert from './unconnected-account-alert'
|
||||
import SwitchToConnectedAlert from './switch-to-connected-alert'
|
||||
import { alertIsOpen as unconnectedAccountAlertIsOpen } from '../../../ducks/alerts/unconnected-account'
|
||||
import { alertIsOpen as switchToConnectedAlertIsOpen } from '../../../ducks/alerts/switch-to-connected'
|
||||
|
||||
const Alerts = () => {
|
||||
const _unconnectedAccountAlertIsOpen = useSelector(unconnectedAccountAlertIsOpen)
|
||||
const _switchToConnectedAlertIsOpen = useSelector(switchToConnectedAlertIsOpen)
|
||||
|
||||
if (_unconnectedAccountAlertIsOpen) {
|
||||
return (
|
||||
<UnconnectedAccountAlert />
|
||||
)
|
||||
} else if (_switchToConnectedAlertIsOpen) {
|
||||
return (
|
||||
<SwitchToConnectedAlert />
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
|
@ -1,3 +1 @@
|
||||
@import './unconnected-account-alert/unconnected-account-alert';
|
||||
|
||||
@import './switch-to-connected-alert/switch-to-connected-alert';
|
||||
|
@ -1 +0,0 @@
|
||||
export { default } from './switch-to-connected-alert'
|
@ -1,121 +0,0 @@
|
||||
import React, { useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
|
||||
import {
|
||||
ALERT_STATE,
|
||||
switchToAccount,
|
||||
dismissAlert,
|
||||
dismissAndDisableAlert,
|
||||
getAlertState,
|
||||
} from '../../../../ducks/alerts/switch-to-connected'
|
||||
import { getPermittedIdentitiesForCurrentTab } from '../../../../selectors'
|
||||
import Popover from '../../../ui/popover'
|
||||
import Button from '../../../ui/button'
|
||||
import Dropdown from '../../../ui/dropdown'
|
||||
import Checkbox from '../../../ui/check-box'
|
||||
import Tooltip from '../../../ui/tooltip-v2'
|
||||
import { useI18nContext } from '../../../../hooks/useI18nContext'
|
||||
|
||||
const {
|
||||
ERROR,
|
||||
LOADING,
|
||||
} = ALERT_STATE
|
||||
|
||||
const SwitchToUnconnectedAccountAlert = () => {
|
||||
const t = useI18nContext()
|
||||
const dispatch = useDispatch()
|
||||
const alertState = useSelector(getAlertState)
|
||||
const connectedAccounts = useSelector(getPermittedIdentitiesForCurrentTab)
|
||||
const [accountToSwitchTo, setAccountToSwitchTo] = useState(connectedAccounts[0].address)
|
||||
const [dontShowThisAgain, setDontShowThisAgain] = useState(false)
|
||||
|
||||
const onClose = async () => {
|
||||
return dontShowThisAgain
|
||||
? await dispatch(dismissAndDisableAlert())
|
||||
: dispatch(dismissAlert())
|
||||
}
|
||||
|
||||
const options = connectedAccounts.map((account) => {
|
||||
return { name: account.name, value: account.address }
|
||||
})
|
||||
|
||||
return (
|
||||
<Popover
|
||||
contentClassName="switch-to-connected-alert__content"
|
||||
footer={(
|
||||
<>
|
||||
{
|
||||
alertState === ERROR
|
||||
? (
|
||||
<div className="switch-to-connected-alert__error">
|
||||
{ t('failureMessage') }
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
<div className="switch-to-connected-alert__footer-buttons">
|
||||
<Button
|
||||
disabled={alertState === LOADING}
|
||||
onClick={onClose}
|
||||
type="secondary"
|
||||
>
|
||||
{ t('dismiss') }
|
||||
</Button>
|
||||
<Button
|
||||
disabled={alertState === LOADING || alertState === ERROR || dontShowThisAgain}
|
||||
onClick={() => dispatch(switchToAccount(accountToSwitchTo))}
|
||||
type="primary"
|
||||
>
|
||||
{ t('switchAccounts') }
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
footerClassName="switch-to-connected-alert__footer"
|
||||
onClose={onClose}
|
||||
subtitle={
|
||||
connectedAccounts.length > 1
|
||||
? t('switchToConnectedAlertMultipleAccountsDescription')
|
||||
: t('switchToConnectedAlertSingleAccountDescription', [connectedAccounts[0].name])
|
||||
}
|
||||
title={t('notConnected')}
|
||||
>
|
||||
{
|
||||
connectedAccounts.length > 1
|
||||
? (
|
||||
<Dropdown
|
||||
className="switch-to-connected-alert__dropdown"
|
||||
title="Switch to account"
|
||||
onChange={(address) => setAccountToSwitchTo(address)}
|
||||
options={options}
|
||||
selectedOption={accountToSwitchTo}
|
||||
/>
|
||||
)
|
||||
: null
|
||||
}
|
||||
<div className="switch-to-connected-alert__checkbox-wrapper">
|
||||
<Checkbox
|
||||
id="switchToConnected_dontShowThisAgain"
|
||||
checked={dontShowThisAgain}
|
||||
className="switch-to-connected-alert__checkbox"
|
||||
onClick={() => setDontShowThisAgain((checked) => !checked)}
|
||||
/>
|
||||
<label
|
||||
className="switch-to-connected-alert__checkbox-label"
|
||||
htmlFor="switchToConnected_dontShowThisAgain"
|
||||
>
|
||||
{ t('dontShowThisAgain') }
|
||||
<Tooltip
|
||||
position="top"
|
||||
title={t('unconnectedAccountAlertDisableTooltip')}
|
||||
wrapperClassName="switch-to-connected-alert__checkbox-label-tooltip"
|
||||
>
|
||||
<i className="fa fa-info-circle" />
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
export default SwitchToUnconnectedAccountAlert
|
@ -1,66 +0,0 @@
|
||||
.switch-to-connected-alert {
|
||||
&__footer {
|
||||
flex-direction: column;
|
||||
|
||||
:only-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__footer-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
button:first-child {
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&__error {
|
||||
margin-bottom: 16px;
|
||||
padding: 16px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #D73A49;
|
||||
background: #F8EAE8;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
align-items: center;
|
||||
padding: 0 24px 24px 24px;
|
||||
}
|
||||
|
||||
&__dropdown {
|
||||
background-color: white;
|
||||
width: 100%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&__checkbox-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__checkbox {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
&__checkbox-label {
|
||||
font-size: 14px;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
color: $Grey-500;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__checkbox-label-tooltip {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
@ -7,12 +7,20 @@ import {
|
||||
dismissAlert,
|
||||
dismissAndDisableAlert,
|
||||
getAlertState,
|
||||
switchToAccount,
|
||||
} from '../../../../ducks/alerts/unconnected-account'
|
||||
import {
|
||||
getOriginOfCurrentTab,
|
||||
getPermittedIdentitiesForCurrentTab,
|
||||
getSelectedAddress,
|
||||
getSelectedIdentity,
|
||||
} from '../../../../selectors'
|
||||
import { isExtensionUrl } from '../../../../helpers/utils/util'
|
||||
import Popover from '../../../ui/popover'
|
||||
import Button from '../../../ui/button'
|
||||
import Checkbox from '../../../ui/check-box'
|
||||
import Tooltip from '../../../ui/tooltip-v2'
|
||||
import { getSelectedIdentity, getOriginOfCurrentTab } from '../../../../selectors'
|
||||
import ConnectedAccountsList from '../../connected-accounts-list'
|
||||
import { useI18nContext } from '../../../../hooks/useI18nContext'
|
||||
|
||||
const {
|
||||
@ -20,12 +28,14 @@ const {
|
||||
LOADING,
|
||||
} = ALERT_STATE
|
||||
|
||||
const SwitchToUnconnectedAccountAlert = () => {
|
||||
const UnconnectedAccountAlert = () => {
|
||||
const t = useI18nContext()
|
||||
const dispatch = useDispatch()
|
||||
const alertState = useSelector(getAlertState)
|
||||
const connectedAccounts = useSelector(getPermittedIdentitiesForCurrentTab)
|
||||
const origin = useSelector(getOriginOfCurrentTab)
|
||||
const selectedIdentity = useSelector(getSelectedIdentity)
|
||||
const selectedAddress = useSelector(getSelectedAddress)
|
||||
const [dontShowThisAgain, setDontShowThisAgain] = useState(false)
|
||||
|
||||
const onClose = async () => {
|
||||
@ -34,67 +44,70 @@ const SwitchToUnconnectedAccountAlert = () => {
|
||||
: dispatch(dismissAlert())
|
||||
}
|
||||
|
||||
const accountName = selectedIdentity?.name || t('thisAccount')
|
||||
const siteName = origin || t('thisSite')
|
||||
const footer = (
|
||||
<>
|
||||
{
|
||||
alertState === ERROR
|
||||
? (
|
||||
<div className="unconnected-account-alert__error">
|
||||
{ t('failureMessage') }
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
<div className="unconnected-account-alert__footer-row">
|
||||
<div className="unconnected-account-alert__checkbox-wrapper">
|
||||
<Checkbox
|
||||
id="unconnectedAccount_dontShowThisAgain"
|
||||
checked={dontShowThisAgain}
|
||||
className="unconnected-account-alert__checkbox"
|
||||
onClick={() => setDontShowThisAgain((checked) => !checked)}
|
||||
/>
|
||||
<label
|
||||
className="unconnected-account-alert__checkbox-label"
|
||||
htmlFor="unconnectedAccount_dontShowThisAgain"
|
||||
>
|
||||
{ t('dontShowThisAgain') }
|
||||
<Tooltip
|
||||
position="top"
|
||||
title={t('alertDisableTooltip')}
|
||||
wrapperClassName="unconnected-account-alert__checkbox-label-tooltip"
|
||||
>
|
||||
<i className="fa fa-info-circle" />
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
<Button
|
||||
disabled={alertState === LOADING}
|
||||
onClick={onClose}
|
||||
type="secondary"
|
||||
className="unconnected-account-alert__dismiss-button"
|
||||
>
|
||||
{ t('dismiss') }
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
return (
|
||||
<Popover
|
||||
contentClassName="unconnected-account-alert__content"
|
||||
title={t('notConnected')}
|
||||
subtitle={t('unconnectedAccountAlertDescription', [ accountName, siteName ])}
|
||||
title={isExtensionUrl(origin) ? t('currentExtension') : new URL(origin).host}
|
||||
subtitle={t('currentAccountNotConnected')}
|
||||
onClose={onClose}
|
||||
footer={(
|
||||
<>
|
||||
{
|
||||
alertState === ERROR
|
||||
? (
|
||||
<div className="unconnected-account-alert__error">
|
||||
{ t('failureMessage') }
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
<div className="unconnected-account-alert__footer-buttons">
|
||||
<Button
|
||||
disabled={alertState === LOADING}
|
||||
onClick={onClose}
|
||||
type="secondary"
|
||||
>
|
||||
{ t('dismiss') }
|
||||
</Button>
|
||||
<Button
|
||||
disabled={alertState === LOADING || alertState === ERROR || dontShowThisAgain }
|
||||
onClick={() => dispatch(connectAccount())}
|
||||
type="primary"
|
||||
>
|
||||
{ t('connect') }
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
contentClassName="unconnected-account-alert__content"
|
||||
footerClassName="unconnected-account-alert__footer"
|
||||
footer={footer}
|
||||
>
|
||||
<Checkbox
|
||||
id="unconnectedAccount_dontShowThisAgain"
|
||||
checked={dontShowThisAgain}
|
||||
className="unconnected-account-alert__checkbox"
|
||||
onClick={() => setDontShowThisAgain((checked) => !checked)}
|
||||
<ConnectedAccountsList
|
||||
accountToConnect={selectedIdentity}
|
||||
connectAccount={() => dispatch(connectAccount(selectedAddress))}
|
||||
connectedAccounts={connectedAccounts}
|
||||
selectedAddress={selectedAddress}
|
||||
setSelectedAddress={(address) => dispatch(switchToAccount(address))}
|
||||
shouldRenderListOptions={false}
|
||||
/>
|
||||
<label
|
||||
className="unconnected-account-alert__checkbox-label"
|
||||
htmlFor="unconnectedAccount_dontShowThisAgain"
|
||||
>
|
||||
{ t('dontShowThisAgain') }
|
||||
<Tooltip
|
||||
position="top"
|
||||
title={t('unconnectedAccountAlertDisableTooltip')}
|
||||
wrapperClassName="unconnected-account-alert__checkbox-label-tooltip"
|
||||
>
|
||||
<i className="fa fa-info-circle" />
|
||||
</Tooltip>
|
||||
</label>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
export default SwitchToUnconnectedAccountAlert
|
||||
export default UnconnectedAccountAlert
|
||||
|
@ -1,25 +1,28 @@
|
||||
.unconnected-account-alert {
|
||||
&__content {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
flex-direction: column;
|
||||
|
||||
:only-child {
|
||||
> :only-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__footer-buttons {
|
||||
&__footer-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
button:first-child {
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
padding: 8px;
|
||||
}
|
||||
&__dismiss-button {
|
||||
background: #037DD6;
|
||||
color: white;
|
||||
height: 40px;
|
||||
width: 100px;
|
||||
border: 0;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
&__error {
|
||||
@ -31,21 +34,24 @@
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
&__checkbox-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0 24px 24px 24px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__checkbox {
|
||||
margin-right: 8px;
|
||||
padding-top: 1px; // better alignment with rest of content
|
||||
}
|
||||
|
||||
&__checkbox-label {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
font-size: 12px;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
color: $Grey-500;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__checkbox-label-tooltip {
|
||||
|
@ -12,13 +12,15 @@ export default class ConnectedAccountsListItem extends PureComponent {
|
||||
address: PropTypes.string.isRequired,
|
||||
className: PropTypes.string,
|
||||
name: PropTypes.node.isRequired,
|
||||
status: PropTypes.node.isRequired,
|
||||
status: PropTypes.string,
|
||||
action: PropTypes.node,
|
||||
options: PropTypes.node,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
className: null,
|
||||
options: null,
|
||||
action: null,
|
||||
}
|
||||
|
||||
render () {
|
||||
@ -27,6 +29,7 @@ export default class ConnectedAccountsListItem extends PureComponent {
|
||||
className,
|
||||
name,
|
||||
status,
|
||||
action,
|
||||
options,
|
||||
} = this.props
|
||||
|
||||
@ -39,18 +42,20 @@ export default class ConnectedAccountsListItem extends PureComponent {
|
||||
diameter={32}
|
||||
/>
|
||||
<div>
|
||||
<p>
|
||||
<strong className="connected-accounts-list__account-name">{name}</strong>
|
||||
<p className="connected-accounts-list__account-name">
|
||||
<strong>{name}</strong>
|
||||
</p>
|
||||
{
|
||||
status
|
||||
? (
|
||||
<p className="connected-accounts-list__account-status">
|
||||
|
||||
{status}
|
||||
</p>
|
||||
)
|
||||
: null
|
||||
}
|
||||
{action}
|
||||
</div>
|
||||
</div>
|
||||
{options}
|
||||
|
@ -1 +0,0 @@
|
||||
export { default } from './connected-accounts-list-permissions.component'
|
@ -1,7 +1,5 @@
|
||||
import { DateTime } from 'luxon'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { PureComponent } from 'react'
|
||||
import ConnectedAccountsListPermissions from './connected-accounts-list-permissions'
|
||||
import ConnectedAccountsListItem from './connected-accounts-list-item'
|
||||
import ConnectedAccountsListOptions from './connected-accounts-list-options'
|
||||
import { MenuItem } from '../../ui/menu'
|
||||
@ -13,7 +11,6 @@ export default class ConnectedAccountsList extends PureComponent {
|
||||
|
||||
static defaultProps = {
|
||||
accountToConnect: null,
|
||||
permissions: undefined,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
@ -26,31 +23,35 @@ export default class ConnectedAccountsList extends PureComponent {
|
||||
name: PropTypes.string.isRequired,
|
||||
lastActive: PropTypes.number,
|
||||
})).isRequired,
|
||||
permissions: PropTypes.arrayOf(PropTypes.shape({
|
||||
key: PropTypes.string.isRequired,
|
||||
})),
|
||||
connectAccount: PropTypes.func.isRequired,
|
||||
selectedAddress: PropTypes.string.isRequired,
|
||||
addPermittedAccount: PropTypes.func.isRequired,
|
||||
removePermittedAccount: PropTypes.func.isRequired,
|
||||
removePermittedAccount: PropTypes.func,
|
||||
setSelectedAddress: PropTypes.func.isRequired,
|
||||
shouldRenderListOptions: (props, propName, componentName) => {
|
||||
if (typeof props[propName] !== 'boolean') {
|
||||
return new Error(
|
||||
`Warning: Failed prop type: '${propName}' of component '${componentName}' must be a boolean. Received: ${typeof props[propName]}`
|
||||
)
|
||||
} else if (props[propName] && !props['removePermittedAccount']) {
|
||||
return new Error(
|
||||
`Warning: Failed prop type: '${propName}' of component '${componentName}' requires prop 'removePermittedAccount'.`
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
state = {
|
||||
accountWithOptionsShown: null,
|
||||
}
|
||||
|
||||
connectAccount = () => {
|
||||
this.props.addPermittedAccount(this.props.accountToConnect?.address)
|
||||
}
|
||||
|
||||
disconnectAccount = () => {
|
||||
this.hideAccountOptions()
|
||||
this.props.removePermittedAccount(this.state.accountWithOptionsShown)
|
||||
}
|
||||
|
||||
switchAccount = () => {
|
||||
switchAccount = (address) => {
|
||||
this.hideAccountOptions()
|
||||
this.props.setSelectedAddress(this.state.accountWithOptionsShown)
|
||||
this.props.setSelectedAddress(address)
|
||||
}
|
||||
|
||||
hideAccountOptions = () => {
|
||||
@ -62,7 +63,7 @@ export default class ConnectedAccountsList extends PureComponent {
|
||||
}
|
||||
|
||||
renderUnconnectedAccount () {
|
||||
const { accountToConnect } = this.props
|
||||
const { accountToConnect, connectAccount } = this.props
|
||||
const { t } = this.context
|
||||
|
||||
if (!accountToConnect) {
|
||||
@ -75,73 +76,87 @@ export default class ConnectedAccountsList extends PureComponent {
|
||||
className="connected-accounts-list__row--highlight"
|
||||
address={address}
|
||||
name={`${name} (…${address.substr(-4, 4)})`}
|
||||
status={(
|
||||
<>
|
||||
{t('statusNotConnected')}
|
||||
·
|
||||
<a className="connected-accounts-list__account-status-link" onClick={this.connectAccount}>
|
||||
{t('connect')}
|
||||
</a>
|
||||
</>
|
||||
status={t('statusNotConnected')}
|
||||
action={(
|
||||
<a
|
||||
className="connected-accounts-list__account-status-link"
|
||||
onClick={() => connectAccount(accountToConnect.address)}
|
||||
>
|
||||
{t('connect')}
|
||||
</a>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { connectedAccounts, permissions, selectedAddress } = this.props
|
||||
renderListItemOptions (address) {
|
||||
const { accountWithOptionsShown } = this.state
|
||||
const { t } = this.context
|
||||
|
||||
return (
|
||||
<ConnectedAccountsListOptions
|
||||
onHideOptions={this.hideAccountOptions}
|
||||
onShowOptions={this.showAccountOptions.bind(null, address)}
|
||||
show={accountWithOptionsShown === address}
|
||||
>
|
||||
<MenuItem
|
||||
iconClassName="disconnect-icon"
|
||||
onClick={this.disconnectAccount}
|
||||
>
|
||||
{t('disconnectThisAccount')}
|
||||
</MenuItem>
|
||||
</ConnectedAccountsListOptions>
|
||||
)
|
||||
}
|
||||
|
||||
renderListItemAction (address) {
|
||||
const { t } = this.context
|
||||
|
||||
return (
|
||||
<a
|
||||
className="connected-accounts-list__account-status-link"
|
||||
onClick={() => this.switchAccount(address)}
|
||||
>
|
||||
{t('switchToThisAccount')}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
connectedAccounts,
|
||||
selectedAddress,
|
||||
shouldRenderListOptions,
|
||||
} = this.props
|
||||
const { t } = this.context
|
||||
|
||||
return (
|
||||
<>
|
||||
<main className="connected-accounts-list">
|
||||
{this.renderUnconnectedAccount()}
|
||||
{
|
||||
connectedAccounts.map(({ address, name, lastActive }, index) => {
|
||||
let status
|
||||
if (index === 0) {
|
||||
status = t('primary')
|
||||
} else if (lastActive) {
|
||||
status = `${t('lastActive')}: ${DateTime.fromMillis(lastActive).toISODate()}`
|
||||
}
|
||||
|
||||
connectedAccounts.map(({ address, name }, index) => {
|
||||
return (
|
||||
<ConnectedAccountsListItem
|
||||
key={address}
|
||||
address={address}
|
||||
name={`${name} (…${address.substr(-4, 4)})`}
|
||||
status={status}
|
||||
options={(
|
||||
<ConnectedAccountsListOptions
|
||||
onHideOptions={this.hideAccountOptions}
|
||||
onShowOptions={this.showAccountOptions.bind(null, address)}
|
||||
show={accountWithOptionsShown === address}
|
||||
>
|
||||
{
|
||||
address === selectedAddress ? null : (
|
||||
<MenuItem
|
||||
iconClassName="fas fa-random"
|
||||
onClick={this.switchAccount}
|
||||
>
|
||||
{t('switchToThisAccount')}
|
||||
</MenuItem>
|
||||
)
|
||||
}
|
||||
<MenuItem
|
||||
iconClassName="disconnect-icon"
|
||||
onClick={this.disconnectAccount}
|
||||
>
|
||||
{t('disconnectThisAccount')}
|
||||
</MenuItem>
|
||||
</ConnectedAccountsListOptions>
|
||||
)}
|
||||
status={index === 0 ? t('active') : null}
|
||||
options={
|
||||
shouldRenderListOptions
|
||||
? this.renderListItemOptions(address)
|
||||
: null
|
||||
}
|
||||
action={
|
||||
address !== selectedAddress
|
||||
? this.renderListItemAction(address)
|
||||
: null
|
||||
}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</main>
|
||||
<ConnectedAccountsListPermissions permissions={permissions} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -8,28 +8,34 @@
|
||||
}
|
||||
|
||||
&__account-name {
|
||||
display: inline;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
%account-status-typography {
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
&__account-status {
|
||||
@extend %account-status-typography;
|
||||
display: inline;
|
||||
color: $Grey-500;
|
||||
}
|
||||
|
||||
&__account-status-link {
|
||||
@extend %account-status-typography;
|
||||
display: block;
|
||||
|
||||
&, &:hover {
|
||||
color: $curious-blue;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&__account-status {
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
&__row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -40,10 +46,6 @@
|
||||
|
||||
border-top: 1px solid $geyser;
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom: 1px solid $geyser;
|
||||
}
|
||||
|
||||
&--highlight {
|
||||
background-color: $warning-light-yellow;
|
||||
border: 1px solid $warning-yellow;
|
||||
@ -70,72 +72,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.connected-accounts-permissions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 24px;
|
||||
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
color: $Grey-500;
|
||||
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
p + p {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #24292E;
|
||||
|
||||
button {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
|
||||
background: none;
|
||||
padding: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&__list {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
&__list-item {
|
||||
display: flex;
|
||||
|
||||
i {
|
||||
display: block;
|
||||
padding-right: 8px;
|
||||
font-size: 18px;
|
||||
color: $Grey-800;
|
||||
}
|
||||
}
|
||||
|
||||
&__list-container {
|
||||
max-height: 0px;
|
||||
overflow: hidden;
|
||||
height: auto;
|
||||
transition: max-height 0.8s cubic-bezier(0.4, 0.0, 0.2, 1);
|
||||
|
||||
&--expanded {
|
||||
// arbitrarily set hard coded value for effect to work
|
||||
max-height: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tippy-tooltip.none-theme {
|
||||
background: none;
|
||||
padding: 0;
|
||||
|
@ -2,7 +2,7 @@ import classnames from 'classnames'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { PureComponent } from 'react'
|
||||
|
||||
export default class ConnectedAccountsListPermissions extends PureComponent {
|
||||
export default class ConnectedAccountsPermissions extends PureComponent {
|
||||
static contextTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
}
|
@ -0,0 +1 @@
|
||||
export { default } from './connected-accounts-permissions.component'
|
@ -0,0 +1,64 @@
|
||||
.connected-accounts-permissions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
color: $Grey-500;
|
||||
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
p + p {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #24292E;
|
||||
|
||||
button {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
|
||||
background: none;
|
||||
padding: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&__list {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
&__list-item {
|
||||
display: flex;
|
||||
|
||||
i {
|
||||
display: block;
|
||||
padding-right: 8px;
|
||||
font-size: 18px;
|
||||
color: $Grey-800;
|
||||
}
|
||||
}
|
||||
|
||||
&__list-container {
|
||||
max-height: 0px;
|
||||
overflow: hidden;
|
||||
height: auto;
|
||||
transition: max-height 0.8s cubic-bezier(0.4, 0.0, 0.2, 1);
|
||||
|
||||
&--expanded {
|
||||
// arbitrarily set hard coded value for effect to work
|
||||
max-height: 100px;
|
||||
}
|
||||
}
|
||||
}
|
@ -88,6 +88,8 @@
|
||||
|
||||
@import 'connected-accounts-list/index';
|
||||
|
||||
@import 'connected-accounts-permissions/index';
|
||||
|
||||
@import '../ui/icon-with-fallback/index';
|
||||
|
||||
@import '../ui/icon/index';
|
||||
|
@ -107,7 +107,7 @@
|
||||
border-top: 1px solid #D2D8DD;
|
||||
padding: 16px 24px 24px;
|
||||
|
||||
& :only-child {
|
||||
> :only-child {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1 @@
|
||||
export { default as switchToConnected } from './switch-to-connected'
|
||||
export { default as unconnectedAccount } from './unconnected-account'
|
||||
|
@ -1,111 +0,0 @@
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
import { captureException } from '@sentry/browser'
|
||||
|
||||
import { ALERT_TYPES } from '../../../../app/scripts/controllers/alert'
|
||||
import * as actionConstants from '../../store/actionConstants'
|
||||
import { setAlertEnabledness, setSelectedAddress } from '../../store/actions'
|
||||
|
||||
// Constants
|
||||
|
||||
export const ALERT_STATE = {
|
||||
CLOSED: 'CLOSED',
|
||||
ERROR: 'ERROR',
|
||||
LOADING: 'LOADING',
|
||||
OPEN: 'OPEN',
|
||||
}
|
||||
|
||||
const name = ALERT_TYPES.switchToConnected
|
||||
|
||||
const initialState = {
|
||||
state: ALERT_STATE.CLOSED,
|
||||
}
|
||||
|
||||
// Slice (reducer plus auto-generated actions and action creators)
|
||||
|
||||
const slice = createSlice({
|
||||
name,
|
||||
initialState,
|
||||
reducers: {
|
||||
disableAlertFailed: (state) => {
|
||||
state.state = ALERT_STATE.ERROR
|
||||
},
|
||||
disableAlertRequested: (state) => {
|
||||
state.state = ALERT_STATE.LOADING
|
||||
},
|
||||
disableAlertSucceeded: (state) => {
|
||||
state.state = ALERT_STATE.CLOSED
|
||||
},
|
||||
dismissAlert: (state) => {
|
||||
state.state = ALERT_STATE.CLOSED
|
||||
},
|
||||
switchAccountFailed: (state) => {
|
||||
state.state = ALERT_STATE.ERROR
|
||||
},
|
||||
switchAccountRequested: (state) => {
|
||||
state.state = ALERT_STATE.LOADING
|
||||
},
|
||||
switchAccountSucceeded: (state) => {
|
||||
state.state = ALERT_STATE.CLOSED
|
||||
},
|
||||
},
|
||||
extraReducers: {
|
||||
[actionConstants.SELECTED_ADDRESS_CHANGED]: (state) => {
|
||||
// close the alert if the account is switched while it's open
|
||||
if (state.state === ALERT_STATE.OPEN) {
|
||||
state.state = ALERT_STATE.CLOSED
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const { actions, reducer } = slice
|
||||
|
||||
export default reducer
|
||||
|
||||
// Selectors
|
||||
|
||||
export const getAlertState = (state) => state[name].state
|
||||
|
||||
export const alertIsOpen = (state) => state[name].state !== ALERT_STATE.CLOSED
|
||||
|
||||
// Actions / action-creators
|
||||
|
||||
const {
|
||||
disableAlertFailed,
|
||||
disableAlertRequested,
|
||||
disableAlertSucceeded,
|
||||
dismissAlert,
|
||||
switchAccountFailed,
|
||||
switchAccountRequested,
|
||||
switchAccountSucceeded,
|
||||
} = actions
|
||||
|
||||
export { dismissAlert }
|
||||
|
||||
export const dismissAndDisableAlert = () => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
await dispatch(disableAlertRequested())
|
||||
await dispatch(setAlertEnabledness(name, false))
|
||||
await dispatch(disableAlertSucceeded())
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
captureException(error)
|
||||
await dispatch(disableAlertFailed())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const switchToAccount = (address) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
await dispatch(switchAccountRequested())
|
||||
await dispatch(setSelectedAddress(address))
|
||||
await dispatch(switchAccountSucceeded())
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
captureException(error)
|
||||
await dispatch(switchAccountFailed())
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,11 @@ import { captureException } from '@sentry/browser'
|
||||
|
||||
import { ALERT_TYPES } from '../../../../app/scripts/controllers/alert'
|
||||
import * as actionConstants from '../../store/actionConstants'
|
||||
import { addPermittedAccount, setAlertEnabledness } from '../../store/actions'
|
||||
import {
|
||||
addPermittedAccount,
|
||||
setAlertEnabledness,
|
||||
setSelectedAddress,
|
||||
} from '../../store/actions'
|
||||
import {
|
||||
getOriginOfCurrentTab,
|
||||
getSelectedAddress,
|
||||
@ -39,9 +43,6 @@ const slice = createSlice({
|
||||
connectAccountSucceeded: (state) => {
|
||||
state.state = ALERT_STATE.CLOSED
|
||||
},
|
||||
dismissAlert: (state) => {
|
||||
state.state = ALERT_STATE.CLOSED
|
||||
},
|
||||
disableAlertFailed: (state) => {
|
||||
state.state = ALERT_STATE.ERROR
|
||||
},
|
||||
@ -51,6 +52,18 @@ const slice = createSlice({
|
||||
disableAlertSucceeded: (state) => {
|
||||
state.state = ALERT_STATE.CLOSED
|
||||
},
|
||||
dismissAlert: (state) => {
|
||||
state.state = ALERT_STATE.CLOSED
|
||||
},
|
||||
switchAccountFailed: (state) => {
|
||||
state.state = ALERT_STATE.ERROR
|
||||
},
|
||||
switchAccountRequested: (state) => {
|
||||
state.state = ALERT_STATE.LOADING
|
||||
},
|
||||
switchAccountSucceeded: (state) => {
|
||||
state.state = ALERT_STATE.CLOSED
|
||||
},
|
||||
switchedToUnconnectedAccount: (state) => {
|
||||
state.state = ALERT_STATE.OPEN
|
||||
},
|
||||
@ -81,10 +94,13 @@ const {
|
||||
connectAccountFailed,
|
||||
connectAccountRequested,
|
||||
connectAccountSucceeded,
|
||||
dismissAlert,
|
||||
disableAlertFailed,
|
||||
disableAlertRequested,
|
||||
disableAlertSucceeded,
|
||||
dismissAlert,
|
||||
switchAccountFailed,
|
||||
switchAccountRequested,
|
||||
switchAccountSucceeded,
|
||||
switchedToUnconnectedAccount,
|
||||
} = actions
|
||||
|
||||
@ -104,6 +120,20 @@ export const dismissAndDisableAlert = () => {
|
||||
}
|
||||
}
|
||||
|
||||
export const switchToAccount = (address) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
await dispatch(switchAccountRequested())
|
||||
await dispatch(setSelectedAddress(address))
|
||||
await dispatch(switchAccountSucceeded())
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
captureException(error)
|
||||
await dispatch(switchAccountFailed())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const connectAccount = () => {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState()
|
||||
|
@ -5,12 +5,11 @@ import sendReducer from './send/send.duck'
|
||||
import appStateReducer from './app/app'
|
||||
import confirmTransactionReducer from './confirm-transaction/confirm-transaction.duck'
|
||||
import gasReducer from './gas/gas.duck'
|
||||
import { switchToConnected, unconnectedAccount } from './alerts'
|
||||
import { unconnectedAccount } from './alerts'
|
||||
import historyReducer from './history/history'
|
||||
import { ALERT_TYPES } from '../../../app/scripts/controllers/alert'
|
||||
|
||||
export default combineReducers({
|
||||
[ALERT_TYPES.switchToConnected]: switchToConnected,
|
||||
[ALERT_TYPES.unconnectedAccount]: unconnectedAccount,
|
||||
activeTab: (s) => (s === undefined ? null : s),
|
||||
metamask: metamaskReducer,
|
||||
|
@ -371,10 +371,8 @@ export const getCurrentLocale = (state) => state.metamask.currentLocale
|
||||
|
||||
export const getAlertEnabledness = (state) => state.metamask.alertEnabledness
|
||||
|
||||
export const getSwitchToConnectedAlertEnabledness = (state) => getAlertEnabledness(state)[ALERT_TYPES.switchToConnected]
|
||||
|
||||
export const getUnconnectedAccountAlertEnabledness = (state) => getAlertEnabledness(state)[ALERT_TYPES.unconnectedAccount]
|
||||
|
||||
export const getSwitchToConnectedAlertShown = (state) => state.metamask.switchToConnectedAlertShown
|
||||
export const getUnconnectedAccountAlertShown = (state) => state.metamask.unconnectedAccountAlertShownOrigins
|
||||
|
||||
export const getTokens = (state) => state.metamask.tokens
|
||||
|
@ -309,3 +309,27 @@ export function getAccountByAddress (accounts = [], targetAddress) {
|
||||
export function stripHttpSchemes (urlString) {
|
||||
return urlString.replace(/^https?:\/\//u, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a URL-like value (object or string) is an extension URL.
|
||||
*
|
||||
* @param {string | URL | object} urlLike - The URL-like value to test.
|
||||
* @returns {boolean} Whether the URL-like value is an extension URL.
|
||||
*/
|
||||
export function isExtensionUrl (urlLike) {
|
||||
|
||||
const EXT_PROTOCOLS = ['chrome-extension:', 'moz-extension:']
|
||||
|
||||
if (typeof urlLike === 'string') {
|
||||
for (const protocol of EXT_PROTOCOLS) {
|
||||
if (urlLike.startsWith(protocol)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (urlLike?.protocol) {
|
||||
return EXT_PROTOCOLS.includes(urlLike.protocol)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { PureComponent } from 'react'
|
||||
import { CONNECTED_ROUTE } from '../../helpers/constants/routes'
|
||||
import Popover from '../../components/ui/popover'
|
||||
import ConnectedAccountsList from '../../components/app/connected-accounts-list'
|
||||
import ConnectedAccountsPermissions from '../../components/app/connected-accounts-permissions'
|
||||
|
||||
export default class ConnectedAccounts extends PureComponent {
|
||||
static contextTypes = {
|
||||
@ -17,7 +17,7 @@ export default class ConnectedAccounts extends PureComponent {
|
||||
static propTypes = {
|
||||
accountToConnect: PropTypes.object,
|
||||
activeTabOrigin: PropTypes.string.isRequired,
|
||||
addPermittedAccount: PropTypes.func.isRequired,
|
||||
connectAccount: PropTypes.func.isRequired,
|
||||
connectedAccounts: PropTypes.array.isRequired,
|
||||
mostRecentOverviewPage: PropTypes.string.isRequired,
|
||||
permissions: PropTypes.array,
|
||||
@ -28,16 +28,12 @@ export default class ConnectedAccounts extends PureComponent {
|
||||
history: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
viewConnectedSites = () => {
|
||||
this.props.history.push(CONNECTED_ROUTE)
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
accountToConnect,
|
||||
activeTabOrigin,
|
||||
isActiveTabExtension,
|
||||
addPermittedAccount,
|
||||
connectAccount,
|
||||
connectedAccounts,
|
||||
history,
|
||||
mostRecentOverviewPage,
|
||||
@ -58,15 +54,16 @@ export default class ConnectedAccounts extends PureComponent {
|
||||
subtitle={connectedAccounts.length ? connectedAccountsDescription : t('connectedAccountsEmptyDescription')}
|
||||
onClose={() => history.push(mostRecentOverviewPage)}
|
||||
footerClassName="connected-accounts__footer"
|
||||
footer={<ConnectedAccountsPermissions permissions={permissions} />}
|
||||
>
|
||||
<ConnectedAccountsList
|
||||
accountToConnect={accountToConnect}
|
||||
addPermittedAccount={addPermittedAccount}
|
||||
connectAccount={connectAccount}
|
||||
connectedAccounts={connectedAccounts}
|
||||
permissions={permissions}
|
||||
selectedAddress={selectedAddress}
|
||||
removePermittedAccount={removePermittedAccount}
|
||||
setSelectedAddress={setSelectedAddress}
|
||||
shouldRenderListOptions
|
||||
/>
|
||||
</Popover>
|
||||
)
|
||||
|
@ -6,11 +6,10 @@ import {
|
||||
getPermissionsForActiveTab,
|
||||
getSelectedAddress,
|
||||
} from '../../selectors'
|
||||
import { isExtensionUrl } from '../../helpers/utils/util'
|
||||
import { addPermittedAccount, removePermittedAccount, setSelectedAddress } from '../../store/actions'
|
||||
import { getMostRecentOverviewPage } from '../../ducks/history/history'
|
||||
|
||||
const EXT_PROTOCOLS = ['chrome-extension:', 'moz-extension:']
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { activeTab } = state
|
||||
const accountToConnect = getAccountToConnectToActiveTab(state)
|
||||
@ -18,7 +17,7 @@ const mapStateToProps = (state) => {
|
||||
const permissions = getPermissionsForActiveTab(state)
|
||||
const selectedAddress = getSelectedAddress(state)
|
||||
|
||||
const isActiveTabExtension = EXT_PROTOCOLS.includes(activeTab.protocol)
|
||||
const isActiveTabExtension = isExtensionUrl(activeTab)
|
||||
return {
|
||||
accountToConnect,
|
||||
isActiveTabExtension,
|
||||
@ -39,14 +38,14 @@ const mapDispatchToProps = (dispatch) => {
|
||||
}
|
||||
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||
const { activeTabOrigin: origin } = stateProps
|
||||
const { activeTabOrigin } = stateProps
|
||||
|
||||
return {
|
||||
...ownProps,
|
||||
...stateProps,
|
||||
...dispatchProps,
|
||||
addPermittedAccount: (address) => dispatchProps.addPermittedAccount(origin, address),
|
||||
removePermittedAccount: (address) => dispatchProps.removePermittedAccount(origin, address),
|
||||
connectAccount: (address) => dispatchProps.addPermittedAccount(activeTabOrigin, address),
|
||||
removePermittedAccount: (address) => dispatchProps.removePermittedAccount(activeTabOrigin, address),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,10 +46,6 @@ const AlertsTab = () => {
|
||||
const t = useI18nContext()
|
||||
|
||||
const alertConfig = {
|
||||
[ALERT_TYPES.switchToConnected]: {
|
||||
title: t('alertSettingsSwitchToConnected'),
|
||||
description: t('alertSettingsSwitchToConnectedDescription'),
|
||||
},
|
||||
[ALERT_TYPES.unconnectedAccount]: {
|
||||
title: t('alertSettingsUnconnectedAccount'),
|
||||
description: t('alertSettingsUnconnectedAccountDescription'),
|
||||
|
@ -1187,7 +1187,7 @@ export function showAccountDetail (address) {
|
||||
log.debug(`background.setSelectedAddress`)
|
||||
|
||||
const state = getState()
|
||||
const unconnectedAccountAlertIsEnabled = getUnconnectedAccountAlertEnabledness(state)
|
||||
const unconnectedAccountAccountAlertIsEnabled = getUnconnectedAccountAlertEnabledness(state)
|
||||
const activeTabOrigin = state.activeTab.origin
|
||||
const selectedAddress = getSelectedAddress(state)
|
||||
const permittedAccountsForCurrentTab = getPermittedAccountsForCurrentTab(state)
|
||||
@ -1207,9 +1207,9 @@ export function showAccountDetail (address) {
|
||||
type: actionConstants.SHOW_ACCOUNT_DETAIL,
|
||||
value: address,
|
||||
})
|
||||
if (unconnectedAccountAlertIsEnabled && switchingToUnconnectedAddress) {
|
||||
if (unconnectedAccountAccountAlertIsEnabled && switchingToUnconnectedAddress) {
|
||||
dispatch(switchedToUnconnectedAccount())
|
||||
await setSwitchToConnectedAlertShown(activeTabOrigin)
|
||||
await setUnconnectedAccountAlertShown(activeTabOrigin)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2139,8 +2139,8 @@ export function setAlertEnabledness (alertId, enabledness) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function setSwitchToConnectedAlertShown (origin) {
|
||||
await promisifiedBackground.setSwitchToConnectedAlertShown(origin)
|
||||
export async function setUnconnectedAccountAlertShown (origin) {
|
||||
await promisifiedBackground.setUnconnectedAccountAlertShown(origin)
|
||||
}
|
||||
|
||||
export function loadingMethodDataStarted () {
|
||||
|
18
ui/index.js
18
ui/index.js
@ -13,10 +13,10 @@ import { ENVIRONMENT_TYPE_POPUP } from '../app/scripts/lib/enums'
|
||||
import { fetchLocale } from './app/helpers/utils/i18n-helper'
|
||||
import switchDirection from './app/helpers/utils/switch-direction'
|
||||
import { getPermittedAccountsForCurrentTab, getSelectedAddress } from './app/selectors'
|
||||
import { ALERT_STATE } from './app/ducks/alerts/switch-to-connected'
|
||||
import { ALERT_STATE } from './app/ducks/alerts/unconnected-account'
|
||||
import {
|
||||
getSwitchToConnectedAlertEnabledness,
|
||||
getSwitchToConnectedAlertShown,
|
||||
getUnconnectedAccountAlertEnabledness,
|
||||
getUnconnectedAccountAlertShown,
|
||||
} from './app/ducks/metamask/metamask'
|
||||
|
||||
log.setLevel(global.METAMASK_DEBUG ? 'debug' : 'warn')
|
||||
@ -71,18 +71,18 @@ async function startApp (metamaskState, backgroundConnection, opts) {
|
||||
const origin = draftInitialState.activeTab.origin
|
||||
const permittedAccountsForCurrentTab = getPermittedAccountsForCurrentTab(draftInitialState)
|
||||
const selectedAddress = getSelectedAddress(draftInitialState)
|
||||
const switchToConnectedAlertShown = getSwitchToConnectedAlertShown(draftInitialState)
|
||||
const switchToConnectedAlertIsEnabled = getSwitchToConnectedAlertEnabledness(draftInitialState)
|
||||
const unconnectedAccountAlertShownOrigins = getUnconnectedAccountAlertShown(draftInitialState)
|
||||
const unconnectedAccountAlertIsEnabled = getUnconnectedAccountAlertEnabledness(draftInitialState)
|
||||
|
||||
if (
|
||||
origin &&
|
||||
switchToConnectedAlertIsEnabled &&
|
||||
!switchToConnectedAlertShown[origin] &&
|
||||
unconnectedAccountAlertIsEnabled &&
|
||||
!unconnectedAccountAlertShownOrigins[origin] &&
|
||||
permittedAccountsForCurrentTab.length > 0 &&
|
||||
!permittedAccountsForCurrentTab.includes(selectedAddress)
|
||||
) {
|
||||
draftInitialState[ALERT_TYPES.switchToConnected] = { state: ALERT_STATE.OPEN }
|
||||
actions.setSwitchToConnectedAlertShown(origin)
|
||||
draftInitialState[ALERT_TYPES.unconnectedAccount] = { state: ALERT_STATE.OPEN }
|
||||
actions.setUnconnectedAccountAlertShown(origin)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user