-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f9ae6cc
commit 936e9b3
Showing
1 changed file
with
70 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,90 @@ | ||
import React, { useState, useEffect } from 'react'; | ||
import { connect } from 'react-redux'; | ||
import { Container } from 'react-bootstrap'; | ||
import { Container, Form } from 'react-bootstrap'; | ||
import { useWaterPumpAPI } from '../contexts/WaterPumpAPIContext'; | ||
import { startPump, stopPump } from '../store/slices/SystemStatus.js'; | ||
|
||
export function HoldToPourComponent({ startPump, stopPump }) { | ||
const pouringTime = 1500; | ||
const api = useWaterPumpAPI().API; | ||
export function HoldToPourComponent({ startPump, stopPump, interval }) { | ||
const [isPouring, setIsPouring] = useState(false); | ||
const [clickToPour, setClickToPour] = useState(false); | ||
// continuously pour water while the button is pressed | ||
const lastPouringTime = React.useRef(0); | ||
const onTick = React.useCallback( | ||
async () => { | ||
if(Date.now() < lastPouringTime.current) return; | ||
try { | ||
lastPouringTime.current = Number.MAX_SAFE_INTEGER; // prevent concurrent calls | ||
await startPump(); | ||
lastPouringTime.current = Date.now() + interval; | ||
} catch(e) { | ||
lastPouringTime.current = 0; // run again on next tick | ||
} | ||
}, | ||
[startPump, interval] | ||
); | ||
|
||
useEffect(() => { | ||
if (!isPouring) return; | ||
|
||
const tid = setInterval(() => { | ||
startPump({ api, pouringTime }); | ||
}, pouringTime - 500); | ||
|
||
return () => { | ||
clearInterval(tid); | ||
stopPump({ api }); | ||
}; | ||
}, [isPouring, api, startPump, stopPump]); | ||
if(!isPouring) { | ||
lastPouringTime.current = 0; | ||
stopPump(); | ||
return; | ||
} | ||
// tick every 100ms | ||
const tid = setInterval(onTick, 100); | ||
return () => { clearInterval(tid); } | ||
}, [onTick, isPouring, stopPump, lastPouringTime]); | ||
|
||
const handlePress = () => { setIsPouring(true); }; | ||
const handleRelease = () => { setIsPouring(false); }; | ||
|
||
const handleCheckboxChange = e => { setClickToPour(e.target.checked); }; | ||
const handleToggle = () => { setIsPouring(!isPouring); }; | ||
// FIX: onMouseDown/onMouseUp is not working on mobile | ||
return ( | ||
<Container className="d-flex justify-content-center mt-3"> | ||
<Container className="d-flex flex-column align-items-center mt-3"> | ||
<img src="valve.png" className='hold-to-pour-image' alt="Hold to pour button" | ||
draggable="false" onMouseDown={handlePress} onMouseUp={handleRelease} | ||
draggable="false" | ||
onMouseDown={clickToPour ? null : handlePress} | ||
onMouseUp={clickToPour ? null : handleRelease} | ||
onClick={clickToPour ? handleToggle : null} | ||
/> | ||
|
||
<Form.Check | ||
type="checkbox" | ||
checked={clickToPour} onChange={handleCheckboxChange} | ||
label={ | ||
<span style={{ color: 'red', fontSize: '1.5rem' }}> | ||
Click to pour (<b>dangerous</b>) | ||
</span> | ||
} | ||
/> | ||
</Container> | ||
); | ||
} | ||
|
||
// Helper wrapper to simplify the code in the component | ||
function HoldToPourComponent_withExtras({ pouringTime, startPump, stopPump }) { | ||
const api = useWaterPumpAPI().API; | ||
|
||
const _startPump = React.useCallback( | ||
async () => { await startPump({ api, pouringTime }); }, | ||
[api, startPump, pouringTime] | ||
); | ||
const _stopPump = React.useCallback( | ||
async () => { await stopPump({ api }); }, | ||
[api, stopPump] | ||
); | ||
// a bit smaller than the actual pouring time, to prevent the pump from stopping | ||
// which could damage the pump | ||
const interval = Math.max(Math.round(pouringTime - 500), 100); | ||
return ( | ||
<HoldToPourComponent | ||
startPump={_startPump} stopPump={_stopPump} | ||
interval={interval} | ||
/> | ||
); | ||
}; | ||
|
||
export default connect( | ||
state => ({}), | ||
state => ({ pouringTime: state.UI.pouringTime }), | ||
{ startPump, stopPump } | ||
)(HoldToPourComponent); | ||
)(HoldToPourComponent_withExtras); |