Refactor signature-request-original (#7415)

The component has been updated to split the container from the base
component, and all hyperscript has been replaced with JSX. ES6 imports
are used throughout as well.
feature/default_network_editable
Mark Stacey 5 years ago committed by GitHub
parent a8be9ae42b
commit 71a0bc8b3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 357
      ui/app/components/app/signature-request-original.js
  2. 1
      ui/app/components/app/signature-request-original/index.js
  3. 318
      ui/app/components/app/signature-request-original/signature-request-original.component.js
  4. 72
      ui/app/components/app/signature-request-original/signature-request-original.container.js
  5. 2
      ui/app/pages/confirm-transaction/conf-tx.js

@ -1,357 +0,0 @@
const Component = require('react').Component
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const inherits = require('util').inherits
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../app/scripts/lib/enums'
import { getEnvironmentType } from '../../../../app/scripts/lib/util'
import Identicon from '../ui/identicon'
const connect = require('react-redux').connect
const ethUtil = require('ethereumjs-util')
const classnames = require('classnames')
const { compose } = require('recompose')
const { withRouter } = require('react-router-dom')
const { ObjectInspector } = require('react-inspector')
import AccountListItem from '../../pages/send/account-list-item/account-list-item.component'
const actions = require('../../store/actions')
const { conversionUtil } = require('../../helpers/utils/conversion-util')
const {
getSelectedAccount,
getCurrentAccountWithSendEtherInfo,
getSelectedAddress,
conversionRateSelector,
} = require('../../selectors/selectors.js')
import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck'
import Button from '../ui/button'
const { DEFAULT_ROUTE } = require('../../helpers/constants/routes')
function mapStateToProps (state) {
return {
balance: getSelectedAccount(state).balance,
selectedAccount: getCurrentAccountWithSendEtherInfo(state),
selectedAddress: getSelectedAddress(state),
requester: null,
requesterAddress: null,
conversionRate: conversionRateSelector(state),
}
}
function mapDispatchToProps (dispatch) {
return {
goHome: () => dispatch(actions.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 {
...ownProps,
...stateProps,
...dispatchProps,
txData,
cancel,
sign,
}
}
SignatureRequest.contextTypes = {
t: PropTypes.func,
metricsEvent: PropTypes.func,
}
module.exports = compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps, mergeProps)
)(SignatureRequest)
inherits(SignatureRequest, Component)
function SignatureRequest (props) {
Component.call(this)
this.state = {
selectedAccount: props.selectedAccount,
}
this._beforeUnload = this._beforeUnload.bind(this)
}
SignatureRequest.prototype._beforeUnload = function (event) {
const { clearConfirmTransaction, cancel } = this.props
const { metricsEvent } = this.context
metricsEvent({
eventOpts: {
category: 'Transactions',
action: 'Sign Request',
name: 'Cancel Sig Request Via Notification Close',
},
})
clearConfirmTransaction()
cancel(event)
}
SignatureRequest.prototype._removeBeforeUnload = function () {
if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_NOTIFICATION) {
window.removeEventListener('beforeunload', this._beforeUnload)
}
}
SignatureRequest.prototype.componentDidMount = function () {
if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_NOTIFICATION) {
window.addEventListener('beforeunload', this._beforeUnload)
}
}
SignatureRequest.prototype.componentWillUnmount = function () {
this._removeBeforeUnload()
}
SignatureRequest.prototype.renderHeader = function () {
return h('div.request-signature__header', [
h('div.request-signature__header-background'),
h('div.request-signature__header__text', this.context.t('sigRequest')),
h('div.request-signature__header__tip-container', [
h('div.request-signature__header__tip'),
]),
])
}
SignatureRequest.prototype.renderAccount = function () {
const { selectedAccount } = this.state
return h('div.request-signature__account', [
h('div.request-signature__account-text', [this.context.t('account') + ':']),
h('div.request-signature__account-item', [
h(AccountListItem, {
account: selectedAccount,
displayBalance: false,
}),
]),
])
}
SignatureRequest.prototype.renderBalance = function () {
const { balance, conversionRate } = this.props
const balanceInEther = conversionUtil(balance, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
fromDenomination: 'WEI',
numberOfDecimals: 6,
conversionRate,
})
return h('div.request-signature__balance', [
h('div.request-signature__balance-text', `${this.context.t('balance')}:`),
h('div.request-signature__balance-value', `${balanceInEther} ETH`),
])
}
SignatureRequest.prototype.renderAccountInfo = function () {
return h('div.request-signature__account-info', [
this.renderAccount(),
this.renderRequestIcon(),
this.renderBalance(),
])
}
SignatureRequest.prototype.renderRequestIcon = function () {
const { requesterAddress } = this.props
return h('div.request-signature__request-icon', [
h(Identicon, {
diameter: 40,
address: requesterAddress,
}),
])
}
SignatureRequest.prototype.renderRequestInfo = function () {
return h('div.request-signature__request-info', [
h('div.request-signature__headline', [
this.context.t('yourSigRequested'),
]),
])
}
SignatureRequest.prototype.msgHexToText = function (hex) {
try {
const stripped = ethUtil.stripHexPrefix(hex)
const buff = Buffer.from(stripped, 'hex')
return buff.length === 32 ? hex : buff.toString('utf8')
} catch (e) {
return hex
}
}
// eslint-disable-next-line react/display-name
SignatureRequest.prototype.renderTypedData = function (data) {
const { domain, message } = JSON.parse(data)
return [
h('div.request-signature__typed-container', [
domain ? h('div', [
h('h1', 'Domain'),
h(ObjectInspector, { data: domain, expandLevel: 1, name: 'domain' }),
]) : '',
message ? h('div', [
h('h1', 'Message'),
h(ObjectInspector, { data: message, expandLevel: 1, name: 'message' }),
]) : '',
]),
]
}
SignatureRequest.prototype.renderBody = function () {
let rows
let notice = this.context.t('youSign') + ':'
const { txData } = this.props
const { type, msgParams: { data } } = txData
if (type === 'personal_sign') {
rows = [{ name: this.context.t('message'), value: this.msgHexToText(data) }]
} else if (type === 'eth_signTypedData') {
rows = data
} else if (type === 'eth_sign') {
rows = [{ name: this.context.t('message'), value: data }]
notice = [this.context.t('signNotice'),
h('span.request-signature__help-link', {
onClick: () => {
global.platform.openWindow({
url: 'https://metamask.zendesk.com/hc/en-us/articles/360015488751',
})
},
}, this.context.t('learnMore'))]
}
return h('div.request-signature__body', {}, [
this.renderAccountInfo(),
this.renderRequestInfo(),
h('div.request-signature__notice', {
className: classnames({
'request-signature__notice': type === 'personal_sign' || type === 'eth_signTypedData',
'request-signature__warning': type === 'eth_sign',
}),
}, [notice]),
h('div.request-signature__rows',
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),
])
})
),
])
}
SignatureRequest.prototype.renderFooter = function () {
const { cancel, sign } = this.props
return h('div.request-signature__footer', [
h(Button, {
type: 'default',
large: true,
className: 'request-signature__footer__cancel-button',
onClick: event => {
this._removeBeforeUnload()
cancel(event).then(() => {
this.context.metricsEvent({
eventOpts: {
category: 'Transactions',
action: 'Sign Request',
name: 'Cancel',
},
})
this.props.clearConfirmTransaction()
this.props.history.push(DEFAULT_ROUTE)
})
},
}, this.context.t('cancel')),
h(Button, {
type: 'secondary',
large: true,
className: 'request-signature__footer__sign-button',
onClick: event => {
this._removeBeforeUnload()
sign(event).then(() => {
this.context.metricsEvent({
eventOpts: {
category: 'Transactions',
action: 'Sign Request',
name: 'Confirm',
},
})
this.props.clearConfirmTransaction()
this.props.history.push(DEFAULT_ROUTE)
})
},
}, this.context.t('sign')),
])
}
SignatureRequest.prototype.render = function () {
return (
h('div.request-signature__container', [
this.renderHeader(),
this.renderBody(),
this.renderFooter(),
])
)
}

