webui: sync organize-by-playlist toggles + stop dashboard poller 401-spam while locked

- The download-modal 'Organize by Playlist' toggle had no onchange, so flipping
  it never saved or synced the saved per-playlist preference. Add the handler
  (source auto-derived from the ref) so both controls read/write the one
  organize_by_playlist value — manual action persists, the other reflects it.
- loadDashboardSyncHistory polled /api/sync/history every 30s even while the
  launch-PIN/login gate was active, 401-spamming the log. Skip when locked, and
  on a 401 (stale session after a restart) surface the unlock screen so it
  self-heals instead of spamming.
This commit is contained in:
BoulderBadgeDad 2026-06-12 13:28:24 -07:00
parent 94a0070fa8
commit 550fca0fe5
2 changed files with 20 additions and 1 deletions

View file

@ -1916,11 +1916,28 @@ setInterval(() => {
}, 30000);
async function loadDashboardSyncHistory() {
// Don't poll the auth-gated sync-history endpoint while the app is locked —
// it would 401 every 30s cycle (the result is discarded anyway). Resumes
// automatically on unlock (init.js removes 'app-locked').
if (document.body.classList.contains('app-locked')) return;
const container = document.getElementById('sync-history-cards');
if (!container) return;
try {
const response = await fetch('/api/sync/history?limit=10');
if (response.status === 401) {
// Session lapsed (e.g. the server restarted) while this tab still
// believed it was unlocked, so the guard above couldn't fire. Surface
// the correct unlock screen — both add 'app-locked', which stops the
// poll until the user re-authenticates (same as a fresh page load).
const info = await response.json().catch(() => ({}));
if (info.login_required && typeof showLoginScreen === 'function') {
showLoginScreen();
} else if (typeof showLaunchPinScreen === 'function') {
showLaunchPinScreen();
}
return;
}
if (!response.ok) return;
const data = await response.json();

View file

@ -1007,9 +1007,11 @@ function normalizePlaylistOrganizeRef(playlistRef, source = 'spotify') {
}
function downloadMissingModalOrganizeCheckboxHtml(playlistId) {
const safeId = String(playlistId).replace(/'/g, "\\'");
return `
<label class="force-download-toggle">
<input type="checkbox" id="playlist-folder-mode-${playlistId}" class="playlist-folder-mode-sync">
<input type="checkbox" id="playlist-folder-mode-${playlistId}" class="playlist-folder-mode-sync"
onchange="onPlaylistOrganizePreferenceChange('${safeId}', this.checked, playlistOrganizeSourceForRef('${safeId}'))">
<span>Organize by Playlist (Downloads/Playlist/Artist - Track.ext)</span>
</label>`;
}