1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 09:57:02 +01:00

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.
This commit is contained in:
Mark Stacey 2019-11-20 18:03:49 -04:00 committed by GitHub
parent a8be9ae42b
commit 71a0bc8b3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 392 additions and 358 deletions

View File

@ -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(),
])
)
}

View File

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

View File

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

View File

@ -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)

View File

@ -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')