@ -0,0 +1 @@
export { default } from './signature-request-original.container'

@ -0,0 +1,318 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import ethUtil from 'ethereumjs-util'
import classnames from 'classnames'
import { ObjectInspector } from 'react-inspector'
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../../app/scripts/lib/enums'
import { getEnvironmentType } from '../../../../../app/scripts/lib/util'
import Identicon from '../../ui/identicon'
import AccountListItem from '../../../pages/send/account-list-item/account-list-item.component'
import { conversionUtil } from '../../../helpers/utils/conversion-util'
import Button from '../../ui/button'
import { DEFAULT_ROUTE } from '../../../helpers/constants/routes'
export default class SignatureRequestOriginal extends Component {
static contextTypes = {
t: PropTypes.func.isRequired,
metricsEvent: PropTypes.func.isRequired,
}
static propTypes = {
balance: PropTypes.string,
cancel: PropTypes.func.isRequired,
clearConfirmTransaction: PropTypes.func.isRequired,
conversionRate: PropTypes.number,
history: PropTypes.object.isRequired,
requesterAddress: PropTypes.string,
selectedAccount: PropTypes.string,
sign: PropTypes.func.isRequired,
txData: PropTypes.object.isRequired,
}
state = {
selectedAccount: this.props.selectedAccount,
}
componentDidMount = () => {
if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_NOTIFICATION) {
window.addEventListener('beforeunload', this._beforeUnload)
}
}
componentWillUnmount = () => {
this._removeBeforeUnload()
}
_beforeUnload = (event) => {
const { clearConfirmTransaction, cancel } = this.props
const { metricsEvent } = this.context
metricsEvent({
eventOpts: {
category: 'Transactions',
action: 'Sign Request',
name: 'Cancel Sig Request Via Notification Close',
},
})
clearConfirmTransaction()
cancel(event)
}
_removeBeforeUnload = () => {
if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_NOTIFICATION) {
window.removeEventListener('beforeunload', this._beforeUnload)
}
}
renderHeader = () => {
return (
<div className="request-signature__header">
<div className="request-signature__header-background" />
<div className="request-signature__header__text">
{ this.context.t('sigRequest') }
</div>
<div className="request-signature__header__tip-container">
<div className="request-signature__header__tip" />
</div>
</div>
)
}
renderAccount = () => {
const { selectedAccount } = this.state
return (
<div className="request-signature__account">
<div className="request-signature__account-text">
{ `${this.context.t('account')}:` }
</div>
<div className="request-signature__account-item">
<AccountListItem
account={selectedAccount}
displayBalance={false}
/>
</div>
</div>
)
}
renderBalance = () => {
const { balance, conversionRate } = this.props
const balanceInEther = conversionUtil(balance, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
fromDenomination: 'WEI',
numberOfDecimals: 6,
conversionRate,
})
return (
<div className="request-signature__balance">
<div className="request-signature__balance-text">
{ `${this.context.t('balance')}:` }
</div>
<div className="request-signature__balance-value">
{ `${balanceInEther} ETH` }
</div>
</div>
)
}
renderRequestIcon = () => {
const { requesterAddress } = this.props
return (
<div className="request-signature__request-icon">
<Identicon
diameter={40}
address={requesterAddress}
/>
</div>
)
}
renderAccountInfo = () => {
return (
<div className="request-signature__account-info">
{ this.renderAccount() }
{ this.renderRequestIcon() }
{ this.renderBalance() }
</div>
)
}
renderRequestInfo = () => {
return (
<div className="request-signature__request-info">
<div className="request-signature__headline">
{ this.context.t('yourSigRequested') }
</div>
</div>
)
}
msgHexToText = (hex) => {
try {
const stripped = ethUtil.stripHexPrefix(hex)
const buff = Buffer.from(stripped, 'hex')
return buff.length === 32 ? hex : buff.toString('utf8')
} catch (e) {
return hex
}
}
renderTypedData = (data) => {
const { domain, message } = JSON.parse(data)
return (
<div className="request-signature__typed-container">
{
domain
? <div>
<h1>
Domain
</h1>
<ObjectInspector data={domain} expandLevel={1} name="domain" />
</div>
: ''
}
{
message
? <div>
<h1>
Message
</h1>
<ObjectInspector data={message} expandLevel={1} name="message" />
</div>
: ''
}
</div>
)
}
renderBody = () => {
let rows
let notice = `${this.context.t('youSign')}:`
const { txData } = this.props
const { type, msgParams: { data } } = txData
if (type === 'personal_sign') {
rows = [{ name: this.context.t('message'), value: this.msgHexToText(data) }]
} else if (type === 'eth_signTypedData') {
rows = data
} else if (type === 'eth_sign') {
rows = [{ name: this.context.t('message'), value: data }]
notice = this.context.t('signNotice')
}
return (
<div className="request-signature__body">
{ this.renderAccountInfo() }
{ this.renderRequestInfo() }
<div
className={classnames('request-signature__notice', {
'request-signature__warning': type === 'eth_sign',
})}
>
{ notice }
{
type === 'eth_sign'
? <span
className="request-signature__help-link"
onClick={() => {
global.platform.openWindow({
url: 'https://metamask.zendesk.com/hc/en-us/articles/360015488751',
})
}}
>
{ this.context.t('learnMore') }
</span>
: null
}
</div>
<div className="request-signature__rows">
{
rows.map(({ name, value }, index) => {
if (typeof value === 'boolean') {
value = value.toString()
}
return (
<div className="request-signature__row" key={`request-signature-row-${index}`}>
<div className="request-signature__row-title">
{ `${name}:` }
</div>
<div className="request-signature__row-value">
{ value }
</div>
</div>
)
})
}
</div>
</div>
)
}
renderFooter = () => {
const { cancel, sign } = this.props
return (
<div className="request-signature__footer">
<Button
type="default"
large
className="request-signature__footer__cancel-button"
onClick={async event => {
this._removeBeforeUnload()
await cancel(event)
this.context.metricsEvent({
eventOpts: {
category: 'Transactions',
action: 'Sign Request',
name: 'Cancel',
},
})
this.props.clearConfirmTransaction()
this.props.history.push(DEFAULT_ROUTE)
}}
>
{ this.context.t('cancel') }
</Button>,
<Button
type="secondary"
large
className="request-signature__footer__sign-button"
onClick={async event => {
this._removeBeforeUnload()
await sign(event)
this.context.metricsEvent({
eventOpts: {
category: 'Transactions',
action: 'Sign Request',
name: 'Confirm',
},
})
this.props.clearConfirmTransaction()
this.props.history.push(DEFAULT_ROUTE)
}}
>
{ this.context.t('sign') }
</Button>
</div>
)
}
render = () => {
return (
<div className="request-signature__container">
{ this.renderHeader() }
{ this.renderBody() }
{ this.renderFooter() }
</div>
)
}
}

