Skip to content

Commit 936e9b3

Browse files
better HoldToPour
1 parent f9ae6cc commit 936e9b3

File tree

1 file changed

+70
-20
lines changed

1 file changed

+70
-20
lines changed

ui/src/components/HoldToPour.js

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,90 @@
11
import React, { useState, useEffect } from 'react';
22
import { connect } from 'react-redux';
3-
import { Container } from 'react-bootstrap';
3+
import { Container, Form } from 'react-bootstrap';
44
import { useWaterPumpAPI } from '../contexts/WaterPumpAPIContext';
55
import { startPump, stopPump } from '../store/slices/SystemStatus.js';
66

7-
export function HoldToPourComponent({ startPump, stopPump }) {
8-
const pouringTime = 1500;
9-
const api = useWaterPumpAPI().API;
7+
export function HoldToPourComponent({ startPump, stopPump, interval }) {
108
const [isPouring, setIsPouring] = useState(false);
9+
const [clickToPour, setClickToPour] = useState(false);
10+
// continuously pour water while the button is pressed
11+
const lastPouringTime = React.useRef(0);
12+
const onTick = React.useCallback(
13+
async () => {
14+
if(Date.now() < lastPouringTime.current) return;
15+
try {
16+
lastPouringTime.current = Number.MAX_SAFE_INTEGER; // prevent concurrent calls
17+
await startPump();
18+
lastPouringTime.current = Date.now() + interval;
19+
} catch(e) {
20+
lastPouringTime.current = 0; // run again on next tick
21+
}
22+
},
23+
[startPump, interval]
24+
);
1125

1226
useEffect(() => {
13-
if (!isPouring) return;
14-
15-
const tid = setInterval(() => {
16-
startPump({ api, pouringTime });
17-
}, pouringTime - 500);
18-
19-
return () => {
20-
clearInterval(tid);
21-
stopPump({ api });
22-
};
23-
}, [isPouring, api, startPump, stopPump]);
27+
if(!isPouring) {
28+
lastPouringTime.current = 0;
29+
stopPump();
30+
return;
31+
}
32+
// tick every 100ms
33+
const tid = setInterval(onTick, 100);
34+
return () => { clearInterval(tid); }
35+
}, [onTick, isPouring, stopPump, lastPouringTime]);
2436

2537
const handlePress = () => { setIsPouring(true); };
2638
const handleRelease = () => { setIsPouring(false); };
27-
39+
const handleCheckboxChange = e => { setClickToPour(e.target.checked); };
40+
const handleToggle = () => { setIsPouring(!isPouring); };
41+
// FIX: onMouseDown/onMouseUp is not working on mobile
2842
return (
29-
<Container className="d-flex justify-content-center mt-3">
43+
<Container className="d-flex flex-column align-items-center mt-3">
3044
<img src="valve.png" className='hold-to-pour-image' alt="Hold to pour button"
31-
draggable="false" onMouseDown={handlePress} onMouseUp={handleRelease}
45+
draggable="false"
46+
onMouseDown={clickToPour ? null : handlePress}
47+
onMouseUp={clickToPour ? null : handleRelease}
48+
onClick={clickToPour ? handleToggle : null}
49+
/>
50+
51+
<Form.Check
52+
type="checkbox"
53+
checked={clickToPour} onChange={handleCheckboxChange}
54+
label={
55+
<span style={{ color: 'red', fontSize: '1.5rem' }}>
56+
Click to pour (<b>dangerous</b>)
57+
</span>
58+
}
3259
/>
3360
</Container>
3461
);
3562
}
3663

64+
// Helper wrapper to simplify the code in the component
65+
function HoldToPourComponent_withExtras({ pouringTime, startPump, stopPump }) {
66+
const api = useWaterPumpAPI().API;
67+
68+
const _startPump = React.useCallback(
69+
async () => { await startPump({ api, pouringTime }); },
70+
[api, startPump, pouringTime]
71+
);
72+
const _stopPump = React.useCallback(
73+
async () => { await stopPump({ api }); },
74+
[api, stopPump]
75+
);
76+
// a bit smaller than the actual pouring time, to prevent the pump from stopping
77+
// which could damage the pump
78+
const interval = Math.max(Math.round(pouringTime - 500), 100);
79+
return (
80+
<HoldToPourComponent
81+
startPump={_startPump} stopPump={_stopPump}
82+
interval={interval}
83+
/>
84+
);
85+
};
86+
3787
export default connect(
38-
state => ({}),
88+
state => ({ pouringTime: state.UI.pouringTime }),
3989
{ startPump, stopPump }
40-
)(HoldToPourComponent);
90+
)(HoldToPourComponent_withExtras);

0 commit comments

Comments
 (0)