video wishlist badge: stop the second writer clobbering it with the TV-only count

the other half of the bug: the endpoint total was fixed, but setCounts() (movie/
episode load) AND setYtCounts() (youtube load) ALSO wrote the nav badge from their
OWN partial state. these load separately, and on the dashboard only one runs — so
whichever fired last overwrote the badge with a partial count (correct number,
then 'switches' to TV-only).

fix: the /wishlist/counts endpoint is the single source of truth for the grand
total; both setters now just call refreshBadge() to (re)sync from it instead of
computing a partial sum. regression test pins it.
This commit is contained in:
BoulderBadgeDad 2026-06-25 23:21:59 -07:00
parent 3fa7b48f4c
commit 1722618c6d
2 changed files with 21 additions and 1 deletions

View file

@ -130,3 +130,16 @@ def test_clear_button_wired_for_all_tabs():
assert "kind: kind" in _JS # clears the active tab
# shown only when the active tab has items
assert "function updateClearBtn(" in _JS
def test_nav_badge_uses_the_endpoint_total_not_a_partial_sum():
# regression: movie/episode and YouTube counts load separately, so neither setter may
# compute the nav badge from its partial state (it 'switches' to the TV-only number).
# Both must defer to the authoritative /wishlist/counts endpoint via refreshBadge().
import re
sc = _JS[_JS.index("function setCounts("):_JS.index("function setYtCounts(")]
syc = _JS[_JS.index("function setYtCounts("):]
syc = syc[:syc.index("function ", 1)]
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

View file

@ -299,12 +299,18 @@
}
// ── counts / badges / pager ───────────────────────────────────────────────
// The nav badge is the WHOLE wishlist (movies + episodes + YouTube videos). movie/episode
// and YouTube counts arrive from SEPARATE loads, and on the dashboard only one of them
// runs — so neither setter can compute the whole total from its partial state without
// clobbering the other (the bug: badge shows the right number, then 'switches' to the
// TV-only count). The endpoint /wishlist/counts is the single source of truth for the
// grand total, so the setters just (re)sync the nav badge from it.
function setCounts(counts) {
state.counts = { movie: (counts && counts.movie) || 0, show: (counts && counts.show) || 0,
episode: (counts && counts.episode) || 0 };
var cm = $('[data-vwsh-count-movie]'); if (cm) cm.textContent = state.counts.movie;
var cs = $('[data-vwsh-count-show]'); if (cs) cs.textContent = state.counts.show;
updateBadges(counts && counts.total != null ? counts.total : (state.counts.movie + state.counts.episode));
refreshBadge(); // authoritative grand total
updateSub();
updateClearBtn();
}
@ -312,6 +318,7 @@
state.ytChannel = (counts && counts.channel) || 0;
state.ytVideo = (counts && counts.video) || 0;
var cy = $('[data-vwsh-count-youtube]'); if (cy) cy.textContent = state.ytVideo;
refreshBadge(); // authoritative grand total
updateSub();
updateClearBtn();
}