Log an event that happened "now" in one tap

Logging a quick pee/poo/meal/sleep boundary took two taps: open the note
dialog, then Save. Make a single tap on a quick-action log the event
immediately at the current time.

Non-weight quick actions now call addEvent(type, "", Date.now()) directly
and show a brief snackbar with Undo (deletes the just-added event) and Add
note (opens the edit dialog for it), so notes, photos and custom times are
never lost. Weigh-ins still open the dialog since they need a value.
addEvent now returns the created event so the snackbar can reference it.
This commit is contained in:
Alexander Heldt
2026-07-10 12:44:41 +00:00
parent 01ad078b2a
commit d267ebef86
3 changed files with 105 additions and 3 deletions
+55 -3
View File
@@ -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());