GithubHelp home page GithubHelp logo

noderaider / redux-idle-monitor Goto Github PK

View Code? Open in Web Editor NEW
105.0 3.0 9.0 3.09 MB

A Redux component to schedule events at stages of user idleness across multiple browser tabs.

Home Page: https://noderaider.github.io/redux-idle-monitor/

License: MIT License

JavaScript 97.58% HTML 2.42%
idle-statuses redux middleware dispatch user-idleness transition

redux-idle-monitor's Introduction

NPM

A React component and Redux connector to add functionality to invoke events at various stages of idleness during a users session.

NPM

Now supports realtime synchronization across tabs when user is active. Tested IE10+ / Edge, Chrome, FireFox

npm i -S redux-idle-monitor

Can be used standalone with redux or in tandem with react-redux-idle-monitor to track whether a user is idle and progress them through a set of idle status actions with varying timeouts.

See a working demo in a real project at redux-webpack-boilerplate


How it works

redux-idle-monitor works similiar to other redux libraries (e.g. redux-form) except that it exports a configure function that accepts options from the library consumer and returns an object with middleware, reducer, and actions keys. The input options allow redux-idle-monitor to configure the idle statuses that will occur at varying times of idleness within your app as well as actions to dispatch when your users transition to an active status, or any one of your configured idle statuses.

