commit
ad009a4606
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,54 @@ |
|||||||
|
const namehash = require('eth-ens-namehash') |
||||||
|
const multihash = require('multihashes') |
||||||
|
const Eth = require('ethjs-query') |
||||||
|
const EthContract = require('ethjs-contract') |
||||||
|
const registrarAbi = require('./contracts/registrar') |
||||||
|
const resolverAbi = require('./contracts/resolver') |
||||||
|
|
||||||
|
module.exports = resolveEnsToIpfsContentId |
||||||
|
|
||||||
|
|
||||||
|
async function resolveEnsToIpfsContentId ({ provider, name }) { |
||||||
|
const eth = new Eth(provider) |
||||||
|
const hash = namehash.hash(name) |
||||||
|
const contract = new EthContract(eth) |
||||||
|
// lookup registrar
|
||||||
|
const chainId = Number.parseInt(await eth.net_version(), 10) |
||||||
|
const registrarAddress = getRegistrarForChainId(chainId) |
||||||
|
if (!registrarAddress) { |
||||||
|
throw new Error(`EnsIpfsResolver - no known ens-ipfs registrar for chainId "${chainId}"`) |
||||||
|
} |
||||||
|
const Registrar = contract(registrarAbi).at(registrarAddress) |
||||||
|
// lookup resolver
|
||||||
|
const resolverLookupResult = await Registrar.resolver(hash) |
||||||
|
const resolverAddress = resolverLookupResult[0] |
||||||
|
if (hexValueIsEmpty(resolverAddress)) { |
||||||
|
throw new Error(`EnsIpfsResolver - no resolver found for name "${name}"`) |
||||||
|
} |
||||||
|
const Resolver = contract(resolverAbi).at(resolverAddress) |
||||||
|
// lookup content id
|
||||||
|
const contentLookupResult = await Resolver.content(hash) |
||||||
|
const contentHash = contentLookupResult[0] |
||||||
|
if (hexValueIsEmpty(contentHash)) { |
||||||
|
throw new Error(`EnsIpfsResolver - no content ID found for name "${name}"`) |
||||||
|
} |
||||||
|
const nonPrefixedHex = contentHash.slice(2) |
||||||
|
const buffer = multihash.fromHexString(nonPrefixedHex) |
||||||
|
const contentId = multihash.toB58String(multihash.encode(buffer, 'sha2-256')) |
||||||
|
return contentId |
||||||
|
} |
||||||
|
|
||||||
|
function hexValueIsEmpty(value) { |
||||||
|
return [undefined, null, '0x', '0x0', '0x0000000000000000000000000000000000000000000000000000000000000000'].includes(value) |
||||||
|
} |
||||||
|
|
||||||
|
function getRegistrarForChainId (chainId) { |
||||||
|
switch (chainId) { |
||||||
|
// mainnet
|
||||||
|
case 1: |
||||||
|
return '0x314159265dd8dbb310642f98f50c066173c1259b' |
||||||
|
// ropsten
|
||||||
|
case 3: |
||||||
|
return '0x112234455c3a32fd11230c42e7bccd4a84e02010' |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
const urlUtil = require('url') |
||||||
|
const extension = require('extensionizer') |
||||||
|
const resolveEnsToIpfsContentId = require('./resolver.js') |
||||||
|
|
||||||
|
const supportedTopLevelDomains = ['eth'] |
||||||
|
|
||||||
|
module.exports = setupEnsIpfsResolver |
||||||
|
|
||||||
|
function setupEnsIpfsResolver({ provider }) { |
||||||
|
|
||||||
|
// install listener
|
||||||
|
const urlPatterns = supportedTopLevelDomains.map(tld => `*://*.${tld}/*`) |
||||||
|
extension.webRequest.onErrorOccurred.addListener(webRequestDidFail, { urls: urlPatterns }) |
||||||
|
|
||||||
|
// return api object
|
||||||
|
return { |
||||||
|
// uninstall listener
|
||||||
|
remove () { |
||||||
|
extension.webRequest.onErrorOccurred.removeListener(webRequestDidFail) |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
async function webRequestDidFail (details) { |
||||||
|
const { tabId, url } = details |
||||||
|
// ignore requests that are not associated with tabs
|
||||||
|
if (tabId === -1) return |
||||||
|
// parse ens name
|
||||||
|
const urlData = urlUtil.parse(url) |
||||||
|
const { hostname: name, path, search } = urlData |
||||||
|
const domainParts = name.split('.') |
||||||
|
const topLevelDomain = domainParts[domainParts.length - 1] |
||||||
|
// if unsupported TLD, abort
|
||||||
|
if (!supportedTopLevelDomains.includes(topLevelDomain)) return |
||||||
|
// otherwise attempt resolve
|
||||||
|
attemptResolve({ tabId, name, path, search }) |
||||||
|
} |
||||||
|
|
||||||
|
async function attemptResolve({ tabId, name, path, search }) { |
||||||
|
extension.tabs.update(tabId, { url: `loading.html` }) |
||||||
|
try { |
||||||
|
const ipfsContentId = await resolveEnsToIpfsContentId({ provider, name }) |
||||||
|
let url = `https://gateway.ipfs.io/ipfs/${ipfsContentId}${path}${search || ''}` |
||||||
|
try { |
||||||
|
// check if ipfs gateway has result
|
||||||
|
const response = await fetch(url, { method: 'HEAD' }) |
||||||
|
// if failure, redirect to 404 page
|
||||||
|
if (response.status !== 200) { |
||||||
|
extension.tabs.update(tabId, { url: '404.html' }) |
||||||
|
return |
||||||
|
} |
||||||
|
// otherwise redirect to the correct page
|
||||||
|
extension.tabs.update(tabId, { url }) |
||||||
|
} catch (err) { |
||||||
|
console.warn(err) |
||||||
|
// if HEAD fetch failed, redirect so user can see relevant error page
|
||||||
|
extension.tabs.update(tabId, { url }) |
||||||
|
} |
||||||
|
} catch (err) { |
||||||
|
console.warn(err) |
||||||
|
extension.tabs.update(tabId, { url: `error.html?name=${name}` }) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,46 +0,0 @@ |
|||||||
const extension = require('extensionizer') |
|
||||||
const resolver = require('./resolver.js') |
|
||||||
|
|
||||||
module.exports = function (provider) { |
|
||||||
function ipfsContent (details) { |
|
||||||
const name = details.url.substring(7, details.url.length - 1) |
|
||||||
let clearTime = null |
|
||||||
if (/^.+\.eth$/.test(name) === false) return |
|
||||||
|
|
||||||
extension.tabs.query({active: true}, tab => { |
|
||||||
extension.tabs.update(tab.id, { url: 'loading.html' }) |
|
||||||
|
|
||||||
clearTime = setTimeout(() => { |
|
||||||
return extension.tabs.update(tab.id, { url: '404.html' }) |
|
||||||
}, 60000) |
|
||||||
|
|
||||||
resolver.resolve(name, provider).then(ipfsHash => { |
|
||||||
clearTimeout(clearTime) |
|
||||||
let url = 'https://ipfs.infura.io/ipfs/' + ipfsHash |
|
||||||
return fetch(url, { method: 'HEAD' }).then(response => response.status).then(statusCode => { |
|
||||||
if (statusCode !== 200) return extension.tabs.update(tab.id, { url: '404.html' }) |
|
||||||
extension.tabs.update(tab.id, { url: url }) |
|
||||||
}) |
|
||||||
.catch(err => { |
|
||||||
url = 'https://ipfs.infura.io/ipfs/' + ipfsHash |
|
||||||
extension.tabs.update(tab.id, {url: url}) |
|
||||||
return err |
|
||||||
}) |
|
||||||
}) |
|
||||||
.catch(err => { |
|
||||||
clearTimeout(clearTime) |
|
||||||
const url = err === 'unsupport' ? 'unsupport' : 'error' |
|
||||||
extension.tabs.update(tab.id, {url: `${url}.html?name=${name}`}) |
|
||||||
}) |
|
||||||
}) |
|
||||||
return { cancel: true } |
|
||||||
} |
|
||||||
|
|
||||||
extension.webRequest.onErrorOccurred.addListener(ipfsContent, {urls: ['*://*.eth/'], types: ['main_frame']}) |
|
||||||
|
|
||||||
return { |
|
||||||
remove () { |
|
||||||
extension.webRequest.onErrorOccurred.removeListener(ipfsContent) |
|
||||||
}, |
|
||||||
} |
|
||||||
} |
|
@ -1,71 +0,0 @@ |
|||||||
const namehash = require('eth-ens-namehash') |
|
||||||
const multihash = require('multihashes') |
|
||||||
const HttpProvider = require('ethjs-provider-http') |
|
||||||
const Eth = require('ethjs-query') |
|
||||||
const EthContract = require('ethjs-contract') |
|
||||||
const registrarAbi = require('./contracts/registrar') |
|
||||||
const resolverAbi = require('./contracts/resolver') |
|
||||||
|
|
||||||
function ens (name, provider) { |
|
||||||
const eth = new Eth(new HttpProvider(getProvider(provider.type))) |
|
||||||
const hash = namehash.hash(name) |
|
||||||
const contract = new EthContract(eth) |
|
||||||
const Registrar = contract(registrarAbi).at(getRegistrar(provider.type)) |
|
||||||
return new Promise((resolve, reject) => { |
|
||||||
if (provider.type === 'mainnet' || provider.type === 'ropsten') { |
|
||||||
Registrar.resolver(hash).then((address) => { |
|
||||||
if (address === '0x0000000000000000000000000000000000000000') { |
|
||||||
reject(null) |
|
||||||
} else { |
|
||||||
const Resolver = contract(resolverAbi).at(address['0']) |
|
||||||
return Resolver.content(hash) |
|
||||||
} |
|
||||||
}).then((contentHash) => { |
|
||||||
if (contentHash['0'] === '0x0000000000000000000000000000000000000000000000000000000000000000') reject(null) |
|
||||||
if (contentHash.ret !== '0x') { |
|
||||||
const hex = contentHash['0'].substring(2) |
|
||||||
const buf = multihash.fromHexString(hex) |
|
||||||
resolve(multihash.toB58String(multihash.encode(buf, 'sha2-256'))) |
|
||||||
} else { |
|
||||||
reject(null) |
|
||||||
} |
|
||||||
}) |
|
||||||
} else { |
|
||||||
return reject('unsupport') |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
function getProvider (type) { |
|
||||||
switch (type) { |
|
||||||
case 'mainnet': |
|
||||||
return 'https://mainnet.infura.io/' |
|
||||||
case 'ropsten': |
|
||||||
return 'https://ropsten.infura.io/' |
|
||||||
default: |
|
||||||
return 'http://localhost:8545/' |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function getRegistrar (type) { |
|
||||||
switch (type) { |
|
||||||
case 'mainnet': |
|
||||||
return '0x314159265dd8dbb310642f98f50c066173c1259b' |
|
||||||
case 'ropsten': |
|
||||||
return '0x112234455c3a32fd11230c42e7bccd4a84e02010' |
|
||||||
default: |
|
||||||
return '0x0000000000000000000000000000000000000000' |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
module.exports.resolve = function (name, provider) { |
|
||||||
const path = name.split('.') |
|
||||||
const topLevelDomain = path[path.length - 1] |
|
||||||
if (topLevelDomain === 'eth' || topLevelDomain === 'test') { |
|
||||||
return ens(name, provider) |
|
||||||
} else { |
|
||||||
return new Promise((resolve, reject) => { |
|
||||||
reject(null) |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
@ -1,124 +0,0 @@ |
|||||||
const Component = require('react').Component |
|
||||||
const h = require('react-hyperscript') |
|
||||||
const inherits = require('util').inherits |
|
||||||
const connect = require('react-redux').connect |
|
||||||
const isNode = require('detect-node') |
|
||||||
const findDOMNode = require('react-dom').findDOMNode |
|
||||||
const jazzicon = require('jazzicon') |
|
||||||
const iconFactoryGen = require('../../lib/icon-factory') |
|
||||||
const iconFactory = iconFactoryGen(jazzicon) |
|
||||||
const { toDataUrl } = require('../../lib/blockies') |
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(IdenticonComponent) |
|
||||||
|
|
||||||
inherits(IdenticonComponent, Component) |
|
||||||
function IdenticonComponent () { |
|
||||||
Component.call(this) |
|
||||||
|
|
||||||
this.defaultDiameter = 46 |
|
||||||
} |
|
||||||
|
|
||||||
function mapStateToProps (state) { |
|
||||||
return { |
|
||||||
useBlockie: state.metamask.useBlockie, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
IdenticonComponent.prototype.render = function () { |
|
||||||
var props = this.props |
|
||||||
const { className = '', address, image } = props |
|
||||||
var diameter = props.diameter || this.defaultDiameter |
|
||||||
const style = { |
|
||||||
height: diameter, |
|
||||||
width: diameter, |
|
||||||
borderRadius: diameter / 2, |
|
||||||
} |
|
||||||
if (image) { |
|
||||||
return h('img', { |
|
||||||
className: `${className} identicon`, |
|
||||||
src: image, |
|
||||||
style: { |
|
||||||
...style, |
|
||||||
}, |
|
||||||
}) |
|
||||||
} else if (address) { |
|
||||||
return h('div', { |
|
||||||
className: `${className} identicon`, |
|
||||||
key: 'identicon-' + address, |
|
||||||
style: { |
|
||||||
display: 'flex', |
|
||||||
flexShrink: 0, |
|
||||||
alignItems: 'center', |
|
||||||
justifyContent: 'center', |
|
||||||
...style, |
|
||||||
overflow: 'hidden', |
|
||||||
}, |
|
||||||
}) |
|
||||||
} else { |
|
||||||
return h('img.balance-icon', { |
|
||||||
className, |
|
||||||
src: './images/eth_logo.svg', |
|
||||||
style: { |
|
||||||
...style, |
|
||||||
}, |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
IdenticonComponent.prototype.componentDidMount = function () { |
|
||||||
var props = this.props |
|
||||||
const { address, useBlockie } = props |
|
||||||
|
|
||||||
if (!address) return |
|
||||||
|
|
||||||
if (!isNode) { |
|
||||||
// eslint-disable-next-line react/no-find-dom-node
|
|
||||||
var container = findDOMNode(this) |
|
||||||
|
|
||||||
const diameter = props.diameter || this.defaultDiameter |
|
||||||
|
|
||||||
if (useBlockie) { |
|
||||||
_generateBlockie(container, address, diameter) |
|
||||||
} else { |
|
||||||
_generateJazzicon(container, address, diameter) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
IdenticonComponent.prototype.componentDidUpdate = function () { |
|
||||||
var props = this.props |
|
||||||
const { address, useBlockie } = props |
|
||||||
|
|
||||||
if (!address) return |
|
||||||
|
|
||||||
if (!isNode) { |
|
||||||
// eslint-disable-next-line react/no-find-dom-node
|
|
||||||
var container = findDOMNode(this) |
|
||||||
|
|
||||||
var children = container.children |
|
||||||
for (var i = 0; i < children.length; i++) { |
|
||||||
container.removeChild(children[i]) |
|
||||||
} |
|
||||||
|
|
||||||
const diameter = props.diameter || this.defaultDiameter |
|
||||||
|
|
||||||
if (useBlockie) { |
|
||||||
_generateBlockie(container, address, diameter) |
|
||||||
} else { |
|
||||||
_generateJazzicon(container, address, diameter) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function _generateBlockie (container, address, diameter) { |
|
||||||
const img = new Image() |
|
||||||
img.src = toDataUrl(address) |
|
||||||
img.height = diameter |
|
||||||
img.width = diameter |
|
||||||
container.appendChild(img) |
|
||||||
} |
|
||||||
|
|
||||||
function _generateJazzicon (container, address, diameter) { |
|
||||||
const img = iconFactory.iconForAddress(address, diameter) |
|
||||||
container.appendChild(img) |
|
||||||
} |
|
@ -0,0 +1,99 @@ |
|||||||
|
import React, { PureComponent } from 'react' |
||||||
|
import PropTypes from 'prop-types' |
||||||
|
import classnames from 'classnames' |
||||||
|
import { toDataUrl } from '../../../lib/blockies' |
||||||
|
import contractMap from 'eth-contract-metadata' |
||||||
|
import { checksumAddress } from '../../../app/util' |
||||||
|
import Jazzicon from '../jazzicon' |
||||||
|
|
||||||
|
const getStyles = diameter => ( |
||||||
|
{ |
||||||
|
height: diameter, |
||||||
|
width: diameter, |
||||||
|
borderRadius: diameter / 2, |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
export default class Identicon extends PureComponent { |
||||||
|
static propTypes = { |
||||||
|
address: PropTypes.string, |
||||||
|
className: PropTypes.string, |
||||||
|
diameter: PropTypes.number, |
||||||
|
image: PropTypes.string, |
||||||
|
useBlockie: PropTypes.bool, |
||||||
|
} |
||||||
|
|
||||||
|
static defaultProps = { |
||||||
|
diameter: 46, |
||||||
|
} |
||||||
|
|
||||||
|
renderImage () { |
||||||
|
const { className, diameter, image } = this.props |
||||||
|
|
||||||
|
return ( |
||||||
|
<img |
||||||
|
className={classnames('identicon', className)} |
||||||
|
src={image} |
||||||
|
style={getStyles(diameter)} |
||||||
|
/> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
renderJazzicon () { |
||||||
|
const { address, className, diameter } = this.props |
||||||
|
|
||||||
|
return ( |
||||||
|
<Jazzicon |
||||||
|
address={address} |
||||||
|
diameter={diameter} |
||||||
|
className={classnames('identicon', className)} |
||||||
|
style={getStyles(diameter)} |
||||||
|
/> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
renderBlockie () { |
||||||
|
const { address, className, diameter } = this.props |
||||||
|
|
||||||
|
return ( |
||||||
|
<div |
||||||
|
className={classnames('identicon', className)} |
||||||
|
style={getStyles(diameter)} |
||||||
|
> |
||||||
|
<img |
||||||
|
src={toDataUrl(address)} |
||||||
|
height={diameter} |
||||||
|
width={diameter} |
||||||
|
/> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
render () { |
||||||
|
const { className, address, image, diameter, useBlockie } = this.props |
||||||
|
|
||||||
|
if (image) { |
||||||
|
return this.renderImage() |
||||||
|
} |
||||||
|
|
||||||
|
if (address) { |
||||||
|
const checksummedAddress = checksumAddress(address) |
||||||
|
|
||||||
|
if (contractMap[checksummedAddress] && contractMap[checksummedAddress].logo) { |
||||||
|
return this.renderJazzicon() |
||||||
|
} |
||||||
|
|
||||||
|
return useBlockie |
||||||
|
? this.renderBlockie() |
||||||
|
: this.renderJazzicon() |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<img |
||||||
|
className={classnames('balance-icon', className)} |
||||||
|
src="./images/eth_logo.svg" |
||||||
|
style={getStyles(diameter)} |
||||||
|
/> |
||||||
|
) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
import { connect } from 'react-redux' |
||||||
|
import Identicon from './identicon.component' |
||||||
|
|
||||||
|
const mapStateToProps = state => { |
||||||
|
const { metamask: { useBlockie } } = state |
||||||
|
|
||||||
|
return { |
||||||
|
useBlockie, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export default connect(mapStateToProps)(Identicon) |
@ -0,0 +1 @@ |
|||||||
|
export { default } from './identicon.container' |
@ -0,0 +1,7 @@ |
|||||||
|
.identicon { |
||||||
|
display: flex; |
||||||
|
flex-shrink: 0; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
overflow: hidden; |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
export { default } from './jazzicon.component' |
@ -0,0 +1,69 @@ |
|||||||
|
import React, { PureComponent } from 'react' |
||||||
|
import PropTypes from 'prop-types' |
||||||
|
import isNode from 'detect-node' |
||||||
|
import { findDOMNode } from 'react-dom' |
||||||
|
import jazzicon from 'jazzicon' |
||||||
|
import iconFactoryGenerator from '../../../lib/icon-factory' |
||||||
|
const iconFactory = iconFactoryGenerator(jazzicon) |
||||||
|
|
||||||
|
/** |
||||||
|
* Wrapper around the jazzicon library to return a React component, as the library returns an |
||||||
|
* HTMLDivElement which needs to be appended. |
||||||
|
*/ |
||||||
|
export default class Jazzicon extends PureComponent { |
||||||
|
static propTypes = { |
||||||
|
address: PropTypes.string.isRequired, |
||||||
|
className: PropTypes.string, |
||||||
|
diameter: PropTypes.number, |
||||||
|
style: PropTypes.object, |
||||||
|
} |
||||||
|
|
||||||
|
static defaultProps = { |
||||||
|
diameter: 46, |
||||||
|
} |
||||||
|
|
||||||
|
componentDidMount () { |
||||||
|
if (!isNode) { |
||||||
|
this.appendJazzicon() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
componentDidUpdate (prevProps) { |
||||||
|
const { address: prevAddress } = prevProps |
||||||
|
const { address } = this.props |
||||||
|
|
||||||
|
if (!isNode && address !== prevAddress) { |
||||||
|
this.removeExistingChildren() |
||||||
|
this.appendJazzicon() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
removeExistingChildren () { |
||||||
|
// eslint-disable-next-line react/no-find-dom-node
|
||||||
|
const container = findDOMNode(this) |
||||||
|
const { children } = container |
||||||
|
|
||||||
|
for (let i = 0; i < children.length; i++) { |
||||||
|
container.removeChild(children[i]) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
appendJazzicon () { |
||||||
|
// eslint-disable-next-line react/no-find-dom-node
|
||||||
|
const container = findDOMNode(this) |
||||||
|
const { address, diameter } = this.props |
||||||
|
const image = iconFactory.iconForAddress(address, diameter) |
||||||
|
container.appendChild(image) |
||||||
|
} |
||||||
|
|
||||||
|
render () { |
||||||
|
const { className, style } = this.props |
||||||
|
|
||||||
|
return ( |
||||||
|
<div |
||||||
|
className={className} |
||||||
|
style={style} |
||||||
|
/> |
||||||
|
) |
||||||
|
} |
||||||
|
} |
@ -1 +0,0 @@ |
|||||||
export { default } from './transaction-details.container' |
|
@ -1,54 +0,0 @@ |
|||||||
import React, { PureComponent } from 'react' |
|
||||||
import PropTypes from 'prop-types' |
|
||||||
import Modal from '../../modal' |
|
||||||
import TransactionListItemDetails from '../../transaction-list-item-details' |
|
||||||
import { hexToDecimal } from '../../../helpers/conversions.util' |
|
||||||
|
|
||||||
export default class TransactionConfirmed extends PureComponent { |
|
||||||
static contextTypes = { |
|
||||||
t: PropTypes.func, |
|
||||||
} |
|
||||||
|
|
||||||
static propTypes = { |
|
||||||
hideModal: PropTypes.func, |
|
||||||
transaction: PropTypes.object, |
|
||||||
onRetry: PropTypes.func, |
|
||||||
showRetry: PropTypes.bool, |
|
||||||
onCancel: PropTypes.func, |
|
||||||
showCancel: PropTypes.bool, |
|
||||||
} |
|
||||||
|
|
||||||
handleSubmit = () => { |
|
||||||
this.props.hideModal() |
|
||||||
} |
|
||||||
|
|
||||||
handleRetry = () => { |
|
||||||
const { onRetry, hideModal } = this.props |
|
||||||
|
|
||||||
Promise.resolve(onRetry()).then(() => hideModal()) |
|
||||||
} |
|
||||||
|
|
||||||
render () { |
|
||||||
const { t } = this.context |
|
||||||
const { transaction, showRetry, onCancel, showCancel } = this.props |
|
||||||
const { txParams: { nonce } = {} } = transaction |
|
||||||
const decimalNonce = nonce && hexToDecimal(nonce) |
|
||||||
|
|
||||||
return ( |
|
||||||
<Modal |
|
||||||
onSubmit={this.handleSubmit} |
|
||||||
onClose={this.handleSubmit} |
|
||||||
submitText={t('ok')} |
|
||||||
headerText={t('transactionWithNonce', [`#${decimalNonce}`])} |
|
||||||
> |
|
||||||
<TransactionListItemDetails |
|
||||||
transaction={transaction} |
|
||||||
onRetry={this.handleRetry} |
|
||||||
showRetry={showRetry} |
|
||||||
onCancel={() => onCancel()} |
|
||||||
showCancel={showCancel} |
|
||||||
/> |
|
||||||
</Modal> |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
@ -1,4 +0,0 @@ |
|||||||
import TransactionDetails from './transaction-details.component' |
|
||||||
import withModalProps from '../../../higher-order-components/with-modal-props' |
|
||||||
|
|
||||||
export default withModalProps(TransactionDetails) |
|
@ -1 +1 @@ |
|||||||
export { default } from './transaction-breakdown.component' |
export { default } from './transaction-breakdown.container' |
||||||
|
@ -0,0 +1,11 @@ |
|||||||
|
import { connect } from 'react-redux' |
||||||
|
import TransactionBreakdown from './transaction-breakdown.component' |
||||||
|
import { getNativeCurrency } from '../../selectors' |
||||||
|
|
||||||
|
const mapStateToProps = (state) => { |
||||||
|
return { |
||||||
|
nativeCurrency: getNativeCurrency(state), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export default connect(mapStateToProps)(TransactionBreakdown) |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue