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

make redirect flow consistent

This commit is contained in:
Erik Marks 2020-06-05 15:46:49 -07:00
parent a4e5fc934d
commit ea398abc5d
10 changed files with 334 additions and 317 deletions

View File

@ -46,15 +46,6 @@
padding-left: 24px;
padding-right: 24px;
&--redirect {
margin-top: 140px;
width: 100%;
display: flex;
align-items: center;
padding-top: 8px;
height: 144px;
}
a, a:hover {
color: $dodger-blue;
}
@ -94,10 +85,6 @@
@extend %content-text;
line-height: 20px;
color: #6A737D;
&--redirect {
text-align: center;
}
}
&__permissions-container {
@ -147,105 +134,3 @@
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 React, { PureComponent } from 'react'
import IconWithFallBack from '../../../ui/icon-with-fallback'
import PermissionsConnectHeader from '../../permissions-connect-header'
import Tooltip from '../../../ui/tooltip-v2'
import classnames from 'classnames'
export default class PermissionPageContainerContent extends PureComponent {
@ -13,13 +11,9 @@ export default class PermissionPageContainerContent extends PureComponent {
onPermissionToggle: PropTypes.func.isRequired,
selectedIdentities: PropTypes.array,
allIdentitiesSelected: PropTypes.bool,
redirect: PropTypes.bool,
permissionRejected: PropTypes.bool,
}
static defaultProps = {
redirect: null,
permissionRejected: null,
selectedIdentities: [],
allIdentitiesSelected: false,
}
@ -28,39 +22,6 @@ export default class PermissionPageContainerContent extends PureComponent {
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 () {
const {
selectedPermissions, onPermissionToggle,
@ -134,14 +95,10 @@ export default class PermissionPageContainerContent extends PureComponent {
}
getTitle () {
const { domainMetadata, redirect, permissionRejected, selectedIdentities, allIdentitiesSelected } = this.props
const { domainMetadata, selectedIdentities, allIdentitiesSelected } = this.props
const { t } = this.context
if (redirect && permissionRejected) {
return t('cancelledConnectionWithMetaMask')
} else if (redirect) {
return t('connectingWithMetaMask')
} else if (domainMetadata.extensionId) {
if (domainMetadata.extensionId) {
return t('externalExtension', [domainMetadata.extensionId])
} else if (allIdentitiesSelected) {
return t(
@ -166,36 +123,27 @@ export default class PermissionPageContainerContent extends PureComponent {
}
render () {
const { domainMetadata, redirect } = this.props
const { domainMetadata } = this.props
const { t } = this.context
const title = this.getTitle()
return (
<div
className={classnames('permission-approval-container__content', {
'permission-approval-container__content--redirect': redirect,
})}
>
{ !redirect
? (
<div className="permission-approval-container__content-container">
<PermissionsConnectHeader
icon={domainMetadata.icon}
iconName={domainMetadata.origin}
headerTitle={title}
headerText={ domainMetadata.extensionId
? t('allowExternalExtensionTo', [domainMetadata.extensionId])
: t('allowThisSiteTo')
}
/>
<section className="permission-approval-container__permissions-container">
{ this.renderRequestedPermissions() }
</section>
</div>
)
: this.renderRedirect()
}
<div className="permission-approval-container__content">
<div className="permission-approval-container__content-container">
<PermissionsConnectHeader
icon={domainMetadata.icon}
iconName={domainMetadata.origin}
headerTitle={title}
headerText={ domainMetadata.extensionId
? t('allowExternalExtensionTo', [domainMetadata.extensionId])
: t('allowThisSiteTo')
}
/>
<section className="permission-approval-container__permissions-container">
{ this.renderRequestedPermissions() }
</section>
</div>
</div>
)
}

View File

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

View File

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

View File

@ -30,6 +30,7 @@ export default class ChooseAccount extends Component {
state = {
selectedAccounts: this.props.selectedAccountAddresses,
buttonsDisabled: false,
}
static defaultProps = {
@ -185,7 +186,7 @@ export default class ChooseAccount extends Component {
targetDomainMetadata,
accounts,
} = this.props
const { selectedAccounts } = this.state
const { selectedAccounts, buttonsDisabled } = this.state
const { t } = this.context
return (
<div className="permissions-connect-choose-account">
@ -198,23 +199,34 @@ export default class ChooseAccount extends Component {
: t('connectAccountOrCreate')
}
/>
{ this.renderAccountsListHeader() }
{ this.renderAccountsList() }
{this.renderAccountsListHeader()}
{this.renderAccountsList()}
<div className="permissions-connect-choose-account__footer-container">
<PermissionsConnectFooter />
<div className="permissions-connect-choose-account__bottom-buttons">
<Button
onClick={ () => cancelPermissionsRequest(permissionsRequestId) }
onClick={() => {
cancelPermissionsRequest(permissionsRequestId)
this.setState({
buttonsDisabled: true,
})
}}
type="default"
disabled={buttonsDisabled}
>
{ t('cancel') }
{t('cancel')}
</Button>
<Button
onClick={ () => selectAccounts(selectedAccounts) }
onClick={() => {
selectAccounts(selectedAccounts)
this.setState({
buttonsDisabled: true,
})
}}
type="primary"
disabled={ selectedAccounts.size === 0 }
disabled={selectedAccounts.size === 0 || buttonsDisabled}
>
{ t('next') }
{t('next')}
</Button>
</div>
</div>

View File

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

View File

@ -1,7 +1,6 @@
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { Switch, Route } from 'react-router-dom'
import ChooseAccount from './choose-account'
import { getEnvironmentType } from '../../../../app/scripts/lib/util'
import {
ENVIRONMENT_TYPE_FULLSCREEN,
@ -11,6 +10,11 @@ import {
DEFAULT_ROUTE,
} from '../../helpers/constants/routes'
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 {
static propTypes = {
@ -53,15 +57,16 @@ export default class PermissionConnect extends Component {
selectedAccountAddresses: this.props.accounts.length === 1
? new Set([this.props.accounts[0].address])
: new Set(),
permissionAccepted: null,
permissionsApproved: null,
origin: this.props.origin,
targetDomainMetadata: this.props.targetDomainMetadata,
}
beforeUnload = () => {
const { permissionsRequestId, rejectPermissionsRequest } = this.props
const { permissionAccepted } = this.state
const { permissionsApproved } = this.state
if (permissionAccepted === null && permissionsRequestId) {
if (permissionsApproved === null && permissionsRequestId) {
rejectPermissionsRequest(permissionsRequestId)
}
}
@ -76,51 +81,6 @@ export default class PermissionConnect extends Component {
}
}
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 () {
const {
getCurrentWindowTab,
@ -144,9 +104,54 @@ export default class PermissionConnect extends Component {
}
}
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 (approved) {
const { history } = this.props
this.setState({
redirecting: true,
permissionsApproved: approved,
})
this.removeBeforeUnload()
const timeout = approved ? APPROVE_TIMEOUT : REJECT_TIMEOUT
if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
setTimeout(async () => {
global.platform.closeCurrentWindow()
}, timeout)
} else {
setTimeout(async () => {
history.push(DEFAULT_ROUTE)
}, timeout)
}
}
cancelPermissionsRequest = async (requestId) => {
const { history, rejectPermissionsRequest } = this.props
const { rejectPermissionsRequest } = this.props
if (requestId) {
await rejectPermissionsRequest(requestId)
@ -154,7 +159,7 @@ export default class PermissionConnect extends Component {
if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
window.close()
} else {
history.push(DEFAULT_ROUTE)
this.redirectFlow(false)
}
}
}
@ -193,7 +198,6 @@ export default class PermissionConnect extends Component {
render () {
const {
approvePermissionsRequest,
rejectPermissionsRequest,
accounts,
showNewAccountModal,
newAccountNumber,
@ -203,63 +207,69 @@ export default class PermissionConnect extends Component {
permissionsRequestId,
connectPath,
confirmPermissionPath,
targetDomainMetadata,
} = this.props
const {
selectedAccountAddresses,
permissionAccepted,
permissionsApproved,
origin,
redirecting,
targetDomainMetadata,
} = this.state
return (
<div className="permissions-connect">
{ this.renderTopBar() }
<Switch>
<Route
path={connectPath}
exact
render={() => (
<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}
{
redirecting
? (
<PermissionsRedirect
domainMetadata={targetDomainMetadata}
permissionsRejected={ permissionsApproved === false }
/>
)}
/>
<Route
path={confirmPermissionPath}
exact
render={() => (
<PermissionPageContainer
request={permissionsRequest || {}}
approvePermissionsRequest={(request, accounts) => {
approvePermissionsRequest(request, accounts)
this.redirectFlow(true)
}}
rejectPermissionsRequest={(requestId) => {
rejectPermissionsRequest(requestId)
this.redirectFlow(false)
}}
selectedIdentities={accounts.filter((account) => selectedAccountAddresses.has(account.address))}
redirect={redirecting}
permissionRejected={ permissionAccepted === false }
cachedOrigin={origin}
/>
)}
/>
</Switch>
)
: (
<Switch>
<Route
path={connectPath}
exact
render={() => (
<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
path={confirmPermissionPath}
exact
render={() => (
<PermissionPageContainer
request={permissionsRequest || {}}
approvePermissionsRequest={(request, accounts) => {
approvePermissionsRequest(request, accounts)
this.redirectFlow(true)
}}
rejectPermissionsRequest={(requestId) => this.cancelPermissionsRequest(requestId)}
selectedIdentities={accounts.filter((account) => selectedAccountAddresses.has(account.address))}
cachedOrigin={origin}
/>
)}
/>
</Switch>
)
}
</div>
)
}

View File

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

View File

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

View File

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