Add a Changelog button at the bottom of the page
Opens a dialog listing the loaded build's full changelog, grouped by date. Fetched cache-first through the service worker, so the list always matches the running version; the update banner still covers what a waiting build adds.
This commit is contained in:
+45
@@ -1916,6 +1916,51 @@
|
||||
settingsDialog.close();
|
||||
});
|
||||
|
||||
// ---------- changelog dialog ----------
|
||||
// Shows the *loaded* build's full changelog: the plain URL is served
|
||||
// cache-first by the controlling service worker, so the list always matches
|
||||
// the version actually running (the update banner handles what's newer).
|
||||
const changelogDialog = document.getElementById("changelog-dialog");
|
||||
const changelogList = document.getElementById("changelog-list");
|
||||
const changelogEmpty = document.getElementById("changelog-empty");
|
||||
|
||||
function formatChangelogDate(iso) {
|
||||
const [y, m, d] = (iso || "").split("-").map(Number);
|
||||
if (!y || !m || !d) return iso || "";
|
||||
return new Date(y, m - 1, d).toLocaleDateString(undefined, {
|
||||
year: "numeric", month: "short", day: "numeric",
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById("changelog-btn").addEventListener("click", async () => {
|
||||
let entries = [];
|
||||
try {
|
||||
const res = await fetch("changelog.json");
|
||||
if (res.ok) {
|
||||
const body = await res.json();
|
||||
if (Array.isArray(body)) entries = body.filter(e => e && e.text);
|
||||
}
|
||||
} catch { /* fall through to empty state */ }
|
||||
|
||||
changelogList.innerHTML = "";
|
||||
changelogEmpty.hidden = entries.length > 0;
|
||||
let lastDate = null;
|
||||
for (const e of entries) {
|
||||
if (e.date !== lastDate) {
|
||||
lastDate = e.date;
|
||||
const dt = document.createElement("li");
|
||||
dt.className = "changelog-date";
|
||||
dt.textContent = formatChangelogDate(e.date);
|
||||
changelogList.appendChild(dt);
|
||||
}
|
||||
const li = document.createElement("li");
|
||||
li.className = "changelog-entry";
|
||||
li.textContent = e.text;
|
||||
changelogList.appendChild(li);
|
||||
}
|
||||
changelogDialog.showModal();
|
||||
});
|
||||
|
||||
// ---------- exercise dialog (add / edit a training exercise) ----------
|
||||
const exerciseDialog = document.getElementById("exercise-dialog");
|
||||
const exerciseForm = document.getElementById("exercise-form");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
[
|
||||
{ "date": "2026-07-12", "text": "Browse the full changelog from the bottom of the page" },
|
||||
{ "date": "2026-07-12", "text": "Finer-grained y-axis on the daily counts chart" },
|
||||
{ "date": "2026-07-12", "text": "The update banner now lists what changed in the new version" },
|
||||
{ "date": "2026-07-12", "text": "Fixed adding an exercise on iPhone: stale app updates, and the keyboard's Go key discarding the input" },
|
||||
|
||||
@@ -230,8 +230,23 @@
|
||||
<p id="empty-state" class="empty">No events logged for this day.</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="app-footer">
|
||||
<button type="button" id="changelog-btn" class="linklike">Changelog</button>
|
||||
</footer>
|
||||
</div><!-- /#app -->
|
||||
|
||||
<dialog id="changelog-dialog">
|
||||
<form method="dialog" id="changelog-form">
|
||||
<h3>Changelog</h3>
|
||||
<ul id="changelog-list" class="changelog-list"></ul>
|
||||
<p id="changelog-empty" class="empty" hidden>No changelog available.</p>
|
||||
<menu>
|
||||
<button value="close">Close</button>
|
||||
</menu>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<dialog id="settings-dialog">
|
||||
<form method="dialog" id="settings-form">
|
||||
<h3>Puppy settings</h3>
|
||||
|
||||
@@ -695,6 +695,40 @@ input.switch:checked::after { transform: translateX(18px); }
|
||||
opacity: 0.92;
|
||||
}
|
||||
.update-changelog[hidden] { display: none; }
|
||||
|
||||
.app-footer {
|
||||
text-align: center;
|
||||
padding: 4px 0 24px;
|
||||
}
|
||||
.app-footer .linklike { color: var(--muted); font-size: 0.8rem; }
|
||||
|
||||
.changelog-list {
|
||||
list-style: none;
|
||||
margin: 0 0 12px;
|
||||
padding: 0;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.changelog-date {
|
||||
color: var(--muted);
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
margin: 10px 0 4px;
|
||||
}
|
||||
.changelog-date:first-child { margin-top: 0; }
|
||||
.changelog-entry {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.45;
|
||||
padding: 3px 0 3px 14px;
|
||||
position: relative;
|
||||
}
|
||||
.changelog-entry::before {
|
||||
content: "•";
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
color: var(--accent);
|
||||
}
|
||||
.update-banner-msg { font-size: 0.9rem; font-weight: 600; }
|
||||
.update-banner-actions { display: flex; gap: 8px; }
|
||||
.update-banner-btn {
|
||||
|
||||
Reference in New Issue
Block a user