1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-28 05:12:18 +01:00
metamask-extension/ui/app/pages/confirm-decrypt-message/confirm-decrypt-message.component.js
Mark Stacey df85ab6e10
Implement asset page (#8696)
A new page has been created for viewing assets. This replaces the old
`selectedToken` state, which previously would augment the home page
to show token-specific information.

The new asset page shows the standard token overview as seen previously
on the home page, plus a history filtered to show just transactions
relevant to that token.

The actions that were available in the old token list menu have been
moved to a "Token Options" menu that mirrors the "Account Options"
menu.

The `selectedTokenAddress` state has been removed, as it is no longer
being used for anything.

`getMetaMetricState` has been renamed to `getBackgroundMetaMetricState`
because its sole purpose is extracting data from the background state
to send metrics from the background. It's not really a selector, but
it was convenient for it to use the same selectors the UI uses to
extract background data, so I left it there for now.

A new Redux store has been added to track state related to browser history.
The most recent "overview" page (i.e. the home page or the asset page) is
currently being tracked, so that actions taken from the asset page can return
the user back to the asset page when the action has finished.
2020-06-01 14:54:32 -03:00

346 lines
10 KiB
JavaScript

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import copyToClipboard from 'copy-to-clipboard'
import classnames from 'classnames'
import AccountListItem from '../send/account-list-item/account-list-item.component'
import Button from '../../components/ui/button'
import Identicon from '../../components/ui/identicon'
import Tooltip from '../../components/ui/tooltip-v2'
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../app/scripts/lib/enums'
import { getEnvironmentType } from '../../../../app/scripts/lib/util'
import { conversionUtil } from '../../helpers/utils/conversion-util'
export default class ConfirmDecryptMessage extends Component {
static contextTypes = {
t: PropTypes.func.isRequired,
metricsEvent: PropTypes.func.isRequired,
}
static propTypes = {
fromAccount: PropTypes.shape({
address: PropTypes.string.isRequired,
balance: PropTypes.string,
name: PropTypes.string,
}).isRequired,
clearConfirmTransaction: PropTypes.func.isRequired,
cancelDecryptMessage: PropTypes.func.isRequired,
decryptMessage: PropTypes.func.isRequired,
decryptMessageInline: PropTypes.func.isRequired,
conversionRate: PropTypes.number,
history: PropTypes.object.isRequired,
mostRecentOverviewPage: PropTypes.string.isRequired,
requesterAddress: PropTypes.string,
txData: PropTypes.object,
domainMetadata: PropTypes.object,
}
state = {
fromAccount: this.props.fromAccount,
copyToClipboardPressed: false,
hasCopied: false,
}
componentDidMount = () => {
if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_NOTIFICATION) {
window.addEventListener('beforeunload', this._beforeUnload)
}
}
componentWillUnmount = () => {
this._removeBeforeUnload()
}
_beforeUnload = (event) => {
const { clearConfirmTransaction, cancelDecryptMessage } = this.props
const { metricsEvent } = this.context
metricsEvent({
eventOpts: {
category: 'Messages',
action: 'Decrypt Message Request',
name: 'Cancel Via Notification Close',
},
})
clearConfirmTransaction()
cancelDecryptMessage(event)
}
_removeBeforeUnload = () => {
if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_NOTIFICATION) {
window.removeEventListener('beforeunload', this._beforeUnload)
}
}
copyMessage = () => {
copyToClipboard(this.state.rawMessage)
this.context.metricsEvent({
eventOpts: {
category: 'Messages',
action: 'Decrypt Message Copy',
name: 'Copy',
},
})
this.setState({ hasCopied: true })
setTimeout(() => this.setState({ hasCopied: false }), 3000)
}
renderHeader = () => {
return (
<div className="request-decrypt-message__header">
<div className="request-decrypt-message__header-background" />
<div className="request-decrypt-message__header__text">
{ this.context.t('decryptRequest') }
</div>
<div className="request-decrypt-message__header__tip-container">
<div className="request-decrypt-message__header__tip" />
</div>
</div>
)
}
renderAccount = () => {
const { fromAccount } = this.state
return (
<div className="request-decrypt-message__account">
<div className="request-decrypt-message__account-text">
{ `${this.context.t('account')}:` }
</div>
<div className="request-decrypt-message__account-item">
<AccountListItem
account={fromAccount}
displayBalance={false}
/>
</div>
</div>
)
}
renderBalance = () => {
const { conversionRate } = this.props
const { fromAccount: { balance } } = this.state
const balanceInEther = conversionUtil(balance, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
fromDenomination: 'WEI',
numberOfDecimals: 6,
conversionRate,
})
return (
<div className="request-decrypt-message__balance">
<div className="request-decrypt-message__balance-text">
{ `${this.context.t('balance')}:` }
</div>
<div className="request-decrypt-message__balance-value">
{ `${balanceInEther} ETH` }
</div>
</div>
)
}
renderRequestIcon = () => {
const { requesterAddress } = this.props
return (
<div className="request-decrypt-message__request-icon">
<Identicon
diameter={40}
address={requesterAddress}
/>
</div>
)
}
renderAccountInfo = () => {
return (
<div className="request-decrypt-message__account-info">
{ this.renderAccount() }
{ this.renderRequestIcon() }
{ this.renderBalance() }
</div>
)
}
renderBody = () => {
const { txData } = this.props
const origin = this.props.domainMetadata[txData.msgParams.origin]
const notice = this.context.t('decryptMessageNotice', [origin.name])
const {
hasCopied,
hasDecrypted,
hasError,
rawMessage,
errorMessage,
copyToClipboardPressed,
} = this.state
return (
<div className="request-decrypt-message__body">
{ this.renderAccountInfo() }
<div
className="request-decrypt-message__visual"
>
<section>
{origin.icon ? (
<img
className="request-decrypt-message__visual-identicon"
src={origin.icon}
/>
) : (
<i className="request-decrypt-message__visual-identicon--default">
{origin.name.charAt(0).toUpperCase()}
</i>
)}
<div
className="request-decrypt-message__notice"
>
{ notice }
</div>
</section>
</div>
<div
className="request-decrypt-message__message"
>
<div
className="request-decrypt-message__message-text"
>
{ !hasDecrypted && !hasError ? txData.msgParams.data : rawMessage }
{ !hasError ? '' : errorMessage }
</div>
<div
className={classnames({
'request-decrypt-message__message-cover': true,
'request-decrypt-message__message-lock--pressed': hasDecrypted || hasError,
})}
>
</div>
<div
className={classnames({
'request-decrypt-message__message-lock': true,
'request-decrypt-message__message-lock--pressed': hasDecrypted || hasError,
})}
onClick={(event) => {
this.props.decryptMessageInline(txData, event).then((result) => {
if (!result.error) {
this.setState({ hasDecrypted: true, rawMessage: result.rawData })
} else {
this.setState({ hasError: true, errorMessage: this.context.t('decryptInlineError', [result.error]) })
}
})
}}
>
<img src="images/lock.svg" />
<div
className="request-decrypt-message__message-lock-text"
>
{this.context.t('decryptMetamask')}
</div>
</div>
</div>
{ hasDecrypted ?
(
<div
className={classnames({
'request-decrypt-message__message-copy': true,
'request-decrypt-message__message-copy--pressed': copyToClipboardPressed,
})}
onClick={() => this.copyMessage()}
onMouseDown={() => this.setState({ copyToClipboardPressed: true })}
onMouseUp={() => this.setState({ copyToClipboardPressed: false })}
>
<Tooltip
position="bottom"
title={hasCopied ? this.context.t('copiedExclamation') : this.context.t('copyToClipboard')}
wrapperClassName="request-decrypt-message__message-copy-tooltip"
>
<div
className="request-decrypt-message__message-copy-text"
>
{this.context.t('decryptCopy')}
</div>
<img src="images/copy-to-clipboard.svg" />
</Tooltip>
</div>
)
:
<div></div>
}
</div>
)
}
renderFooter = () => {
const {
cancelDecryptMessage,
clearConfirmTransaction,
decryptMessage,
history,
mostRecentOverviewPage,
txData,
} = this.props
return (
<div className="request-decrypt-message__footer">
<Button
type="default"
large
className="request-decrypt-message__footer__cancel-button"
onClick={async (event) => {
this._removeBeforeUnload()
await cancelDecryptMessage(txData, event)
this.context.metricsEvent({
eventOpts: {
category: 'Messages',
action: 'Decrypt Message Request',
name: 'Cancel',
},
})
clearConfirmTransaction()
history.push(mostRecentOverviewPage)
}}
>
{ this.context.t('cancel') }
</Button>
<Button
type="secondary"
large
className="request-decrypt-message__footer__sign-button"
onClick={async (event) => {
this._removeBeforeUnload()
await decryptMessage(txData, event)
this.context.metricsEvent({
eventOpts: {
category: 'Messages',
action: 'Decrypt Message Request',
name: 'Confirm',
},
})
clearConfirmTransaction()
history.push(mostRecentOverviewPage)
}}
>
{ this.context.t('decrypt') }
</Button>
</div>
)
}
render = () => {
return (
<div className="request-decrypt-message__container">
{ this.renderHeader() }
{ this.renderBody() }
{ this.renderFooter() }
</div>
)
}
}