@ -0,0 +1,72 @@
import { connect } from 'react-redux'
import { compose } from 'recompose'
import { withRouter } from 'react-router-dom'
import actions from '../../../store/actions'
import {
getSelectedAccount,
getCurrentAccountWithSendEtherInfo,
getSelectedAddress,
conversionRateSelector,
} from '../../../selectors/selectors.js'
import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck'
import SignatureRequestOriginal from './signature-request-original.component'
function mapStateToProps (state) {
return {
balance: getSelectedAccount(state).balance,
selectedAccount: getCurrentAccountWithSendEtherInfo(state),
selectedAddress: getSelectedAddress(state),
requester: null,
requesterAddress: null,
conversionRate: conversionRateSelector(state),
}
}
function mapDispatchToProps (dispatch) {
return {
goHome: () => dispatch(actions.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 {
...ownProps,
...stateProps,
...dispatchProps,
txData,
cancel,
sign,
}
}
export default compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps, mergeProps)
)(SignatureRequestOriginal)

@ -10,7 +10,7 @@ const log = require('loglevel')
const R = require('ramda')
const SignatureRequest = require('../../components/app/signature-request').default
const SignatureRequestOriginal = require('../../components/app/signature-request-original')
const SignatureRequestOriginal = require('../../components/app/signature-request-original').default
const Loading = require('../../components/ui/loading-screen')
const { DEFAULT_ROUTE } = require('../../helpers/constants/routes')

Loading…
Cancel
Save