1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Make permission approval redirect flow consistent (#8755)

* make redirect flow consistent

* remove cancel redirect

* extract redirect component into own file
This commit is contained in:
Erik Marks 2020-06-12 09:38:20 -07:00 committed by GitHub
commit 1323233cfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 303 additions and 362 deletions

View File

@ -73,12 +73,6 @@
"showIncomingTransactionsDescription": { "showIncomingTransactionsDescription": {
"message": "Select this to use Etherscan to show incoming transactions in the transactions list" "message": "Select this to use Etherscan to show incoming transactions in the transactions list"
}, },
"cancelling": {
"message": "Cancelling..."
},
"cancelledConnectionWithMetaMask": {
"message": "Cancelled Connection With MetaMask"
},
"chartOnlyAvailableEth": { "chartOnlyAvailableEth": {
"message": "Chart only available on Ethereum networks." "message": "Chart only available on Ethereum networks."
}, },
@ -88,9 +82,6 @@
"connectWithMetaMask": { "connectWithMetaMask": {
"message": "Connect With MetaMask" "message": "Connect With MetaMask"
}, },
"connectingWithMetaMask": {
"message": "Connecting With MetaMask..."
},
"connectTo": { "connectTo": {
"message": "Connect to $1", "message": "Connect to $1",
"description": "$1 is the name/origin of a site/dapp that the user can connect to metamask" "description": "$1 is the name/origin of a site/dapp that the user can connect to metamask"

View File

@ -5,18 +5,12 @@
"showIncomingTransactionsDescription": { "showIncomingTransactionsDescription": {
"message": "Usa Etherscan per visualizzare le transazioni in ingresso nella lista delle transazioni" "message": "Usa Etherscan per visualizzare le transazioni in ingresso nella lista delle transazioni"
}, },
"cancelledConnectionWithMetaMask": {
"message": "Transazioni Annullate con MetaMask"
},
"chartOnlyAvailableEth": { "chartOnlyAvailableEth": {
"message": "Grafico disponibile solo per le reti Ethereum." "message": "Grafico disponibile solo per le reti Ethereum."
}, },
"connectedSites": { "connectedSites": {
"message": "Siti connessi" "message": "Siti connessi"
}, },
"connectingWithMetaMask": {
"message": "Connettendo con MetaMask..."
},
"connectTo": { "connectTo": {
"message": "Collegati a $1", "message": "Collegati a $1",
"description": "$1 is the name/origin of a site/dapp that the user can connect to metamask" "description": "$1 is the name/origin of a site/dapp that the user can connect to metamask"

View File

@ -46,15 +46,6 @@
padding-left: 24px; padding-left: 24px;
padding-right: 24px; padding-right: 24px;
&--redirect {
margin-top: 140px;
width: 100%;
display: flex;
align-items: center;
padding-top: 8px;
height: 144px;
}
a, a:hover { a, a:hover {
color: $dodger-blue; color: $dodger-blue;
} }
@ -94,10 +85,6 @@
@extend %content-text; @extend %content-text;
line-height: 20px; line-height: 20px;
color: #6A737D; color: #6A737D;
&--redirect {
text-align: center;
}
} }
&__permissions-container { &__permissions-container {
@ -147,105 +134,3 @@
font-weight: bold; font-weight: bold;
} }
} }
.permission-result {
@extend %header--24;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
text-align: center;
color: $Black-100;
&__icons {
display: flex;
}
&__center-icon {
display: flex;
position: relative;
justify-content: center;
align-items: center;
font-size: 12px;
}
h1 {
font-size: 14px;
line-height: 18px;
padding: 8px 0 0;
}
h2 {
font-size: 12px;
line-height: 17px;
color: #6A737D;
padding: 0;
}
&__check {
width: 40px;
height: 40px;
background: white url("/images/permissions-check.svg") no-repeat;
position: absolute;
}
&__reject {
position: absolute;
background: white;
display: flex;
justify-content: center;
align-items: center;
i {
color: #D73A49;
transform: scale(3);
}
}
&__identicon, .icon-with-fallback__identicon {
width: 32px;
height: 32px;
&--default {
background-color: #777A87;
color: white;
width: 64px;
height: 64px;
border-radius: 32px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
}
&__identicon-container, .icon-with-fallback__identicon-container {
height: auto;
position: relative;
display: flex;
justify-content: center;
align-items: center;
height: 64px;
width: 64px;
}
&__identicon-border, .icon-with-fallback__identicon-border {
height: 64px;
width: 64px;
border-radius: 50%;
border: 1px solid white;
background: #FFFFFF;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.25);
}
&__identicon-border {
display: flex;
justify-content: center;
align-items: center;
}
.icon-with-fallback__identicon-border {
position: absolute;
}
}

View File

@ -1,9 +1,7 @@
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import IconWithFallBack from '../../../ui/icon-with-fallback'
import PermissionsConnectHeader from '../../permissions-connect-header' import PermissionsConnectHeader from '../../permissions-connect-header'
import Tooltip from '../../../ui/tooltip-v2' import Tooltip from '../../../ui/tooltip-v2'
import classnames from 'classnames'
export default class PermissionPageContainerContent extends PureComponent { export default class PermissionPageContainerContent extends PureComponent {
@ -13,13 +11,9 @@ export default class PermissionPageContainerContent extends PureComponent {
onPermissionToggle: PropTypes.func.isRequired, onPermissionToggle: PropTypes.func.isRequired,
selectedIdentities: PropTypes.array, selectedIdentities: PropTypes.array,
allIdentitiesSelected: PropTypes.bool, allIdentitiesSelected: PropTypes.bool,
redirect: PropTypes.bool,
permissionRejected: PropTypes.bool,
} }
static defaultProps = { static defaultProps = {
redirect: null,
permissionRejected: null,
selectedIdentities: [], selectedIdentities: [],
allIdentitiesSelected: false, allIdentitiesSelected: false,
} }
@ -28,39 +22,6 @@ export default class PermissionPageContainerContent extends PureComponent {
t: PropTypes.func, t: PropTypes.func,
} }
renderBrokenLine () {
return (
<svg width="131" height="2" viewBox="0 0 131 2" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 1H134" stroke="#CDD1E4" strokeLinejoin="round" strokeDasharray="8 7" />
</svg>
)
}
renderRedirect () {
const { t } = this.context
const { permissionRejected, domainMetadata } = this.props
return (
<div className="permission-result">
{ permissionRejected ? t('cancelling') : t('connecting') }
<div className="permission-result__icons">
<IconWithFallBack icon={domainMetadata.icon} name={domainMetadata.name} />
<div className="permission-result__center-icon">
{ permissionRejected
? <span className="permission-result__reject" ><i className="fa fa-times-circle" /></span>
: <span className="permission-result__check" />
}
{ this.renderBrokenLine() }
</div>
<div className="permission-result__identicon-container">
<div className="permission-result__identicon-border">
<img src="/images/logo/metamask-fox.svg" />
</div>
</div>
</div>
</div>
)
}
renderRequestedPermissions () { renderRequestedPermissions () {
const { const {
selectedPermissions, onPermissionToggle, selectedPermissions, onPermissionToggle,
@ -134,14 +95,10 @@ export default class PermissionPageContainerContent extends PureComponent {
} }
getTitle () { getTitle () {
const { domainMetadata, redirect, permissionRejected, selectedIdentities, allIdentitiesSelected } = this.props const { domainMetadata, selectedIdentities, allIdentitiesSelected } = this.props
const { t } = this.context const { t } = this.context
if (redirect && permissionRejected) { if (domainMetadata.extensionId) {
return t('cancelledConnectionWithMetaMask')
} else if (redirect) {
return t('connectingWithMetaMask')
} else if (domainMetadata.extensionId) {
return t('externalExtension', [domainMetadata.extensionId]) return t('externalExtension', [domainMetadata.extensionId])
} else if (allIdentitiesSelected) { } else if (allIdentitiesSelected) {
return t( return t(
@ -166,36 +123,27 @@ export default class PermissionPageContainerContent extends PureComponent {
} }
render () { render () {
const { domainMetadata, redirect } = this.props const { domainMetadata } = this.props
const { t } = this.context const { t } = this.context
const title = this.getTitle() const title = this.getTitle()
return ( return (
<div <div className="permission-approval-container__content">
className={classnames('permission-approval-container__content', { <div className="permission-approval-container__content-container">
'permission-approval-container__content--redirect': redirect, <PermissionsConnectHeader
})} icon={domainMetadata.icon}
> iconName={domainMetadata.origin}
{ !redirect headerTitle={title}
? ( headerText={ domainMetadata.extensionId
<div className="permission-approval-container__content-container"> ? t('allowExternalExtensionTo', [domainMetadata.extensionId])
<PermissionsConnectHeader : t('allowThisSiteTo')
icon={domainMetadata.icon} }
iconName={domainMetadata.origin} />
headerTitle={title} <section className="permission-approval-container__permissions-container">
headerText={ domainMetadata.extensionId { this.renderRequestedPermissions() }
? t('allowExternalExtensionTo', [domainMetadata.extensionId]) </section>
: t('allowThisSiteTo') </div>
}
/>
<section className="permission-approval-container__permissions-container">
{ this.renderRequestedPermissions() }
</section>
</div>
)
: this.renderRedirect()
}
</div> </div>
) )
} }

View File

@ -13,15 +13,11 @@ export default class PermissionPageContainer extends Component {
selectedIdentities: PropTypes.array, selectedIdentities: PropTypes.array,
allIdentitiesSelected: PropTypes.bool, allIdentitiesSelected: PropTypes.bool,
request: PropTypes.object, request: PropTypes.object,
redirect: PropTypes.bool,
permissionRejected: PropTypes.bool,
requestMetadata: PropTypes.object, requestMetadata: PropTypes.object,
targetDomainMetadata: PropTypes.object.isRequired, targetDomainMetadata: PropTypes.object.isRequired,
} }
static defaultProps = { static defaultProps = {
redirect: null,
permissionRejected: null,
request: {}, request: {},
requestMetadata: {}, requestMetadata: {},
selectedIdentities: [], selectedIdentities: [],
@ -116,8 +112,6 @@ export default class PermissionPageContainer extends Component {
requestMetadata, requestMetadata,
targetDomainMetadata, targetDomainMetadata,
selectedIdentities, selectedIdentities,
redirect,
permissionRejected,
allIdentitiesSelected, allIdentitiesSelected,
} = this.props } = this.props
@ -129,27 +123,20 @@ export default class PermissionPageContainer extends Component {
selectedPermissions={this.state.selectedPermissions} selectedPermissions={this.state.selectedPermissions}
onPermissionToggle={this.onPermissionToggle} onPermissionToggle={this.onPermissionToggle}
selectedIdentities={selectedIdentities} selectedIdentities={selectedIdentities}
redirect={redirect}
permissionRejected={permissionRejected}
allIdentitiesSelected={allIdentitiesSelected} allIdentitiesSelected={allIdentitiesSelected}
/> />
{ !redirect <div className="permission-approval-container__footers">
? ( <PermissionsConnectFooter />
<div className="permission-approval-container__footers"> <PageContainerFooter
<PermissionsConnectFooter /> cancelButtonType="default"
<PageContainerFooter onCancel={() => this.onCancel()}
cancelButtonType="default" cancelText={this.context.t('cancel')}
onCancel={() => this.onCancel()} onSubmit={() => this.onSubmit()}
cancelText={this.context.t('cancel')} submitText={this.context.t('connect')}
onSubmit={() => this.onSubmit()} submitButtonType="confirm"
submitText={this.context.t('connect')} buttonSizeLarge={false}
submitButtonType="confirm" />
buttonSizeLarge={false} </div>
/>
</div>
)
: null
}
</div> </div>
) )
} }

View File

@ -5,13 +5,14 @@ import IconWithFallBack from '../../ui/icon-with-fallback'
export default class PermissionsConnectHeader extends Component { export default class PermissionsConnectHeader extends Component {
static propTypes = { static propTypes = {
icon: PropTypes.string, icon: PropTypes.string,
iconName: PropTypes.string.isRequired, iconName: PropTypes.string,
headerTitle: PropTypes.node, headerTitle: PropTypes.node,
headerText: PropTypes.string, headerText: PropTypes.string,
} }
static defaultProps = { static defaultProps = {
icon: null, icon: null,
iconName: '',
headerTitle: '', headerTitle: '',
headerText: '', headerText: '',
} }

View File

@ -198,23 +198,23 @@ export default class ChooseAccount extends Component {
: t('connectAccountOrCreate') : t('connectAccountOrCreate')
} }
/> />
{ this.renderAccountsListHeader() } {this.renderAccountsListHeader()}
{ this.renderAccountsList() } {this.renderAccountsList()}
<div className="permissions-connect-choose-account__footer-container"> <div className="permissions-connect-choose-account__footer-container">
<PermissionsConnectFooter /> <PermissionsConnectFooter />
<div className="permissions-connect-choose-account__bottom-buttons"> <div className="permissions-connect-choose-account__bottom-buttons">
<Button <Button
onClick={ () => cancelPermissionsRequest(permissionsRequestId) } onClick={() => cancelPermissionsRequest(permissionsRequestId)}
type="default" type="default"
> >
{ t('cancel') } {t('cancel')}
</Button> </Button>
<Button <Button
onClick={ () => selectAccounts(selectedAccounts) } onClick={() => selectAccounts(selectedAccounts)}
type="primary" type="primary"
disabled={ selectedAccounts.size === 0 } disabled={selectedAccounts.size === 0}
> >
{ t('next') } {t('next')}
</Button> </Button>
</div> </div>
</div> </div>

View File

@ -1,4 +1,5 @@
@import 'choose-account/index'; @import 'choose-account/index';
@import 'redirect/index';
.permissions-connect { .permissions-connect {
width: 100%; width: 100%;

View File

@ -1,16 +1,14 @@
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import React, { Component } from 'react' import React, { Component } from 'react'
import { Switch, Route } from 'react-router-dom' import { Switch, Route } from 'react-router-dom'
import ChooseAccount from './choose-account'
import { getEnvironmentType } from '../../../../app/scripts/lib/util' import { getEnvironmentType } from '../../../../app/scripts/lib/util'
import { import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../app/scripts/lib/enums'
ENVIRONMENT_TYPE_FULLSCREEN, import { DEFAULT_ROUTE } from '../../helpers/constants/routes'
ENVIRONMENT_TYPE_NOTIFICATION,
} from '../../../../app/scripts/lib/enums'
import {
DEFAULT_ROUTE,
} from '../../helpers/constants/routes'
import PermissionPageContainer from '../../components/app/permission-page-container' import PermissionPageContainer from '../../components/app/permission-page-container'
import ChooseAccount from './choose-account'
import PermissionsRedirect from './redirect'
const APPROVE_TIMEOUT = 1200
export default class PermissionConnect extends Component { export default class PermissionConnect extends Component {
static propTypes = { static propTypes = {
@ -27,14 +25,12 @@ export default class PermissionConnect extends Component {
addressLastConnectedMap: PropTypes.object.isRequired, addressLastConnectedMap: PropTypes.object.isRequired,
lastConnectedInfo: PropTypes.object.isRequired, lastConnectedInfo: PropTypes.object.isRequired,
permissionsRequestId: PropTypes.string, permissionsRequestId: PropTypes.string,
hasPendingPermissionsRequests: PropTypes.bool.isRequired,
history: PropTypes.object.isRequired, history: PropTypes.object.isRequired,
connectPath: PropTypes.string.isRequired, connectPath: PropTypes.string.isRequired,
confirmPermissionPath: PropTypes.string.isRequired, confirmPermissionPath: PropTypes.string.isRequired,
page: PropTypes.string.isRequired, page: PropTypes.string.isRequired,
targetDomainMetadata: PropTypes.object, targetDomainMetadata: PropTypes.object,
location: PropTypes.shape({
pathname: PropTypes.string,
}).isRequired,
} }
static defaultProps = { static defaultProps = {
@ -53,74 +49,27 @@ export default class PermissionConnect extends Component {
selectedAccountAddresses: this.props.accounts.length === 1 selectedAccountAddresses: this.props.accounts.length === 1
? new Set([this.props.accounts[0].address]) ? new Set([this.props.accounts[0].address])
: new Set(), : new Set(),
permissionAccepted: null, permissionsApproved: null,
origin: this.props.origin, origin: this.props.origin,
targetDomainMetadata: this.props.targetDomainMetadata || {},
} }
beforeUnload = () => { beforeUnload = () => {
const { permissionsRequestId, rejectPermissionsRequest } = this.props const { permissionsRequestId, rejectPermissionsRequest } = this.props
const { permissionAccepted } = this.state const { permissionsApproved } = this.state
if (permissionAccepted === null && permissionsRequestId) { if (permissionsApproved === null && permissionsRequestId) {
rejectPermissionsRequest(permissionsRequestId) rejectPermissionsRequest(permissionsRequestId)
} }
} }
removeBeforeUnload = () => { removeBeforeUnload = () => {
const environmentType = getEnvironmentType() const environmentType = getEnvironmentType()
if ( if (environmentType === ENVIRONMENT_TYPE_NOTIFICATION) {
environmentType === ENVIRONMENT_TYPE_FULLSCREEN ||
environmentType === ENVIRONMENT_TYPE_NOTIFICATION
) {
window.removeEventListener('beforeunload', this.beforeUnload) window.removeEventListener('beforeunload', this.beforeUnload)
} }
} }
componentDidUpdate (prevProps) {
const { permissionsRequest, lastConnectedInfo } = this.props
const { redirecting, origin } = this.state
if (!permissionsRequest && prevProps.permissionsRequest && !redirecting) {
const accountsLastApprovedTime = lastConnectedInfo[origin]?.lastApproved || 0
const initialAccountsLastApprovedTime = prevProps.lastConnectedInfo[origin]?.lastApproved || 0
if (accountsLastApprovedTime > initialAccountsLastApprovedTime) {
this.redirectFlow(true)
} else {
this.redirectFlow(false)
}
}
}
selectAccounts = (addresses) => {
this.setState({
selectedAccountAddresses: addresses,
}, () => this.props.history.push(this.props.confirmPermissionPath))
}
redirectFlow (accepted) {
const { history, location, confirmPermissionPath } = this.props
this.setState({
redirecting: true,
permissionAccepted: accepted,
})
this.removeBeforeUnload()
if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
setTimeout(async () => {
global.platform.closeCurrentWindow()
}, 1500)
} else if (location.pathname === confirmPermissionPath) {
setTimeout(async () => {
history.push(DEFAULT_ROUTE)
}, 1500)
} else {
history.push(DEFAULT_ROUTE)
}
}
componentDidMount () { componentDidMount () {
const { const {
getCurrentWindowTab, getCurrentWindowTab,
@ -136,26 +85,78 @@ export default class PermissionConnect extends Component {
} }
const environmentType = getEnvironmentType() const environmentType = getEnvironmentType()
if ( if (environmentType === ENVIRONMENT_TYPE_NOTIFICATION) {
environmentType === ENVIRONMENT_TYPE_FULLSCREEN ||
environmentType === ENVIRONMENT_TYPE_NOTIFICATION
) {
window.addEventListener('beforeunload', this.beforeUnload) window.addEventListener('beforeunload', this.beforeUnload)
} }
} }
static getDerivedStateFromProps (props, state) {
const { permissionsRequest, targetDomainMetadata } = props
const { targetDomainMetadata: savedMetadata } = state
if (
permissionsRequest &&
savedMetadata.name !== targetDomainMetadata?.name
) {
return { targetDomainMetadata }
}
return null
}
componentDidUpdate (prevProps) {
const { permissionsRequest, lastConnectedInfo } = this.props
const { redirecting, origin } = this.state
if (!permissionsRequest && prevProps.permissionsRequest && !redirecting) {
const accountsLastApprovedTime = lastConnectedInfo[origin]?.lastApproved || 0
const initialAccountsLastApprovedTime = prevProps.lastConnectedInfo[origin]?.lastApproved || 0
const approved = accountsLastApprovedTime > initialAccountsLastApprovedTime
this.redirect(approved)
}
}
selectAccounts = (addresses) => {
this.setState({
selectedAccountAddresses: addresses,
}, () => this.props.history.push(this.props.confirmPermissionPath))
}
redirect (approved) {
this.setState({
redirecting: true,
permissionsApproved: approved,
})
this.removeBeforeUnload()
if (approved) {
setTimeout(this._doRedirect.bind(this), APPROVE_TIMEOUT)
} else {
this._doRedirect()
}
}
_doRedirect () {
const { history, hasPendingPermissionsRequests } = this.props
if (
!hasPendingPermissionsRequests &&
getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION
) {
global.platform.closeCurrentWindow()
} else {
history.push(DEFAULT_ROUTE)
}
}
cancelPermissionsRequest = async (requestId) => { cancelPermissionsRequest = async (requestId) => {
const { history, rejectPermissionsRequest } = this.props const { rejectPermissionsRequest } = this.props
if (requestId) { if (requestId) {
await rejectPermissionsRequest(requestId) await rejectPermissionsRequest(requestId)
this.redirect(false)
if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
window.close()
} else {
history.push(DEFAULT_ROUTE)
}
} }
} }
@ -193,7 +194,6 @@ export default class PermissionConnect extends Component {
render () { render () {
const { const {
approvePermissionsRequest, approvePermissionsRequest,
rejectPermissionsRequest,
accounts, accounts,
showNewAccountModal, showNewAccountModal,
newAccountNumber, newAccountNumber,
@ -203,63 +203,68 @@ export default class PermissionConnect extends Component {
permissionsRequestId, permissionsRequestId,
connectPath, connectPath,
confirmPermissionPath, confirmPermissionPath,
targetDomainMetadata,
} = this.props } = this.props
const { const {
selectedAccountAddresses, selectedAccountAddresses,
permissionAccepted, permissionsApproved,
origin, origin,
redirecting, redirecting,
targetDomainMetadata,
} = this.state } = this.state
return ( return (
<div className="permissions-connect"> <div className="permissions-connect">
{ this.renderTopBar() } { this.renderTopBar() }
<Switch> {
<Route redirecting && permissionsApproved
path={connectPath} ? (
exact <PermissionsRedirect
render={() => ( domainMetadata={targetDomainMetadata}
<ChooseAccount
accounts={accounts}
nativeCurrency={nativeCurrency}
selectAccounts={(addresses) => this.selectAccounts(addresses)}
selectNewAccountViaModal={(handleAccountClick) => {
showNewAccountModal({
onCreateNewAccount: (address) => handleAccountClick(address),
newAccountNumber,
})
}}
addressLastConnectedMap={addressLastConnectedMap}
cancelPermissionsRequest={(requestId) => this.cancelPermissionsRequest(requestId)}
permissionsRequestId={permissionsRequestId}
selectedAccountAddresses={selectedAccountAddresses}
targetDomainMetadata={targetDomainMetadata}
/> />
)} )
/> : (
<Route <Switch>
path={confirmPermissionPath} <Route
exact path={connectPath}
render={() => ( exact
<PermissionPageContainer render={() => (
request={permissionsRequest || {}} <ChooseAccount
approvePermissionsRequest={(request, accounts) => { accounts={accounts}
approvePermissionsRequest(request, accounts) nativeCurrency={nativeCurrency}
this.redirectFlow(true) selectAccounts={(addresses) => this.selectAccounts(addresses)}
}} selectNewAccountViaModal={(handleAccountClick) => {
rejectPermissionsRequest={(requestId) => { showNewAccountModal({
rejectPermissionsRequest(requestId) onCreateNewAccount: (address) => handleAccountClick(address),
this.redirectFlow(false) newAccountNumber,
}} })
selectedIdentities={accounts.filter((account) => selectedAccountAddresses.has(account.address))} }}
redirect={redirecting} addressLastConnectedMap={addressLastConnectedMap}
permissionRejected={ permissionAccepted === false } cancelPermissionsRequest={(requestId) => this.cancelPermissionsRequest(requestId)}
cachedOrigin={origin} permissionsRequestId={permissionsRequestId}
/> selectedAccountAddresses={selectedAccountAddresses}
)} targetDomainMetadata={targetDomainMetadata}
/> />
</Switch> )}
/>
<Route
path={confirmPermissionPath}
exact
render={() => (
<PermissionPageContainer
request={permissionsRequest || {}}
approvePermissionsRequest={(request, accounts) => {
approvePermissionsRequest(request, accounts)
this.redirect(true)
}}
rejectPermissionsRequest={(requestId) => this.cancelPermissionsRequest(requestId)}
selectedIdentities={accounts.filter((account) => selectedAccountAddresses.has(account.address))}
cachedOrigin={origin}
/>
)}
/>
</Switch>
)
}
</div> </div>
) )
} }

View File

@ -32,6 +32,10 @@ const mapStateToProps = (state, ownProps) => {
const permissionsRequest = permissionsRequests const permissionsRequest = permissionsRequests
.find((permissionsRequest) => permissionsRequest.metadata.id === permissionsRequestId) .find((permissionsRequest) => permissionsRequest.metadata.id === permissionsRequestId)
const hasPendingPermissionsRequests = permissionsRequest
? permissionsRequests.length > 1
: permissionsRequests.length > 0
const { metadata = {} } = permissionsRequest || {} const { metadata = {} } = permissionsRequest || {}
const { origin } = metadata const { origin } = metadata
const nativeCurrency = getNativeCurrency(state) const nativeCurrency = getNativeCurrency(state)
@ -62,6 +66,7 @@ const mapStateToProps = (state, ownProps) => {
return { return {
permissionsRequest, permissionsRequest,
permissionsRequestId, permissionsRequestId,
hasPendingPermissionsRequests,
accounts: accountsWithLabels, accounts: accountsWithLabels,
origin, origin,
newAccountNumber: accountsWithLabels.length + 1, newAccountNumber: accountsWithLabels.length + 1,

View File

@ -0,0 +1 @@
export { default } from './permissions-redirect.component'

View File

@ -0,0 +1,82 @@
.permissions-redirect {
display: flex;
height: 100%;
justify-content: center;
&__result {
@extend %header--24;
position: absolute;
top: 30%;
flex-direction: column;
justify-content: space-between;
align-items: center;
text-align: center;
color: $Black-100;
}
&__icons {
display: flex;
}
&__center-icon {
display: flex;
position: relative;
justify-content: center;
align-items: center;
font-size: 12px;
}
&__check {
width: 40px;
height: 40px;
background: white url("/images/permissions-check.svg") no-repeat;
position: absolute;
}
&__identicon, .icon-with-fallback__identicon {
width: 32px;
height: 32px;
&--default {
background-color: #777A87;
color: white;
width: 64px;
height: 64px;
border-radius: 32px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
}
&__identicon-container, .icon-with-fallback__identicon-container {
height: auto;
position: relative;
display: flex;
justify-content: center;
align-items: center;
height: 64px;
width: 64px;
}
&__identicon-border, .icon-with-fallback__identicon-border {
height: 64px;
width: 64px;
border-radius: 50%;
border: 1px solid white;
background: #FFFFFF;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.25);
}
&__identicon-border {
display: flex;
justify-content: center;
align-items: center;
}
.icon-with-fallback__identicon-border {
position: absolute;
}
}

View File

@ -0,0 +1,41 @@
import React, { useContext } from 'react'
import PropTypes from 'prop-types'
import IconWithFallBack from '../../../components/ui/icon-with-fallback'
import { I18nContext } from '../../../contexts/i18n'
export default function PermissionsRedirect ({ domainMetadata }) {
const t = useContext(I18nContext)
return (
<div className="permissions-redirect">
<div className="permissions-redirect__result">
{ t('connecting') }
<div className="permissions-redirect__icons">
<IconWithFallBack icon={domainMetadata.icon} name={domainMetadata.name} />
<div className="permissions-redirect__center-icon">
<span className="permissions-redirect__check" />
{ renderBrokenLine() }
</div>
<div className="permissions-redirect__identicon-container">
<div className="permissions-redirect__identicon-border">
<img src="/images/logo/metamask-fox.svg" />
</div>
</div>
</div>
</div>
</div>
)
function renderBrokenLine () {
return (
<svg width="131" height="2" viewBox="0 0 131 2" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 1H134" stroke="#CDD1E4" strokeLinejoin="round" strokeDasharray="8 7" />
</svg>
)
}
}
PermissionsRedirect.propTypes = {
domainMetadata: PropTypes.object.isRequired,
}

View File

@ -173,17 +173,15 @@ export default class Routes extends Component {
return true return true
} }
if (windowType === ENVIRONMENT_TYPE_POPUP) { if (windowType === ENVIRONMENT_TYPE_POPUP && this.onConfirmPage()) {
return this.onConfirmPage() || hasPermissionsRequests return true
} }
const isHandlingPermissionsRequest = Boolean(matchPath(location.pathname, { const isHandlingPermissionsRequest = Boolean(matchPath(location.pathname, {
path: CONNECT_ROUTE, exact: false, path: CONNECT_ROUTE, exact: false,
})) })) || hasPermissionsRequests
if (hasPermissionsRequests || isHandlingPermissionsRequest) { return isHandlingPermissionsRequest
return true
}
} }
render () { render () {

View File

@ -2056,9 +2056,11 @@ export function rejectPermissionsRequest (requestId) {
background.rejectPermissionsRequest(requestId, (err) => { background.rejectPermissionsRequest(requestId, (err) => {
if (err) { if (err) {
dispatch(displayWarning(err.message)) dispatch(displayWarning(err.message))
reject() return reject(err)
} }
resolve() return forceUpdateMetamaskState(dispatch)
.then(resolve)
.catch(reject)
}) })
}) })
} }