From 28c4001f5269eb170f14bd2829698ee402b2cfbf Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Mon, 13 May 2019 13:46:09 -0230 Subject: Update auto-logout to recognize idle time in background (#6593) * Fix wording of autoLogoutTimeLimitDescription * AppStateController and update auto-logout functionality --- app/_locales/en/messages.json | 2 +- app/scripts/controllers/app-state.js | 73 ++++++++++++++++++++++++++++++++++++ app/scripts/metamask-controller.js | 12 ++++++ ui/app/pages/routes/index.js | 17 +++------ ui/app/store/actions.js | 14 +++++++ 5 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 app/scripts/controllers/app-state.js diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 254bfdfb9..bef278f79 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -161,7 +161,7 @@ "message": "Auto-Logout Timer (minutes)" }, "autoLogoutTimeLimitDescription": { - "message": "Set the number of idle time in minutes before Metamask automatically log out" + "message": "Set the idle time in minutes before MetaMask will automatically log out" }, "available": { "message": "Available" diff --git a/app/scripts/controllers/app-state.js b/app/scripts/controllers/app-state.js new file mode 100644 index 000000000..9533fd458 --- /dev/null +++ b/app/scripts/controllers/app-state.js @@ -0,0 +1,73 @@ +const ObservableStore = require('obs-store') +const extend = require('xtend') + +class AppStateController { + /** + * @constructor + * @param opts + */ + constructor (opts = {}) { + const {initState, onInactiveTimeout, preferencesStore} = opts + const {preferences} = preferencesStore.getState() + + this.onInactiveTimeout = onInactiveTimeout || (() => {}) + this.store = new ObservableStore(extend({ + timeoutMinutes: 0, + }, initState)) + this.timer = null + + preferencesStore.subscribe(state => { + this._setInactiveTimeout(state.preferences.autoLogoutTimeLimit) + }) + + this._setInactiveTimeout(preferences.autoLogoutTimeLimit) + } + + /** + * Sets the last active time to the current time + * @return {void} + */ + setLastActiveTime () { + this._resetTimer() + } + + /** + * Sets the inactive timeout for the app + * @param {number} timeoutMinutes the inactive timeout in minutes + * @return {void} + * @private + */ + _setInactiveTimeout (timeoutMinutes) { + this.store.putState({ + timeoutMinutes, + }) + + this._resetTimer() + } + + /** + * Resets the internal inactive timer + * + * If the {@code timeoutMinutes} state is falsy (i.e., zero) then a new + * timer will not be created. + * + * @return {void} + * @private + */ + _resetTimer () { + const {timeoutMinutes} = this.store.getState() + + if (this.timer) { + clearTimeout(this.timer) + } + + if (!timeoutMinutes) { + return + } + + this.timer = setTimeout(() => this.onInactiveTimeout(), timeoutMinutes * 60 * 1000) + } +} + +module.exports = AppStateController + diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index bdfff9827..07054f84b 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -25,6 +25,7 @@ const {setupMultiplex} = require('./lib/stream-utils.js') const KeyringController = require('eth-keyring-controller') const NetworkController = require('./controllers/network') const PreferencesController = require('./controllers/preferences') +const AppStateController = require('./controllers/app-state') const CurrencyController = require('./controllers/currency') const ShapeShiftController = require('./controllers/shapeshift') const InfuraController = require('./controllers/infura') @@ -101,6 +102,12 @@ module.exports = class MetamaskController extends EventEmitter { network: this.networkController, }) + // app-state controller + this.appStateController = new AppStateController({ + preferencesStore: this.preferencesController.store, + onInactiveTimeout: () => this.setLocked(), + }) + // currency controller this.currencyController = new CurrencyController({ initState: initState.CurrencyController, @@ -252,6 +259,7 @@ module.exports = class MetamaskController extends EventEmitter { }) this.store.updateStructure({ + AppStateController: this.appStateController.store, TransactionController: this.txController.store, KeyringController: this.keyringController.store, PreferencesController: this.preferencesController.store, @@ -264,6 +272,7 @@ module.exports = class MetamaskController extends EventEmitter { }) this.memStore = new ComposableObservableStore(null, { + AppStateController: this.appStateController.store, NetworkController: this.networkController.store, AccountTracker: this.accountTracker.store, TxController: this.txController.memStore, @@ -462,6 +471,9 @@ module.exports = class MetamaskController extends EventEmitter { // AddressController setAddressBook: this.addressBookController.set.bind(this.addressBookController), + // AppStateController + setLastActiveTime: nodeify(this.appStateController.setLastActiveTime, this.appStateController), + // KeyringController setLocked: nodeify(this.setLocked, this), createNewVaultAndKeychain: nodeify(this.createNewVaultAndKeychain, this), diff --git a/ui/app/pages/routes/index.js b/ui/app/pages/routes/index.js index 9c30da086..9eeac2da2 100644 --- a/ui/app/pages/routes/index.js +++ b/ui/app/pages/routes/index.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types' import { connect } from 'react-redux' import { Route, Switch, withRouter, matchPath } from 'react-router-dom' import { compose } from 'recompose' -import actions, {hideSidebar, hideWarning, lockMetamask} from '../../store/actions' +import actions from '../../store/actions' import log from 'loglevel' import IdleTimer from 'react-idle-timer' import {getMetaMaskAccounts, getNetworkIdentifier, preferencesSelector} from '../../selectors/selectors' @@ -99,7 +99,7 @@ class Routes extends Component { } renderRoutes () { - const { autoLogoutTimeLimit, lockMetamask } = this.props + const { autoLogoutTimeLimit, setLastActiveTime } = this.props const routes = ( @@ -122,10 +122,7 @@ class Routes extends Component { if (autoLogoutTimeLimit > 0) { return ( - + {routes} ) @@ -338,7 +335,7 @@ Routes.propTypes = { networkDropdownOpen: PropTypes.bool, showNetworkDropdown: PropTypes.func, hideNetworkDropdown: PropTypes.func, - lockMetamask: PropTypes.func, + setLastActiveTime: PropTypes.func, history: PropTypes.object, location: PropTypes.object, dispatch: PropTypes.func, @@ -447,11 +444,7 @@ function mapDispatchToProps (dispatch) { setCurrentCurrencyToUSD: () => dispatch(actions.setCurrentCurrency('usd')), toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()), setMouseUserState: (isMouseUser) => dispatch(actions.setMouseUserState(isMouseUser)), - lockMetamask: () => { - dispatch(lockMetamask()) - dispatch(hideWarning()) - dispatch(hideSidebar()) - }, + setLastActiveTime: () => dispatch(actions.setLastActiveTime()), } } diff --git a/ui/app/store/actions.js b/ui/app/store/actions.js index 7d45f0932..7f6cbea1f 100644 --- a/ui/app/store/actions.js +++ b/ui/app/store/actions.js @@ -356,6 +356,10 @@ var actions = { setSelectedSettingsRpcUrl, SET_NETWORKS_TAB_ADD_MODE: 'SET_NETWORKS_TAB_ADD_MODE', setNetworksTabAddMode, + + // AppStateController-related actions + SET_LAST_ACTIVE_TIME: 'SET_LAST_ACTIVE_TIME', + setLastActiveTime, } module.exports = actions @@ -2760,3 +2764,13 @@ function setNetworksTabAddMode (isInAddMode) { value: isInAddMode, } } + +function setLastActiveTime () { + return (dispatch) => { + background.setLastActiveTime((err) => { + if (err) { + return dispatch(actions.displayWarning(err.message)) + } + }) + } +} -- cgit v1.2.3