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();
|
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) ----------
|
// ---------- exercise dialog (add / edit a training exercise) ----------
|
||||||
const exerciseDialog = document.getElementById("exercise-dialog");
|
const exerciseDialog = document.getElementById("exercise-dialog");
|
||||||
const exerciseForm = document.getElementById("exercise-form");
|
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": "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": "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" },
|
{ "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>
|
<p id="empty-state" class="empty">No events logged for this day.</p>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<footer class="app-footer">
|
||||||
|
<button type="button" id="changelog-btn" class="linklike">Changelog</button>
|
||||||
|
</footer>
|
||||||
</div><!-- /#app -->
|
</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">
|
<dialog id="settings-dialog">
|
||||||
<form method="dialog" id="settings-form">
|
<form method="dialog" id="settings-form">
|
||||||
<h3>Puppy settings</h3>
|
<h3>Puppy settings</h3>
|
||||||
|
|||||||
@@ -695,6 +695,40 @@ input.switch:checked::after { transform: translateX(18px); }
|
|||||||
opacity: 0.92;
|
opacity: 0.92;
|
||||||
}
|
}
|
||||||
.update-changelog[hidden] { display: none; }
|
.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-msg { font-size: 0.9rem; font-weight: 600; }
|
||||||
.update-banner-actions { display: flex; gap: 8px; }
|
.update-banner-actions { display: flex; gap: 8px; }
|
||||||
.update-banner-btn {
|
.update-banner-btn {
|
||||||
|
|||||||
Reference in New Issue
Block a user