-
Notifications
You must be signed in to change notification settings - Fork 46
/
create-reactor-bundle.js
116 lines (105 loc) · 3.06 KB
/
create-reactor-bundle.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import { IS_BROWSER, debounce, ric, raf } from '../utils'
const defaults = {
idleTimeout: 30000,
idleAction: 'APP_IDLE',
doneCallback: null,
stopWhenTabInactive: true,
cancelIdleWhenDone: true,
reactorPermissionCheck: null
}
const ricOptions = { timeout: 500 }
export const getIdleDispatcher = (stopWhenInactive, timeout, fn) =>
debounce(() => {
// the requestAnimationFrame ensures it doesn't run when tab isn't active
stopWhenInactive ? raf(() => ric(fn, ricOptions)) : ric(fn, ricOptions)
}, timeout)
export default spec => ({
name: 'reactors',
init: store => {
const {
idleAction,
idleTimeout,
cancelIdleWhenDone,
doneCallback,
stopWhenTabInactive,
reactorPermissionCheck
} = Object.assign({}, defaults, spec)
let idleDispatcher
if (idleTimeout) {
idleDispatcher = getIdleDispatcher(stopWhenTabInactive, idleTimeout, () =>
store.dispatch({ type: idleAction })
)
}
store.getNextReaction = (skipPermissionCheck = false) => {
for (let i = 0, l = store.meta.reactorNames.length; i < l; i++) {
const name = store.meta.reactorNames[i]
const result = store[name]()
if (result) {
// enable passing a fn to check whether a given reactor should be allowed
if (
!skipPermissionCheck &&
reactorPermissionCheck &&
!reactorPermissionCheck(name, result)
) {
continue
}
return { name, result }
}
}
}
if (process.env.NODE_ENV !== 'production') {
store.meta.reactorNames.forEach(name => {
if (!store[name]) {
throw Error(
`Reactor '${name}' not found on the store. Make sure you're defining as selector by that name.`
)
}
})
}
const cancelIfDone = () => {
if (
!IS_BROWSER &&
!store.nextReaction &&
(!store.selectAsyncActive || !store.selectAsyncActive())
) {
idleDispatcher && idleDispatcher.cancel()
doneCallback && doneCallback()
}
}
const dispatchNextReaction = () => {
// make sure it's still relevant and store it
// this time don't verify permission, we already
// did once.
store.nextReaction = store.getNextReaction(true)
if (store.nextReaction) {
const { nextReaction } = store
store.nextReaction = null
store.dispatch(nextReaction.result)
}
}
const dispatchNext = () => {
// one at a time
if (store.nextReaction) {
return
}
// store next reaction for reference
store.nextReaction = store.getNextReaction()
if (store.nextReaction) {
ric(dispatchNextReaction, ricOptions)
}
}
const callback = () => {
dispatchNext()
if (idleDispatcher) {
idleDispatcher()
cancelIdleWhenDone && cancelIfDone()
}
}
const unsubscribe = store.subscribe(callback)
callback()
return () => {
idleDispatcher && idleDispatcher.cancel()
unsubscribe()
}
}
})