diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 2b957129d..86bc1397b 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1,4 +1,10 @@ { + "showIncomingTransactions": { + "message": "Show Incoming Transactions" + }, + "showIncomingTransactionsDescription": { + "message": "Select this to use Etherscan to show incoming transactions in the transactions list" + }, "exposeAccounts": { "message": "Expose Accounts" }, diff --git a/app/scripts/controllers/incoming-transactions.js b/app/scripts/controllers/incoming-transactions.js index 1a970cdfe..032ef8a77 100644 --- a/app/scripts/controllers/incoming-transactions.js +++ b/app/scripts/controllers/incoming-transactions.js @@ -56,6 +56,35 @@ class IncomingTransactionsController { }, opts.initState) this.store = new ObservableStore(initState) + this.preferencesController.store.subscribe(pairwise((prevState, currState) => { + const { featureFlags: { showIncomingTransactions: prevShowIncomingTransactions } = {} } = prevState + const { featureFlags: { showIncomingTransactions: currShowIncomingTransactions } = {} } = currState + + if (currShowIncomingTransactions === prevShowIncomingTransactions) { + return + } + + if (prevShowIncomingTransactions && !currShowIncomingTransactions) { + this.stop() + return + } + + this.start() + })) + + this.preferencesController.store.subscribe(pairwise(async (prevState, currState) => { + const { selectedAddress: prevSelectedAddress } = prevState + const { selectedAddress: currSelectedAddress } = currState + + if (currSelectedAddress === prevSelectedAddress) { + return + } + + await this._update({ + address: currSelectedAddress, + }) + })) + this.networkController.on('networkDidChange', async (newType) => { const address = this.preferencesController.getSelectedAddress() await this._update({ @@ -63,14 +92,16 @@ class IncomingTransactionsController { networkType: newType, }) }) - this.preferencesController.store.subscribe(async ({ selectedAddress }) => { - await this._update({ - address: selectedAddress, - }) - }) } start () { + const { featureFlags = {} } = this.preferencesController.store.getState() + const { showIncomingTransactions } = featureFlags + + if (!showIncomingTransactions) { + return + } + this.blockTracker.removeListener('latest', this._onLatestBlock) this.blockTracker.addListener('latest', this._onLatestBlock) } @@ -234,3 +265,20 @@ class IncomingTransactionsController { } module.exports = IncomingTransactionsController + +function pairwise (fn) { + let first = true + let cache + return (value) => { + try { + if (first) { + first = false + return fn(value, value) + } else { + return fn(cache, value) + } + } finally { + cache = value + } + } +} diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index f02708d40..4ed3afb6c 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -41,6 +41,7 @@ class PreferencesController { // for convenient testing of pre-release features, and should never // perform sensitive operations. featureFlags: { + showIncomingTransactions: true, }, knownMethodData: {}, participateInMetaMetrics: null, diff --git a/test/data/mock-state.json b/test/data/mock-state.json index ed25b0abe..0be365249 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -1,5 +1,8 @@ { "metamask": { + "featureFlags": { + "showIncomingTransactions": true + }, "network": "4", "identities": { "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": { diff --git a/test/unit/app/controllers/incoming-transactions-test.js b/test/unit/app/controllers/incoming-transactions-test.js index e77c83a3d..0f8385af2 100644 --- a/test/unit/app/controllers/incoming-transactions-test.js +++ b/test/unit/app/controllers/incoming-transactions-test.js @@ -63,6 +63,11 @@ describe('IncomingTransactionsController', () => { const MOCK_PREFERENCES_CONTROLLER = { getSelectedAddress: sinon.stub().returns('0x0101'), store: { + getState: sinon.stub().returns({ + featureFlags: { + showIncomingTransactions: true, + }, + }), subscribe: sinon.spy(), }, } diff --git a/ui/app/pages/settings/security-tab/security-tab.component.js b/ui/app/pages/settings/security-tab/security-tab.component.js index 600ec7069..117010d0f 100644 --- a/ui/app/pages/settings/security-tab/security-tab.component.js +++ b/ui/app/pages/settings/security-tab/security-tab.component.js @@ -18,6 +18,8 @@ export default class SecurityTab extends PureComponent { mobileSync: PropTypes.bool, participateInMetaMetrics: PropTypes.bool, setParticipateInMetaMetrics: PropTypes.func, + showIncomingTransactions: PropTypes.bool, + setShowIncomingTransactionsFeatureFlag: PropTypes.func, } renderSeedWords () { @@ -80,6 +82,32 @@ export default class SecurityTab extends PureComponent { ) } + renderIncomingTransactionsOptIn () { + const { t } = this.context + const { showIncomingTransactions, setShowIncomingTransactionsFeatureFlag } = this.props + + return ( +
+
+ { t('showIncomingTransactions') } +
+ { t('showIncomingTransactionsDescription') } +
+
+
+
+ setShowIncomingTransactionsFeatureFlag(!value)} + offLabel={t('off')} + onLabel={t('on')} + /> +
+
+
+ ) + } + renderContent () { const { warning } = this.props @@ -87,6 +115,7 @@ export default class SecurityTab extends PureComponent {
{ warning &&
{ warning }
} { this.renderSeedWords() } + { this.renderIncomingTransactionsOptIn() } { this.renderMetaMetricsOptIn() }
) diff --git a/ui/app/pages/settings/security-tab/security-tab.container.js b/ui/app/pages/settings/security-tab/security-tab.container.js index fe79d86af..35375ebf5 100644 --- a/ui/app/pages/settings/security-tab/security-tab.container.js +++ b/ui/app/pages/settings/security-tab/security-tab.container.js @@ -5,17 +5,22 @@ import { withRouter } from 'react-router-dom' import { displayWarning, revealSeedConfirmation, + setFeatureFlag, setParticipateInMetaMetrics, } from '../../../store/actions' const mapStateToProps = state => { const { appState: { warning }, metamask } = state const { + featureFlags: { + showIncomingTransactions, + } = {}, participateInMetaMetrics, } = metamask return { warning, + showIncomingTransactions, participateInMetaMetrics, } } @@ -25,6 +30,7 @@ const mapDispatchToProps = dispatch => { displayWarning: warning => dispatch(displayWarning(warning)), revealSeedConfirmation: () => dispatch(revealSeedConfirmation()), setParticipateInMetaMetrics: (val) => dispatch(setParticipateInMetaMetrics(val)), + setShowIncomingTransactionsFeatureFlag: shouldShow => dispatch(setFeatureFlag('showIncomingTransactions', shouldShow)), } } diff --git a/ui/app/selectors/transactions.js b/ui/app/selectors/transactions.js index d52170c34..2a6a92ddf 100644 --- a/ui/app/selectors/transactions.js +++ b/ui/app/selectors/transactions.js @@ -16,6 +16,11 @@ import txHelper from '../../lib/tx-helper' export const shapeShiftTxListSelector = state => state.metamask.shapeShiftTxList export const incomingTxListSelector = state => { + const { showIncomingTransactions } = state.metamask.featureFlags + if (!showIncomingTransactions) { + return [] + } + const network = state.metamask.network const selectedAddress = state.metamask.selectedAddress return Object.values(state.metamask.incomingTransactions)