diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 3733830cc..d4c8caffa 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -1188,6 +1188,9 @@
"signatureRequest": {
"message": "Signature Request"
},
+ "signatureRequest1": {
+ "message": "Message"
+ },
"signed": {
"message": "Signed"
},
diff --git a/ui/app/components/app/index.scss b/ui/app/components/app/index.scss
index 1ccb6a94a..d1ffa4949 100644
--- a/ui/app/components/app/index.scss
+++ b/ui/app/components/app/index.scss
@@ -84,4 +84,4 @@
@import 'home-notification/index';
-@import 'multiple-notifications/index';
+@import 'signature-request/index';
diff --git a/ui/app/components/app/signature-request.js b/ui/app/components/app/signature-request-original.js
similarity index 94%
rename from ui/app/components/app/signature-request.js
rename to ui/app/components/app/signature-request-original.js
index e7370c124..0a9a43593 100644
--- a/ui/app/components/app/signature-request.js
+++ b/ui/app/components/app/signature-request-original.js
@@ -243,7 +243,7 @@ SignatureRequest.prototype.renderBody = function () {
let notice = this.context.t('youSign') + ':'
const { txData } = this.props
- const { type, msgParams: { data, version } } = txData
+ const { type, msgParams: { data } } = txData
if (type === 'personal_sign') {
rows = [{ name: this.context.t('message'), value: this.msgHexToText(data) }]
@@ -275,17 +275,15 @@ SignatureRequest.prototype.renderBody = function () {
}, [notice]),
h('div.request-signature__rows',
- type === 'eth_signTypedData' && (version === 'V3' || version === 'V4') ?
- this.renderTypedData(data) :
- rows.map(({ name, value }) => {
- if (typeof value === 'boolean') {
- value = value.toString()
- }
- return h('div.request-signature__row', [
- h('div.request-signature__row-title', [`${name}:`]),
- h('div.request-signature__row-value', value),
- ])
- }),
+ rows.map(({ name, value }, index) => {
+ if (typeof value === 'boolean') {
+ value = value.toString()
+ }
+ return h('div.request-signature__row', { key: `request-signature-row-${index}` }, [
+ h('div.request-signature__row-title', [`${name}:`]),
+ h('div.request-signature__row-value', value),
+ ])
+ })
),
])
}
diff --git a/ui/app/components/app/signature-request/index.js b/ui/app/components/app/signature-request/index.js
new file mode 100644
index 000000000..b1c8a1960
--- /dev/null
+++ b/ui/app/components/app/signature-request/index.js
@@ -0,0 +1 @@
+export { default } from './signature-request.container'
diff --git a/ui/app/components/app/signature-request/index.scss b/ui/app/components/app/signature-request/index.scss
new file mode 100644
index 000000000..69115681f
--- /dev/null
+++ b/ui/app/components/app/signature-request/index.scss
@@ -0,0 +1,96 @@
+@import 'signature-request-footer/index';
+@import 'signature-request-header/index';
+@import 'signature-request-message/index';
+
+.signature-request {
+ display: flex;
+ flex: 1 1 auto;
+ flex-direction: column;
+ min-width: 0;
+
+ @media screen and (min-width: 576px) {
+ flex: initial;
+ }
+}
+
+.signature-request-header {
+ flex: 1;
+
+ .network-display__container {
+ padding: 0;
+ justify-content: flex-end;
+ }
+
+ .network-display__name {
+ font-size: 12px;
+ white-space: nowrap;
+ font-weight: 500;
+ }
+}
+
+.signature-request-content {
+ flex: 1 40%;
+ margin-top: 1rem;
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ margin-bottom: 25px;
+ min-height: min-content;
+
+ &__title {
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: 500;
+ font-size: 18px;
+ }
+
+ &__identicon-container {
+ padding: 1rem;
+ flex: 1;
+ position: relative;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ &__identicon-border {
+ height: 75px;
+ width: 75px;
+ border-radius: 50%;
+ border: 1px solid white;
+ position: absolute;
+ box-shadow: 0 2px 2px 0.5px rgba(0, 0, 0, 0.19);
+ }
+
+ &__identicon-initial {
+ position: absolute;
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: 500;
+ font-size: 60px;
+ color: white;
+ z-index: 1;
+ text-shadow: 0px 4px 6px rgba(0, 0, 0, 0.422);
+ }
+
+ &__info {
+ font-size: 12px;
+ }
+
+ &__info--bolded {
+ font-size: 16px;
+ font-weight: 500;
+ }
+
+ p {
+ color: #999999;
+ font-size: 0.8rem;
+ }
+
+ .identicon {}
+}
+
+.signature-request-footer {
+ flex: 1 1 auto;
+}
\ No newline at end of file
diff --git a/ui/app/components/app/signature-request/signature-request-footer/index.js b/ui/app/components/app/signature-request/signature-request-footer/index.js
new file mode 100644
index 000000000..11d0b3944
--- /dev/null
+++ b/ui/app/components/app/signature-request/signature-request-footer/index.js
@@ -0,0 +1 @@
+export { default } from './signature-request-footer.component'
diff --git a/ui/app/components/app/signature-request/signature-request-footer/index.scss b/ui/app/components/app/signature-request/signature-request-footer/index.scss
new file mode 100644
index 000000000..d8c6b36d6
--- /dev/null
+++ b/ui/app/components/app/signature-request/signature-request-footer/index.scss
@@ -0,0 +1,18 @@
+.signature-request-footer {
+ display: flex;
+ border-top: 1px solid #d2d8dd;
+
+ button {
+ text-transform: uppercase;
+ flex: 1;
+ margin: 1rem 0.5rem;
+ border-radius: 3px;
+ }
+
+ button:first-child() {
+ margin-left: 1rem;
+ }
+ button:last-child() {
+ margin-right: 1rem;
+ }
+}
\ No newline at end of file
diff --git a/ui/app/components/app/signature-request/signature-request-footer/signature-request-footer.component.js b/ui/app/components/app/signature-request/signature-request-footer/signature-request-footer.component.js
new file mode 100644
index 000000000..591b9a03a
--- /dev/null
+++ b/ui/app/components/app/signature-request/signature-request-footer/signature-request-footer.component.js
@@ -0,0 +1,24 @@
+import React, { PureComponent } from 'react'
+import PropTypes from 'prop-types'
+import Button from '../../../ui/button'
+
+export default class SignatureRequestFooter extends PureComponent {
+ static propTypes = {
+ cancelAction: PropTypes.func.isRequired,
+ signAction: PropTypes.func.isRequired,
+ }
+
+ static contextTypes = {
+ t: PropTypes.func,
+ }
+
+ render () {
+ const { cancelAction, signAction } = this.props
+ return (
+
+
+
+
+ )
+ }
+}
diff --git a/ui/app/components/app/signature-request/signature-request-header/index.js b/ui/app/components/app/signature-request/signature-request-header/index.js
new file mode 100644
index 000000000..fa596383a
--- /dev/null
+++ b/ui/app/components/app/signature-request/signature-request-header/index.js
@@ -0,0 +1 @@
+export { default } from './signature-request-header.component'
diff --git a/ui/app/components/app/signature-request/signature-request-header/index.scss b/ui/app/components/app/signature-request/signature-request-header/index.scss
new file mode 100644
index 000000000..7a33f85f2
--- /dev/null
+++ b/ui/app/components/app/signature-request/signature-request-header/index.scss
@@ -0,0 +1,25 @@
+.signature-request-header {
+ display: flex;
+ padding: 1rem;
+ border-bottom: 1px solid $geyser;
+ justify-content: space-between;
+ font-size: .75rem;
+
+ &--account, &--network {
+ flex: 1;
+ }
+
+ &--account {
+ display: flex;
+ align-items: center;
+
+ .account-list-item__account-name {
+ font-size: 12px;
+ font-weight: 500;
+ }
+
+ .account-list-item__top-row {
+ margin: 0px;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/app/components/app/signature-request/signature-request-header/signature-request-header.component.js b/ui/app/components/app/signature-request/signature-request-header/signature-request-header.component.js
new file mode 100644
index 000000000..3ac0c9afb
--- /dev/null
+++ b/ui/app/components/app/signature-request/signature-request-header/signature-request-header.component.js
@@ -0,0 +1,29 @@
+import React, { PureComponent } from 'react'
+import PropTypes from 'prop-types'
+import AccountListItem from '../../../../pages/send/account-list-item/account-list-item.component'
+import NetworkDisplay from '../../network-display'
+
+export default class SignatureRequestHeader extends PureComponent {
+ static propTypes = {
+ selectedAccount: PropTypes.object.isRequired,
+ }
+
+ render () {
+ const { selectedAccount } = this.props
+
+ return (
+
+
+ {selectedAccount &&
}
+ {name}
+
+
+
+
+
+ )
+ }
+}
diff --git a/ui/app/components/app/signature-request/signature-request-message/index.js b/ui/app/components/app/signature-request/signature-request-message/index.js
new file mode 100644
index 000000000..e62265a5f
--- /dev/null
+++ b/ui/app/components/app/signature-request/signature-request-message/index.js
@@ -0,0 +1 @@
+export { default } from './signature-request-message.component'
diff --git a/ui/app/components/app/signature-request/signature-request-message/index.scss b/ui/app/components/app/signature-request/signature-request-message/index.scss
new file mode 100644
index 000000000..aec597f89
--- /dev/null
+++ b/ui/app/components/app/signature-request/signature-request-message/index.scss
@@ -0,0 +1,67 @@
+.signature-request-message {
+ flex: 1 60%;
+ display: flex;
+ flex-direction: column;
+
+ &__title {
+ font-weight: 500;
+ font-size: 14px;
+ color: #636778;
+ margin-left: 12px;
+ }
+
+ h2 {
+ flex: 1 1 0;
+ text-align: left;
+ font-size: 0.8rem;
+ border-bottom: 1px solid #d2d8dd;
+ padding: 0.5rem;
+ margin: 0;
+ color: #ccc;
+ }
+
+ &--root {
+ flex: 1 100%;
+ background-color: #f8f9fb;
+ padding-bottom: 0.5rem;
+ overflow: auto;
+ padding-left: 12px;
+ padding-right: 12px;
+ width: 360px;
+ font-family: monospace;
+
+ @media screen and (min-width: 576px) {
+ width: auto;
+ }
+ }
+
+ &__type-title {
+ font-family: monospace;
+ font-style: normal;
+ font-weight: normal;
+ font-size: 14px;
+ margin-left: 12px;
+ margin-top: 6px;
+ margin-bottom: 10px;
+ }
+
+ &--node, &--node-leaf {
+ padding-left: 0.8rem;
+
+ &-label {
+ color: #5B5D67;
+ }
+
+ &-value {
+ color: black;
+ margin-left: 0.5rem;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ }
+ }
+
+ &--node-leaf {
+ display: flex;
+ }
+}
\ No newline at end of file
diff --git a/ui/app/components/app/signature-request/signature-request-message/signature-request-message.component.js b/ui/app/components/app/signature-request/signature-request-message/signature-request-message.component.js
new file mode 100644
index 000000000..16b6c3bea
--- /dev/null
+++ b/ui/app/components/app/signature-request/signature-request-message/signature-request-message.component.js
@@ -0,0 +1,50 @@
+import React, { PureComponent } from 'react'
+import PropTypes from 'prop-types'
+import classnames from 'classnames'
+
+export default class SignatureRequestMessage extends PureComponent {
+ static propTypes = {
+ data: PropTypes.object.isRequired,
+ }
+
+ static contextTypes = {
+ t: PropTypes.func,
+ }
+
+ renderNode (data) {
+ return (
+
+ {Object.entries(data).map(([ label, value ], i) => (
+
+ {label}:
+ {
+ typeof value === 'object' && value !== null ?
+ this.renderNode(value)
+ : {value}
+ }
+
+ ))}
+
+ )
+ }
+
+
+ render () {
+ const { data } = this.props
+
+ return (
+
+
{this.context.t('signatureRequest1')}
+
+
{this.context.t('signatureRequest1')}
+ {this.renderNode(data)}
+
+
+ )
+ }
+}
diff --git a/ui/app/components/app/signature-request/signature-request.component.js b/ui/app/components/app/signature-request/signature-request.component.js
new file mode 100644
index 000000000..7029b1e00
--- /dev/null
+++ b/ui/app/components/app/signature-request/signature-request.component.js
@@ -0,0 +1,81 @@
+import React, { PureComponent } from 'react'
+import PropTypes from 'prop-types'
+import Header from './signature-request-header'
+import Footer from './signature-request-footer'
+import Message from './signature-request-message'
+import { ENVIRONMENT_TYPE_NOTIFICATION } from './signature-request.constants'
+import { getEnvironmentType } from '../../../../../app/scripts/lib/util'
+import Identicon from '../../ui/identicon'
+
+export default class SignatureRequest extends PureComponent {
+ static propTypes = {
+ txData: PropTypes.object.isRequired,
+ selectedAccount: PropTypes.shape({
+ address: PropTypes.string,
+ balance: PropTypes.string,
+ name: PropTypes.string,
+ }).isRequired,
+
+ clearConfirmTransaction: PropTypes.func.isRequired,
+ cancel: PropTypes.func.isRequired,
+ sign: PropTypes.func.isRequired,
+ }
+
+ static contextTypes = {
+ t: PropTypes.func,
+ }
+
+ componentDidMount () {
+ const { clearConfirmTransaction, cancel } = this.props
+ const { metricsEvent } = this.context
+ if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_NOTIFICATION) {
+ window.addEventListener('beforeunload', (event) => {
+ metricsEvent({
+ eventOpts: {
+ category: 'Transactions',
+ action: 'Sign Request',
+ name: 'Cancel Sig Request Via Notification Close',
+ },
+ })
+ clearConfirmTransaction()
+ cancel(event)
+ })
+ }
+ }
+
+ formatWallet (wallet) {
+ return `${wallet.slice(0, 8)}...${wallet.slice(wallet.length - 8, wallet.length)}`
+ }
+
+ render () {
+ const {
+ selectedAccount,
+ txData: { msgParams: { data, origin, from: senderWallet }},
+ cancel,
+ sign,
+ } = this.props
+ const { message } = JSON.parse(data)
+
+ return (
+
+
+
+
{this.context.t('sigRequest')}
+
+
{ message.from.name && message.from.name[0] }
+
+
+
+
{message.from.name}
+
{origin}
+
{this.formatWallet(senderWallet)}
+
+
+
+
+ )
+ }
+}
diff --git a/ui/app/components/app/signature-request/signature-request.constants.js b/ui/app/components/app/signature-request/signature-request.constants.js
new file mode 100644
index 000000000..9cf241928
--- /dev/null
+++ b/ui/app/components/app/signature-request/signature-request.constants.js
@@ -0,0 +1,3 @@
+import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../../app/scripts/lib/enums'
+
+export { ENVIRONMENT_TYPE_NOTIFICATION }
diff --git a/ui/app/components/app/signature-request/signature-request.container.js b/ui/app/components/app/signature-request/signature-request.container.js
new file mode 100644
index 000000000..0b09c1a64
--- /dev/null
+++ b/ui/app/components/app/signature-request/signature-request.container.js
@@ -0,0 +1,72 @@
+import { connect } from 'react-redux'
+import { withRouter } from 'react-router-dom'
+import { compose } from 'recompose'
+import SignatureRequest from './signature-request.component'
+import { goHome } from '../../../store/actions'
+import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck'
+import {
+ getSelectedAccount,
+ getCurrentAccountWithSendEtherInfo,
+ getSelectedAddress,
+ accountsWithSendEtherInfoSelector,
+ conversionRateSelector,
+} from '../../../selectors/selectors.js'
+
+function mapStateToProps (state) {
+ return {
+ balance: getSelectedAccount(state).balance,
+ selectedAccount: getCurrentAccountWithSendEtherInfo(state),
+ selectedAddress: getSelectedAddress(state),
+ accounts: accountsWithSendEtherInfoSelector(state),
+ conversionRate: conversionRateSelector(state),
+ }
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ goHome: () => dispatch(goHome()),
+ clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
+ }
+}
+
+function mergeProps (stateProps, dispatchProps, ownProps) {
+ const {
+ signPersonalMessage,
+ signTypedMessage,
+ cancelPersonalMessage,
+ cancelTypedMessage,
+ signMessage,
+ cancelMessage,
+ txData,
+ } = ownProps
+
+ const { type } = txData
+
+ let cancel
+ let sign
+
+ if (type === 'personal_sign') {
+ cancel = cancelPersonalMessage
+ sign = signPersonalMessage
+ } else if (type === 'eth_signTypedData') {
+ cancel = cancelTypedMessage
+ sign = signTypedMessage
+ } else if (type === 'eth_sign') {
+ cancel = cancelMessage
+ sign = signMessage
+ }
+
+ return {
+ ...stateProps,
+ ...dispatchProps,
+ ...ownProps,
+ txData,
+ cancel,
+ sign,
+ }
+}
+
+export default compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps, mergeProps)
+)(SignatureRequest)
diff --git a/ui/app/components/app/signature-request/tests/signature-request.test.js b/ui/app/components/app/signature-request/tests/signature-request.test.js
new file mode 100644
index 000000000..68b114dd8
--- /dev/null
+++ b/ui/app/components/app/signature-request/tests/signature-request.test.js
@@ -0,0 +1,25 @@
+import React from 'react'
+import assert from 'assert'
+import shallow from '../../../../../lib/shallow-with-context'
+import SignatureRequest from '../signature-request.component'
+
+
+describe('Signature Request Component', function () {
+ let wrapper
+
+ beforeEach(() => {
+ wrapper = shallow()
+ })
+
+ describe('render', () => {
+ it('should render a div with one child', () => {
+ assert(wrapper.is('div'))
+ assert.equal(wrapper.length, 1)
+ assert(wrapper.hasClass('signature-request'))
+ })
+ })
+})
diff --git a/ui/app/pages/confirm-transaction/conf-tx.js b/ui/app/pages/confirm-transaction/conf-tx.js
index 4f3868bc8..ce1edde5c 100644
--- a/ui/app/pages/confirm-transaction/conf-tx.js
+++ b/ui/app/pages/confirm-transaction/conf-tx.js
@@ -9,7 +9,8 @@ const txHelper = require('../../../lib/tx-helper')
const log = require('loglevel')
const R = require('ramda')
-const SignatureRequest = require('../../components/app/signature-request')
+const SignatureRequest = require('../../components/app/signature-request').default
+const SignatureRequestOriginal = require('../../components/app/signature-request-original')
const Loading = require('../../components/ui/loading-screen')
const { DEFAULT_ROUTE } = require('../../helpers/constants/routes')
@@ -137,34 +138,45 @@ ConfirmTxScreen.prototype.getTxData = function () {
: unconfTxList[index]
}
+ConfirmTxScreen.prototype.signatureSelect = function (type, version) {
+ // Temporarily direct only v3 and v4 requests to new code.
+ if (type === 'eth_signTypedData' && (version === 'V3' || version === 'V4')) {
+ return SignatureRequest
+ }
+
+ return SignatureRequestOriginal
+}
+
ConfirmTxScreen.prototype.render = function () {
const props = this.props
const {
currentCurrency,
blockGasLimit,
+ conversionRate,
} = props
var txData = this.getTxData() || {}
- const { msgParams } = txData
+ const { msgParams, type, msgParams: { version } } = txData
log.debug('msgParams detected, rendering pending msg')
- return msgParams
- ? h(SignatureRequest, {
- // Properties
- txData: txData,
- key: txData.id,
- identities: props.identities,
- currentCurrency,
- blockGasLimit,
- // Actions
- signMessage: this.signMessage.bind(this, txData),
- signPersonalMessage: this.signPersonalMessage.bind(this, txData),
- signTypedMessage: this.signTypedMessage.bind(this, txData),
- cancelMessage: this.cancelMessage.bind(this, txData),
- cancelPersonalMessage: this.cancelPersonalMessage.bind(this, txData),
- cancelTypedMessage: this.cancelTypedMessage.bind(this, txData),
- })
- : h(Loading)
+ return msgParams ? h(this.signatureSelect(type, version), {
+ // Properties
+ txData: txData,
+ key: txData.id,
+ selectedAddress: props.selectedAddress,
+ accounts: props.accounts,
+ identities: props.identities,
+ conversionRate,
+ currentCurrency,
+ blockGasLimit,
+ // Actions
+ signMessage: this.signMessage.bind(this, txData),
+ signPersonalMessage: this.signPersonalMessage.bind(this, txData),
+ signTypedMessage: this.signTypedMessage.bind(this, txData),
+ cancelMessage: this.cancelMessage.bind(this, txData),
+ cancelPersonalMessage: this.cancelPersonalMessage.bind(this, txData),
+ cancelTypedMessage: this.cancelTypedMessage.bind(this, txData),
+ }) : h(Loading)
}
ConfirmTxScreen.prototype.signMessage = function (msgData, event) {
diff --git a/ui/app/pages/confirm-transaction/confirm-transaction.component.js b/ui/app/pages/confirm-transaction/confirm-transaction.component.js
index 4b37cf2b1..9cb69e0da 100644
--- a/ui/app/pages/confirm-transaction/confirm-transaction.component.js
+++ b/ui/app/pages/confirm-transaction/confirm-transaction.component.js
@@ -45,6 +45,7 @@ export default class ConfirmTransaction extends Component {
isTokenMethodAction: PropTypes.bool,
fullScreenVsPopupTestGroup: PropTypes.string,
trackABTest: PropTypes.bool,
+ conversionRate: PropTypes.number,
}
componentDidMount () {
@@ -118,7 +119,6 @@ export default class ConfirmTransaction extends Component {
// Show routes when state.confirmTransaction has been set and when either the ID in the params
// isn't specified or is specified and matches the ID in state.confirmTransaction in order to
// support URLs of /confirm-transaction or /confirm-transaction/
-
return transactionId && (!paramsTransactionId || paramsTransactionId === transactionId)
? (
diff --git a/ui/app/pages/confirm-transaction/confirm-transaction.container.js b/ui/app/pages/confirm-transaction/confirm-transaction.container.js
index 9625db8ec..7c3986441 100644
--- a/ui/app/pages/confirm-transaction/confirm-transaction.container.js
+++ b/ui/app/pages/confirm-transaction/confirm-transaction.container.js
@@ -25,6 +25,7 @@ const mapStateToProps = (state, ownProps) => {
send,
unapprovedTxs,
abTests: { fullScreenVsPopup },
+ conversionRate,
},
confirmTransaction,
} = state
@@ -53,6 +54,7 @@ const mapStateToProps = (state, ownProps) => {
isTokenMethodAction: isTokenMethodAction(transactionCategory),
trackABTest,
fullScreenVsPopupTestGroup: fullScreenVsPopup,
+ conversionRate,
}
}