Connect flow via popup (#8269)

* Connect screen popup redesign

* Open permission request in notification instead of tab

* Remove no longer user locales

* Update permissions unit test mock to accout for change of opts passed to permissions controller

* Lint fix

* Inline broken line svg in permission-page-container-content.component.js for faster loading

* Add back button to second screen on connect flow

* Add xOfY locale and use for the page count in the connect flow

* Lint fix for svgs permission-page-container-content.component.js

* Fix rebase error

* Lint fix

* Clean up styles on the connect-screen-into-popup branch

* Use closeCurrentWindow to close window on cancel when in full screen connect flow

* Handle errors in rejectPermissionsRequest

* Full screen styles for connect flow

* Lint fixed in permissions-connect and actions.js

* Redirect screen now shows metamask icon instead of users identicon

* Fix subtitle spacing in permissions-connect-header'

* Use window.close instead of closeCurrentWindow() in cancelPermissionsRequest

* Use permissions-connect-header__subtitle in permissions-connect-header.component
feature/default_network_editable
Dan J Miller 5 years ago committed by GitHub
parent d8e0c9edd9
commit d1e078b8de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      app/_locales/en/messages.json
  2. 3
      app/_locales/it/messages.json
  3. 3
      app/images/broken-line.svg
  4. 1
      app/scripts/background.js
  5. 6
      app/scripts/controllers/permissions/index.js
  6. 2
      app/scripts/metamask-controller.js
  7. 6
      test/unit/app/controllers/permissions/mocks.js
  8. 2
      ui/app/components/app/index.scss
  9. 58
      ui/app/components/app/permission-page-container/index.scss
  10. 45
      ui/app/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js
  11. 6
      ui/app/components/app/permission-page-container/permission-page-container.component.js
  12. 0
      ui/app/components/app/permissions-connect-footer/index.js
  13. 6
      ui/app/components/app/permissions-connect-footer/index.scss
  14. 0
      ui/app/components/app/permissions-connect-footer/permissions-connect-footer.component.js
  15. 10
      ui/app/components/app/permissions-connect-header/index.scss
  16. 2
      ui/app/components/app/permissions-connect-header/permissions-connect-header.component.js
  17. 4
      ui/app/pages/permissions-connect/choose-account/choose-account.component.js
  18. 66
      ui/app/pages/permissions-connect/choose-account/index.scss
  19. 41
      ui/app/pages/permissions-connect/index.scss
  20. 89
      ui/app/pages/permissions-connect/permissions-connect.component.js
  21. 3
      ui/app/pages/permissions-connect/permissions-connect.container.js
  22. 12
      ui/app/store/actions.js

@ -1157,9 +1157,6 @@
"readyToConnect": {
"message": "Ready to Connect?"
},
"revokeInPermissions": {
"message": "* You can view and revoke permissions in MetaMask settings."
},
"rinkeby": {
"message": "Rinkeby Test Network"
},
@ -1628,6 +1625,10 @@
"writePhrase": {
"message": "Write this phrase on a piece of paper and store in a secure location. If you want even more security, write it down on multiple pieces of paper and store each in 2 - 3 different locations."
},
"xOfY": {
"message": "$1 of $2",
"description": "$1 and $2 are intended to be two numbers, where $2 is a total, and $1 is a count towards that total"
},
"yesLetsTry": {
"message": "Yes, let's try"
},

@ -1101,9 +1101,6 @@
"readyToConnect": {
"message": "Pronto a Connetterti?"
},
"revokeInPermissions": {
"message": "* Puoi vedere e revocare i permessi nelle impostazioni di MetaMask."
},
"rinkeby": {
"message": "Rete di test Rinkeby"
},

@ -1,3 +0,0 @@
<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" stroke-linejoin="round" stroke-dasharray="8 7"/>
</svg>

Before

Width:  |  Height:  |  Size: 188 B

@ -240,6 +240,7 @@ function setupController (initState, initLangCode) {
// User confirmation callbacks:
showUnconfirmedMessage: triggerUi,
showUnapprovedTx: triggerUi,
showPermissionRequest: triggerUi,
openPopup: openPopup,
closePopup: notificationManager.closePopup.bind(notificationManager),
// initial state

@ -29,7 +29,7 @@ export class PermissionsController {
getUnlockPromise,
notifyDomain,
notifyAllDomains,
platform,
showPermissionRequest,
} = {},
restoredPermissions = {},
restoredState = {}) {
@ -44,7 +44,7 @@ export class PermissionsController {
this.getUnlockPromise = getUnlockPromise
this._notifyDomain = notifyDomain
this.notifyAllDomains = notifyAllDomains
this._platform = platform
this._showPermissionRequest = showPermissionRequest
this._restrictedMethods = getRestrictedMethods(this)
this.permissionsLog = new PermissionsLogController({
@ -544,7 +544,7 @@ export class PermissionsController {
)
}
this._platform.openExtensionInBrowser(`connect/${id}`)
this._showPermissionRequest()
return new Promise((resolve, reject) => {
this._addPendingApproval(id, origin, resolve, reject)

@ -216,7 +216,7 @@ export default class MetamaskController extends EventEmitter {
getUnlockPromise: this.appStateController.getUnlockPromise.bind(this.appStateController),
notifyDomain: this.notifyConnections.bind(this),
notifyAllDomains: this.notifyAllConnections.bind(this),
platform: opts.platform,
showPermissionRequest: opts.showPermissionRequest,
}, initState.PermissionsController, initState.PermissionsMetadata)
this.detectTokensController = new DetectTokensController({

@ -25,10 +25,6 @@ export const noop = () => {}
* Mock Permissions Controller and Middleware
*/
const platform = {
openExtensionInBrowser: noop,
}
const keyringAccounts = deepFreeze([
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
'0xc42edfcc21ed14dda456aa0756c153f7985d8813',
@ -67,7 +63,7 @@ const getUnlockPromise = () => Promise.resolve()
*/
export function getPermControllerOpts () {
return {
platform,
showPermissionRequest: noop,
getKeyringAccounts,
getUnlockPromise,
getRestrictedMethods,

@ -99,3 +99,5 @@
@import '../ui/check-box/index';
@import 'permissions-connect-header/index';
@import 'permissions-connect-footer/index';

@ -2,9 +2,25 @@
display: flex;
border: none;
box-shadow: none;
margin-top: 45px;
width: 488px;
min-height: 468px;
width: 100%;
margin-top: 2px;
height: 100%;
flex-direction: column;
justify-content: space-between;
@media screen and (min-width: 576px) {
width: 426px;
flex: 1;
&__footers {
display: flex;
flex-direction: column-reverse;
flex: 1;
padding-bottom: 20px;
justify-content: space-between;
}
}
&__header {
display: flex;
@ -25,12 +41,13 @@
&__content {
display: flex;
overflow-y: auto;
flex: 1;
flex-direction: column;
color: #7C808E;
padding-left: 24px;
padding-right: 24px;
&--redirect {
margin-top: 50px;
margin-top: 140px;
width: 100%;
display: flex;
align-items: center;
@ -51,19 +68,27 @@
&__permission {
display: flex;
margin-top: 18px;
align-items: center;
i {
font-size: 1.4rem;
color: $Grey-200;
}
label {
margin-left: 6px;
font-size: 14px;
margin-left: 16px;
color: $Black-100;
}
}
}
&__content-container {
display: flex;
flex-direction: column;
align-items: center;
}
&__permissions-header {
@extend %content-text;
line-height: 20px;
@ -77,23 +102,28 @@
&__permissions-container {
display: flex;
flex-direction: column;
margin-top: 33px;
margin-top: 38px;
}
.page-container__footer {
border-top: none;
align-items: center;
margin-top: 8px;
@media screen and (min-width: 576px) {
border-top: none;
}
footer {
width: 300px;
width: 100%;
justify-content: space-between;
button {
width: 124px;
}
}
}
@media screen and (max-width: 575px) {
width: 100%;
margin-top: 25px;
padding: 10px;
&__title {
position: initial;
}

@ -1,6 +1,5 @@
import PropTypes from 'prop-types'
import React, { PureComponent } from 'react'
import Identicon from '../../../ui/identicon'
import IconWithFallBack from '../../../ui/icon-with-fallback'
import PermissionsConnectHeader from '../../permissions-connect-header'
import Tooltip from '../../../ui/tooltip-v2'
@ -30,9 +29,17 @@ 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, selectedIdentities, domainMetadata } = this.props
const { permissionRejected, domainMetadata } = this.props
return (
<div className="permission-result">
{ permissionRejected ? t('cancelling') : t('connecting') }
@ -43,15 +50,11 @@ export default class PermissionPageContainerContent extends PureComponent {
? <span className="permission-result__reject" ><i className="fa fa-times-circle" /></span>
: <span className="permission-result__check" />
}
<img className="permission-result__broken-line" src="/images/broken-line.svg" />
{ this.renderBrokenLine() }
</div>
<div className="permission-result__identicon-container">
<div className="permission-result__identicon-border">
<Identicon
className="permission-result__identicon"
address={selectedIdentities[0].address}
diameter={54}
/>
<img src="/images/logo/metamask-fox.svg" />
</div>
</div>
</div>
@ -97,7 +100,6 @@ export default class PermissionPageContainerContent extends PureComponent {
return (
<div className="permission-approval-container__content__requested">
{items}
<div className="permission-approval-container__content__revoke-note">{ t('revokeInPermissions') }</div>
</div>
)
}
@ -149,16 +151,7 @@ export default class PermissionPageContainerContent extends PureComponent {
} else if (allIdentitiesSelected) {
return t(
'connectToAll',
[
(
<span
key="multi-account-connect-all-accounts"
className="permission-approval-container__bold-title-elements"
>
{ this.renderAccountTooltip(t('connectToAllAccounts')) }
</span>
),
]
[ this.renderAccountTooltip(t('connectToAllAccounts')) ]
)
} else if (selectedIdentities.length > 1) {
return t(
@ -169,15 +162,9 @@ export default class PermissionPageContainerContent extends PureComponent {
)
} else {
return t(
'connectTo', [
(
<span
key="connect-to-one-account"
className="permission-approval-container__bold-title-elements"
>
{ this.getAccountDescriptor(selectedIdentities[0]) }
</span>
),
'connectTo',
[
this.getAccountDescriptor(selectedIdentities[0]),
]
)
}
@ -197,7 +184,7 @@ export default class PermissionPageContainerContent extends PureComponent {
>
{ !redirect
? (
<div>
<div className="permission-approval-container__content-container">
<PermissionsConnectHeader
icon={domainMetadata.icon}
iconName={domainMetadata.origin}

@ -3,6 +3,7 @@ import React, { Component } from 'react'
import { isEqual } from 'lodash'
import { PermissionPageContainerContent } from '.'
import { PageContainerFooter } from '../../ui/page-container'
import PermissionsConnectFooter from '../permissions-connect-footer'
export default class PermissionPageContainer extends Component {
@ -137,8 +138,10 @@ export default class PermissionPageContainer extends Component {
/>
{ !redirect
? (
<div className="permission-approval-container__footers">
<PermissionsConnectFooter />
<PageContainerFooter
cancelButtonType="primary"
cancelButtonType="default"
onCancel={() => this.onCancel()}
cancelText={this.context.t('cancel')}
onSubmit={() => this.onSubmit()}
@ -146,6 +149,7 @@ export default class PermissionPageContainer extends Component {
submitButtonType="confirm"
buttonSizeLarge={false}
/>
</div>
)
: null
}

@ -3,12 +3,6 @@
flex-direction: column;
width: 100%;
align-items: center;
position: absolute;
bottom: 50px;
@media screen and (max-width: 575px) {
bottom: 5px;
}
&__text {
@extend %content-text;

@ -2,6 +2,7 @@
display: flex;
flex-direction: column;
justify-content: center;
width: 92%;
&__icon {
display: flex;
@ -27,13 +28,16 @@
@extend %header--24;
text-align: center;
color: $Black-100;
margin-top: 16px;
margin-top: 14px;
}
&__text {
&__text, &__subtitle {
@extend %content-text;
text-align: center;
margin-top: 4px;
color: $Grey-500;
}
&__subtitle {
margin-top: 4px;
}
}

@ -35,7 +35,7 @@ export default class PermissionsConnectHeader extends Component {
<div className="permissions-connect-header__title">
{ headerTitle }
</div>
<div className="permissions-connect-header__text">
<div className="permissions-connect-header__subtitle">
{ headerText }
</div>
</div>

@ -8,6 +8,7 @@ import Tooltip from '../../../components/ui/tooltip-v2'
import { PRIMARY } from '../../../helpers/constants/common'
import UserPreferencedCurrencyDisplay from '../../../components/app/user-preferenced-currency-display'
import PermissionsConnectHeader from '../../../components/app/permissions-connect-header'
import PermissionsConnectFooter from '../../../components/app/permissions-connect-footer'
export default class ChooseAccount extends Component {
static propTypes = {
@ -190,6 +191,8 @@ export default class ChooseAccount extends Component {
/>
{ 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) }
@ -206,6 +209,7 @@ export default class ChooseAccount extends Component {
</Button>
</div>
</div>
</div>
)
}
}

@ -2,18 +2,22 @@
display: flex;
flex-direction: column;
align-items: center;
margin-top: 40px;
width: 428px;
width: 100%;
margin-left: auto;
margin-right: auto;
height: 100%;
@media screen and (min-width: 576px) {
width: 426px;
}
&__title {
@extend %header--18;
}
&__text, &__text--blue, &__text--grey {
&__text, &__text-blue, &__text-grey {
@extend %content-text;
line-height: 25px;
}
&__text-blue {
@ -26,17 +30,13 @@
}
&__accounts-list {
width: 100%;
width: 92%;
border: 1px solid #D0D5DA;
box-sizing: border-box;
border-radius: 8px;
margin-top: 8px;
max-height: 338px;
max-height: 238px;
overflow-y: auto;
@media screen and (max-width: 575px) {
width: 100%;
}
}
&__accounts-list-header--one-item,
@ -44,7 +44,8 @@
display: flex;
margin-top: 36px;
width: 100%;
padding-right: 2px;
padding-left: 15px;
padding-right: 17px;
}
&__accounts-list-header--one-item {
@ -62,7 +63,11 @@
}
&__list-check-box {
margin-right: 24px;
margin-right: 16px;
i {
font-size: 0.8rem;
}
}
&__header-check-box {
@ -113,15 +118,14 @@
}
&__label {
@extend %header--18;
line-height: 25px;
color: #000000;
@extend %content-text;
color: $Black-100;
}
&__balance {
@extend %content-text;
line-height: 17px;
color: #6A737D;
font-size: 12px;
color: $Grey-500;
}
&__last-connected {
@ -139,11 +143,32 @@
color: $Red-400;
}
&__footer-container {
width: 100%;
flex: 1 1 auto;
display: flex;
flex-direction: column;
justify-content: flex-end;
@media screen and (min-width: 576px) {
flex-direction: column-reverse;
justify-content: space-between;
padding-bottom: 20px;
}
}
&__bottom-buttons {
display: flex;
justify-content: space-between;
width: 100%;
margin-top: 16px;
padding-top: 16px;
padding-bottom: 16px;
margin-top: 8px;
border-top: 1px solid #D6D9DC;
@media screen and (min-width: 576px) {
border-top: none;
}
button {
width: 124px;
@ -151,6 +176,11 @@
.btn-default {
background: white;
margin-left: 16px;
}
.btn-primary {
margin-right: 16px;
}
}

@ -1,25 +1,44 @@
@import 'permissions-connect-footer/index';
@import 'choose-account/index';
.permissions-connect {
width: 100%;
height: 100%;
position: relative;
background: white;
&__page-count-wrapper {
margin-top: 26px;
margin-left: 20px;
display: flex;
justify-content: flex-end;
align-items: flex-end;
flex-direction: column;
@media screen and (min-width: 576px) {
.page-container {
max-height: none;
min-height: auto;
}
}
&__top-bar {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 16px 16px 0px 16px;
align-items: center;
}
&__back {
@extend %content-text;
color: $curious-blue;
cursor: pointer;
i {
margin-right: 10px;
}
}
&__page-count {
@extend %content-text;
margin-right: 30px;
font-size: 12px;
line-height: 17px;
color: #6A737D;
}
grid-column: 2;
justify-self: end;
font-weight: bold;
line-height: 21px;
}
}

@ -1,7 +1,6 @@
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { Switch, Route } from 'react-router-dom'
import PermissionsConnectFooter from './permissions-connect-footer'
import ChooseAccount from './choose-account'
import { getEnvironmentType } from '../../../../app/scripts/lib/util'
import {
@ -28,7 +27,6 @@ export default class PermissionConnect extends Component {
nativeCurrency: PropTypes.string,
permissionsRequest: PropTypes.object,
addressLastConnectedMap: PropTypes.object,
requestAccountTabs: PropTypes.object,
permissionsRequestId: PropTypes.string,
domains: PropTypes.object,
history: PropTypes.object.isRequired,
@ -44,7 +42,6 @@ export default class PermissionConnect extends Component {
nativeCurrency: '',
permissionsRequest: undefined,
addressLastConnectedMap: {},
requestAccountTabs: {},
permissionsRequestId: '',
domains: {},
redirecting: false,
@ -71,7 +68,7 @@ export default class PermissionConnect extends Component {
}
removeBeforeUnload = () => {
if (getEnvironmentType() === ENVIRONMENT_TYPE_FULLSCREEN) {
if (getEnvironmentType() === ENVIRONMENT_TYPE_FULLSCREEN || getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
window.removeEventListener('beforeunload', this.beforeUnload)
}
}
@ -101,8 +98,7 @@ export default class PermissionConnect extends Component {
}
redirectFlow (accepted) {
const { requestAccountTabs, history } = this.props
const { originName } = this.state
const { history } = this.props
this.setState({
redirecting: true,
@ -113,16 +109,12 @@ export default class PermissionConnect extends Component {
if (getEnvironmentType() === ENVIRONMENT_TYPE_FULLSCREEN) {
setTimeout(async () => {
const currentTab = await global.platform.currentTab()
try {
if (currentTab.active) {
await global.platform.switchToTab(requestAccountTabs[originName])
}
} finally {
global.platform.closeTab(currentTab.id)
}
}, 2000)
} else if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
history.push(DEFAULT_ROUTE)
setTimeout(async () => {
global.platform.closeCurrentWindow()
}, 2000)
} else if (getEnvironmentType() === ENVIRONMENT_TYPE_POPUP) {
history.push(CONNECTED_ROUTE)
}
@ -142,11 +134,55 @@ export default class PermissionConnect extends Component {
return history.push(DEFAULT_ROUTE)
}
if (getEnvironmentType() === ENVIRONMENT_TYPE_FULLSCREEN) {
if (getEnvironmentType() === ENVIRONMENT_TYPE_FULLSCREEN || getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
window.addEventListener('beforeunload', this.beforeUnload)
}
}
cancelPermissionsRequest = async (requestId) => {
const { rejectPermissionsRequest } = this.props
if (requestId) {
await rejectPermissionsRequest(requestId)
if (getEnvironmentType() === ENVIRONMENT_TYPE_FULLSCREEN || getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
window.close()
} else if (getEnvironmentType() === ENVIRONMENT_TYPE_POPUP) {
history.push(DEFAULT_ROUTE)
}
}
}
goBack () {
const { history, connectPath } = this.props
history.push(connectPath)
}
renderTopBar () {
const { redirecting } = this.state
const { page } = this.props
const { t } = this.context
return !redirecting
? (
<div
className="permissions-connect__top-bar"
>
{ page === '2'
? (
<div className="permissions-connect__back" onClick={() => this.goBack()}>
<i className="fas fa-chevron-left" />
{ t('back') }
</div>
)
: null
}
<div className="permissions-connect__page-count">
{ t('xOfY', [ page, '2' ]) }
</div>
</div>
)
: null
}
render () {
const {
approvePermissionsRequest,
@ -160,7 +196,6 @@ export default class PermissionConnect extends Component {
permissionsRequestId,
connectPath,
confirmPermissionPath,
page,
targetDomainMetadata,
} = this.props
const {
@ -172,22 +207,12 @@ export default class PermissionConnect extends Component {
return (
<div className="permissions-connect">
{ !redirecting
? (
<div className="permissions-connect__page-count-wrapper">
<div className="permissions-connect-header__page-count">
{ `${page}/2` }
</div>
</div>
)
: null
}
{ this.renderTopBar() }
<Switch>
<Route
path={connectPath}
exact
render={() => (
<div>
<ChooseAccount
accounts={accounts}
originName={originName}
@ -200,25 +225,17 @@ export default class PermissionConnect extends Component {
})
}}
addressLastConnectedMap={addressLastConnectedMap}
cancelPermissionsRequest={(requestId) => {
if (requestId) {
rejectPermissionsRequest(requestId)
this.redirectFlow(false)
}
}}
cancelPermissionsRequest={(requestId) => this.cancelPermissionsRequest(requestId)}
permissionsRequestId={permissionsRequestId}
selectedAccountAddresses={selectedAccountAddresses}
targetDomainMetadata={targetDomainMetadata}
/>
{ !redirecting ? <PermissionsConnectFooter /> : null }
</div>
)}
/>
<Route
path={confirmPermissionPath}
exact
render={() => (
<div>
<PermissionPageContainer
request={permissionsRequest || {}}
approvePermissionsRequest={(request, accounts) => {
@ -234,8 +251,6 @@ export default class PermissionConnect extends Component {
permissionRejected={ permissionAccepted === false }
cachedOrigin={originName}
/>
{ !redirecting ? <PermissionsConnectFooter /> : null }
</div>
)}
/>
</Switch>

@ -32,8 +32,6 @@ const mapStateToProps = (state, ownProps) => {
const accountsWithLabels = getAccountsWithLabels(state)
const { requestAccountTabs = {} } = state.appState
const lastConnectedInfo = getLastConnectedInfo(state) || {}
const addressLastConnectedMap = lastConnectedInfo[origin] || {}
@ -62,7 +60,6 @@ const mapStateToProps = (state, ownProps) => {
originName: origin,
newAccountNumber: accountsWithLabels.length + 1,
nativeCurrency,
requestAccountTabs,
addressLastConnectedMap,
domains: getPermissionsDomains(state),
connectPath,

@ -2217,8 +2217,16 @@ export function approvePermissionsRequest (request, accounts) {
* @param {string} requestId - The id of the request to be rejected
*/
export function rejectPermissionsRequest (requestId) {
return () => {
background.rejectPermissionsRequest(requestId)
return (dispatch) => {
return new Promise((resolve, reject) => {
background.rejectPermissionsRequest(requestId, (err) => {
if (err) {
dispatch(displayWarning(err.message))
reject()
}
resolve()
})
})
}
}

Loading…
Cancel
Save