Files
Bastian Wagner 13864a77f7 Auto-dismiss sync toasts after a few seconds
Fixes two issues found during manual verification:
- fragments/toast.html was missing class="toast-container" on the
  swapped-in element, so the container lost its fixed-position
  styling after the first swap.
- htmx:oobAfterSwap's event.detail.target is the *old* element that
  just got replaced (outerHTML oob-swaps detach it), so the dismiss
  timer must look up the live #toast-container by id instead of
  trusting that stale reference.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-16 13:32:03 +02:00

58 lines
1.9 KiB
JavaScript

function formatCountdown(remainingMs) {
if (remainingMs <= 0) {
return "due now";
}
const totalSeconds = Math.floor(remainingMs / 1000);
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
const pad = (n) => String(n).padStart(2, "0");
if (hours > 0) {
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
}
return `${pad(minutes)}:${pad(seconds)}`;
}
function startSyncCountdown(el) {
const target = new Date(el.dataset.utc);
if (Number.isNaN(target.getTime())) {
return;
}
el.title = `${target.toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" })} · ${el.dataset.utc} UTC`;
let intervalId = null;
const tick = () => {
const remaining = target.getTime() - Date.now();
el.textContent = formatCountdown(remaining);
if (remaining <= 0 && intervalId !== null) {
clearInterval(intervalId);
}
};
tick();
if (target.getTime() - Date.now() > 0) {
intervalId = setInterval(tick, 1000);
}
}
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll("time.next-sync[data-utc]").forEach(startSyncCountdown);
});
document.body.addEventListener("htmx:oobAfterSwap", (event) => {
if (event.detail.target.id !== "toast-container") {
return;
}
// event.detail.target is the *old* element htmx just swapped out (an
// outerHTML oob-swap detaches it), so look the live one up by id
// rather than trusting that reference.
const container = document.getElementById("toast-container");
const toast = container ? container.querySelector(".toast") : null;
if (!toast) {
return;
}
setTimeout(() => {
toast.classList.add("toast-leaving");
setTimeout(() => toast.remove(), 300);
}, 4000);
});