diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 27e8e1dae..f14dfdbdf 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -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" } } diff --git a/app/scripts/controllers/alert.js b/app/scripts/controllers/alert.js index e2b477905..ff0d7fb89 100644 --- a/app/scripts/controllers/alert.js +++ b/app/scripts/controllers/alert.js @@ -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 }) } } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 9e50a1f51..6b2da2946 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -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), diff --git a/development/states/confirm-sig-requests.json b/development/states/confirm-sig-requests.json index a8eb67cdd..18a31a93f 100644 --- a/development/states/confirm-sig-requests.json +++ b/development/states/confirm-sig-requests.json @@ -522,9 +522,6 @@ "priceAndTimeEstimatesLastRetrieved": 1541527901281, "errors": {} }, - "switchToConnected": { - "state": "CLOSED" - }, "unconnectedAccount": { "state": "CLOSED" }, diff --git a/development/states/currency-localization.json b/development/states/currency-localization.json index a61ab10ed..ca8ee7c42 100644 --- a/development/states/currency-localization.json +++ b/development/states/currency-localization.json @@ -473,9 +473,6 @@ "priceAndTimeEstimatesLastRetrieved": 1541527901281, "errors": {} }, - "switchToConnected": { - "state": "CLOSED" - }, "unconnectedAccount": { "state": "CLOSED" }, diff --git a/development/states/tx-list-items.json b/development/states/tx-list-items.json index 969535180..17a2435d5 100644 --- a/development/states/tx-list-items.json +++ b/development/states/tx-list-items.json @@ -1295,9 +1295,6 @@ "errors": {} }, "confirmTransaction": {}, - "switchToConnected": { - "state": "CLOSED" - }, "unconnectedAccount": { "state": "CLOSED" } diff --git a/ui/app/components/app/alerts/alerts.js b/ui/app/components/app/alerts/alerts.js index 9f804d7d9..5d376494c 100644 --- a/ui/app/components/app/alerts/alerts.js +++ b/ui/app/components/app/alerts/alerts.js @@ -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 ( ) - } else if (_switchToConnectedAlertIsOpen) { - return ( - - ) } return null diff --git a/ui/app/components/app/alerts/alerts.scss b/ui/app/components/app/alerts/alerts.scss index 82ec8ead0..280171f5e 100644 --- a/ui/app/components/app/alerts/alerts.scss +++ b/ui/app/components/app/alerts/alerts.scss @@ -1,3 +1 @@ @import './unconnected-account-alert/unconnected-account-alert'; - -@import './switch-to-connected-alert/switch-to-connected-alert'; diff --git a/ui/app/components/app/alerts/switch-to-connected-alert/index.js b/ui/app/components/app/alerts/switch-to-connected-alert/index.js deleted file mode 100644 index 9811c0df2..000000000 --- a/ui/app/components/app/alerts/switch-to-connected-alert/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './switch-to-connected-alert' diff --git a/ui/app/components/app/alerts/switch-to-connected-alert/switch-to-connected-alert.js b/ui/app/components/app/alerts/switch-to-connected-alert/switch-to-connected-alert.js deleted file mode 100644 index d440c250c..000000000 --- a/ui/app/components/app/alerts/switch-to-connected-alert/switch-to-connected-alert.js +++ /dev/null @@ -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 ( - - { - alertState === ERROR - ? ( -
- { t('failureMessage') } -
- ) - : null - } -
- - -
- - )} - 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 - ? ( - setAccountToSwitchTo(address)} - options={options} - selectedOption={accountToSwitchTo} - /> - ) - : null - } -
- setDontShowThisAgain((checked) => !checked)} - /> - -
-
- ) -} - -export default SwitchToUnconnectedAccountAlert diff --git a/ui/app/components/app/alerts/switch-to-connected-alert/switch-to-connected-alert.scss b/ui/app/components/app/alerts/switch-to-connected-alert/switch-to-connected-alert.scss deleted file mode 100644 index e4e7d3f80..000000000 --- a/ui/app/components/app/alerts/switch-to-connected-alert/switch-to-connected-alert.scss +++ /dev/null @@ -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; - } -} diff --git a/ui/app/components/app/alerts/unconnected-account-alert/unconnected-account-alert.js b/ui/app/components/app/alerts/unconnected-account-alert/unconnected-account-alert.js index ca5ed7db4..38a8fda6e 100644 --- a/ui/app/components/app/alerts/unconnected-account-alert/unconnected-account-alert.js +++ b/ui/app/components/app/alerts/unconnected-account-alert/unconnected-account-alert.js @@ -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 + ? ( +
+ { t('failureMessage') } +
+ ) + : null + } +
+
+ setDontShowThisAgain((checked) => !checked)} + /> + +
+ +
+ + ) return ( - { - alertState === ERROR - ? ( -
- { t('failureMessage') } -
- ) - : null - } -
- - -
- - )} + contentClassName="unconnected-account-alert__content" footerClassName="unconnected-account-alert__footer" + footer={footer} > - setDontShowThisAgain((checked) => !checked)} + dispatch(connectAccount(selectedAddress))} + connectedAccounts={connectedAccounts} + selectedAddress={selectedAddress} + setSelectedAddress={(address) => dispatch(switchToAccount(address))} + shouldRenderListOptions={false} /> -
) } -export default SwitchToUnconnectedAccountAlert +export default UnconnectedAccountAlert diff --git a/ui/app/components/app/alerts/unconnected-account-alert/unconnected-account-alert.scss b/ui/app/components/app/alerts/unconnected-account-alert/unconnected-account-alert.scss index 61ecf9a18..54f37beac 100644 --- a/ui/app/components/app/alerts/unconnected-account-alert/unconnected-account-alert.scss +++ b/ui/app/components/app/alerts/unconnected-account-alert/unconnected-account-alert.scss @@ -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 { diff --git a/ui/app/components/app/connected-accounts-list/connected-accounts-list-item/connected-accounts-list-item.component.js b/ui/app/components/app/connected-accounts-list/connected-accounts-list-item/connected-accounts-list-item.component.js index 11b30660d..cfff63c1c 100644 --- a/ui/app/components/app/connected-accounts-list/connected-accounts-list-item/connected-accounts-list-item.component.js +++ b/ui/app/components/app/connected-accounts-list/connected-accounts-list-item/connected-accounts-list-item.component.js @@ -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} />
-

