Skip to content

Commit

Permalink
move logic to the context provider
Browse files Browse the repository at this point in the history
  • Loading branch information
GreenWizard2015 committed Feb 9, 2024
1 parent 34c73e1 commit dd13daa
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 59 deletions.
45 changes: 10 additions & 35 deletions ui/src/components/HoldToPour.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { Container, Form } from 'react-bootstrap';
import { useWaterPumpAPI } from '../contexts/WaterPumpAPIContext';
import { startPump, stopPump } from '../store/slices/SystemStatus.js';

export function HoldToPourComponent({ startPump, stopPump, interval }) {
export function HoldToPourComponent({ interval }) {
const { API }= useWaterPumpAPI();
const [isPouring, setIsPouring] = useState(false);
const [clickToPour, setClickToPour] = useState(false);
// continuously pour water while the button is pressed
Expand All @@ -14,28 +14,28 @@ export function HoldToPourComponent({ startPump, stopPump, interval }) {
if(Date.now() < lastPouringTime.current) return;
try {
lastPouringTime.current = Number.MAX_SAFE_INTEGER; // prevent concurrent calls
await startPump();
await API.startPump();
lastPouringTime.current = Date.now() + interval;
} catch(e) {
lastPouringTime.current = 0; // run again on next tick
}
},
[startPump, interval]
[interval, API]
);

useEffect(() => {
if(!isPouring) {
lastPouringTime.current = 0;
stopPump();
API.stopPump();
return;
}
// tick every 100ms
const tid = setInterval(onTick, 100);
return async () => {
clearInterval(tid);
if(isPouring) await stopPump();
if(isPouring) await API.stopPump();
};
}, [onTick, isPouring, stopPump, lastPouringTime]);
}, [onTick, isPouring, API]);

const handlePress = () => { setIsPouring(true); };
const handleRelease = () => { setIsPouring(false); };
Expand Down Expand Up @@ -65,43 +65,18 @@ export function HoldToPourComponent({ startPump, stopPump, interval }) {
}

// Helper wrapper to simplify the code in the component
function HoldToPourComponent_withExtras({ pouringTime, powerLevel, startPump, stopPump }) {
const api = useWaterPumpAPI().API;
// to prevent the callback from changing when the pouringTime or powerLevel changes
const _pouringTime = React.useRef(pouringTime);
React.useEffect(() => { _pouringTime.current = pouringTime; }, [pouringTime]);

const _powerLevel = React.useRef(powerLevel);
React.useEffect(() => { _powerLevel.current = powerLevel; }, [powerLevel]);

const _startPump = React.useCallback(
async () => {
await startPump({
api,
pouringTime: _pouringTime.current,
powerLevel: _powerLevel.current,
});
}, [api, startPump, _pouringTime, _powerLevel]
);
const _stopPump = React.useCallback(
async () => { await stopPump({ api }); },
[api, stopPump]
);
function HoldToPourComponent_withExtras({ pouringTime, ...props }) {
// 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}
/>
<HoldToPourComponent {...props} interval={interval} />
);
};

export default connect(
state => ({
pouringTime: state.UI.pouringTime,
powerLevel: state.UI.powerLevelInPercents,
}),
{ startPump, stopPump }
{ }
)(HoldToPourComponent_withExtras);
20 changes: 5 additions & 15 deletions ui/src/components/SystemControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,11 @@ import { connect } from 'react-redux';
import { Button, Container } from 'react-bootstrap';

import { useWaterPumpAPI } from '../contexts/WaterPumpAPIContext';
import { startPump, stopPump } from '../store/slices/SystemStatus.js';

