1
1
import React from 'react' ;
2
- import { useSelector } from 'react-redux' ;
2
+ import { connect } from 'react-redux' ;
3
3
import { CWaterPumpAPI } from '../api/CWaterPumpAPI.js' ;
4
- import WaterPumpStatusProvider from '../components/WaterPumpStatusProvider .js' ;
4
+ import { updateSystemStatus } from '../store/slices/SystemStatus .js' ;
5
5
6
6
const WaterPumpAPIContext = React . createContext ( ) ;
7
7
8
8
export function useWaterPumpAPI ( ) {
9
9
return React . useContext ( WaterPumpAPIContext ) ;
10
10
}
11
11
12
- export function WaterPumpAPIProvider ( { children } ) {
13
- const apiHost = useSelector ( ( state ) => state . UI . apiHost ) ;
14
- const apiObject = React . useMemo (
15
- ( ) => new CWaterPumpAPI ( { URL : apiHost } ) ,
12
+ const FETCH_STATUS_INTERVAL = 5000 ;
13
+
14
+ function _publicWrapper ( { apiObject, apiQueue, _pouringTime, _powerLevel } ) {
15
+ if ( null == apiObject ) return { API : null } ;
16
+ return {
17
+ API : {
18
+ stopPump : ( ) => {
19
+ apiQueue . push ( {
20
+ action : async ( ) => await apiObject . stop ( ) ,
21
+ failMessage : 'Failed to stop the pump'
22
+ } ) ;
23
+ } ,
24
+ startPump : ( ) => {
25
+ apiQueue . push ( {
26
+ action : async ( ) => await apiObject . start (
27
+ _pouringTime . current ,
28
+ _powerLevel . current
29
+ ) ,
30
+ failMessage : 'Failed to start the pump'
31
+ } ) ;
32
+ } ,
33
+ }
34
+ } ;
35
+ }
36
+
37
+ function _makeStatusAction ( apiObject ) {
38
+ return {
39
+ action : async ( ) => await apiObject . status ( ) ,
40
+ failMessage : 'Failed to get the pump status'
41
+ } ;
42
+ }
43
+
44
+ async function _processQueue ( { apiQueue, lastUpdateTime, statusAction, updateStatus } ) {
45
+ const deltaTime = Date . now ( ) - lastUpdateTime . current ;
46
+ const hasTasks = ( 0 < apiQueue . length ) ;
47
+ if ( ( deltaTime < FETCH_STATUS_INTERVAL ) && ! hasTasks ) return ;
48
+
49
+ const action = hasTasks ? apiQueue . shift ( ) : statusAction ;
50
+ const oldTime = lastUpdateTime . current ;
51
+ lastUpdateTime . current = Number . MAX_SAFE_INTEGER ; // prevent concurrent tasks, just in case
52
+ try {
53
+ await updateStatus ( action ) ;
54
+ lastUpdateTime . current = Date . now ( ) ;
55
+ } catch ( error ) {
56
+ lastUpdateTime . current = oldTime ;
57
+ if ( hasTasks ) { // re-queue the action if it failed
58
+ apiQueue . unshift ( action ) ;
59
+ }
60
+ throw error ;
61
+ }
62
+ }
63
+
64
+ function WaterPumpAPIProviderComponent ( {
65
+ children,
66
+ apiHost, pouringTime, powerLevel,
67
+ updateStatus,
68
+ } ) {
69
+ // to prevent the callbacks from changing when the pouringTime or powerLevel changes
70
+ const _pouringTime = React . useRef ( pouringTime ) ;
71
+ React . useEffect ( ( ) => { _pouringTime . current = pouringTime ; } , [ pouringTime ] ) ;
72
+
73
+ const _powerLevel = React . useRef ( powerLevel ) ;
74
+ React . useEffect ( ( ) => { _powerLevel . current = powerLevel ; } , [ powerLevel ] ) ;
75
+
76
+ const { apiObject, apiQueue } = React . useMemo (
77
+ ( ) => ( {
78
+ apiObject : new CWaterPumpAPI ( { URL : apiHost } ) ,
79
+ apiQueue : [ ]
80
+ } ) ,
16
81
[ apiHost ]
17
82
) ;
18
- // TODO: provide also the API methods with binded values from the store
19
- // to simplify the code in the components (HodlToPour and PowerLevel)
20
- const value = { API : apiObject , } ;
83
+ ////////////////
84
+ const statusAction = React . useMemo ( ( ) => _makeStatusAction ( apiObject ) , [ apiObject ] ) ;
85
+ const lastUpdateTime = React . useRef ( 0 ) ;
86
+ const onTick = React . useCallback (
87
+ async ( ) => _processQueue ( { apiQueue, lastUpdateTime, statusAction, updateStatus } ) ,
88
+ [ apiQueue , lastUpdateTime , updateStatus , statusAction ]
89
+ ) ;
90
+
91
+ // Run the timer
92
+ React . useEffect ( ( ) => {
93
+ const timer = setInterval ( onTick , 100 ) ;
94
+ return ( ) => { clearInterval ( timer ) ; } ;
95
+ } , [ onTick ] ) ;
96
+
97
+ ////////////////
98
+ const value = React . useMemo (
99
+ ( ) => _publicWrapper ( { apiObject, apiQueue, _pouringTime, _powerLevel } ) ,
100
+ [ apiObject , apiQueue , _pouringTime , _powerLevel ]
101
+ ) ;
21
102
return (
22
103
< WaterPumpAPIContext . Provider value = { value } >
23
- < WaterPumpStatusProvider >
24
- { children }
25
- </ WaterPumpStatusProvider >
104
+ { children }
26
105
</ WaterPumpAPIContext . Provider >
27
106
) ;
28
- }
107
+ }
108
+
109
+ const WaterPumpAPIProvider = connect (
110
+ state => ( {
111
+ apiHost : state . UI . apiHost ,
112
+ pouringTime : state . UI . pouringTime ,
113
+ powerLevel : state . UI . powerLevelInPercents ,
114
+ } ) ,
115
+ { updateStatus : updateSystemStatus }
116
+ ) ( WaterPumpAPIProviderComponent ) ;
117
+
118
+ export default WaterPumpAPIProvider ;
119
+ export { WaterPumpAPIProvider } ;
0 commit comments