aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/_locales/en/messages.json6
-rw-r--r--package-lock.json5
-rw-r--r--package.json1
-rw-r--r--ui/app/pages/routes/index.js31
-rw-r--r--ui/app/pages/settings/advanced-tab/advanced-tab.component.js45
-rw-r--r--ui/app/pages/settings/advanced-tab/advanced-tab.container.js11
-rw-r--r--ui/app/pages/settings/advanced-tab/tests/advanced-tab-component.test.js44
-rw-r--r--ui/app/pages/settings/advanced-tab/tests/advanced-tab-container.test.js46
-rw-r--r--ui/app/store/actions.js5
9 files changed, 188 insertions, 6 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 184507cbb..7a5f9297c 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -154,6 +154,12 @@
"attributions": {
"message": "Attributions"
},
+ "autoLogoutTimeLimit": {
+ "message": "Auto-Logout Timer (minutes)"
+ },
+ "autoLogoutTimeLimitDescription": {
+ "message": "Set the number of idle time in minutes before Metamask automatically log out"
+ },
"available": {
"message": "Available"
},
diff --git a/package-lock.json b/package-lock.json
index de174b789..e26c37150 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32939,6 +32939,11 @@
"react-icon-base": "2.1.0"
}
},
+ "react-idle-timer": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/react-idle-timer/-/react-idle-timer-4.2.5.tgz",
+ "integrity": "sha512-8B/OwjG8E/DTx1fHYKTpZ4cnCbL9+LOc5I9t8SYe8tbEkP14KChiYg0xPIuyRpO33wUZHcgmQl93CVePaDhmRA=="
+ },
"react-input-autosize": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.1.2.tgz",
diff --git a/package.json b/package.json
index 0ab366c17..8d9da397d 100644
--- a/package.json
+++ b/package.json
@@ -155,6 +155,7 @@
"react-dnd-html5-backend": "^7.4.4",
"react-dom": "^15.6.2",
"react-hyperscript": "^3.0.0",
+ "react-idle-timer": "^4.2.5",
"react-inspector": "^2.3.0",
"react-markdown": "^3.0.0",
"react-media": "^1.8.0",
diff --git a/ui/app/pages/routes/index.js b/ui/app/pages/routes/index.js
index e38a6d6ce..7ff741405 100644
--- a/ui/app/pages/routes/index.js
+++ b/ui/app/pages/routes/index.js
@@ -3,9 +3,10 @@ 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 from '../../store/actions'
+import actions, {hideSidebar, hideWarning, lockMetamask} from '../../store/actions'
import log from 'loglevel'
-import { getMetaMaskAccounts, getNetworkIdentifier } from '../../selectors/selectors'
+import IdleTimer from 'react-idle-timer'
+import {getMetaMaskAccounts, getNetworkIdentifier, preferencesSelector} from '../../selectors/selectors'
// init
import FirstTimeFlow from '../first-time-flow'
@@ -98,7 +99,9 @@ class Routes extends Component {
}
renderRoutes () {
- return (
+ const { autoLogoutTimeLimit, lockMetamask } = this.props
+
+ const routes = (
<Switch>
<Route path={LOCK_ROUTE} component={Lock} exact />
<Route path={INITIALIZE_ROUTE} component={FirstTimeFlow} />
@@ -116,6 +119,19 @@ class Routes extends Component {
<Authenticated path={DEFAULT_ROUTE} component={Home} exact />
</Switch>
)
+
+ if (autoLogoutTimeLimit > 0) {
+ return (
+ <IdleTimer
+ onIdle={lockMetamask}
+ timeout={autoLogoutTimeLimit * 1000 * 60}
+ >
+ {routes}
+ </IdleTimer>
+ )
+ }
+
+ return routes
}
onInitializationUnlockPage () {
@@ -322,6 +338,7 @@ Routes.propTypes = {
networkDropdownOpen: PropTypes.bool,
showNetworkDropdown: PropTypes.func,
hideNetworkDropdown: PropTypes.func,
+ lockMetamask: PropTypes.func,
history: PropTypes.object,
location: PropTypes.object,
dispatch: PropTypes.func,
@@ -344,6 +361,7 @@ Routes.propTypes = {
t: PropTypes.func,
providerId: PropTypes.string,
providerRequests: PropTypes.array,
+ autoLogoutTimeLimit: PropTypes.number,
}
function mapStateToProps (state) {
@@ -358,6 +376,7 @@ function mapStateToProps (state) {
} = appState
const accounts = getMetaMaskAccounts(state)
+ const { autoLogoutTimeLimit = 0 } = preferencesSelector(state)
const {
identities,
@@ -409,6 +428,7 @@ function mapStateToProps (state) {
Qr: state.appState.Qr,
welcomeScreenSeen: state.metamask.welcomeScreenSeen,
providerId: getNetworkIdentifier(state),
+ autoLogoutTimeLimit,
// state needed to get account dropdown temporarily rendering from app bar
identities,
@@ -427,6 +447,11 @@ function mapDispatchToProps (dispatch, ownProps) {
setCurrentCurrencyToUSD: () => dispatch(actions.setCurrentCurrency('usd')),
toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()),
setMouseUserState: (isMouseUser) => dispatch(actions.setMouseUserState(isMouseUser)),
+ lockMetamask: () => {
+ dispatch(lockMetamask())
+ dispatch(hideWarning())
+ dispatch(hideSidebar())
+ },
}
}
diff --git a/ui/app/pages/settings/advanced-tab/advanced-tab.component.js b/ui/app/pages/settings/advanced-tab/advanced-tab.component.js
index 14b9daae6..8d70cd2df 100644
--- a/ui/app/pages/settings/advanced-tab/advanced-tab.component.js
+++ b/ui/app/pages/settings/advanced-tab/advanced-tab.component.js
@@ -24,6 +24,8 @@ export default class AdvancedTab extends PureComponent {
setAdvancedInlineGasFeatureFlag: PropTypes.func,
advancedInlineGas: PropTypes.bool,
showFiatInTestnets: PropTypes.bool,
+ autoLogoutTimeLimit: PropTypes.number,
+ setAutoLogoutTimeLimit: PropTypes.func.isRequired,
setShowFiatConversionOnTestnetsPreference: PropTypes.func.isRequired,
}
@@ -355,6 +357,48 @@ export default class AdvancedTab extends PureComponent {
)
}
+ renderAutoLogoutTimeLimit () {
+ const { t } = this.context
+ const {
+ autoLogoutTimeLimit,
+ setAutoLogoutTimeLimit,
+ } = this.props
+
+ return (
+ <div className="settings-page__content-row">
+ <div className="settings-page__content-item">
+ <span>{ t('autoLogoutTimeLimit') }</span>
+ <div className="settings-page__content-description">
+ { t('autoLogoutTimeLimitDescription') }
+ </div>
+ </div>
+ <div className="settings-page__content-item">
+ <div className="settings-page__content-item-col">
+ <TextField
+ type="number"
+ id="autoTimeout"
+ placeholder="5"
+ value={this.state.autoLogoutTimeLimit}
+ defaultValue={autoLogoutTimeLimit}
+ onChange={e => this.setState({ autoLogoutTimeLimit: Math.max(Number(e.target.value), 0) })}
+ fullWidth
+ margin="dense"
+ min={0}
+ />
+ <button
+ className="button btn-primary settings-tab__rpc-save-button"
+ onClick={() => {
+ setAutoLogoutTimeLimit(this.state.autoLogoutTimeLimit)
+ }}
+ >
+ { t('save') }
+ </button>
+ </div>
+ </div>
+ </div>
+ )
+ }
+
renderContent () {
const { warning } = this.props
@@ -368,6 +412,7 @@ export default class AdvancedTab extends PureComponent {
{ this.renderAdvancedGasInputInline() }
{ this.renderHexDataOptIn() }
{ this.renderShowConversionInTestnets() }
+ { this.renderAutoLogoutTimeLimit() }
</div>
)
}
diff --git a/ui/app/pages/settings/advanced-tab/advanced-tab.container.js b/ui/app/pages/settings/advanced-tab/advanced-tab.container.js
index 69d7e07e6..bcac55f5e 100644
--- a/ui/app/pages/settings/advanced-tab/advanced-tab.container.js
+++ b/ui/app/pages/settings/advanced-tab/advanced-tab.container.js
@@ -8,10 +8,11 @@ import {
setFeatureFlag,
showModal,
setShowFiatConversionOnTestnetsPreference,
+ setAutoLogoutTimeLimit,
} from '../../../store/actions'
import {preferencesSelector} from '../../../selectors/selectors'
-const mapStateToProps = state => {
+export const mapStateToProps = state => {
const { appState: { warning }, metamask } = state
const {
featureFlags: {
@@ -19,17 +20,18 @@ const mapStateToProps = state => {
advancedInlineGas,
} = {},
} = metamask
- const { showFiatInTestnets } = preferencesSelector(state)
+ const { showFiatInTestnets, autoLogoutTimeLimit } = preferencesSelector(state)
return {
warning,
sendHexData,
advancedInlineGas,
showFiatInTestnets,
+ autoLogoutTimeLimit,
}
}
-const mapDispatchToProps = dispatch => {
+export const mapDispatchToProps = dispatch => {
return {
setHexDataFeatureFlag: shouldShow => dispatch(setFeatureFlag('sendHexData', shouldShow)),
setRpcTarget: (newRpc, chainId, ticker, nickname) => dispatch(updateAndSetCustomRpc(newRpc, chainId, ticker, nickname)),
@@ -39,6 +41,9 @@ const mapDispatchToProps = dispatch => {
setShowFiatConversionOnTestnetsPreference: value => {
return dispatch(setShowFiatConversionOnTestnetsPreference(value))
},
+ setAutoLogoutTimeLimit: value => {
+ return dispatch(setAutoLogoutTimeLimit(value))
+ },
}
}
diff --git a/ui/app/pages/settings/advanced-tab/tests/advanced-tab-component.test.js b/ui/app/pages/settings/advanced-tab/tests/advanced-tab-component.test.js
new file mode 100644
index 000000000..f81329533
--- /dev/null
+++ b/ui/app/pages/settings/advanced-tab/tests/advanced-tab-component.test.js
@@ -0,0 +1,44 @@
+import React from 'react'
+import assert from 'assert'
+import sinon from 'sinon'
+import { shallow } from 'enzyme'
+import AdvancedTab from '../advanced-tab.component'
+import TextField from '../../../../components/ui/text-field'
+
+describe('AdvancedTab Component', () => {
+ it('should render correctly', () => {
+ const root = shallow(
+ <AdvancedTab />,
+ {
+ context: {
+ t: s => `_${s}`,
+ },
+ }
+ )
+
+ assert.equal(root.find('.settings-page__content-row').length, 8)
+ })
+
+ it('should update autoLogoutTimeLimit', () => {
+ const setAutoLogoutTimeLimitSpy = sinon.spy()
+ const root = shallow(
+ <AdvancedTab
+ setAutoLogoutTimeLimit={setAutoLogoutTimeLimitSpy}
+ />,
+ {
+ context: {
+ t: s => `_${s}`,
+ },
+ }
+ )
+
+ const autoTimeout = root.find('.settings-page__content-row').last()
+ const textField = autoTimeout.find(TextField)
+
+ textField.props().onChange({ target: { value: 1440 } })
+ assert.equal(root.state().autoLogoutTimeLimit, 1440)
+
+ autoTimeout.find('button').simulate('click')
+ assert.equal(setAutoLogoutTimeLimitSpy.args[0][0], 1440)
+ })
+})
diff --git a/ui/app/pages/settings/advanced-tab/tests/advanced-tab-container.test.js b/ui/app/pages/settings/advanced-tab/tests/advanced-tab-container.test.js
new file mode 100644
index 000000000..62122073d
--- /dev/null
+++ b/ui/app/pages/settings/advanced-tab/tests/advanced-tab-container.test.js
@@ -0,0 +1,46 @@
+import assert from 'assert'
+import { mapStateToProps, mapDispatchToProps } from '../advanced-tab.container'
+
+const defaultState = {
+ appState: {
+ warning: null,
+ },
+ metamask: {
+ featureFlags: {
+ sendHexData: false,
+ advancedInlineGas: false,
+ },
+ preferences: {
+ autoLogoutTimeLimit: 0,
+ showFiatInTestnets: false,
+ useNativeCurrencyAsPrimaryCurrency: true,
+ },
+ },
+}
+
+describe('AdvancedTab Container', () => {
+ it('should map state to props correctly', () => {
+ const props = mapStateToProps(defaultState)
+ const expected = {
+ warning: null,
+ sendHexData: false,
+ advancedInlineGas: false,
+ showFiatInTestnets: false,
+ autoLogoutTimeLimit: 0,
+ }
+
+ assert.deepEqual(props, expected)
+ })
+
+ it('should map dispatch to props correctly', () => {
+ const props = mapDispatchToProps(() => 'mockDispatch')
+
+ assert.ok(typeof props.setHexDataFeatureFlag === 'function')
+ assert.ok(typeof props.setRpcTarget === 'function')
+ assert.ok(typeof props.displayWarning === 'function')
+ assert.ok(typeof props.showResetAccountConfirmationModal === 'function')
+ assert.ok(typeof props.setAdvancedInlineGasFeatureFlag === 'function')
+ assert.ok(typeof props.setShowFiatConversionOnTestnetsPreference === 'function')
+ assert.ok(typeof props.setAutoLogoutTimeLimit === 'function')
+ })
+})
diff --git a/ui/app/store/actions.js b/ui/app/store/actions.js
index e7b855119..f2a9ed08f 100644
--- a/ui/app/store/actions.js
+++ b/ui/app/store/actions.js
@@ -316,6 +316,7 @@ var actions = {
UPDATE_PREFERENCES: 'UPDATE_PREFERENCES',
setUseNativeCurrencyAsPrimaryCurrencyPreference,
setShowFiatConversionOnTestnetsPreference,
+ setAutoLogoutTimeLimit,
// Migration of users to new UI
setCompletedUiMigration,
@@ -2439,6 +2440,10 @@ function setShowFiatConversionOnTestnetsPreference (value) {
return setPreference('showFiatInTestnets', value)
}
+function setAutoLogoutTimeLimit (value) {
+ return setPreference('autoLogoutTimeLimit', value)
+}
+
function setCompletedOnboarding () {
return async dispatch => {
dispatch(actions.showLoadingIndication())