make redirect flow consistent

feature/default_network_editable
Erik Marks 5 years ago
parent a4e5fc934d
commit ea398abc5d
  1. 115
      ui/app/components/app/permission-page-container/index.scss
  2. 60
      ui/app/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js
  3. 13
      ui/app/components/app/permission-page-container/permission-page-container.component.js
  4. 3
      ui/app/components/app/permissions-connect-header/permissions-connect-header.component.js
  5. 20
      ui/app/pages/permissions-connect/choose-account/choose-account.component.js
  6. 1
      ui/app/pages/permissions-connect/index.scss
  7. 102
      ui/app/pages/permissions-connect/permissions-connect.component.js
  8. 1
      ui/app/pages/permissions-connect/redirect/index.js
  9. 121
      ui/app/pages/permissions-connect/redirect/index.scss
  10. 51
      ui/app/pages/permissions-connect/redirect/permissions-redirect.component.js

@ -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;
}
}

@ -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,19 +123,13 @@ 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', {
'permission-approval-container__content--redirect': redirect,
})}
>
{ !redirect
? (
<div className="permission-approval-container__content-container"> <div className="permission-approval-container__content-container">
<PermissionsConnectHeader <PermissionsConnectHeader
icon={domainMetadata.icon} icon={domainMetadata.icon}
@ -193,9 +144,6 @@ export default class PermissionPageContainerContent extends PureComponent {
{ this.renderRequestedPermissions() } { this.renderRequestedPermissions() }
</section> </section>
</div> </div>
)
: this.renderRedirect()
}
</div> </div>
) )
} }

@ -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,12 +123,8 @@ 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"> <div className="permission-approval-container__footers">
<PermissionsConnectFooter /> <PermissionsConnectFooter />
<PageContainerFooter <PageContainerFooter
@ -147,9 +137,6 @@ export default class PermissionPageContainer extends Component {
buttonSizeLarge={false} buttonSizeLarge={false}
/> />
</div> </div>
)
: null
}
</div> </div>
) )
} }

@ -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: 'Unknown External Domain',
headerTitle: '', headerTitle: '',
headerText: '', headerText: '',
} }

