diff --git a/src/app.js b/src/app.js
index 36dca93..064fc5f 100644
--- a/src/app.js
+++ b/src/app.js
@@ -258,7 +258,7 @@
function addEvent(type, note, at, photoId, weight) {
const events = loadAll();
const now = Date.now();
- events.push({
+ const ev = {
id: uuid(),
type,
at: Number.isFinite(at) ? at : now,
@@ -266,10 +266,12 @@
photoId: photoId || "",
weight: Number.isFinite(weight) ? weight : undefined,
updatedAt: now,
- });
+ };
+ events.push(ev);
saveAll(events);
scheduleSync();
render();
+ return ev;
}
function updateEvent(id, patch) {
@@ -1602,9 +1604,59 @@
}
});
+ // ---------- quick-log snackbar ----------
+ // A single tap on a quick-action logs the event at the current instant, then
+ // shows a brief snackbar to undo it or add detail — so the two-tap dialog flow
+ // is never required for a plain pee/poo/meal/sleep boundary. Weigh-ins still
+ // open the dialog since they need a value.
+ const snackbar = document.getElementById("snackbar");
+ const snackbarMsg = document.getElementById("snackbar-msg");
+ const snackbarUndo = document.getElementById("snackbar-undo");
+ const snackbarNote = document.getElementById("snackbar-note");
+ let snackbarEvent = null;
+ let snackbarTimer = null;
+
+ function hideSnackbar() {
+ clearTimeout(snackbarTimer);
+ snackbarTimer = null;
+ snackbarEvent = null;
+ snackbar.classList.remove("show");
+ snackbar.hidden = true;
+ }
+
+ function showSnackbar(msg, ev) {
+ snackbarEvent = ev;
+ snackbarMsg.textContent = msg;
+ snackbar.hidden = false;
+ void snackbar.offsetWidth; // reflow so the fade-in transition runs
+ snackbar.classList.add("show");
+ clearTimeout(snackbarTimer);
+ snackbarTimer = setTimeout(hideSnackbar, 5000);
+ }
+
+ function quickLog(type) {
+ const ev = addEvent(type, "", Date.now());
+ showSnackbar(`${EVENT_LABELS[type]} logged`, ev);
+ }
+
+ snackbarUndo.addEventListener("click", () => {
+ if (snackbarEvent) deleteEvent(snackbarEvent.id);
+ hideSnackbar();
+ });
+ snackbarNote.addEventListener("click", () => {
+ const ev = snackbarEvent;
+ hideSnackbar();
+ if (ev) openEditDialog(ev);
+ });
+
// ---------- wiring ----------
document.querySelectorAll("button.action").forEach(btn => {
- btn.addEventListener("click", () => openNoteDialog(btn.dataset.type));
+ btn.addEventListener("click", () => {
+ const type = btn.dataset.type;
+ // Weigh-ins need a typed value, so they keep the full dialog.
+ if (type === "weight") { openNoteDialog(type); return; }
+ quickLog(type);
+ });
});
dayPicker.value = ymd(new Date());
diff --git a/src/index.html b/src/index.html
index 8b89353..445b6c3 100644
--- a/src/index.html
+++ b/src/index.html
@@ -303,6 +303,13 @@
+
+