this.props.hideSidebar()} />
+ const { onOverlayClose } = this.props
+
+ return
{
+ onOverlayClose && onOverlayClose()
+ this.props.hideSidebar()
+ }
+ } />
}
renderSidebarContent () {
diff --git a/ui/app/components/signature-request.js b/ui/app/components/signature-request.js
index a76064bff..25bd9a7b1 100644
--- a/ui/app/components/signature-request.js
+++ b/ui/app/components/signature-request.js
@@ -49,6 +49,7 @@ function mapDispatchToProps (dispatch) {
SignatureRequest.contextTypes = {
t: PropTypes.func,
+ metricsEvent: PropTypes.func,
}
module.exports = compose(
@@ -264,6 +265,13 @@ SignatureRequest.prototype.renderFooter = function () {
className: 'request-signature__footer__cancel-button',
onClick: event => {
cancel(event).then(() => {
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Transactions',
+ action: 'Sign Request',
+ name: 'Cancel',
+ },
+ })
this.props.clearConfirmTransaction()
this.props.history.push(DEFAULT_ROUTE)
})
@@ -275,6 +283,13 @@ SignatureRequest.prototype.renderFooter = function () {
className: 'request-signature__footer__sign-button',
onClick: event => {
sign(event).then(() => {
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Transactions',
+ action: 'Sign Request',
+ name: 'Confirm',
+ },
+ })
this.props.clearConfirmTransaction()
this.props.history.push(DEFAULT_ROUTE)
})
diff --git a/ui/app/components/token-cell.js b/ui/app/components/token-cell.js
index 75ba347fa..d9c80b4f4 100644
--- a/ui/app/components/token-cell.js
+++ b/ui/app/components/token-cell.js
@@ -1,4 +1,5 @@
const Component = require('react').Component
+const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
@@ -40,6 +41,10 @@ function TokenCell () {
}
}
+TokenCell.contextTypes = {
+ metricsEvent: PropTypes.func,
+}
+
TokenCell.prototype.render = function () {
const { tokenMenuOpen } = this.state
const props = this.props
@@ -88,6 +93,13 @@ TokenCell.prototype.render = function () {
// onClick: this.view.bind(this, address, userAddress, network),
onClick: () => {
setSelectedToken(address)
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Navigation',
+ action: 'Token Menu',
+ name: 'Clicked Token',
+ },
+ })
selectedTokenAddress !== address && sidebarOpen && hideSidebar()
},
}, [
diff --git a/ui/app/components/transaction-activity-log/transaction-activity-log.component.js b/ui/app/components/transaction-activity-log/transaction-activity-log.component.js
index 4e4e553c0..ca46d7830 100644
--- a/ui/app/components/transaction-activity-log/transaction-activity-log.component.js
+++ b/ui/app/components/transaction-activity-log/transaction-activity-log.component.js
@@ -10,6 +10,7 @@ import prefixForNetwork from '../../../lib/etherscan-prefix-for-network'
export default class TransactionActivityLog extends PureComponent {
static contextTypes = {
t: PropTypes.func,
+ metricEvent: PropTypes.func,
}
static propTypes = {
diff --git a/ui/app/components/transaction-list-item-details/transaction-list-item-details.component.js b/ui/app/components/transaction-list-item-details/transaction-list-item-details.component.js
index eaf1166f0..3e39212d3 100644
--- a/ui/app/components/transaction-list-item-details/transaction-list-item-details.component.js
+++ b/ui/app/components/transaction-list-item-details/transaction-list-item-details.component.js
@@ -12,6 +12,7 @@ import prefixForNetwork from '../../../lib/etherscan-prefix-for-network'
export default class TransactionListItemDetails extends PureComponent {
static contextTypes = {
t: PropTypes.func,
+ metricsEvent: PropTypes.func,
}
static propTypes = {
@@ -33,6 +34,14 @@ export default class TransactionListItemDetails extends PureComponent {
const prefix = prefixForNetwork(metamaskNetworkId)
const etherscanUrl = `https://${prefix}etherscan.io/tx/${hash}`
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Navigation',
+ action: 'Activity Log',
+ name: 'Clicked "View on Etherscan"',
+ },
+ })
+
global.platform.openWindow({ url: etherscanUrl })
}
@@ -55,6 +64,14 @@ export default class TransactionListItemDetails extends PureComponent {
const { primaryTransaction: transaction } = transactionGroup
const { hash } = transaction
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Navigation',
+ action: 'Activity Log',
+ name: 'Copied Transaction ID',
+ },
+ })
+
this.setState({ justCopied: true }, () => {
copyToClipboard(hash)
setTimeout(() => this.setState({ justCopied: false }), 1000)
@@ -125,6 +142,24 @@ export default class TransactionListItemDetails extends PureComponent {
addressOnly
recipientAddress={to}
senderAddress={from}
+ onRecipientClick={() => {
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Navigation',
+ action: 'Activity Log',
+ name: 'Copied "To" Address',
+ },
+ })
+ }}
+ onSenderClick={() => {
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Navigation',
+ action: 'Activity Log',
+ name: 'Copied "From" Address',
+ },
+ })
+ }}
/>
diff --git a/ui/app/components/transaction-list-item/transaction-list-item.component.js b/ui/app/components/transaction-list-item/transaction-list-item.component.js
index 29d3a7b1f..e843fe1a0 100644
--- a/ui/app/components/transaction-list-item/transaction-list-item.component.js
+++ b/ui/app/components/transaction-list-item/transaction-list-item.component.js
@@ -38,6 +38,10 @@ export default class TransactionListItem extends PureComponent {
showFiat: true,
}
+ static contextTypes = {
+ metricsEvent: PropTypes.func,
+ }
+
state = {
showTransactionDetails: false,
}
@@ -55,6 +59,16 @@ export default class TransactionListItem extends PureComponent {
return
}
+ if (!showTransactionDetails) {
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Navigation',
+ action: 'Home',
+ name: 'Expand Transaction',
+ },
+ })
+ }
+
this.setState({ showTransactionDetails: !showTransactionDetails })
}
diff --git a/ui/app/components/transaction-view-balance/tests/token-view-balance.component.test.js b/ui/app/components/transaction-view-balance/tests/token-view-balance.component.test.js
index 513a8aac9..efc987371 100644
--- a/ui/app/components/transaction-view-balance/tests/token-view-balance.component.test.js
+++ b/ui/app/components/transaction-view-balance/tests/token-view-balance.component.test.js
@@ -16,6 +16,7 @@ const historySpies = {
}
const t = (str1, str2) => str2 ? str1 + str2 : str1
+const metricsEvent = () => ({})
describe('TransactionViewBalance Component', () => {
afterEach(() => {
@@ -31,7 +32,7 @@ describe('TransactionViewBalance Component', () => {
ethBalance={123}
fiatBalance={456}
currentCurrency="usd"
- />, { context: { t } })
+ />, { context: { t, metricsEvent } })
assert.equal(wrapper.find('.transaction-view-balance').length, 1)
assert.equal(wrapper.find('.transaction-view-balance__button').length, 2)
diff --git a/ui/app/components/transaction-view-balance/transaction-view-balance.component.js b/ui/app/components/transaction-view-balance/transaction-view-balance.component.js
index b16e04f4f..a18e959b5 100644
--- a/ui/app/components/transaction-view-balance/transaction-view-balance.component.js
+++ b/ui/app/components/transaction-view-balance/transaction-view-balance.component.js
@@ -12,6 +12,7 @@ import Tooltip from '../tooltip-v2'
export default class TransactionViewBalance extends PureComponent {
static contextTypes = {
t: PropTypes.func,
+ metricsEvent: PropTypes.func,
}
static propTypes = {
@@ -78,7 +79,7 @@ export default class TransactionViewBalance extends PureComponent {
}
renderButtons () {
- const { t } = this.context
+ const { t, metricsEvent } = this.context
const { selectedToken, showDepositModal, history } = this.props
return (
@@ -88,7 +89,16 @@ export default class TransactionViewBalance extends PureComponent {
@@ -97,7 +107,16 @@ export default class TransactionViewBalance extends PureComponent {
diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js
index 4566cb390..400b9aa90 100644
--- a/ui/app/components/wallet-view.js
+++ b/ui/app/components/wallet-view.js
@@ -26,6 +26,7 @@ module.exports = compose(
WalletView.contextTypes = {
t: PropTypes.func,
+ metricsEvent: PropTypes.func,
}
WalletView.defaultProps = {
@@ -39,7 +40,6 @@ function mapStateToProps (state) {
sidebarOpen: state.appState.sidebar.isOpen,
identities: state.metamask.identities,
accounts: selectors.getMetaMaskAccounts(state),
- tokens: state.metamask.tokens,
keyrings: state.metamask.keyrings,
selectedAddress: selectors.getSelectedAddress(state),
selectedAccount: selectors.getSelectedAccount(state),
@@ -110,6 +110,13 @@ WalletView.prototype.renderAddToken = function () {
return h(AddTokenButton, {
onClick () {
history.push(ADD_TOKEN_ROUTE)
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Navigation',
+ action: 'Token Menu',
+ name: 'Clicked "Add Token"',
+ },
+ })
if (sidebarOpen) {
hideSidebar()
}
@@ -197,6 +204,13 @@ WalletView.prototype.render = function () {
}),
onClick: () => {
copyToClipboard(checksummedAddress)
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Activation',
+ action: 'userClicks',
+ name: 'navCopyToClipboard',
+ },
+ })
this.setState({ hasCopied: true })
setTimeout(() => this.setState({ hasCopied: false }), 3000)
},
diff --git a/ui/app/ducks/confirm-transaction.duck.js b/ui/app/ducks/confirm-transaction.duck.js
index c6e2c1be3..f75ff809a 100644
--- a/ui/app/ducks/confirm-transaction.duck.js
+++ b/ui/app/ducks/confirm-transaction.duck.js
@@ -372,6 +372,7 @@ export function setTransactionToConfirm (transactionId) {
try {
dispatch(setFetchingData(true))
const methodData = await getMethodData(data)
+
dispatch(updateMethodData(methodData))
} catch (error) {
dispatch(updateMethodData({}))
diff --git a/ui/app/metametrics/metametrics.provider.js b/ui/app/metametrics/metametrics.provider.js
new file mode 100644
index 000000000..5ff0294e5
--- /dev/null
+++ b/ui/app/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'
+import {
+ txDataSelector,
+} from '../selectors/confirm-transaction'
+import { getEnvironmentType } from '../../../app/scripts/lib/util'
+import {
+ sendMetaMetricsEvent,
+ sendCountIsTrackable,
+} from './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/metametrics/metametrics.util.js b/ui/app/metametrics/metametrics.util.js
new file mode 100644
index 000000000..2da7e2da8
--- /dev/null
+++ b/ui/app/metametrics/metametrics.util.js
@@ -0,0 +1,188 @@
+/* eslint camelcase: 0 */
+
+const ethUtil = require('ethereumjs-util')
+
+const inDevelopment = process.env.METAMETRICS_URL === 'development'
+
+const METAMETRICS_BASE_URL = 'https://chromeextensionmm.innocraft.cloud/piwik.php'
+const METAMETRICS_REQUIRED_PARAMS = `?idsite=${inDevelopment ? 1 : 2}&rec=1&apiv=1`
+const METAMETRICS_BASE_FULL = METAMETRICS_BASE_URL + METAMETRICS_REQUIRED_PARAMS
+
+const METAMETRICS_TRACKING_URL = inDevelopment
+ ? 'http://www.metamask.io/metametrics'
+ : 'http://www.metamask.io/metametrics-prod'
+
+const METAMETRICS_CUSTOM_HAD_ERROR = 'hadError'
+const METAMETRICS_CUSTOM_HEX_DATA = 'hexData'
+const METAMETRICS_CUSTOM_FUNCTION_TYPE = 'functionType'
+const METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE = 'gasLimitChange'
+const METAMETRICS_CUSTOM_GAS_PRICE_CHANGE = 'gasPriceChange'
+const METAMETRICS_CUSTOM_RECIPIENT_KNOWN = 'recipientKnown'
+const METAMETRICS_CUSTOM_CONFIRM_SCREEN_ORIGIN = 'origin'
+const METAMETRICS_CUSTOM_FROM_NETWORK = 'fromNetwork'
+const METAMETRICS_CUSTOM_TO_NETWORK = 'toNetwork'
+const METAMETRICS_CUSTOM_ERROR_FIELD = 'errorField'
+const METAMETRICS_CUSTOM_ERROR_MESSAGE = 'errorMessage'
+const METAMETRICS_CUSTOM_RPC_NETWORK_ID = 'networkId'
+const METAMETRICS_CUSTOM_RPC_CHAIN_ID = 'chainId'
+
+const METAMETRICS_CUSTOM_NETWORK = 'network'
+const METAMETRICS_CUSTOM_ENVIRONMENT_TYPE = 'environmentType'
+const METAMETRICS_CUSTOM_ACTIVE_CURRENCY = 'activeCurrency'
+const METAMETRICS_CUSTOM_ACCOUNT_TYPE = 'accountType'
+const METAMETRICS_CUSTOM_NUMBER_OF_TOKENS = 'numberOfTokens'
+const METAMETRICS_CUSTOM_NUMBER_OF_ACCOUNTS = 'numberOfAccounts'
+
+const customVariableNameIdMap = {
+ [METAMETRICS_CUSTOM_HAD_ERROR]: 1,
+ [METAMETRICS_CUSTOM_HEX_DATA]: 2,
+ [METAMETRICS_CUSTOM_FUNCTION_TYPE]: 3,
+ [METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE]: 4,
+ [METAMETRICS_CUSTOM_GAS_PRICE_CHANGE]: 5,
+ [METAMETRICS_CUSTOM_RECIPIENT_KNOWN]: 6,
+ [METAMETRICS_CUSTOM_CONFIRM_SCREEN_ORIGIN]: 7,
+ [METAMETRICS_CUSTOM_FROM_NETWORK]: 8,
+ [METAMETRICS_CUSTOM_TO_NETWORK]: 9,
+ [METAMETRICS_CUSTOM_RPC_NETWORK_ID]: 10,
+ [METAMETRICS_CUSTOM_RPC_CHAIN_ID]: 11,
+ [METAMETRICS_CUSTOM_ERROR_FIELD]: 12,
+ [METAMETRICS_CUSTOM_ERROR_MESSAGE]: 13,
+}
+
+const customDimensionsNameIdMap = {
+ [METAMETRICS_CUSTOM_NETWORK]: 5,
+ [METAMETRICS_CUSTOM_ENVIRONMENT_TYPE]: 6,
+ [METAMETRICS_CUSTOM_ACTIVE_CURRENCY]: 7,
+ [METAMETRICS_CUSTOM_ACCOUNT_TYPE]: 8,
+ [METAMETRICS_CUSTOM_NUMBER_OF_TOKENS]: 9,
+ [METAMETRICS_CUSTOM_NUMBER_OF_ACCOUNTS]: 10,
+}
+
+function composeUrlRefParamAddition (previousPath, confirmTransactionOrigin) {
+ const externalOrigin = confirmTransactionOrigin && confirmTransactionOrigin !== 'MetaMask'
+ return `&urlref=${externalOrigin ? 'EXTERNAL' : encodeURIComponent(previousPath.replace(/chrome-extension:\/\/\w+/, METAMETRICS_TRACKING_URL))}`
+}
+
+function composeCustomDimensionParamAddition (customDimensions) {
+ const customDimensionParamStrings = Object.keys(customDimensions).reduce((acc, name) => {
+ return [...acc, `dimension${customDimensionsNameIdMap[name]}=${customDimensions[name]}`]
+ }, [])
+ return `&${customDimensionParamStrings.join('&')}`
+}
+
+function composeCustomVarParamAddition (customVariables) {
+ const customVariableIdValuePairs = Object.keys(customVariables).reduce((acc, name) => {
+ return {
+ [customVariableNameIdMap[name]]: [name, customVariables[name]],
+ ...acc,
+ }
+ }, {})
+ return `&cvar=${encodeURIComponent(JSON.stringify(customVariableIdValuePairs))}`
+}
+
+function composeParamAddition (paramValue, paramName) {
+ return paramValue !== 0 && !paramValue
+ ? ''
+ : `&${paramName}=${paramValue}`
+}
+
+function composeUrl (config, permissionPreferences = {}) {
+ const {
+ eventOpts = {},
+ customVariables = '',
+ pageOpts = '',
+ network,
+ environmentType,
+ activeCurrency,
+ accountType,
+ numberOfTokens,
+ numberOfAccounts,
+ previousPath = '',
+ currentPath,
+ metaMetricsId,
+ confirmTransactionOrigin,
+ url: configUrl,
+ excludeMetaMetricsId,
+ isNewVisit,
+ } = config
+ const base = METAMETRICS_BASE_FULL
+
+ const e_c = composeParamAddition(eventOpts.category, 'e_c')
+ const e_a = composeParamAddition(eventOpts.action, 'e_a')
+ const e_n = composeParamAddition(eventOpts.name, 'e_n')
+ const new_visit = isNewVisit ? `&new_visit=1` : ''
+
+ const cvar = customVariables && composeCustomVarParamAddition(customVariables) || ''
+
+ const action_name = ''
+
+ const urlref = previousPath && composeUrlRefParamAddition(previousPath, confirmTransactionOrigin)
+
+ const dimensions = !pageOpts.hideDimensions ? composeCustomDimensionParamAddition({
+ network,
+ environmentType,
+ activeCurrency,
+ accountType,
+ numberOfTokens: customVariables && customVariables.numberOfTokens || numberOfTokens,
+ numberOfAccounts: customVariables && customVariables.numberOfAccounts || numberOfAccounts,
+ }) : ''
+ const url = configUrl || `&url=${encodeURIComponent(currentPath.replace(/chrome-extension:\/\/\w+/, METAMETRICS_TRACKING_URL))}`
+ const _id = metaMetricsId && !excludeMetaMetricsId ? `&_id=${metaMetricsId.slice(2, 18)}` : ''
+ const rand = `&rand=${String(Math.random()).slice(2)}`
+ const pv_id = `&pv_id=${ethUtil.bufferToHex(ethUtil.sha3(url || currentPath.match(/chrome-extension:\/\/\w+\/(.+)/)[0])).slice(2, 8)}`
+ const uid = metaMetricsId && !excludeMetaMetricsId
+ ? `&uid=${metaMetricsId.slice(2, 18)}`
+ : excludeMetaMetricsId
+ ? '&uid=0000000000000000'
+ : ''
+
+ return [ base, e_c, e_a, e_n, cvar, action_name, urlref, dimensions, url, _id, rand, pv_id, uid, new_visit ].join('')
+}
+
+export function sendMetaMetricsEvent (config, permissionPreferences) {
+ return fetch(composeUrl(config, permissionPreferences), {
+ 'headers': {},
+ 'method': 'GET',
+ })
+}
+
+export function verifyUserPermission (config, props) {
+ const {
+ eventOpts = {},
+ } = config
+ const { userPermissionPreferences } = props
+ const {
+ allowAll,
+ allowNone,
+ allowSendMetrics,
+ } = userPermissionPreferences
+
+ if (allowNone) {
+ return false
+ } else if (allowAll) {
+ return true
+ } else if (allowSendMetrics && eventOpts.name === 'send') {
+ return true
+ } else {
+ return false
+ }
+}
+
+const trackableSendCounts = {
+ 1: true,
+ 10: true,
+ 30: true,
+ 50: true,
+ 100: true,
+ 250: true,
+ 500: true,
+ 1000: true,
+ 2500: true,
+ 5000: true,
+ 10000: true,
+ 25000: true,
+}
+
+export function sendCountIsTrackable (sendCount) {
+ return Boolean(trackableSendCounts[sendCount])
+}
diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js
index fb0fd7130..c1aa20bf7 100644
--- a/ui/app/reducers/metamask.js
+++ b/ui/app/reducers/metamask.js
@@ -55,8 +55,11 @@ function reduceMetamask (state, action) {
useNativeCurrencyAsPrimaryCurrency: true,
showFiatInTestnets: false,
},
+ firstTimeFlowType: null,
completedOnboarding: false,
knownMethodData: {},
+ participateInMetaMetrics: null,
+ metaMetricsSendCount: 0,
}, state.metamask)
switch (action.type) {
@@ -338,6 +341,16 @@ function reduceMetamask (state, action) {
coinOptions,
})
+ case actions.SET_PARTICIPATE_IN_METAMETRICS:
+ return extend(metamaskState, {
+ participateInMetaMetrics: action.value,
+ })
+
+ case actions.SET_METAMETRICS_SEND_COUNT:
+ return extend(metamaskState, {
+ metaMetricsSendCount: action.value,
+ })
+
case actions.SET_USE_BLOCKIE:
return extend(metamaskState, {
useBlockie: action.value,
@@ -395,6 +408,12 @@ function reduceMetamask (state, action) {
})
}
+ case actions.SET_FIRST_TIME_FLOW_TYPE: {
+ return extend(metamaskState, {
+ firstTimeFlowType: action.value,
+ })
+ }
+
default:
return metamaskState
diff --git a/ui/app/root.js b/ui/app/root.js
index f9e3709a0..c95c56581 100644
--- a/ui/app/root.js
+++ b/ui/app/root.js
@@ -5,6 +5,7 @@ const h = require('react-hyperscript')
const { HashRouter } = require('react-router-dom')
const App = require('./app')
const I18nProvider = require('./i18n-provider')
+const MetaMetricsProvider = require('./metametrics/metametrics.provider')
class Root extends Component {
render () {
@@ -15,8 +16,10 @@ class Root extends Component {
h(HashRouter, {
hashType: 'noslash',
}, [
- h(I18nProvider, [
- h(App),
+ h(MetaMetricsProvider, [
+ h(I18nProvider, [
+ h(App),
+ ]),
]),
]),
])
diff --git a/ui/app/routes.js b/ui/app/routes.js
index 7c4e805ab..932dfa7df 100644
--- a/ui/app/routes.js
+++ b/ui/app/routes.js
@@ -29,6 +29,7 @@ const INITIALIZE_SELECT_ACTION_ROUTE = '/initialize/select-action'
const INITIALIZE_SEED_PHRASE_ROUTE = '/initialize/seed-phrase'
const INITIALIZE_END_OF_FLOW_ROUTE = '/initialize/end-of-flow'
const INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE = '/initialize/seed-phrase/confirm'
+const INITIALIZE_METAMETRICS_OPT_IN_ROUTE = '/initialize/metametrics-opt-in'
const CONFIRM_TRANSACTION_ROUTE = '/confirm-transaction'
const CONFIRM_SEND_ETHER_PATH = '/send-ether'
@@ -78,4 +79,5 @@ module.exports = {
CONFIRM_TRANSFER_FROM_PATH,
CONFIRM_TOKEN_METHOD_PATH,
SIGNATURE_REQUEST_PATH,
+ INITIALIZE_METAMETRICS_OPT_IN_ROUTE,
}
diff --git a/ui/app/selectors.js b/ui/app/selectors.js
index a36671b42..663c3f12b 100644
--- a/ui/app/selectors.js
+++ b/ui/app/selectors.js
@@ -1,4 +1,5 @@
import {NETWORK_TYPES} from './constants/common'
+import { stripHexPrefix } from 'ethereumjs-util'
const abi = require('human-standard-token-abi')
import {
@@ -40,6 +41,12 @@ const selectors = {
isBalanceCached,
getAdvancedInlineGasShown,
getIsMainnet,
+ getCurrentNetworkId,
+ getSelectedAsset,
+ getCurrentKeyring,
+ getAccountType,
+ getNumberOfAccounts,
+ getNumberOfTokens,
}
module.exports = selectors
@@ -50,6 +57,46 @@ function getNetworkIdentifier (state) {
return nickname || rpcTarget || type
}
+function getCurrentKeyring (state) {
+ const identity = getSelectedIdentity(state)
+
+ if (!identity) {
+ return null
+ }
+
+ const simpleAddress = stripHexPrefix(identity.address).toLowerCase()
+
+ const keyring = state.metamask.keyrings.find((kr) => {
+ return kr.accounts.includes(simpleAddress) ||
+ kr.accounts.includes(identity.address)
+ })
+
+ return keyring
+}
+
+function getAccountType (state) {
+ const currentKeyring = getCurrentKeyring(state)
+ const type = currentKeyring && currentKeyring.type
+
+ switch (type) {
+ case 'Trezor Hardware':
+ case 'Ledger Hardware':
+ return 'hardware'
+ case 'Simple Key Pair':
+ return 'imported'
+ default:
+ return 'default'
+ }
+}
+
+function getSelectedAsset (state) {
+ return getSelectedToken(state) || 'ETH'
+}
+
+function getCurrentNetworkId (state) {
+ return state.metamask.network
+}
+
function getSelectedAddress (state) {
const selectedAddress = state.metamask.selectedAddress || Object.keys(getMetaMaskAccounts(state))[0]
@@ -63,6 +110,15 @@ function getSelectedIdentity (state) {
return identities[selectedAddress]
}
+function getNumberOfAccounts (state) {
+ return Object.keys(state.metamask.accounts).length
+}
+
+function getNumberOfTokens (state) {
+ const tokens = state.metamask.tokens
+ return tokens ? tokens.length : 0
+}
+
function getMetaMaskAccounts (state) {
const currentAccounts = state.metamask.accounts
const cachedBalances = state.metamask.cachedBalances[state.metamask.network]
diff --git a/ui/app/selectors/confirm-transaction.js b/ui/app/selectors/confirm-transaction.js
index 23ef26d95..ccd16fadd 100644
--- a/ui/app/selectors/confirm-transaction.js
+++ b/ui/app/selectors/confirm-transaction.js
@@ -95,7 +95,7 @@ export const currentCurrencySelector = state => state.metamask.currentCurrency
export const conversionRateSelector = state => state.metamask.conversionRate
export const getNativeCurrency = state => state.metamask.nativeCurrency
-const txDataSelector = state => state.confirmTransaction.txData
+export const txDataSelector = state => state.confirmTransaction.txData
const tokenDataSelector = state => state.confirmTransaction.tokenData
const tokenPropsSelector = state => state.confirmTransaction.tokenProps