aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/helpers/higher-order-components
diff options
context:
space:
mode:
Diffstat (limited to 'ui/app/helpers/higher-order-components')
-rw-r--r--ui/app/helpers/higher-order-components/authenticated/authenticated.component.js22
-rw-r--r--ui/app/helpers/higher-order-components/authenticated/authenticated.container.js12
-rw-r--r--ui/app/helpers/higher-order-components/authenticated/index.js1
-rw-r--r--ui/app/helpers/higher-order-components/i18n-provider.js55
-rw-r--r--ui/app/helpers/higher-order-components/initialized/index.js1
-rw-r--r--ui/app/helpers/higher-order-components/initialized/initialized.component.js14
-rw-r--r--ui/app/helpers/higher-order-components/initialized/initialized.container.js12
-rw-r--r--ui/app/helpers/higher-order-components/metametrics/metametrics.provider.js106
-rw-r--r--ui/app/helpers/higher-order-components/with-method-data/index.js1
-rw-r--r--ui/app/helpers/higher-order-components/with-method-data/with-method-data.component.js65
-rw-r--r--ui/app/helpers/higher-order-components/with-modal-props/index.js1
-rw-r--r--ui/app/helpers/higher-order-components/with-modal-props/tests/with-modal-props.test.js43
-rw-r--r--ui/app/helpers/higher-order-components/with-modal-props/with-modal-props.js21
-rw-r--r--ui/app/helpers/higher-order-components/with-token-tracker/index.js1
-rw-r--r--ui/app/helpers/higher-order-components/with-token-tracker/with-token-tracker.component.js106
15 files changed, 461 insertions, 0 deletions
diff --git a/ui/app/helpers/higher-order-components/authenticated/authenticated.component.js b/ui/app/helpers/higher-order-components/authenticated/authenticated.component.js
new file mode 100644
index 000000000..c195d0e21
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/authenticated/authenticated.component.js
@@ -0,0 +1,22 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import { Redirect, Route } from 'react-router-dom'
+import { UNLOCK_ROUTE, INITIALIZE_ROUTE } from '../../constants/routes'
+
+export default function Authenticated (props) {
+ const { isUnlocked, completedOnboarding } = props
+
+ switch (true) {
+ case isUnlocked && completedOnboarding:
+ return <Route { ...props } />
+ case !completedOnboarding:
+ return <Redirect to={{ pathname: INITIALIZE_ROUTE }} />
+ default:
+ return <Redirect to={{ pathname: UNLOCK_ROUTE }} />
+ }
+}
+
+Authenticated.propTypes = {
+ isUnlocked: PropTypes.bool,
+ completedOnboarding: PropTypes.bool,
+}
diff --git a/ui/app/helpers/higher-order-components/authenticated/authenticated.container.js b/ui/app/helpers/higher-order-components/authenticated/authenticated.container.js
new file mode 100644
index 000000000..6124b0fcd
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/authenticated/authenticated.container.js
@@ -0,0 +1,12 @@
+import { connect } from 'react-redux'
+import Authenticated from './authenticated.component'
+
+const mapStateToProps = state => {
+ const { metamask: { isUnlocked, completedOnboarding } } = state
+ return {
+ isUnlocked,
+ completedOnboarding,
+ }
+}
+
+export default connect(mapStateToProps)(Authenticated)
diff --git a/ui/app/helpers/higher-order-components/authenticated/index.js b/ui/app/helpers/higher-order-components/authenticated/index.js
new file mode 100644
index 000000000..05632ed21
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/authenticated/index.js
@@ -0,0 +1 @@
+export { default } from './authenticated.container'
diff --git a/ui/app/helpers/higher-order-components/i18n-provider.js b/ui/app/helpers/higher-order-components/i18n-provider.js
new file mode 100644
index 000000000..0e34e17e0
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/i18n-provider.js
@@ -0,0 +1,55 @@
+const { Component } = require('react')
+const connect = require('react-redux').connect
+const PropTypes = require('prop-types')
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
+const t = require('../utils/i18n-helper').getMessage
+
+class I18nProvider extends Component {
+ tOrDefault = (key, defaultValue, ...args) => {
+ const { localeMessages: { current, en } = {} } = this.props
+ return t(current, key, ...args) || t(en, key, ...args) || defaultValue
+ }
+
+ getChildContext () {
+ const { localeMessages } = this.props
+ const { current, en } = localeMessages
+ return {
+ t (key, ...args) {
+ return t(current, key, ...args) || t(en, key, ...args) || `[${key}]`
+ },
+ tOrDefault: this.tOrDefault,
+ tOrKey (key, ...args) {
+ return this.tOrDefault(key, key, ...args)
+ },
+ }
+ }
+
+ render () {
+ return this.props.children
+ }
+}
+
+I18nProvider.propTypes = {
+ localeMessages: PropTypes.object,
+ children: PropTypes.object,
+}
+
+I18nProvider.childContextTypes = {
+ t: PropTypes.func,
+ tOrDefault: PropTypes.func,
+ tOrKey: PropTypes.func,
+}
+
+const mapStateToProps = state => {
+ const { localeMessages } = state
+ return {
+ localeMessages,
+ }
+}
+
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps)
+)(I18nProvider)
+
diff --git a/ui/app/helpers/higher-order-components/initialized/index.js b/ui/app/helpers/higher-order-components/initialized/index.js
new file mode 100644
index 000000000..863fcb389
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/initialized/index.js
@@ -0,0 +1 @@
+export { default } from './initialized.container.js'
diff --git a/ui/app/helpers/higher-order-components/initialized/initialized.component.js b/ui/app/helpers/higher-order-components/initialized/initialized.component.js
new file mode 100644
index 000000000..2042c0046
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/initialized/initialized.component.js
@@ -0,0 +1,14 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import { Redirect, Route } from 'react-router-dom'
+import { INITIALIZE_ROUTE } from '../../constants/routes'
+
+export default function Initialized (props) {
+ return props.completedOnboarding
+ ? <Route { ...props } />
+ : <Redirect to={{ pathname: INITIALIZE_ROUTE }} />
+}
+
+Initialized.propTypes = {
+ completedOnboarding: PropTypes.bool,
+}
diff --git a/ui/app/helpers/higher-order-components/initialized/initialized.container.js b/ui/app/helpers/higher-order-components/initialized/initialized.container.js
new file mode 100644
index 000000000..0e7f72bcb
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/initialized/initialized.container.js
@@ -0,0 +1,12 @@
+import { connect } from 'react-redux'
+import Initialized from './initialized.component'
+
+const mapStateToProps = state => {
+ const { metamask: { completedOnboarding } } = state
+
+ return {
+ completedOnboarding,
+ }
+}
+
+export default connect(mapStateToProps)(Initialized)
diff --git a/ui/app/helpers/higher-order-components/metametrics/metametrics.provider.js b/ui/app/helpers/higher-order-components/metametrics/metametrics.provider.js
new file mode 100644
index 000000000..6086e03fb
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/metametrics/metametrics.provider.js
@@ -0,0 +1,106 @@
+import { Component } from 'react'
+import { connect } from 'react-redux'
+import PropTypes from 'prop-types'
+import { withRouter } from 'react-router-dom'
+import { compose } from 'recompose'
+import {
+ getCurrentNetworkId,
+ getSelectedAsset,
+ getAccountType,
+ getNumberOfAccounts,
+ getNumberOfTokens,
+} from '../../../selectors/selectors'
+import {
+ txDataSelector,
+} from '../../../selectors/confirm-transaction'
+import { getEnvironmentType } from '../../../../../app/scripts/lib/util'
+import {
+ sendMetaMetricsEvent,
+ sendCountIsTrackable,
+} from '../../utils/metametrics.util'
+
+class MetaMetricsProvider extends Component {
+ static propTypes = {
+ network: PropTypes.string.isRequired,
+ environmentType: PropTypes.string.isRequired,
+ activeCurrency: PropTypes.string.isRequired,
+ accountType: PropTypes.string.isRequired,
+ metaMetricsSendCount: PropTypes.number.isRequired,
+ children: PropTypes.object.isRequired,
+ history: PropTypes.object.isRequired,
+ }
+
+ static childContextTypes = {
+ metricsEvent: PropTypes.func,
+ }
+
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ previousPath: '',
+ currentPath: window.location.href,
+ }
+
+ props.history.listen(locationObj => {
+ this.setState({
+ previousPath: this.state.currentPath,
+ currentPath: window.location.href,
+ })
+ })
+ }
+
+ getChildContext () {
+ const props = this.props
+ const { pathname } = location
+ const { previousPath, currentPath } = this.state
+
+ return {
+ metricsEvent: (config = {}, overrides = {}) => {
+ const { eventOpts = {} } = config
+ const { name = '' } = eventOpts
+ const { pathname: overRidePathName = '' } = overrides
+ const isSendFlow = Boolean(name.match(/^send|^confirm/) || overRidePathName.match(/send|confirm/))
+
+ if (props.participateInMetaMetrics || config.isOptIn) {
+ return sendMetaMetricsEvent({
+ ...props,
+ ...config,
+ previousPath,
+ currentPath,
+ pathname,
+ excludeMetaMetricsId: isSendFlow && !sendCountIsTrackable(props.metaMetricsSendCount + 1),
+ ...overrides,
+ })
+ }
+ },
+ }
+ }
+
+ render () {
+ return this.props.children
+ }
+}
+
+const mapStateToProps = state => {
+ const txData = txDataSelector(state) || {}
+
+ return {
+ network: getCurrentNetworkId(state),
+ environmentType: getEnvironmentType(),
+ activeCurrency: getSelectedAsset(state),
+ accountType: getAccountType(state),
+ confirmTransactionOrigin: txData.origin,
+ metaMetricsId: state.metamask.metaMetricsId,
+ participateInMetaMetrics: state.metamask.participateInMetaMetrics,
+ metaMetricsSendCount: state.metamask.metaMetricsSendCount,
+ numberOfTokens: getNumberOfTokens(state),
+ numberOfAccounts: getNumberOfAccounts(state),
+ }
+}
+
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps)
+)(MetaMetricsProvider)
+
diff --git a/ui/app/helpers/higher-order-components/with-method-data/index.js b/ui/app/helpers/higher-order-components/with-method-data/index.js
new file mode 100644
index 000000000..f511e1ae7
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-method-data/index.js
@@ -0,0 +1 @@
+export { default } from './with-method-data.component'
diff --git a/ui/app/helpers/higher-order-components/with-method-data/with-method-data.component.js b/ui/app/helpers/higher-order-components/with-method-data/with-method-data.component.js
new file mode 100644
index 000000000..efa9ad0a2
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-method-data/with-method-data.component.js
@@ -0,0 +1,65 @@
+import React, { PureComponent } from 'react'
+import PropTypes from 'prop-types'
+import { getMethodData, getFourBytePrefix } from '../../utils/transactions.util'
+
+export default function withMethodData (WrappedComponent) {
+ return class MethodDataWrappedComponent extends PureComponent {
+ static propTypes = {
+ transaction: PropTypes.object,
+ knownMethodData: PropTypes.object,
+ addKnownMethodData: PropTypes.func,
+ }
+
+ static defaultProps = {
+ transaction: {},
+ knownMethodData: {},
+ }
+
+ state = {
+ methodData: {},
+ done: false,
+ error: null,
+ }
+
+ componentDidMount () {
+ this.fetchMethodData()
+ }
+
+ async fetchMethodData () {
+ const { transaction, knownMethodData, addKnownMethodData } = this.props
+ const { txParams: { data = '' } = {} } = transaction
+
+ if (data) {
+ try {
+ let methodData
+ const fourBytePrefix = getFourBytePrefix(data)
+ if (fourBytePrefix in knownMethodData) {
+ methodData = knownMethodData[fourBytePrefix]
+ } else {
+ methodData = await getMethodData(data)
+ if (!Object.entries(methodData).length === 0) {
+ addKnownMethodData(fourBytePrefix, methodData)
+ }
+ }
+
+ this.setState({ methodData, done: true })
+ } catch (error) {
+ this.setState({ done: true, error })
+ }
+ } else {
+ this.setState({ done: true })
+ }
+ }
+
+ render () {
+ const { methodData, done, error } = this.state
+
+ return (
+ <WrappedComponent
+ { ...this.props }
+ methodData={{ data: methodData, done, error }}
+ />
+ )
+ }
+ }
+}
diff --git a/ui/app/helpers/higher-order-components/with-modal-props/index.js b/ui/app/helpers/higher-order-components/with-modal-props/index.js
new file mode 100644
index 000000000..e476b51d2
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-modal-props/index.js
@@ -0,0 +1 @@
+export { default } from './with-modal-props'
diff --git a/ui/app/helpers/higher-order-components/with-modal-props/tests/with-modal-props.test.js b/ui/app/helpers/higher-order-components/with-modal-props/tests/with-modal-props.test.js
new file mode 100644
index 000000000..654e7062a
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-modal-props/tests/with-modal-props.test.js
@@ -0,0 +1,43 @@
+
+import assert from 'assert'
+import configureMockStore from 'redux-mock-store'
+import { mount } from 'enzyme'
+import React from 'react'
+import withModalProps from '../with-modal-props'
+
+const mockState = {
+ appState: {
+ modal: {
+ modalState: {
+ props: {
+ prop1: 'prop1',
+ prop2: 2,
+ prop3: true,
+ },
+ },
+ },
+ },
+}
+
+describe('withModalProps', () => {
+ it('should return a component wrapped with modal state props', () => {
+ const TestComponent = props => (
+ <div className="test">Testing</div>
+ )
+ const WrappedComponent = withModalProps(TestComponent)
+ const store = configureMockStore()(mockState)
+ const wrapper = mount(
+ <WrappedComponent store={store} />
+ )
+
+ assert.ok(wrapper)
+ const testComponent = wrapper.find(TestComponent).at(0)
+ assert.equal(testComponent.length, 1)
+ assert.equal(testComponent.find('.test').text(), 'Testing')
+ const testComponentProps = testComponent.props()
+ assert.equal(testComponentProps.prop1, 'prop1')
+ assert.equal(testComponentProps.prop2, 2)
+ assert.equal(testComponentProps.prop3, true)
+ assert.equal(typeof testComponentProps.hideModal, 'function')
+ })
+})
diff --git a/ui/app/helpers/higher-order-components/with-modal-props/with-modal-props.js b/ui/app/helpers/higher-order-components/with-modal-props/with-modal-props.js
new file mode 100644
index 000000000..aac6b5a61
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-modal-props/with-modal-props.js
@@ -0,0 +1,21 @@
+import { connect } from 'react-redux'
+import { hideModal } from '../../../store/actions'
+
+const mapStateToProps = state => {
+ const { appState } = state
+ const { props: modalProps } = appState.modal.modalState
+
+ return {
+ ...modalProps,
+ }
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ hideModal: () => dispatch(hideModal()),
+ }
+}
+
+export default function withModalProps (Component) {
+ return connect(mapStateToProps, mapDispatchToProps)(Component)
+}
diff --git a/ui/app/helpers/higher-order-components/with-token-tracker/index.js b/ui/app/helpers/higher-order-components/with-token-tracker/index.js
new file mode 100644
index 000000000..d401e81f1
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-token-tracker/index.js
@@ -0,0 +1 @@
+export { default } from './with-token-tracker.component'
diff --git a/ui/app/helpers/higher-order-components/with-token-tracker/with-token-tracker.component.js b/ui/app/helpers/higher-order-components/with-token-tracker/with-token-tracker.component.js
new file mode 100644
index 000000000..36f6a6efd
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-token-tracker/with-token-tracker.component.js
@@ -0,0 +1,106 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import TokenTracker from 'eth-token-tracker'
+
+export default function withTokenTracker (WrappedComponent) {
+ return class TokenTrackerWrappedComponent extends Component {
+ static propTypes = {
+ userAddress: PropTypes.string.isRequired,
+ token: PropTypes.object.isRequired,
+ }
+
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ string: '',
+ symbol: '',
+ error: null,
+ }
+
+ this.tracker = null
+ this.updateBalance = this.updateBalance.bind(this)
+ this.setError = this.setError.bind(this)
+ }
+
+ componentDidMount () {
+ this.createFreshTokenTracker()
+ }
+
+ componentDidUpdate (prevProps) {
+ const { userAddress: newAddress, token: { address: newTokenAddress } } = this.props
+ const { userAddress: oldAddress, token: { address: oldTokenAddress } } = prevProps
+
+ if ((oldAddress === newAddress) && (oldTokenAddress === newTokenAddress)) {
+ return
+ }
+
+ if ((!oldAddress || !newAddress) && (!oldTokenAddress || !newTokenAddress)) {
+ return
+ }
+
+ this.createFreshTokenTracker()
+ }
+
+ componentWillUnmount () {
+ this.removeListeners()
+ }
+
+ createFreshTokenTracker () {
+ this.removeListeners()
+
+ if (!global.ethereumProvider) {
+ return
+ }
+
+ const { userAddress, token } = this.props
+
+ this.tracker = new TokenTracker({
+ userAddress,
+ provider: global.ethereumProvider,
+ tokens: [token],
+ pollingInterval: 8000,
+ })
+
+ this.tracker.on('update', this.updateBalance)
+ this.tracker.on('error', this.setError)
+
+ this.tracker.updateBalances()
+ .then(() => this.updateBalance(this.tracker.serialize()))
+ .catch(error => this.setState({ error: error.message }))
+ }
+
+ setError (error) {
+ this.setState({ error })
+ }
+
+ updateBalance (tokens = []) {
+ if (!this.tracker.running) {
+ return
+ }
+ const [{ string, symbol }] = tokens
+ this.setState({ string, symbol, error: null })
+ }
+
+ removeListeners () {
+ if (this.tracker) {
+ this.tracker.stop()
+ this.tracker.removeListener('update', this.updateBalance)
+ this.tracker.removeListener('error', this.setError)
+ }
+ }
+
+ render () {
+ const { string, symbol, error } = this.state
+
+ return (
+ <WrappedComponent
+ { ...this.props }
+ string={string}
+ symbol={symbol}
+ error={error}
+ />
+ )
+ }
+ }
+}