middleware - The middleware that gets returned from configure handles listening for and scheduling idle status events, and enabling and disabling the detection of user activity. It should be added to your redux middleware array in both development and production versions. redux-idle-monitor requires you to be using thunk-middleware at this time (I'd like to add saga support soon). When process.env.NODE_ENV !== 'production' additional assertions are made throughout redux-idle-monitor that will be removed in your production build if you use UglifyJS or similiar mangler.

reducer - The reducer that gets returned from configure should be imported into your reducer tree as a top level 'idle' node (the same way that you would import redux-form or react-redux-router). At this time, the idle node cannot be changed, but if its a common request, it can be modified to support other arrangements easily.

actions - The actions object that is returned from configure contains start and stop actions that can be dispatched from anywhere in your app that has access to your stores dispatch function. redux-idle-monitor does not start monitoring user activity until you dispatch the start action. Good places to run these might be in the same area that your app authorizes users (start monitoring whether the user is idle when authorized and stop when the user is logged out).


Configuration

The best way to configure redux-idle-monitor and then use the configured middleware, reducer, and actions within your app are to create a redux-idle-monitor component directory in the same area of your app that you configure your redux store. For this example, I've put it in src/state/components/redux-idle-monitor. Create an index.js file to house your configuration:

src/state/components/redux-idle-monitor/index.js

import configure from 'redux-idle-monitor'
import { IDLE_STATUSES } from './constants'
import { idleStatusDelay, activeStatusAction, idleStatusAction } from './actions'

// These are the default events that will trigger user active status but can be customized if provided.
const activeEvents =  [ 'mousemove', 'keydown', 'wheel', 'DOMMouseScroll', 'mouseWheel', 'mousedown', 'touchstart', 'touchmove', 'MSPointerDown', 'MSPointerMove' ]

const opts =  { appName: 'todo-app'
              , IDLE_STATUSES
              , idleStatusDelay
              , activeStatusAction
              , idleStatusAction
              , activeEvents
              }

const { middleware, reducer, actions } = configure(opts)
export { middleware, reducer, actions }
Configurable Constants

As shown above this is importing an IDLE_STATUSES constant from constants.js. IDLE_STATUSES are a simple array of string constant statuses that are used for the idleStatus property of redux idle state. The initial value for idleStatus will always be ACTIVE and from there it will progress through your configured IDLE_STATUSES in order until it reaches the final one where it will progress no further. Here is an example of what the constants.js could look like:

src/state/components/react-idle-monitor/constants.js

export const IDLESTATUS_AWAY = 'AWAY'
export const IDLESTATUS_INACTIVE = 'INACTIVE'
export const IDLESTATUS_EXPIRED = 'EXPIRED'

export const IDLE_STATUSES = [IDLESTATUS_AWAY, IDLESTATUS_INACTIVE, IDLESTATUS_EXPIRED]
Configurable Actions

In addition, we are also importing idleStatusDelay, activeStatusAction, and idleStatusAction from actions.js within the same directory.

src/state/components/react-idle-monitor/actions.js

import { IDLESTATUS_AWAY, IDLESTATUS_INACTIVE, IDLESTATUS_EXPIRED } from './constants'

//...

export const idleStatusDelay = idleStatus => (dispatch, getState) => {
  if(idleStatus === IDLESTATUS_AWAY)
    return 20000 // User becomes away after 20 seconds inactivity
  if(idleStatus === IDLESTATUS_INACTIVE)
    return getInactiveDelayFromDB() // Call database to look up the users delay time
  if(idleStatus === IDLESTATUS_EXPIRED)
    return 60000 // Log them out after another minute after they enter the inactive status
}

export const activeStatusAction = (dispatch, getState) => alert('welcome back!')

export const idleStatusAction = idleStatus => (dispatch, getState) => {
  if(idleStatus === IDLESTATUS_AWAY)
    console.info('user is away...')
  if(idleStatus === IDLESTATUS_INACTIVE)
    alert('You still there or what??')
  if(idleStatus === IDLESTATUS_EXPIRED)
    dispatch(logout())
}

idleStatusDelay: (idleStatus) => (dispatch, getState) => delay

where

typeof delay === 'number'

  • accepts idleStatus string argument and returns a thunk action that will return the delay for any idle status that you've configured.
  • gets dispatched by idle middleware to get the number of milliseconds of user idleness that must occur before transitioning into the specified idle status.
  • if user activity is detected the user will transition back to the ACTIVE state.
  • will throw if the thunk action does not return a number type for any idleStatus specified in the IDLE_STATUSES array.

activeStatusAction: (dispatch, getState) => void

  • returns logic to be executed in your app whenever the user enters the ACTIVE status.
  • dispatched by idle middleware only when the user has transitioned to one of your idle statuses and then back into the ACTIVE status.

idleStatusAction: (idleStatus) => (dispatch, getState) => void

  • accepts idleStatus string argument and returns a thunk action to run app logic that should occur when user enters one of your configured idle statuses.
  • should contain logic that handles every configured idle status that was passed in the IDLE_STATUSES array when configured.
  • run logic to show the user alerts, screensavers, auto-logout etc. from here.

Integration

Now you must import import the configured reducer into your top level combineReducers as the 'idle' node like so (api and errors are two of your other top level reducers in this example).

src/state/reducers/index.js

import { combineReducers } from 'redux'
import { api } from './api'
import { errors } from './errors'
import { reducer as form } from 'redux-form'
import { reducer as idle } from '../components/redux-idle-monitor'

const rootReducer = combineReducers({ api
                                    , errors
                                    , form
                                    , idle
                                    })
export default rootReducer

The last step is to import the idle middleware into your store and dispatch the start action when you want to start monitoring user idleness.

src/state/store/configureStore.js

import { createStore, applyMiddleware, compose } from 'redux'
import rootReducer from '../reducers'
import DevTools from '../../containers/DevTools'
import { middleware as idleMiddleware } from '../components/redux-idle-monitor'
import { actions as idleActions } from '../comonents/redux-idle-monitor'

import { thunk, readyStatePromise, createLogger, crashReporter } from 'redux-middleware'

const logger = createLogger()

const composeStore = compose( applyMiddleware(thunk, idleMiddleware, readyStatePromise, logger, crashReporter)
                            , DevTools.instrument()
                            )(createStore)

export default function configureStore() {
  const store = composeStore(rootReducer)

  // Will start the idle monitoring when the user logs in, and stop it if the user is signed out.
  store.subscribe(() => {
    let previousIsAuthorized = currentIsAuthorized
    let state = store.getState()
    // calls a function that selects whether the user is authorized from the current state
    currentIsAuthorized = selectIsAuthorized(state)

    if(currentIsAuthorized !== previousIsAuthorized)
      store.dispatch((currentIsAuthorized ? idleActions.start : idleActions.stop)())
  })
  return store
}

You're done. Please open an issue if there are any features that you would like to see added.

redux-idle-monitor's People

Contributors

cchamberlain avatar davidgilbertson avatar pinwheeler avatar tomchentw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

redux-idle-monitor's Issues

Consider switching chai to invariant

Hi,

I'm in the process reviewing the bundle size of our web app and I found that redux-idle-monitor is taking chai as a dependency. Wouldn't it be better if we switch to a more lightweight, production-ready package like invariant? What do you think?

I could help a PR to switch it over.

Thanks

Disable console info messages

Is there any way to disable the console.info messages such as idle | info: 'activity detection starting' or idle | info: 'Setting local tab to active'?

Initial state is idle on load

EDIT: About 5 mins after making opening this ticket, I realised I'd misinterpreted the initial state. Where I thought it was starting in the first inactive state, it turned out it was actually starting in the last inactive state. i.e. we seem to be assuming the user is already inactive at the point that monitoring is started. I've rewritten the ticket to reflect this.

Hi,

I've noticed that redux-idle-monitor seems to start with status set to the final inactive state, rather than the active state, even if the user was moving their cursor immediately before the idleMonitorActions.start() action was dispatched. I think this is a bug.

I have two inactive states (inactive and away) and expect the states to go active -> inactive -> away.

export const IDLE_STATUS_INACTIVE = 'INACTIVE';
export const IDLE_STATUS_AWAY = 'AWAY';

export const IDLE_STATUSES = [IDLE_STATUS_INACTIVE, IDLE_STATUS_AWAY];

If I never do anything after the idleMonitorActions.start() action is dispatched, the status starts as away, then updates to inactive, then goes back to away. I would expect the initial state to be active, since it seems a safe assumption that the user is active up until the point that we start monitoring. In my case, I start monitoring immediately after login.

If this isn't a bug, is there a safe way to configure the initial state before starting monitoring? (By safe, I mean something that doesn't risk interfering with the monitoring component. I'm not sure how redux-idle-monitor handles state updates and whether me changing the state directly might break things).

Expected initial state

idleStatus: "ACTIVE"
isDetectionRunning: true
isIdle: false
isRunning: true
lastActive: 1520246760457
lastEvent: {x: 501, y: 238, type: "mousemove"}

Actual initial state

idleStatus: "AWAY"
isDetectionRunning: true
isIdle: true
isRunning: true
lastActive: 1520246760457
lastEvent: {x: 501, y: 238, type: "mousemove"}

I got this initial state from my LocalStorage immediately after the idleMonitorActions.start() action has completed by putting a break point into my only component that depends on idle state changes (using createConnector from react-redux-idle-monitor.

Upgrade to Redux 4.1.0

I really need this to use your project in my project.

I am not sure how hard it is to do this.

  1. Will you accept a PR for this?
  2. Can I start working on a PR?
  3. Are there any other instructions other than these?

Thank you.

Upgrade source code to TypeScript

Think this library could get a lot of benefit from refactoring the core to TypeScript and compiling to the same targets. Targeting 1.0 release.

Object.assign error on IE11

On IE11 I get an error when I use redux-idle-monitor Object doesn't support property or method 'assign' I of coarse use Object assign in my redux app in other places with no issues, but when I wire up redux-idle-monitor IE11 has this error and does not load the page.

idle monitor state should have idle value

I've set it up as described, with a few tweaks as i'm using https://github.com/davezuko/react-redux-starter-kit
I get this error continuously:
image

And i don't see 'idle' as a key in my state.
The library seems to react to state changes however.

I also get No reducer provided for key "idle"
at start

  const middleware = [thunk, idleMiddleware]`
....
const store = createStore(
    makeRootReducer(),
    initialState,
    composeEnhancers(
      applyMiddleware(...middleware),
      ...enhancers
    )
  )

reducers.js

import { combineReducers } from 'redux'
import locationReducer from './location'
import { reducer as idle } from 'redux-idle-monitor'

export const makeRootReducer = (asyncReducers) => {
  return combineReducers({
    location: locationReducer,
    idle: idle,
    ...asyncReducers
  })
}

Get lib issues while trying to use the module

Getting following error while trying to use this package in my project:-

ERROR in ./~/redux-idle-monitor/lib/context.js
Module not found: Error: Cannot resolve module 'babel-runtime/core-js/json/stringify' in /Users/aragorn/taxo/node_modules/redux-idle-monitor/lib
 @ ./~/redux-idle-monitor/lib/context.js 7:17-64

ERROR in ./~/redux-idle-monitor/lib/context.js
Module not found: Error: Cannot resolve module 'babel-runtime/core-js/object/assign' in /Users/aragorn/taxo/node_modules/redux-idle-monitor/lib
 @ ./~/redux-idle-monitor/lib/context.js 11:14-60

ERROR in ./~/redux-idle-monitor/lib/reducer.js
Module not found: Error: Cannot resolve module 'babel-runtime/core-js/object/assign' in /Users/aragorn/taxo/node_modules/redux-idle-monitor/lib
 @ ./~/redux-idle-monitor/lib/reducer.js 8:14-60

ERROR in ./~/redux-idle-monitor/lib/middleware.js
Module not found: Error: Cannot resolve module 'babel-runtime/helpers/toConsumableArray' in /Users/aragorn/taxo/node_modules/redux-idle-monitor/lib
 @ ./~/redux-idle-monitor/lib/middleware.js 8:26-76

ERROR in ./~/redux-idle-monitor/lib/actions.js
Module not found: Error: Cannot resolve module 'babel-runtime/helpers/typeof' in /Users/aragorn/taxo/node_modules/redux-idle-monitor/lib
 @ ./~/redux-idle-monitor/lib/actions.js 8:15-54

Also looks like its quite heavy, my app size increase by ~0.3mb after adding this. Can you please help me on this?

Enable cross-tab support

Are there any actions you have to take to enable cross-tab support? I have everything setup and it works in one tab fine, but opening a second tab they seem to not be aware of each other's events to reset the idle expiration. One will expire and the other is still inactive.

Package is too heavy

Per issue on #6, there is a request that the package be made smaller.

Opening this issue to track.

How to add tests?

We use redux-idle-monitor and I want to ensure that it works as expected for us in production--my idea was to do this in a front-end test by manually dispatching a NEXT_IDLE_STATE or GOTO_IDLE_STATE action. I can dispatch the actions but NEXT_IDLE_STATE starts a timer, which is not what I want, and GOTO_IDLE_STATE updates the state but never triggers my idleStatusAction.

What would you recommend?

Uncaught (in promise) Error: idle status delay must be a number type for idleStatus === 'AWAY'

Hi,
I setup redux-idle-monitor as described in the readme. When running it, the above error appears in the console.
With developer tools I digged into this:

var delay = dispatch(idleStatusDelay(idleStatus));

However, the returned type is a Promise, not a number. What's wrong here? My action looks:

export const idleStatusDelay = idleStatus => (dispatch, getState) => {
  const exp_delta = getExpirationDelta();
  if(idleStatus === IDLESTATUS_AWAY)
    return 20000;
  if(idleStatus === IDLESTATUS_INACTIVE)
    return exp_delta;
  if(idleStatus === IDLESTATUS_EXPIRED)
    return exp_delta + 1000; // Log them out after another minute after they enter the inactive status
};

Also store configuration is as you described:

import { middleware as idleMiddleware, actions as idleActions } from "../components/redux-idle-monitor"


export default function configureStore(initialState) {
  const middewares = [
    // Add other middleware on this line...
    apiMiddleware,
    // Redux middleware that spits an error on you when you try to mutate your state either inside a dispatch or between dispatches.
    reduxImmutableStateInvariant(),

    // thunk middleware can also accept an extra argument to be passed to each thunk action
    // https://github.com/gaearon/redux-thunk#injecting-a-custom-argument
    thunkMiddleware,
    idleMiddleware
  ];

  const store = createStore(rootReducer, initialState, compose(
    applyMiddleware(...middewares),
    window.devToolsExtension ? window.devToolsExtension() : f => f // add support for Redux dev tools
    )
  );

  let currentIsAuthorized = null;

  store.subscribe(() => {
    let previousIsAuthorized = currentIsAuthorized;
    let state = store.getState();
    currentIsAuthorized = state.auth.get("isAuthenticated");
    if(currentIsAuthorized !== previousIsAuthorized)
      store.dispatch((currentIsAuthorized ? idleActions.start : idleActions.stop)())
  });

Support for Sagas

Thanks for this library. I know you are still about to support saga but have you had any progress with it? I am keen to help. Are there pointers you can give where to start looking and be able to support Sagas?

Object doesn't support property or method 'save', 'load', 'remove' on IE11

webpack:///node_modules/localsync/lib/cookiesync.js.

Installed yesterday and i'm getting these three errors on IE11. Works fine in latest version of Chrome and Mozilla.
I currently am on 0.9.1 version of redux-idle-monitor, and 1.6.2 of localsync. I also have babelpolyfill.js

Am i missing some polyfill or something?

Cheers

Cannot use library behind corporate proxy

Hello,

I'm using npm behind corporate proxy, thus I do not have access to gihub. I can see that some redux-idle-monitor are downloaded directly from github (please check the console output below). Would that be possible to fix dependencies to versions already released to https://registry.npmjs.org/

$ npm install
[email protected] C:\git\farefocus-app\react
-- [email protected] -- [email protected]
+-- [email protected]
| -- [email protected] (git://github.com/noderaider/react-cookie.git#eb419258badf89893595119d73b88ad6cd53e089) -- [email protected]
`-- [email protected] (git://github.com/noderaider/local-storage.git#b9725b0fc77faabc737ba7c6ee57d343afa95102)

BR,
Marek Adamek

Deactivate cross-tab synchronization?

I am actually using the package to determine polling intervals for my site, and for that use-case, synchronizing the activity state across browser intervals is actually the opposite of what I want, since I don't want the client to request new data for tabs that they aren't active in.

Is there any way to deactivate the cross-tab activity detection?

Consant errors from invariant dependenvy in DEV mode

redux-idle-monitor: 0.5.3
invariant: 2.2.2

Hello,

In dev mode (NODE_ENV !== 'production') invariant library throws errors every time a monitored action is fired. The error is as follows:

browser.js:26 Uncaught Error: invariant requires an error message argument
at invariant (browser.js:26)
at selectIdleState (actions.js:69)
at actions.js:48
at HTMLDocument.onActivity (actions.js:115)

and is caused by the line:
(0, _invariant2.default)((0, _typeof3.default)(state.idle) === 'object');

The problem for this is that second parameter for invariant function (format) is not passed. In the result it fails on the condition:

if (format === undefined) {
throw new Error('invariant requires an error message argument');
}

Could you confirm whether or not it'll be fixed?

Thanks,
Marek

Support Immutable.js

I am using the lib wit redux-immutable store. My state is Immutable.Map(). That causes problem with action.js selectIdleState() function, since the lib is retrieving idle state with state.idle instead of state.get('idle'). That causes error in the console like 'Uncaught Error: idle monitor state should have idle value'. Basides above, the lib works great, I really appreciate your work.

My proposition is to add something like stateTransformer function, configurable with opts. If present, the user will have an option to define how his state is going to be transformed into expected pure JS Object. Like this:
const opts = { ... idleStatusAction, activeEvents, stateTransformer: state => state.toJS() // assuming its Immutable.js }

What do you think? Maybe there is an other way to achieve this?

redux-blueprints and redux-mux have Chai as a dependency

It seems that both redux-blueprints and redux-mux have chai as a dependency. Here is the dependency graph npm printed out for me when I installed the package.

└─┬ [email protected]
  ├─┬ [email protected]
  │ ├─┬ [email protected]
  │ │ └── [email protected]
  │ ├── [email protected]
  │ ├─┬ [email protected]
  │ │ ├── [email protected]
  │ │ └── [email protected]
  │ ├─┬ [email protected]
  │ │ └── [email protected]  deduped
  │ └── [email protected]
  ├─┬ [email protected]
  │ ├─┬ [email protected]
  │ │ └─┬ [email protected]
  │ │   ├── [email protected]
  │ │   ├── [email protected]
  │ │   └─┬ [email protected]
  │ │     ├── [email protected]  deduped
  │ │     └── [email protected]
  │ └── [email protected]
  ├─┬ [email protected]
  │ ├─┬ [email protected]
  │ │ ├── [email protected]
  │ │ ├─┬ [email protected]
  │ │ │ └── [email protected]
  │ │ └── [email protected]
  │ └── [email protected]  deduped
  ├─┬ [email protected]
  │ ├── [email protected]  deduped
  │ ├── [email protected]  deduped
  │ └── [email protected]  deduped
  └─┬ [email protected]
    └── [email protected]  deduped

Chai is a pretty big package, and this seems to unnecessarily bloat the size of this package. Not sure what one can do about it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.