Surface WAIT_USER and add Confirm to the browser client
Ports show_user_message() from client/brewpi_gui.py: a plain <dialog>
pops with the step's user_message the instant WAIT_USER is newly
entered (tracked via a prevState comparison in onSudChanged()'s State
branch), and sends {"Sud": {"Confirm": true}} on the dialog's close
event - covers both the OK button and Escape dismissal.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0189FkmyJkY77HqsNomr1DwV
This commit is contained in:
@@ -540,9 +540,11 @@ unresolved - every step still needs `default.step` merged in), and the
|
|||||||
`StepPlate`/`_update_step_plates()`/`update_status_step_label()` logic
|
`StepPlate`/`_update_step_plates()`/`update_status_step_label()` logic
|
||||||
behind the Progress tab and status line. New/Load/Save (a plain
|
behind the Progress tab and status line. New/Load/Save (a plain
|
||||||
`<input type="file">`/`FileReader` and a `Blob`/`<a download>` standing in
|
`<input type="file">`/`FileReader` and a `Blob`/`<a download>` standing in
|
||||||
for the desktop app's native file dialogs) and Start/Stop are wired up too
|
for the desktop app's native file dialogs), Start/Stop, and Confirm (a
|
||||||
- see `web/TODO.md` for what's still missing (Pause/Confirm, `WAIT_USER`
|
plain `<dialog>` pops with the step's message on `WAIT_USER` and sends
|
||||||
surfacing, and more). Deliberately deferred rather than stubbed: the
|
Confirm when dismissed) are all wired up too - see `web/TODO.md` for
|
||||||
|
what's still missing (Pause, and more). Deliberately deferred rather than
|
||||||
|
stubbed: the
|
||||||
Automatic tab's forecast-vs-actual plot and the live Plot tab's strip
|
Automatic tab's forecast-vs-actual plot and the live Plot tab's strip
|
||||||
charts - both need a charting approach, revisit once Manual + Progress are
|
charts - both need a charting approach, revisit once Manual + Progress are
|
||||||
validated.
|
validated.
|
||||||
|
|||||||
+12
-15
@@ -7,22 +7,19 @@ flagged as deferred in the plan, or only noticed once the port was done
|
|||||||
and checked against the Qt source side by side.
|
and checked against the Qt source side by side.
|
||||||
|
|
||||||
- [x] **No way to actually run a brew.** ~~There's no Start/Pause/Stop/
|
- [x] **No way to actually run a brew.** ~~There's no Start/Pause/Stop/
|
||||||
Confirm anywhere in `web/index.html`~~ Start/Stop are now wired up
|
Confirm anywhere in `web/index.html`~~ Start/Stop/Confirm are now wired
|
||||||
(`btn-sud-start`/`btn-sud-stop` in `app.js`, gated by `updateSudActions()`
|
up (`btn-sud-start`/`btn-sud-stop` in `app.js`, gated by
|
||||||
mirroring `update_sud_actions()`'s enabled/disabled logic). Still
|
`updateSudActions()` mirroring `update_sud_actions()`'s
|
||||||
missing: Pause, and `{"Sud": {"Confirm": true}}` - that one needs
|
enabled/disabled logic; Confirm via the `#sud-message-dialog` below).
|
||||||
`sudUserMessage`/`sudState` (next item) to know *when* it's actually
|
Still missing: Pause - trivial to add the same way as Start/Stop
|
||||||
waiting on one.
|
whenever it's actually needed.
|
||||||
|
|
||||||
- [ ] **`WAIT_USER` never surfaces to the user.** `sudUserMessage` is
|
- [x] **`WAIT_USER` never surfaces to the user.** `showUserMessage()`
|
||||||
tracked in `app.js` but nothing ever shows it - `show_user_message()`
|
pops `#sud-message-dialog` (a plain `<dialog>`, no library) the instant
|
||||||
(`client/brewpi_gui.py:877-882`) pops a modal the instant `WAIT_USER` is
|
`WAIT_USER` is newly entered (`onSudChanged()`'s `State` branch tracks
|
||||||
newly entered (`prev_state != WAIT_USER`) and sends `Confirm` itself
|
`prevState` for this), and sends `{"Sud": {"Confirm": true}}` on the
|
||||||
when the user dismisses it. Needs the same "newly entered" edge
|
dialog's `close` event - covers both the OK button and Escape, mirrors
|
||||||
detection in `onSudChanged()`'s `State` branch (the `wasRunning`
|
`show_user_message()` (`client/brewpi_gui.py:877-882`).
|
||||||
tracking there is close, but checks running-vs-not, not specifically
|
|
||||||
the `WAIT_USER` transition) plus a modal (a plain `<dialog>` works,
|
|
||||||
no library needed) that sends `Confirm` on close.
|
|
||||||
|
|
||||||
- [x] **No schedule management.** New/Load/Save are now wired up in
|
- [x] **No schedule management.** New/Load/Save are now wired up in
|
||||||
`app.js`: Load is `<input type="file">` + `FileReader` +
|
`app.js`: Load is `<input type="file">` + `FileReader` +
|
||||||
|
|||||||
+16
@@ -9,6 +9,7 @@ const CHANNELS = ['Pot', 'Sensor', 'Heater', 'Stirrer', 'TempCtrl', 'Sud', 'Syst
|
|||||||
// "running" - mirrors client/brewpi_gui.py's SUD_RUNNING_STATES.
|
// "running" - mirrors client/brewpi_gui.py's SUD_RUNNING_STATES.
|
||||||
const RUNNING_STATES = new Set(['SudState.RAMPING', 'SudState.HOLDING', 'SudState.WAIT_USER']);
|
const RUNNING_STATES = new Set(['SudState.RAMPING', 'SudState.HOLDING', 'SudState.WAIT_USER']);
|
||||||
const PAUSED_STATE = 'SudState.PAUSED';
|
const PAUSED_STATE = 'SudState.PAUSED';
|
||||||
|
const WAIT_USER_STATE = 'SudState.WAIT_USER';
|
||||||
|
|
||||||
let ws = null;
|
let ws = null;
|
||||||
let connected = false;
|
let connected = false;
|
||||||
@@ -306,6 +307,14 @@ function updateSudActions() {
|
|||||||
document.getElementById('btn-sud-stop').disabled = !(running || paused);
|
document.getElementById('btn-sud-stop').disabled = !(running || paused);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mirrors client/brewpi_gui.py's show_user_message(): the Sud is already
|
||||||
|
// blocked in WAIT_USER server-side - dismissing this dialog (OK or Escape,
|
||||||
|
// both fire the dialog's 'close' event) is what unblocks it.
|
||||||
|
function showUserMessage(message) {
|
||||||
|
document.getElementById('sud-message-text').textContent = message;
|
||||||
|
document.getElementById('sud-message-dialog').showModal();
|
||||||
|
}
|
||||||
|
|
||||||
function downloadJson(doc) {
|
function downloadJson(doc) {
|
||||||
const blob = new Blob([JSON.stringify(doc, null, '\t')], {type: 'application/json'});
|
const blob = new Blob([JSON.stringify(doc, null, '\t')], {type: 'application/json'});
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
@@ -462,6 +471,7 @@ function onSudChanged(msg) {
|
|||||||
}
|
}
|
||||||
} else if (key === 'State') {
|
} else if (key === 'State') {
|
||||||
const newState = msg.State;
|
const newState = msg.State;
|
||||||
|
const prevState = sudState;
|
||||||
const isRunningNow = RUNNING_STATES.has(newState);
|
const isRunningNow = RUNNING_STATES.has(newState);
|
||||||
if (isRunningNow && !wasRunning) {
|
if (isRunningNow && !wasRunning) {
|
||||||
elapsed = 0;
|
elapsed = 0;
|
||||||
@@ -470,6 +480,9 @@ function onSudChanged(msg) {
|
|||||||
}
|
}
|
||||||
wasRunning = isRunningNow;
|
wasRunning = isRunningNow;
|
||||||
sudState = newState;
|
sudState = newState;
|
||||||
|
if (newState === WAIT_USER_STATE && prevState !== WAIT_USER_STATE && sudUserMessage) {
|
||||||
|
showUserMessage(sudUserMessage);
|
||||||
|
}
|
||||||
updateStepPlates();
|
updateStepPlates();
|
||||||
updateSudActions();
|
updateSudActions();
|
||||||
} else if (key === 'UserMessage') {
|
} else if (key === 'UserMessage') {
|
||||||
@@ -661,6 +674,9 @@ document.getElementById('btn-sud-start').addEventListener('click', () => {
|
|||||||
document.getElementById('btn-sud-stop').addEventListener('click', () => {
|
document.getElementById('btn-sud-stop').addEventListener('click', () => {
|
||||||
sendMsg('Sud', {Stop: true});
|
sendMsg('Sud', {Stop: true});
|
||||||
});
|
});
|
||||||
|
document.getElementById('sud-message-dialog').addEventListener('close', () => {
|
||||||
|
sendMsg('Sud', {Confirm: true});
|
||||||
|
});
|
||||||
|
|
||||||
document.querySelectorAll('.tab-btn').forEach((btn) => {
|
document.querySelectorAll('.tab-btn').forEach((btn) => {
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', () => {
|
||||||
|
|||||||
@@ -102,6 +102,13 @@
|
|||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<dialog id="sud-message-dialog">
|
||||||
|
<p id="sud-message-text"></p>
|
||||||
|
<form method="dialog">
|
||||||
|
<button value="ok">OK</button>
|
||||||
|
</form>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<div id="sud-status-line"></div>
|
<div id="sud-status-line"></div>
|
||||||
<div id="env-status-line"></div>
|
<div id="env-status-line"></div>
|
||||||
|
|||||||
@@ -123,6 +123,15 @@ nav#tabs {
|
|||||||
}
|
}
|
||||||
.step-plate .span2 { grid-column: span 2; }
|
.step-plate .span2 { grid-column: span 2; }
|
||||||
|
|
||||||
|
#sud-message-dialog {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 1em 1.4em;
|
||||||
|
max-width: 24em;
|
||||||
|
}
|
||||||
|
#sud-message-dialog::backdrop { background: rgba(0, 0, 0, 0.3); }
|
||||||
|
#sud-message-dialog form { margin-top: 1em; text-align: right; }
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user