- {name} +

+ {name}

{ status ? (

+    {status}

) : null } + {action}
{options} diff --git a/ui/app/components/app/connected-accounts-list/connected-accounts-list-permissions/index.js b/ui/app/components/app/connected-accounts-list/connected-accounts-list-permissions/index.js deleted file mode 100644 index 7733018f8..000000000 --- a/ui/app/components/app/connected-accounts-list/connected-accounts-list-permissions/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './connected-accounts-list-permissions.component' diff --git a/ui/app/components/app/connected-accounts-list/connected-accounts-list.component.js b/ui/app/components/app/connected-accounts-list/connected-accounts-list.component.js index 78627c160..d4e10db7f 100644 --- a/ui/app/components/app/connected-accounts-list/connected-accounts-list.component.js +++ b/ui/app/components/app/connected-accounts-list/connected-accounts-list.component.js @@ -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')} -  ·  - - {t('connect')} - - + status={t('statusNotConnected')} + action={( + connectAccount(accountToConnect.address)} + > + {t('connect')} + )} /> ) } - render () { - const { connectedAccounts, permissions, selectedAddress } = this.props + renderListItemOptions (address) { const { accountWithOptionsShown } = this.state const { t } = this.context + return ( + + + {t('disconnectThisAccount')} + + + ) + } + + renderListItemAction (address) { + const { t } = this.context + + return ( + this.switchAccount(address)} + > + {t('switchToThisAccount')} + + ) + } + + render () { + const { + connectedAccounts, + selectedAddress, + shouldRenderListOptions, + } = this.props + const { t } = this.context + return ( <>
{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 ( - { - address === selectedAddress ? null : ( - - {t('switchToThisAccount')} - - ) - } - - {t('disconnectThisAccount')} - - - )} + status={index === 0 ? t('active') : null} + options={ + shouldRenderListOptions + ? this.renderListItemOptions(address) + : null + } + action={ + address !== selectedAddress + ? this.renderListItemAction(address) + : null + } /> ) }) }
- ) } diff --git a/ui/app/components/app/connected-accounts-list/index.scss b/ui/app/components/app/connected-accounts-list/index.scss index edb77009d..fb462775e 100644 --- a/ui/app/components/app/connected-accounts-list/index.scss +++ b/ui/app/components/app/connected-accounts-list/index.scss @@ -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; diff --git a/ui/app/components/app/connected-accounts-list/connected-accounts-list-permissions/connected-accounts-list-permissions.component.js b/ui/app/components/app/connected-accounts-permissions/connected-accounts-permissions.component.js similarity index 95% rename from ui/app/components/app/connected-accounts-list/connected-accounts-list-permissions/connected-accounts-list-permissions.component.js rename to ui/app/components/app/connected-accounts-permissions/connected-accounts-permissions.component.js index 7ee497d53..45f886912 100644 --- a/ui/app/components/app/connected-accounts-list/connected-accounts-list-permissions/connected-accounts-list-permissions.component.js +++ b/ui/app/components/app/connected-accounts-permissions/connected-accounts-permissions.component.js @@ -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, } diff --git a/ui/app/components/app/connected-accounts-permissions/index.js b/ui/app/components/app/connected-accounts-permissions/index.js new file mode 100644 index 000000000..6c6113c82 --- /dev/null +++ b/ui/app/components/app/connected-accounts-permissions/index.js @@ -0,0 +1 @@ +export { default } from './connected-accounts-permissions.component' diff --git a/ui/app/components/app/connected-accounts-permissions/index.scss b/ui/app/components/app/connected-accounts-permissions/index.scss new file mode 100644 index 000000000..838e727dc --- /dev/null +++ b/ui/app/components/app/connected-accounts-permissions/index.scss @@ -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; + } + } +} diff --git a/ui/app/components/app/index.scss b/ui/app/components/app/index.scss index cf413f001..9c8b4a8d5 100644 --- a/ui/app/components/app/index.scss +++ b/ui/app/components/app/index.scss @@ -88,6 +88,8 @@ @import 'connected-accounts-list/index'; +@import 'connected-accounts-permissions/index'; + @import '../ui/icon-with-fallback/index'; @import '../ui/icon/index'; diff --git a/ui/app/components/ui/popover/index.scss b/ui/app/components/ui/popover/index.scss index 1cf453914..1742c7754 100644 --- a/ui/app/components/ui/popover/index.scss +++ b/ui/app/components/ui/popover/index.scss @@ -107,7 +107,7 @@ border-top: 1px solid #D2D8DD; padding: 16px 24px 24px; - & :only-child { + > :only-child { margin: 0 auto; } } diff --git a/ui/app/ducks/alerts/index.js b/ui/app/ducks/alerts/index.js index d1cdd0374..a28d23ecb 100644 --- a/ui/app/ducks/alerts/index.js +++ b/ui/app/ducks/alerts/index.js @@ -1,2 +1 @@ -export { default as switchToConnected } from './switch-to-connected' export { default as unconnectedAccount } from './unconnected-account' diff --git a/ui/app/ducks/alerts/switch-to-connected.js b/ui/app/ducks/alerts/switch-to-connected.js deleted file mode 100644 index fb6d478e2..000000000 --- a/ui/app/ducks/alerts/switch-to-connected.js +++ /dev/null @@ -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()) - } - } -} diff --git a/ui/app/ducks/alerts/unconnected-account.js b/ui/app/ducks/alerts/unconnected-account.js index 9610d5080..397533463 100644 --- a/ui/app/ducks/alerts/unconnected-account.js +++ b/ui/app/ducks/alerts/unconnected-account.js @@ -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() diff --git a/ui/app/ducks/index.js b/ui/app/ducks/index.js index 5e7cd7889..09af8914e 100644 --- a/ui/app/ducks/index.js +++ b/ui/app/ducks/index.js @@ -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, diff --git a/ui/app/ducks/metamask/metamask.js b/ui/app/ducks/metamask/metamask.js index 0fe16e77e..e3f35bf38 100644 --- a/ui/app/ducks/metamask/metamask.js +++ b/ui/app/ducks/metamask/metamask.js @@ -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 diff --git a/ui/app/helpers/utils/util.js b/ui/app/helpers/utils/util.js index 33cad8860..cc1aba75c 100644 --- a/ui/app/helpers/utils/util.js +++ b/ui/app/helpers/utils/util.js @@ -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 +} diff --git a/ui/app/pages/connected-accounts/connected-accounts.component.js b/ui/app/pages/connected-accounts/connected-accounts.component.js index cd0900f93..3fea60ed3 100644 --- a/ui/app/pages/connected-accounts/connected-accounts.component.js +++ b/ui/app/pages/connected-accounts/connected-accounts.component.js @@ -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={} > ) diff --git a/ui/app/pages/connected-accounts/connected-accounts.container.js b/ui/app/pages/connected-accounts/connected-accounts.container.js index 1aab4ebfb..1bb5ba84a 100644 --- a/ui/app/pages/connected-accounts/connected-accounts.container.js +++ b/ui/app/pages/connected-accounts/connected-accounts.container.js @@ -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), } } diff --git a/ui/app/pages/settings/alerts-tab/alerts-tab.js b/ui/app/pages/settings/alerts-tab/alerts-tab.js index d2e911f56..1c503e06f 100644 --- a/ui/app/pages/settings/alerts-tab/alerts-tab.js +++ b/ui/app/pages/settings/alerts-tab/alerts-tab.js @@ -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'), diff --git a/ui/app/store/actions.js b/ui/app/store/actions.js index ef3c9d79f..c99a566ba 100644 --- a/ui/app/store/actions.js +++ b/ui/app/store/actions.js @@ -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 () { diff --git a/ui/index.js b/ui/index.js index c0c47b0c9..8f868f317 100644 --- a/ui/index.js +++ b/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) } }