lib/actions.js
import invariant from 'invariant'
import { startBlueprint, stopBlueprint, resetIdleStatusBlueprint, activityBlueprint, activityDetectionBlueprint } from './blueprints'
import { IS_DEV, IDLESTATUS_ACTIVE, USER_ACTIVE, NEXT_IDLE_STATUS, RESET_IDLE_STATUS } from './constants'
import localsync from 'localsync'
const STOP_TYPES = [ 'pointermove', 'MSPointerMove' ]
const FILTER_TYPES = [ 'mousemove' ]
const isBrowser = () => typeof window === 'object'
/** Detects whether the activity should trigger a redux update */
const createShouldActivityUpdate = ({ log, thresholds }) => store => ({ type, pageX, pageY }) => {
if(STOP_TYPES.includes(type))
return false
if(!FILTER_TYPES.includes(type))
return true
/** If last event was not the same event type, trigger an update. */
const { lastActive, lastEvent } = selectIdleState(store.getState())
if(lastEvent.type !== type)
return true
/** If last mouse events coordinates were not within mouse threshold, trigger an update. */
const { x, y } = lastEvent
if((pageX && pageY && x && y) && Math.abs(pageX - x) < thresholds.mouse && Math.abs(pageY - y) < thresholds.mouse)
return false
return true
}
const selectIdleState = state => {
const { idle } = state
if(IS_DEV) {
invariant(idle, 'idle monitor state should have idle value')
invariant(typeof idle === 'object', 'idle monitor state should have type object')
}
return idle
}
const isRunning = (dispatch, getState) => {
const { isDetectionRunning } = selectIdleState(getState())
if(IS_DEV) {
invariant(isDetectionRunning, 'idle monitor state should have idDetectionRunning defined')
invariant(typeof isDetectionRunning === 'boolean', 'isDetectionRunning should be type boolean')
}
return isDetectionRunning
}
const createLocalSync = ({ log, activity, getIsTransition }) => store => {
const action = isActive => {
if(isActive)
return { isActive, lastActive: Date.now() }
else
return { isActive }
}
const handler = (value, old, url) => {
log.info({ value, old, url }, 'local sync')
if(value.isActive)
store.dispatch(activity({ type: 'local', isTransition: getIsTransition() }))
}
return localsync('idlemonitor', action, handler)
}
const createActivityDetection = ({ log, thresholds, activeEvents, activity, activityDetection, getIsTransition }) => store => {
const { dispatch } = store
const shouldActivityUpdate = createShouldActivityUpdate({ log, thresholds })(store)
/** One of the event listeners triggered an activity occurrence event. This gets spammed */
const onActivity = e => {
if (!shouldActivityUpdate(e))
return
dispatch(activity({ x: e.pageX, y: e.pageY, type: e.type, isTransition: getIsTransition() }))
}
const startActivityDetection = () => {
if(isBrowser()) activeEvents.forEach(x => document.addEventListener(x, onActivity))
dispatch(activityDetection(true))
}
const stopActivityDetection = () => {
if(isBrowser()) activeEvents.forEach(x => document.removeEventListener(x, onActivity))
dispatch(activityDetection(false))
}
return { startActivityDetection, stopActivityDetection }
}
export const createDetection = ({ log, activeEvents, thresholds, translateBlueprints }) => store => {
const { activity
, activityDetection
} = translateBlueprints({ activity: activityBlueprint
, activityDetection: activityDetectionBlueprint
})
const getIsTransition = () => selectIdleState(store.getState()).idleStatus !== IDLESTATUS_ACTIVE
const { startActivityDetection, stopActivityDetection } = createActivityDetection({ log, thresholds, activeEvents, activity, activityDetection, getIsTransition })(store)
const localSync = createLocalSync({ log, activity, getIsTransition })(store)
invariant(startActivityDetection, 'startActivityDetection should be a return property of createActivityDetection')
invariant(stopActivityDetection, 'stopActivityDetection should be a return property of createActivityDetection')
invariant(localSync, 'localSync should exist')
invariant(localSync.start, 'localSync.start should exist')
invariant(localSync.stop, 'localSync.stop should exist')
invariant(localSync.trigger, 'localSync.trigger should exist')
log.info('activity detection starting')
return { startActivityDetection, stopActivityDetection, localSync }
}