export function SystemControlsComponent({
pouringTime, powerLevel, systemStatus, startPump, stopPump
}) {
const api = useWaterPumpAPI().API;
const handleStart = async () => {
await startPump({ api, pouringTime, powerLevel });
};

const handleStop = async () => {
await stopPump({ api });
};
export function SystemControlsComponent({ systemStatus }) {
const { API } = useWaterPumpAPI();
const handleStart = async () => { await API.startPump(); };
const handleStop = async () => { await API.stopPump(); };

const isRunning = systemStatus.pump.running;
return (
Expand All @@ -32,8 +24,6 @@ export function SystemControlsComponent({

export default connect(
state => ({
pouringTime: state.UI.pouringTime,
powerLevel: state.UI.powerLevelInPercents,
systemStatus: state.systemStatus,
}), { startPump, stopPump }
}), { }
)(SystemControlsComponent);
2 changes: 1 addition & 1 deletion ui/src/components/WaterPumpStatusProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function WaterPumpStatusProviderComoponent({ children, updateStatus, systemStatu
if(null == API) return;

nextFetchTime.current = Number.MAX_SAFE_INTEGER; // prevent concurrent fetches
await updateStatus(API);
await updateStatus({ api: API });
nextFetchTime.current = Date.now() + FETCH_INTERVAL;
},
[API, updateStatus, nextFetchTime]
Expand Down
60 changes: 53 additions & 7 deletions ui/src/contexts/WaterPumpAPIContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { connect } from 'react-redux';
import { startPump, stopPump } from '../store/slices/SystemStatus.js';
import { CWaterPumpAPI } from '../api/CWaterPumpAPI.js';
import WaterPumpStatusProvider from '../components/WaterPumpStatusProvider.js';

Expand All @@ -9,20 +10,65 @@ export function useWaterPumpAPI() {
return React.useContext(WaterPumpAPIContext);
}

export function WaterPumpAPIProvider({ children }) {
const apiHost = useSelector((state) => state.UI.apiHost);
function WaterPumpAPIProviderComponent({
children,
apiHost, pouringTime, powerLevel,
startPump, stopPump,
}) {
// to prevent the callbacks from changing when the pouringTime or powerLevel changes
const _pouringTime = React.useRef(pouringTime);
React.useEffect(() => { _pouringTime.current = pouringTime; }, [pouringTime]);

const _powerLevel = React.useRef(powerLevel);
React.useEffect(() => { _powerLevel.current = powerLevel; }, [powerLevel]);

const apiObject = React.useMemo(
() => new CWaterPumpAPI({ URL: apiHost }),
[apiHost]
);
// TODO: provide also the API methods with binded values from the store
// to simplify the code in the components (HodlToPour and PowerLevel)
const value = { API: apiObject, };
////////////////
// create an API wrapper that dispatches actions to the Redux store
const value = React.useMemo(
() => {
if(null == apiObject) return { API: null };
return {
API: {
stopPump: async () => {
return await stopPump({ api: apiObject });
},
startPump: async () => {
return await startPump({
api: apiObject,
pouringTime: _pouringTime.current,
powerLevel: _powerLevel.current,
});
},
status: async () => {
return await apiObject.status();
}
}
};
},
[apiObject, startPump, stopPump, _pouringTime, _powerLevel]
);

return (
<WaterPumpAPIContext.Provider value={value}>
<WaterPumpStatusProvider>
{children}
</WaterPumpStatusProvider>
</WaterPumpAPIContext.Provider>
);
}
}

const WaterPumpAPIProvider = connect(
state => ({
apiHost: state.UI.apiHost,
pouringTime: state.UI.pouringTime,
powerLevel: state.UI.powerLevelInPercents,
}),
{ startPump, stopPump }
)(WaterPumpAPIProviderComponent);

export default WaterPumpAPIProvider;
export { WaterPumpAPIProvider };
2 changes: 1 addition & 1 deletion ui/src/store/slices/SystemStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const stopPump = createAsyncThunk(
export const updateSystemStatus = createAsyncThunk(
'systemStatus/update',
withNotification(
async ( api ) => {
async ({ api }) => {
return await api.status();
},
'Failed to update system status'
Expand Down

0 comments on commit dd13daa

Please sign in to comment.