video wishlist badge: poll so it reflects server-side removals (downloads)

the remaining badge bug: when a download finishes it removes its item from the
wishlist SERVER-SIDE, which fires no frontend 'changed' event — so the badge went
stale and only 'jumped' (e.g. down to the tv-only count) on the next navigation.

add a lightweight badge poll: refreshes the authoritative /wishlist/counts every
8s while downloads are active (uses the downloads page's _vdpgAnyActive), every
30s idle, paused when the tab is hidden. now the count tracks down live as wishlist
items get downloaded. frontend add/remove events still fire instantly as before.
This commit is contained in:
BoulderBadgeDad 2026-06-26 00:19:54 -07:00
parent 1722618c6d
commit f0e1fda0fc
2 changed files with 23 additions and 0 deletions

View file

@ -143,3 +143,12 @@ def test_nav_badge_uses_the_endpoint_total_not_a_partial_sum():
assert "refreshBadge();" in sc and "refreshBadge();" in syc
assert "state.counts.movie + state.counts.episode" not in _JS # old partial-total math gone
assert "/api/video/wishlist/counts" in _JS # the endpoint is the source
def test_nav_badge_polls_for_server_side_wishlist_changes():
# a finished download removes its wishlist item server-side and fires no frontend event,
# so the badge must poll the count to stay honest (faster while downloads are active).
assert "function scheduleBadgePoll(" in _JS
assert "_vdpgAnyActive" in _JS # polls quicker while downloads run
assert "document.hidden" in _JS # paused when the tab is hidden
assert "scheduleBadgePoll();" in _JS # started from init

View file

@ -600,11 +600,25 @@
function onShown(e) { if (e && e.detail === PAGE_ID) { state.page = 1; load(); refreshYtCount(); } }
// The nav badge also has to track wishlist changes that happen SERVER-SIDE — when a
// download finishes it removes its item from the wishlist, and that fires no frontend
// 'changed' event. So poll the authoritative count: quicker while downloads are active
// (the downloads page exposes _vdpgAnyActive), slower when idle, paused when hidden.
function scheduleBadgePoll() {
var active = (typeof window._vdpgAnyActive === 'function') && window._vdpgAnyActive();
if (scheduleBadgePoll._t) clearTimeout(scheduleBadgePoll._t);
scheduleBadgePoll._t = setTimeout(function () {
if (!document.hidden) refreshBadge();
scheduleBadgePoll();
}, active ? 8000 : 30000);
}
function init() {
wire();
document.addEventListener('soulsync:video-page-shown', onShown);
refreshBadge();
refreshYtCount();
scheduleBadgePoll();
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);