@ -30,6 +30,7 @@ export default class ChooseAccount extends Component {
state = { state = {
selectedAccounts: this.props.selectedAccountAddresses, selectedAccounts: this.props.selectedAccountAddresses,
buttonsDisabled: false,
} }
static defaultProps = { static defaultProps = {
@ -185,7 +186,7 @@ export default class ChooseAccount extends Component {
targetDomainMetadata, targetDomainMetadata,
accounts, accounts,
} = this.props } = this.props
const { selectedAccounts } = this.state const { selectedAccounts, buttonsDisabled } = this.state
const { t } = this.context const { t } = this.context
return ( return (
<div className="permissions-connect-choose-account"> <div className="permissions-connect-choose-account">
@ -204,15 +205,26 @@ export default class ChooseAccount extends Component {
<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)
this.setState({
buttonsDisabled: true,
})
}}
type="default" type="default"
disabled={buttonsDisabled}
> >
{t('cancel')} {t('cancel')}
</Button> </Button>
<Button <Button
onClick={ () => selectAccounts(selectedAccounts) } onClick={() => {
selectAccounts(selectedAccounts)
this.setState({
buttonsDisabled: true,
})
}}
type="primary" type="primary"
disabled={ selectedAccounts.size === 0 } disabled={selectedAccounts.size === 0 || buttonsDisabled}
> >
{t('next')} {t('next')}
</Button> </Button>

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

@ -1,7 +1,6 @@
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_FULLSCREEN, ENVIRONMENT_TYPE_FULLSCREEN,
@ -11,6 +10,11 @@ import {
DEFAULT_ROUTE, DEFAULT_ROUTE,
} from '../../helpers/constants/routes' } 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 = 1500
const REJECT_TIMEOUT = 750
export default class PermissionConnect extends Component { export default class PermissionConnect extends Component {
static propTypes = { static propTypes = {
@ -53,15 +57,16 @@ 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)
} }
} }
@ -76,6 +81,29 @@ export default class PermissionConnect extends Component {
} }
} }
componentDidMount () {
const {
getCurrentWindowTab,
getRequestAccountTabIds,
permissionsRequest,
history,
} = this.props
getCurrentWindowTab()
getRequestAccountTabIds()
if (!permissionsRequest) {
return history.push(DEFAULT_ROUTE)
}
const environmentType = getEnvironmentType()
if (
environmentType === ENVIRONMENT_TYPE_FULLSCREEN ||
environmentType === ENVIRONMENT_TYPE_NOTIFICATION
) {
window.addEventListener('beforeunload', this.beforeUnload)
}
}
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
const { permissionsRequest, lastConnectedInfo } = this.props const { permissionsRequest, lastConnectedInfo } = this.props
const { redirecting, origin } = this.state const { redirecting, origin } = this.state
@ -99,54 +127,31 @@ export default class PermissionConnect extends Component {
}, () => this.props.history.push(this.props.confirmPermissionPath)) }, () => this.props.history.push(this.props.confirmPermissionPath))
} }
redirectFlow (accepted) { redirectFlow (approved) {
const { history, location, confirmPermissionPath } = this.props const { history } = this.props
this.setState({ this.setState({
redirecting: true, redirecting: true,
permissionAccepted: accepted, permissionsApproved: approved,
}) })
this.removeBeforeUnload() this.removeBeforeUnload()
const timeout = approved ? APPROVE_TIMEOUT : REJECT_TIMEOUT
if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) { if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
setTimeout(async () => { setTimeout(async () => {
global.platform.closeCurrentWindow() global.platform.closeCurrentWindow()
}, 1500) }, timeout)
} else if (location.pathname === confirmPermissionPath) {
setTimeout(async () => {
history.push(DEFAULT_ROUTE)
}, 1500)
} else { } else {
setTimeout(async () => {
history.push(DEFAULT_ROUTE) history.push(DEFAULT_ROUTE)
} }, timeout)
}
componentDidMount () {
const {
getCurrentWindowTab,
getRequestAccountTabIds,
permissionsRequest,
history,
} = this.props
getCurrentWindowTab()
getRequestAccountTabIds()
if (!permissionsRequest) {
return history.push(DEFAULT_ROUTE)
}
const environmentType = getEnvironmentType()
if (
environmentType === ENVIRONMENT_TYPE_FULLSCREEN ||
environmentType === ENVIRONMENT_TYPE_NOTIFICATION
) {
window.addEventListener('beforeunload', this.beforeUnload)
} }
} }
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)
@ -154,7 +159,7 @@ export default class PermissionConnect extends Component {
if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) { if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
window.close() window.close()
} else { } else {
history.push(DEFAULT_ROUTE) this.redirectFlow(false)
} }
} }
} }
@ -193,7 +198,6 @@ export default class PermissionConnect extends Component {
render () { render () {
const { const {
approvePermissionsRequest, approvePermissionsRequest,
rejectPermissionsRequest,
accounts, accounts,
showNewAccountModal, showNewAccountModal,
newAccountNumber, newAccountNumber,
@ -203,18 +207,27 @@ 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() }
{
redirecting
? (
<PermissionsRedirect
domainMetadata={targetDomainMetadata}
permissionsRejected={ permissionsApproved === false }
/>
)
: (
<Switch> <Switch>
<Route <Route
path={connectPath} path={connectPath}
@ -248,18 +261,15 @@ export default class PermissionConnect extends Component {
approvePermissionsRequest(request, accounts) approvePermissionsRequest(request, accounts)
this.redirectFlow(true) this.redirectFlow(true)
}} }}
rejectPermissionsRequest={(requestId) => { rejectPermissionsRequest={(requestId) => this.cancelPermissionsRequest(requestId)}
rejectPermissionsRequest(requestId)
this.redirectFlow(false)
}}
selectedIdentities={accounts.filter((account) => selectedAccountAddresses.has(account.address))} selectedIdentities={accounts.filter((account) => selectedAccountAddresses.has(account.address))}
redirect={redirecting}
permissionRejected={ permissionAccepted === false }
cachedOrigin={origin} cachedOrigin={origin}
/> />
)} )}
/> />
</Switch> </Switch>
)
}
</div> </div>
) )
} }

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

@ -0,0 +1,121 @@
.permissions-redirect-container {
display: flex;
border: none;
box-shadow: none;
width: 100%;
margin-top: 2px;
height: 100%;
flex-direction: column;
&__content {
display: flex;
width: 100%;
height: 144px;
margin-top: 140px;
padding-top: 8px;
align-items: center;
justify-content: center;
}
}
.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;
}
}

@ -0,0 +1,51 @@
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, permissionsRejected }) {
const t = useContext(I18nContext)
return (
<div className="page-container permissions-redirect-container">
<div className="permissions-redirect-container__content">
<div className="permission-result">
{ permissionsRejected ? t('cancelling') : t('connecting') }
<div className="permission-result__icons">
<IconWithFallBack icon={domainMetadata.icon} name={domainMetadata.name} />
<div className="permission-result__center-icon">
{ permissionsRejected
? <span className="permission-result__reject" ><i className="fa fa-times-circle" /></span>
: <span className="permission-result__check" />
}
{ 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>
</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,
permissionsRejected: PropTypes.bool,
}
PermissionsRedirect.defaultProps = {
permissionsRejected: null,
}
Loading…
Cancel
Save