fix: preserve recipe progress view state

Refs #1, #5
This commit is contained in:
2026-08-14 21:02:21 -05:00
parent 22daf703bb
commit a8fbcf4104
4 changed files with 59 additions and 8 deletions
+35 -3
View File
@@ -28,9 +28,17 @@ Module.register("MMM-NextcloudCookbook", {
socketNotificationReceived(notification, payload) {
if (notification !== "STATE") return;
const previous = this.state;
this.state = payload;
this.setRecipeMode(Boolean(payload.active));
this.updateDom(this.config.animationSpeed);
const requiresDomUpdate = previous.active !== payload.active
|| previous.recipeId !== payload.recipeId
|| previous.portions !== payload.portions
|| previous.theme !== payload.theme
|| previous.error !== payload.error
|| previous.recipe?.recipeYield !== payload.recipe?.recipeYield;
if (requiresDomUpdate) this.updateDom(this.config.animationSpeed);
else this.updateProgressDom();
},
notificationReceived(notification) {
@@ -86,6 +94,24 @@ Module.register("MMM-NextcloudCookbook", {
}
},
updateProgressDom() {
const root = document.getElementById(this.identifier)?.querySelector(".ncc-active");
if (!root) return;
root.querySelectorAll("[data-ncc-progress-kind]").forEach((item) => {
const kind = item.dataset.nccProgressKind;
const index = Number(item.dataset.nccProgressIndex);
const completed = kind === "ingredient"
? this.state.progress?.completedIngredients?.includes(index)
: this.state.progress?.completedSteps?.includes(index);
item.classList.toggle("ncc-completed", completed);
const checkbox = item.querySelector("input[type=checkbox]");
if (checkbox) {
checkbox.checked = completed;
checkbox.disabled = false;
}
});
},
getDom() {
const root = document.createElement("section");
root.className = `ncc-root ${this.state.active ? "ncc-active" : "ncc-idle"} ncc-theme-${this.state.theme || this.config.theme}`;
@@ -143,9 +169,11 @@ Module.register("MMM-NextcloudCookbook", {
title.textContent = recipe.name || "Untitled recipe";
header.appendChild(title);
const yieldValue = recipe.originalRecipeYield && recipe.originalRecipeYield !== recipe.recipeYield
? `${recipe.recipeYield} (originally ${recipe.originalRecipeYield})`
: recipe.recipeYield;
const metadata = [
["Yield", recipe.recipeYield],
["Original yield", recipe.originalRecipeYield !== recipe.recipeYield ? recipe.originalRecipeYield : null],
["Yield", yieldValue],
["Prep", NCCFormatDuration(recipe.prepTime)],
["Cook", NCCFormatDuration(recipe.cookTime)],
["Total", NCCFormatDuration(recipe.totalTime)]
@@ -178,6 +206,8 @@ Module.register("MMM-NextcloudCookbook", {
const ingredientList = document.createElement("ul");
(recipe.recipeIngredient || []).forEach((ingredient, index) => {
const item = document.createElement("li");
item.dataset.nccProgressKind = "ingredient";
item.dataset.nccProgressIndex = index;
const status = recipe.recipeIngredientStatus?.[index];
if (status === "unquantified") {
item.classList.add("ncc-quantity-warning");
@@ -215,6 +245,8 @@ Module.register("MMM-NextcloudCookbook", {
const instructionList = document.createElement("ol");
this.flattenInstructions(recipe.recipeInstructions || []).forEach((step, index) => {
const item = document.createElement("li");
item.dataset.nccProgressKind = "step";
item.dataset.nccProgressIndex = index;
const completed = progress.completedSteps?.includes(index);
if (completed) item.classList.add("ncc-completed");
const label = document.createElement("label");
+5 -1
View File
@@ -20,7 +20,8 @@ link; it should look like `https://cloud.example.test/s/SHARE_TOKEN`.
config: {
shareUrl: "https://cloud.example.test/s/SHARE_TOKEN",
controlUrl: "http://mirror.example.test:8080/MMM-NextcloudCookbook/control",
layout: "stacked"
layout: "stacked",
theme: "light"
}
}
```
@@ -38,6 +39,9 @@ the controller and recipe overlay also offer a toggle, and the chosen theme is
saved locally. After exiting recipe mode, use **Resume last recipe** in the
controller to restore its portions and completion marks.
Ingredients and instructions can be marked complete from either the phone
controller or a touchscreen on the mirror; both views remain synchronized.
When portions change, the display shows both the selected yield and the
recipe's original yield. Ingredients without a leading quantity are marked in
yellow because they cannot be scaled; unsupported or imprecise numeric scaling
+1 -2
View File
@@ -106,7 +106,7 @@ function renderActiveRecipe() {
activeRecipeName.textContent = recipe.name || "Untitled recipe";
const original = recipe.originalRecipeYield;
activeRecipeYield.textContent = original && original !== recipe.recipeYield
? `Yield: ${recipe.recipeYield} (original: ${original})`
? `Yield: ${recipe.recipeYield} (originally ${original})`
: `Yield: ${recipe.recipeYield || "Not specified"}`;
renderChecklist(activeIngredients, recipe.recipeIngredient || [], progress.completedIngredients, "ingredient",
recipe.recipeIngredientStatus || []);
@@ -162,7 +162,6 @@ async function updateProgress(kind, index, completed) {
body: JSON.stringify({ kind, index, completed })
});
applyState(state);
render();
} catch (error) {
status.textContent = error.message;
render();
+18 -2
View File
@@ -122,8 +122,15 @@ button {
}
.checklist li { padding-left: .2rem; }
.checklist label { display: flex; gap: .6rem; align-items: flex-start; }
.checklist input { margin-top: .25rem; min-width: 1.1rem; min-height: 1.1rem; }
.checklist label { display: flex; gap: .6rem; align-items: flex-start; margin: 0; font-weight: normal; }
.checklist input {
width: 1.2rem;
min-width: 1.2rem;
height: 1.2rem;
min-height: 1.2rem;
margin: .18rem 0 0;
padding: 0;
}
.checklist .completed { color: #777; text-decoration: line-through; }
.checklist .quantity-warning { color: #876500; }
.checklist .quantity-error { color: #a2342b; }
@@ -135,3 +142,12 @@ button {
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
@media (max-width: 560px) {
main { padding: 18px 14px 44px; }
header { align-items: flex-start; flex-direction: column; }
.header-actions { width: 100%; justify-content: stretch; }
.header-actions button { flex: 1 1 auto; }
.active-recipe { margin: 1rem 0; padding: .85rem; }
.checklist { gap: .55rem; padding-left: 1.2rem; }
}