6
6
removeActionFromQueue ,
7
7
dismissActionsFromQueue ,
8
8
} from './actionCreators' ;
9
- import getSimilarActionInQueue from '../utils/getSimilarActionInQueue' ;
10
9
import type { NetworkState } from '../types' ;
10
+ import networkActionTypes from './actionTypes' ;
11
+ import wait from '../utils/wait' ;
11
12
12
13
type MiddlewareAPI < S > = {
13
14
dispatch : ( action : any ) => void ,
@@ -21,59 +22,114 @@ type State = {
21
22
type Arguments = { |
22
23
regexActionType : RegExp ,
23
24
actionTypes : Array < string > ,
25
+ queueReleaseThrottle : number ,
24
26
| } ;
25
27
28
+ function validateParams ( regexActionType , actionTypes ) {
29
+ if ( { } . toString . call ( regexActionType ) !== '[object RegExp]' )
30
+ throw new Error ( 'You should pass a regex as regexActionType param' ) ;
31
+
32
+ if ( { } . toString . call ( actionTypes ) !== '[object Array]' )
33
+ throw new Error ( 'You should pass an array as actionTypes param' ) ;
34
+ }
35
+
36
+ function findActionToBeDismissed ( action , actionQueue ) {
37
+ return find ( actionQueue , ( a : * ) => {
38
+ const actionsToDismiss = get ( a , 'meta.dismiss' , [ ] ) ;
39
+ return actionsToDismiss . includes ( action . type ) ;
40
+ } ) ;
41
+ }
42
+
43
+ function isObjectAndShouldBeIntercepted ( action , regexActionType , actionTypes ) {
44
+ return (
45
+ typeof action === 'object' &&
46
+ ( regexActionType . test ( action . type ) || actionTypes . includes ( action . type ) )
47
+ ) ;
48
+ }
49
+
50
+ function isThunkAndShouldBeIntercepted ( action ) {
51
+ return typeof action === 'function' && action . interceptInOffline === true ;
52
+ }
53
+
54
+ function checkIfActionShouldBeIntercepted (
55
+ action ,
56
+ regexActionType ,
57
+ actionTypes ,
58
+ ) {
59
+ return (
60
+ isObjectAndShouldBeIntercepted ( action , regexActionType , actionTypes ) ||
61
+ isThunkAndShouldBeIntercepted ( action )
62
+ ) ;
63
+ }
64
+
65
+ function didComeBackOnline ( action , wasConnected ) {
66
+ return (
67
+ action . type === networkActionTypes . CONNECTION_CHANGE &&
68
+ ! wasConnected &&
69
+ action . payload === true
70
+ ) ;
71
+ }
72
+
73
+ export const createReleaseQueue = ( getState , next , delay ) => async queue => {
74
+ // eslint-disable-next-line
75
+ for ( const action of queue ) {
76
+ const { isConnected } = getState ( ) . network ;
77
+ if ( isConnected ) {
78
+ next ( removeActionFromQueue ( action ) ) ;
79
+ next ( action ) ;
80
+ // eslint-disable-next-line
81
+ await wait ( delay ) ;
82
+ } else {
83
+ break ;
84
+ }
85
+ }
86
+ } ;
87
+
26
88
function createNetworkMiddleware ( {
27
89
regexActionType = / F E T C H .* R E Q U E S T / ,
28
90
actionTypes = [ ] ,
91
+ queueReleaseThrottle = 50 ,
29
92
} : Arguments = { } ) {
30
93
return ( { getState } : MiddlewareAPI < State > ) => (
31
94
next : ( action : any ) = > void ,
32
95
) => ( action : any ) = > {
33
- if ( { } . toString . call ( regexActionType ) !== '[object RegExp]' )
34
- throw new Error ( 'You should pass a regex as regexActionType param' ) ;
96
+ const { isConnected, actionQueue } = getState ( ) . network ;
97
+ const releaseQueue = createReleaseQueue (
98
+ getState ,
99
+ next ,
100
+ queueReleaseThrottle ,
101
+ ) ;
102
+ validateParams ( regexActionType , actionTypes ) ;
35
103
36
- if ( { } . toString . call ( actionTypes ) !== '[object Array]' )
37
- throw new Error ( 'You should pass an array as actionTypes param' ) ;
104
+ const shouldInterceptAction = checkIfActionShouldBeIntercepted (
105
+ action ,
106
+ regexActionType ,
107
+ actionTypes ,
108
+ ) ;
38
109
39
- const { isConnected, actionQueue } = getState ( ) . network ;
110
+ if ( shouldInterceptAction && isConnected === false ) {
111
+ // Offline, preventing the original action from being dispatched.
112
+ // Dispatching an internal action instead.
113
+ return next ( fetchOfflineMode ( action ) ) ;
114
+ }
40
115
41
- const isObjectAndMatchCondition =
42
- typeof action === 'object' &&
43
- ( regexActionType . test ( action . type ) || actionTypes . includes ( action . type ) ) ;
44
-
45
- const isFunctionAndMatchCondition =
46
- typeof action === 'function' && action . interceptInOffline === true ;
47
-
48
- if ( isObjectAndMatchCondition || isFunctionAndMatchCondition ) {
49
- if ( isConnected === false ) {
50
- // Offline, preventing the original action from being dispatched.
51
- // Dispatching an internal action instead.
52
- return next ( fetchOfflineMode ( action ) ) ;
53
- }
54
- const actionQueued =
55
- actionQueue . length > 0
56
- ? getSimilarActionInQueue ( action , actionQueue )
57
- : null ;
58
- if ( actionQueued ) {
59
- // Back online and the action that was queued is about to be dispatched.
60
- // Removing action from queue, prior to handing over to next middleware or final dispatch
61
- next ( removeActionFromQueue ( action ) ) ;
62
-
63
- return next ( action ) ;
64
- }
116
+ const isBackOnline = didComeBackOnline ( action , isConnected ) ;
117
+ if ( isBackOnline ) {
118
+ // Dispatching queued actions in order of arrival (if we have any)
119
+ next ( action ) ;
120
+ return releaseQueue ( actionQueue ) ;
65
121
}
66
122
67
- // We don't want to dispatch actions all the time, but rather when there is a dismissal case
68
- const isAnyActionToBeDismissed = find ( actionQueue , ( a : * ) => {
69
- const actionsToDismiss = get ( a , 'meta.dismiss' , [ ] ) ;
70
- return actionsToDismiss . includes ( action . type ) ;
71
- } ) ;
123
+ // Checking if we have a dismissal case
124
+ const isAnyActionToBeDismissed = findActionToBeDismissed (
125
+ action ,
126
+ actionQueue ,
127
+ ) ;
72
128
if ( isAnyActionToBeDismissed && ! isConnected ) {
73
129
next ( dismissActionsFromQueue ( action . type ) ) ;
74
- return next ( action ) ;
75
130
}
76
131
132
+ // Proxy the original action to the next middleware on the chain or final dispatch
77
133
return next ( action ) ;
78
134
} ;
79
135
}
0 commit comments