mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Update Connect Request screen design (#5644)
* Parameterize NetworkDisplay background colour * Update design for login request screen * Pass siteTitle, siteImage through for calls to ethereum.enable() * Bring the site images closer together
This commit is contained in:
parent
31cb111d2e
commit
26ada8a828
@ -32,14 +32,11 @@
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"providerAPIRequest": {
|
||||
"message": "Ethereum API Request"
|
||||
},
|
||||
"reviewProviderRequest": {
|
||||
"message": "Please review this Ethereum API request."
|
||||
"providerRequest": {
|
||||
"message": "$1 would like to connect to your account"
|
||||
},
|
||||
"providerRequestInfo": {
|
||||
"message": "The domain listed below is requesting access to the Ethereum blockchain and to view your current account. Always double check that you're on the correct site before approving access."
|
||||
"message": "This site is requesting access to view your current account address. Always make sure you trust the sites you interact with."
|
||||
},
|
||||
"accept": {
|
||||
"message": "Accept"
|
||||
@ -212,6 +209,9 @@
|
||||
"connect": {
|
||||
"message": "Connect"
|
||||
},
|
||||
"connectRequest": {
|
||||
"message": "Connect Request"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Connecting..."
|
||||
},
|
||||
|
7
app/images/mm-secure.svg
Normal file
7
app/images/mm-secure.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 10 KiB |
20
app/images/provider-approval-check.svg
Normal file
20
app/images/provider-approval-check.svg
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d)">
|
||||
<path d="M34.5 19C34.5 27.0081 28.0081 33.5 20 33.5C11.9919 33.5 5.5 27.0081 5.5 19C5.5 10.9919 11.9919 4.5 20 4.5C28.0081 4.5 34.5 10.9919 34.5 19Z" fill="#61BA00" stroke="#61BA00"/>
|
||||
<path d="M13.2998 19.7195L16.813 23.3195L26.1998 14.0195" stroke="white" stroke-width="2"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d" x="0" y="0" width="40" height="40" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dy="1"/>
|
||||
<feGaussianBlur stdDeviation="2.5"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@ -126,6 +126,8 @@ function listenForProviderRequest () {
|
||||
extension.runtime.sendMessage({
|
||||
action: 'init-provider-request',
|
||||
origin: source.location.hostname,
|
||||
siteImage: getSiteIcon(source),
|
||||
siteTitle: getSiteName(source),
|
||||
})
|
||||
break
|
||||
case 'ETHEREUM_IS_APPROVED':
|
||||
@ -285,3 +287,31 @@ function redirectToPhishingWarning () {
|
||||
href: window.location.href,
|
||||
})}`
|
||||
}
|
||||
|
||||
function getSiteName (window) {
|
||||
const document = window.document
|
||||
const siteName = document.querySelector('head > meta[property="og:site_name"]')
|
||||
if (siteName) {
|
||||
return siteName.content
|
||||
}
|
||||
|
||||
return document.title
|
||||
}
|
||||
|
||||
function getSiteIcon (window) {
|
||||
const document = window.document
|
||||
|
||||
// Use the site's favicon if it exists
|
||||
const shortcutIcon = document.querySelector('head > link[rel="shortcut icon"]')
|
||||
if (shortcutIcon) {
|
||||
return shortcutIcon.href
|
||||
}
|
||||
|
||||
// Search through available icons in no particular order
|
||||
const icon = Array.from(document.querySelectorAll('head > link[rel="icon"]')).find((icon) => Boolean(icon.href))
|
||||
if (icon) {
|
||||
return icon.href
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
@ -24,31 +24,35 @@ class ProviderApprovalController {
|
||||
this.publicConfigStore = publicConfigStore
|
||||
this.store = new ObservableStore()
|
||||
|
||||
platform && platform.addMessageListener && platform.addMessageListener(({ action = '', origin }) => {
|
||||
switch (action) {
|
||||
case 'init-provider-request':
|
||||
this._handleProviderRequest(origin)
|
||||
break
|
||||
case 'init-is-approved':
|
||||
this._handleIsApproved(origin)
|
||||
break
|
||||
case 'init-is-unlocked':
|
||||
this._handleIsUnlocked()
|
||||
break
|
||||
case 'init-privacy-request':
|
||||
this._handlePrivacyRequest()
|
||||
break
|
||||
}
|
||||
})
|
||||
if (platform && platform.addMessageListener) {
|
||||
platform.addMessageListener(({ action = '', origin, siteTitle, siteImage }) => {
|
||||
switch (action) {
|
||||
case 'init-provider-request':
|
||||
this._handleProviderRequest(origin, siteTitle, siteImage)
|
||||
break
|
||||
case 'init-is-approved':
|
||||
this._handleIsApproved(origin)
|
||||
break
|
||||
case 'init-is-unlocked':
|
||||
this._handleIsUnlocked()
|
||||
break
|
||||
case 'init-privacy-request':
|
||||
this._handlePrivacyRequest()
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a tab requests access to a full Ethereum provider API
|
||||
*
|
||||
* @param {string} origin - Origin of the window requesting full provider access
|
||||
* @param {string} siteTitle - The title of the document requesting full provider access
|
||||
* @param {string} siteImage - The icon of the window requesting full provider access
|
||||
*/
|
||||
_handleProviderRequest (origin) {
|
||||
this.store.updateState({ providerRequests: [{ origin }] })
|
||||
_handleProviderRequest (origin, siteTitle, siteImage) {
|
||||
this.store.updateState({ providerRequests: [{ origin, siteTitle, siteImage }] })
|
||||
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
|
||||
if (this.isApproved(origin) && this.caching && isUnlocked) {
|
||||
this.approveProviderRequest(origin)
|
||||
|
@ -459,7 +459,7 @@ describe('MetaMask', function () {
|
||||
dapp = windowHandles.find(handle => handle !== extension && handle !== popup)
|
||||
|
||||
await delay(regularDelayMs)
|
||||
const approveButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Approve')]`), 10000)
|
||||
const approveButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Connect')]`))
|
||||
await approveButton.click()
|
||||
})
|
||||
|
||||
|
@ -32,6 +32,8 @@
|
||||
|
||||
@import './pages/index';
|
||||
|
||||
@import './provider-page-container/index';
|
||||
|
||||
@import './selected-account/index';
|
||||
|
||||
@import './sender-to-recipient/index';
|
||||
|
@ -3,11 +3,14 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
background-color: lighten(rgb(125, 128, 130), 45%);
|
||||
padding: 0 10px;
|
||||
border-radius: 4px;
|
||||
height: 25px;
|
||||
|
||||
&--colored {
|
||||
background-color: lighten(rgb(125, 128, 130), 45%);
|
||||
}
|
||||
|
||||
&--mainnet {
|
||||
background-color: lighten($blue-lagoon, 68%);
|
||||
}
|
||||
|
@ -16,7 +16,12 @@ const networkToClassHash = {
|
||||
}
|
||||
|
||||
export default class NetworkDisplay extends Component {
|
||||
static defaultProps = {
|
||||
colored: true,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
colored: PropTypes.bool,
|
||||
network: PropTypes.string,
|
||||
provider: PropTypes.object,
|
||||
}
|
||||
@ -41,14 +46,16 @@ export default class NetworkDisplay extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { network, provider: { type, nickname } } = this.props
|
||||
const { colored, network, provider: { type, nickname } } = this.props
|
||||
const networkClass = networkToClassHash[network]
|
||||
|
||||
return (
|
||||
<div className={classnames(
|
||||
'network-display__container',
|
||||
networkClass && ('network-display__container--' + networkClass)
|
||||
)}>
|
||||
<div
|
||||
className={classnames('network-display__container', {
|
||||
'network-display__container--colored': colored,
|
||||
['network-display__container--' + networkClass]: colored && networkClass,
|
||||
})}
|
||||
>
|
||||
{
|
||||
networkClass
|
||||
? <div className={`network-display__icon network-display__icon--${networkClass}`} />
|
||||
|
@ -67,7 +67,9 @@ export default class Home extends PureComponent {
|
||||
}
|
||||
|
||||
if (providerRequests && providerRequests.length > 0) {
|
||||
return <ProviderApproval origin={providerRequests[0].origin} />
|
||||
return (
|
||||
<ProviderApproval providerRequest={providerRequests[0]} />
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -1,12 +1,12 @@
|
||||
import PageContainerContent from '../../page-container'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { Component } from 'react'
|
||||
import ProviderPageContainer from '../../provider-page-container'
|
||||
|
||||
export default class ProviderApproval extends Component {
|
||||
static propTypes = {
|
||||
approveProviderRequest: PropTypes.func,
|
||||
origin: PropTypes.string,
|
||||
rejectProviderRequest: PropTypes.func,
|
||||
approveProviderRequest: PropTypes.func.isRequired,
|
||||
providerRequest: PropTypes.object.isRequired,
|
||||
rejectProviderRequest: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
@ -14,22 +14,15 @@ export default class ProviderApproval extends Component {
|
||||
};
|
||||
|
||||
render () {
|
||||
const { approveProviderRequest, origin, rejectProviderRequest } = this.props
|
||||
const { approveProviderRequest, providerRequest, rejectProviderRequest } = this.props
|
||||
return (
|
||||
<PageContainerContent
|
||||
title={this.context.t('providerAPIRequest')}
|
||||
subtitle={this.context.t('reviewProviderRequest')}
|
||||
contentComponent={(
|
||||
<div className="provider_approval_content">
|
||||
{this.context.t('providerRequestInfo')}
|
||||
<div className="provider_approval_origin">{origin}</div>
|
||||
</div>
|
||||
)}
|
||||
submitText={this.context.t('approve')}
|
||||
cancelText={this.context.t('reject')}
|
||||
onSubmit={() => { approveProviderRequest(origin) }}
|
||||
onCancel={() => { rejectProviderRequest(origin) }}
|
||||
onClose={() => { rejectProviderRequest(origin) }} />
|
||||
<ProviderPageContainer
|
||||
approveProviderRequest={approveProviderRequest}
|
||||
origin={providerRequest.origin}
|
||||
rejectProviderRequest={rejectProviderRequest}
|
||||
siteImage={providerRequest.siteImage}
|
||||
siteTitle={providerRequest.siteTitle}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
3
ui/app/components/provider-page-container/index.js
Normal file
3
ui/app/components/provider-page-container/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
export {default} from './provider-page-container.component'
|
||||
export {default as ProviderPageContainerContent} from './provider-page-container-content'
|
||||
export {default as ProviderPageContainerHeader} from './provider-page-container-header'
|
120
ui/app/components/provider-page-container/index.scss
Normal file
120
ui/app/components/provider-page-container/index.scss
Normal file
@ -0,0 +1,120 @@
|
||||
.provider-approval-container {
|
||||
display: flex;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
border-bottom: 1px solid $geyser;
|
||||
padding: 9px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
color: #7C808E;
|
||||
|
||||
h1, h2 {
|
||||
color: #4A4A4A;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
line-height: 18px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 22px;
|
||||
line-height: 26px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
p {
|
||||
padding: 0 40px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
a, a:hover {
|
||||
color: $dodger-blue;
|
||||
}
|
||||
|
||||
.provider-approval-visual {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
position: relative;
|
||||
margin: 0 32px;
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
padding: 8px 0 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 10px;
|
||||
line-height: 14px;
|
||||
padding: 0;
|
||||
color: #A2A4AC;
|
||||
}
|
||||
|
||||
&__check {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: white url("/images/provider-approval-check.svg") no-repeat;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
&__identicon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
|
||||
&--default {
|
||||
background-color: lightgray;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
&:before {
|
||||
border-top: 2px dashed #CDD1E4;
|
||||
content: "";
|
||||
margin: 0 auto;
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 65%;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
|
||||
.secure-badge {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 25px;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export {default} from './provider-page-container-content.container'
|
@ -0,0 +1,71 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React, {PureComponent} from 'react'
|
||||
import Identicon from '../../identicon'
|
||||
|
||||
export default class ProviderPageContainerContent extends PureComponent {
|
||||
static propTypes = {
|
||||
origin: PropTypes.string.isRequired,
|
||||
selectedIdentity: PropTypes.string.isRequired,
|
||||
siteImage: PropTypes.string,
|
||||
siteTitle: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
};
|
||||
|
||||
renderConnectVisual = () => {
|
||||
const { origin, selectedIdentity, siteImage, siteTitle } = this.props
|
||||
|
||||
return (
|
||||
<div className="provider-approval-visual">
|
||||
<section>
|
||||
{siteImage ? (
|
||||
<img
|
||||
className="provider-approval-visual__identicon"
|
||||
src={siteImage}
|
||||
/>
|
||||
) : (
|
||||
<i className="provider-approval-visual__identicon--default">
|
||||
{siteTitle.charAt(0).toUpperCase()}
|
||||
</i>
|
||||
)}
|
||||
<h1>{siteTitle}</h1>
|
||||
<h2>{origin}</h2>
|
||||
</section>
|
||||
<span className="provider-approval-visual__check" />
|
||||
<section>
|
||||
<Identicon
|
||||
className="provider-approval-visual__identicon"
|
||||
address={selectedIdentity.address}
|
||||
diameter={64}
|
||||
/>
|
||||
<h1>{selectedIdentity.name}</h1>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { siteTitle } = this.props
|
||||
const { t } = this.context
|
||||
|
||||
return (
|
||||
<div className="provider-approval-container__content">
|
||||
<section>
|
||||
<h2>{t('connectRequest')}</h2>
|
||||
{this.renderConnectVisual()}
|
||||
<h1>{t('providerRequest', [siteTitle])}</h1>
|
||||
<p>
|
||||
{t('providerRequestInfo')}
|
||||
<br/>
|
||||
<a href="#">{t('learnMore')}.</a>
|
||||
</p>
|
||||
</section>
|
||||
<section className="secure-badge">
|
||||
<img src="/images/mm-secure.svg" />
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { connect } from 'react-redux'
|
||||
import ProviderPageContainerContent from './provider-page-container-content.component'
|
||||
import { getSelectedIdentity } from '../../../selectors'
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
selectedIdentity: getSelectedIdentity(state),
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(ProviderPageContainerContent)
|
@ -0,0 +1 @@
|
||||
export {default} from './provider-page-container-header.component'
|
@ -0,0 +1,12 @@
|
||||
import React, {PureComponent} from 'react'
|
||||
import NetworkDisplay from '../../network-display'
|
||||
|
||||
export default class ProviderPageContainerHeader extends PureComponent {
|
||||
render () {
|
||||
return (
|
||||
<div className="provider-approval-container__header">
|
||||
<NetworkDisplay colored={false} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React, {PureComponent} from 'react'
|
||||
import { ProviderPageContainerContent, ProviderPageContainerHeader } from './'
|
||||
import { PageContainerFooter } from '../page-container'
|
||||
|
||||
export default class ProviderPageContainer extends PureComponent {
|
||||
static propTypes = {
|
||||
approveProviderRequest: PropTypes.func.isRequired,
|
||||
origin: PropTypes.string.isRequired,
|
||||
rejectProviderRequest: PropTypes.func.isRequired,
|
||||
siteImage: PropTypes.string,
|
||||
siteTitle: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
};
|
||||
|
||||
onCancel = () => {
|
||||
const { origin, rejectProviderRequest } = this.props
|
||||
rejectProviderRequest(origin)
|
||||
}
|
||||
|
||||
onSubmit = () => {
|
||||
const { approveProviderRequest, origin } = this.props
|
||||
approveProviderRequest(origin)
|
||||
}
|
||||
|
||||
render () {
|
||||
const {origin, siteImage, siteTitle} = this.props
|
||||
|
||||
return (
|
||||
<div className="page-container provider-approval-container">
|
||||
<ProviderPageContainerHeader />
|
||||
<ProviderPageContainerContent
|
||||
origin={origin}
|
||||
siteImage={siteImage}
|
||||
siteTitle={siteTitle}
|
||||
/>
|
||||
<PageContainerFooter
|
||||
onCancel={() => this.onCancel()}
|
||||
cancelText={this.context.t('cancel')}
|
||||
onSubmit={() => this.onSubmit()}
|
||||
submitText={this.context.t('connect')}
|
||||
submitButtonType="confirm"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user