66 lines
2.1 KiB
Django/Jinja
66 lines
2.1 KiB
Django/Jinja
{% extends "base.html.jinja" %}
|
|
|
|
{% block content %}
|
|
<section class="max-w-4xl mx-auto px-4 pt-12 pb-12">
|
|
<h1 class="text-2xl font-bold mb-8">Notifications</h1>
|
|
|
|
<div id="notifications-list">
|
|
{% include "components/notifications_list.html.jinja" %}
|
|
</div>
|
|
</section>
|
|
<script>
|
|
(function() {
|
|
const container = document.getElementById("notifications-list");
|
|
if (!container) return;
|
|
|
|
// Track which release slugs the user has manually toggled open,
|
|
// so we don't slam them shut on the next poll.
|
|
const userToggled = new Set();
|
|
container.addEventListener("toggle", function(e) {
|
|
const details = e.target;
|
|
if (details.tagName !== "DETAILS") return;
|
|
const slug = details.dataset.slug;
|
|
if (!slug) return;
|
|
if (details.open) {
|
|
userToggled.add(slug);
|
|
} else {
|
|
userToggled.delete(slug);
|
|
}
|
|
}, true);
|
|
|
|
async function refresh() {
|
|
try {
|
|
const res = await fetch("/notifications?_partial=1", { credentials: "same-origin" });
|
|
if (!res.ok) return;
|
|
const html = await res.text();
|
|
container.innerHTML = html;
|
|
// Re-open any releases the user manually toggled open.
|
|
userToggled.forEach(function(slug) {
|
|
const el = container.querySelector('details[data-slug="' + slug + '"]');
|
|
if (el) el.open = true;
|
|
});
|
|
// Clean up slugs no longer in the DOM.
|
|
userToggled.forEach(function(slug) {
|
|
if (!container.querySelector('details[data-slug="' + slug + '"]')) {
|
|
userToggled.delete(slug);
|
|
}
|
|
});
|
|
} catch {}
|
|
}
|
|
|
|
let timer = setInterval(refresh, 10000);
|
|
|
|
document.addEventListener("visibilitychange", () => {
|
|
if (document.hidden) {
|
|
clearInterval(timer);
|
|
timer = null;
|
|
} else {
|
|
// Immediate refresh on tab focus, then resume interval.
|
|
refresh();
|
|
timer = setInterval(refresh, 10000);
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
{% endblock %}
|