diff --git a/ui/src/App.js b/ui/src/App.js index 167d12e..f27ad68 100644 --- a/ui/src/App.js +++ b/ui/src/App.js @@ -1,6 +1,7 @@ -import React, { useState } from 'react'; +import React from 'react'; import './App.css'; import { Container, Form } from 'react-bootstrap'; +import { connect } from 'react-redux'; import NotificationsArea from './components/NotificationsArea.js'; import APIAddressField from './components/APIAddressField'; @@ -8,11 +9,8 @@ import PourTimeField from './components/PourTimeField'; import SystemControls from './components/SystemControls'; import SystemStatusArea from './components/SystemStatusArea'; -function App() { - // TODO: Move this to a redux store - const [pourTime, setPourTime] = useState(1000); +function App({ isConnected }) { // TODO: Add a fake countdown timer of timeLeft - const systemStatus = null; // TODO: Remove usage of this variable and use redux store instead return (

Tea System UI

@@ -20,12 +18,12 @@ function App() {
- {(null === systemStatus) ? null : ( + {isConnected ? ( <> - - + + - )} + ) : null}
@@ -34,4 +32,8 @@ function App() { ); } -export default App; \ No newline at end of file +export default connect( + state => ({ + isConnected: (null !== state.systemStatus), + }), [] +)(App); \ No newline at end of file diff --git a/ui/src/components/PourTimeField.js b/ui/src/components/PourTimeField.js index 51120fd..c75e1f7 100644 --- a/ui/src/components/PourTimeField.js +++ b/ui/src/components/PourTimeField.js @@ -1,26 +1,16 @@ -import React, { useState, useEffect } from 'react'; +import React from 'react'; import { Form, Row, Col } from 'react-bootstrap'; +import { connect } from 'react-redux'; +import { updatePouringTime } from '../store/slices/UI'; -const STORE_POURRTIME = 'pourTime'; - -function PourTimeField({ onChange, min, max }) { - const [pourTime, setPourTime] = useState(1000); - - useEffect(() => { - const time = localStorage.getItem(STORE_POURRTIME); - if (time) setPourTime(parseInt(time, 10)); - }, []); // on mount - // call onChange when pourTime changes - useEffect(() => onChange(pourTime), [pourTime, onChange]); - +export function PourTimeFieldComponent({ onChange, min, max, value }) { const handleChange = (event) => { let newTime = parseInt(event.target.value, 10); if (isNaN(newTime)) return; if (newTime < min) newTime = min; if (max < newTime) newTime = max; - setPourTime(newTime); - localStorage.setItem(STORE_POURRTIME, newTime); + onChange(newTime); }; return ( @@ -29,10 +19,18 @@ function PourTimeField({ onChange, min, max }) { Run Time (ms): - + ); } -export default PourTimeField; \ No newline at end of file +export default connect( + state => ({ + min: 100, + max: state.systemStatus?.waterThreshold, + value: state.UI.pouringTime, + }), { + onChange: updatePouringTime, + } +)(PourTimeFieldComponent); \ No newline at end of file diff --git a/ui/src/components/SystemControls.js b/ui/src/components/SystemControls.js index 77d014d..eeaab88 100644 --- a/ui/src/components/SystemControls.js +++ b/ui/src/components/SystemControls.js @@ -1,10 +1,11 @@ import React from 'react'; +import { connect } from 'react-redux'; import { Button } from 'react-bootstrap'; import { useWaterPumpAPI } from '../contexts/WaterPumpAPIContext'; import { useNotificationsSystem } from '../contexts/NotificationsContext.js'; -function SystemControls({ pouringTime, systemStatus }) { +export function SystemControlsComponent({ pouringTime, systemStatus }) { const waterPump = useWaterPumpAPI().API; const NotificationsSystem = useNotificationsSystem(); @@ -39,4 +40,9 @@ function SystemControls({ pouringTime, systemStatus }) { ); } -export default SystemControls; \ No newline at end of file +export default connect( + state => ({ + pouringTime: state.UI.pouringTime, + systemStatus: state.systemStatus, + }), [] +)(SystemControlsComponent); \ No newline at end of file diff --git a/ui/src/store/slices/SystemStatus.js b/ui/src/store/slices/SystemStatus.js index 78eb583..e9e9c91 100644 --- a/ui/src/store/slices/SystemStatus.js +++ b/ui/src/store/slices/SystemStatus.js @@ -12,6 +12,7 @@ const systemStatus = { }, updated: new Date(), }; +// NOTE: SystemStatusSlice can't store unseralizable data, such as Date objects! // slice for system status export const SystemStatusSlice = createSlice({ diff --git a/ui/src/store/slices/UI.js b/ui/src/store/slices/UI.js new file mode 100644 index 0000000..d383fbe --- /dev/null +++ b/ui/src/store/slices/UI.js @@ -0,0 +1,18 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const INITIAL_STATE = { + pouringTime: 0, +}; +// slice for system status +export const UISlice = createSlice({ + name: 'UI', + initialState: INITIAL_STATE, + reducers: { + updatePouringTime(state, action) { + state.pouringTime = action.payload; + } + }, +}); + +export const actions = UISlice.actions; +export const { updatePouringTime } = actions; \ No newline at end of file diff --git a/ui/src/store/slices/index.js b/ui/src/store/slices/index.js index 65ffe9a..6f7cd36 100644 --- a/ui/src/store/slices/index.js +++ b/ui/src/store/slices/index.js @@ -1,6 +1,7 @@ import { SystemStatusSlice } from "./SystemStatus"; +import { UISlice } from "./UI"; -const slices = [ SystemStatusSlice ]; +const slices = [ SystemStatusSlice, UISlice ]; // export all slices as an object { [sliceName]: slice } export const ALL_APP_SLICES = slices.reduce((acc, slice) => { acc[slice.name] = slice;