ui(downloads): Unverified review rows get the Quarantine-style card design
The Quarantine sub-view was reworked into rich cards (artwork, source line, row-click to expand an inline detail panel, consistent action cluster) but the Unverified sub-view was left on the generic download-row layout that opened a modal on click. Bring it to parity: - dedicated _verifUnverifiedRowHtml / _verifUnverifiedRows renderer, used via a sibling branch in _adlRender (mirrors the quarantine sub-view branch). - row click toggles an inline details panel (why flagged, download source, quality, file, downloaded-at), open-state keyed by stable id so it survives the 2 s poll re-render — same pattern as verifQuarInspect. - reuses the existing verif-quar-* / verif-actions / adl-row CSS (no new styles) and the existing play/compare/audit/approve/delete handlers. - NO grouping: each unverified import is its own track (grouping only makes sense for the quarantine alternates), per design intent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LWJk7EuM7YktQeNyqQwTZY
This commit is contained in:
parent
a42cf3b865
commit
027cf74d47
1 changed files with 90 additions and 0 deletions
|
|
@ -2760,6 +2760,84 @@ async function verifDelete(hid, btn) {
|
|||
} catch (e) { showToast && showToast('Delete failed', 'error'); }
|
||||
if (btn) btn.disabled = false;
|
||||
}
|
||||
|
||||
// ---- Unverified review rows (Quarantine-style cards) ----
|
||||
// The Unverified sub-view used to piggyback on the generic download row and
|
||||
// open a modal on click. These give it the same card design the Quarantine
|
||||
// sub-view got — row click expands an inline detail panel in place — minus the
|
||||
// grouping (each unverified import is its own track, nothing to group).
|
||||
let _verifUnvData = [];
|
||||
const _verifUnvOpenDetails = new Set();
|
||||
|
||||
function _verifUnvKey(dl) {
|
||||
return verifHistoryId(dl) || dl.task_id || '';
|
||||
}
|
||||
|
||||
function _verifUnvDetailRows(dl) {
|
||||
const reason = dl.verification_status === 'force_imported'
|
||||
? 'Accepted as the best available candidate after the retry budget was exhausted (version-mismatch fallback). A library AcoustID scan reports these as informational.'
|
||||
: 'AcoustID could not hard-confirm this file (ambiguous / cross-script metadata / no fingerprint match). Imported, but not verified.';
|
||||
const source = dl.batch_source || dl.batch_name || '';
|
||||
return [
|
||||
`<div><span class="verif-detail-label">Why flagged:</span> ${_adlEsc(reason)}</div>`,
|
||||
source ? `<div><span class="verif-detail-label">Download source:</span> ${_adlEsc(source)}</div>` : '',
|
||||
dl.quality ? `<div><span class="verif-detail-label">Quality:</span> ${_adlEsc(dl.quality)}</div>` : '',
|
||||
dl.file_path ? `<div><span class="verif-detail-label">File:</span> ${_adlEsc(dl.file_path)}</div>` : '',
|
||||
dl.created_at ? `<div><span class="verif-detail-label">Downloaded:</span> ${_adlEsc(dl.created_at)}</div>` : '',
|
||||
].filter(Boolean).join('');
|
||||
}
|
||||
|
||||
function _verifUnverifiedRowHtml(dl, idx) {
|
||||
const hid = verifHistoryId(dl);
|
||||
const title = _adlEsc(dl.title || 'Unknown Track');
|
||||
const meta = [_adlEsc(dl.artist || ''), _adlEsc(dl.album || '')].filter(Boolean).join(' · ');
|
||||
const source = _adlEsc(dl.batch_source || dl.batch_name || '');
|
||||
const timeAgo = _verifTimeAgo(dl.created_at);
|
||||
const artHtml = dl.artwork
|
||||
? `<img class="adl-row-art" src="${_adlEsc(dl.artwork)}" alt="" onerror="this.style.display='none'">`
|
||||
: '<div class="adl-row-art adl-row-art-empty"></div>';
|
||||
const detailsOpen = _verifUnvOpenDetails.has(_verifUnvKey(dl));
|
||||
const actions = hid ? `
|
||||
<button class="verif-act verif-act-play" onclick="verifPlay('${hid}')" title="Play the downloaded file in the media player">▶</button>
|
||||
<button class="verif-act" onclick="verifCompare('${hid}', this)" title="Find this track on Soulseek/streaming sources and play it in the media player — compare against your file">⇆</button>
|
||||
<button class="verif-act" onclick="verifAudit('${hid}')" title="Open the full audit trail for this download (lifecycle, embedded tags, lyrics)">🔍</button>
|
||||
<button class="verif-act verif-act-ok" onclick="verifApprove('${hid}', this)" title="Approve: mark as human-verified (tag + DB). The AcoustID scanner will skip it.">✔</button>
|
||||
<button class="verif-act verif-act-del" onclick="verifDelete('${hid}', this)" title="Wrong file: delete it from disk and remove this entry">🗑</button>` : '';
|
||||
return `<div class="adl-row adl-row-completed verif-quar-row" data-task-id="${_adlEsc(dl.task_id || '')}" onclick="verifUnvInspect(${idx})" title="Click to show/hide details (why it was flagged, source, quality, file)">
|
||||
${artHtml}
|
||||
<div class="adl-row-info">
|
||||
<div class="adl-row-title">${title}</div>
|
||||
${meta ? `<div class="adl-row-meta">${meta}</div>` : ''}
|
||||
${source ? `<div class="adl-row-batch">${source}</div>` : ''}
|
||||
<div class="verif-quar-details" id="verif-unv-details-${idx}" style="display:${detailsOpen ? '' : 'none'}">${_verifUnvDetailRows(dl)}</div>
|
||||
</div>
|
||||
<div class="verif-actions" onclick="event.stopPropagation()">
|
||||
${_verifReasonBadge(dl)}
|
||||
${dl.quality ? `<span class="adl-quality-chip" title="Audio quality of the downloaded file (read from the file itself)">${_adlEsc(dl.quality)}</span>` : ''}
|
||||
${timeAgo ? `<span class="verif-time">${timeAgo}</span>` : ''}
|
||||
${actions}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function _verifUnverifiedRows(items) {
|
||||
_verifUnvData = items;
|
||||
if (!items.length) return '';
|
||||
return items.map((dl, idx) => _verifUnverifiedRowHtml(dl, idx)).join('');
|
||||
}
|
||||
|
||||
function verifUnvInspect(idx) {
|
||||
// Open-state lives in a Set keyed by the row's stable id (not the DOM) so it
|
||||
// survives the 2 s polling re-render — same pattern as verifQuarInspect.
|
||||
const dl = _verifUnvData[idx];
|
||||
if (!dl) return;
|
||||
const key = _verifUnvKey(dl);
|
||||
if (_verifUnvOpenDetails.has(key)) _verifUnvOpenDetails.delete(key);
|
||||
else _verifUnvOpenDetails.add(key);
|
||||
const el = document.getElementById(`verif-unv-details-${idx}`);
|
||||
if (el) el.style.display = _verifUnvOpenDetails.has(key) ? '' : 'none';
|
||||
}
|
||||
|
||||
// ---- Quarantine sub-view inside the ⚠ filter ----
|
||||
// The review queue covers BOTH kinds of suspect files: imported-but-
|
||||
// unconfirmed (unverified/force_imported) and not-imported-at-all
|
||||
|
|
@ -3293,6 +3371,18 @@ function _adlRender() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Unverified review sub-view: render the Quarantine-style cards (inline
|
||||
// expandable details), not the generic download rows.
|
||||
if (_adlFilter === 'unverified' && _verifSubView === 'unverified') {
|
||||
const uhtml = _verifUnverifiedRows(filtered);
|
||||
const uEmptyEl = document.getElementById('adl-empty');
|
||||
const uEmptyHtml = uEmptyEl ? uEmptyEl.outerHTML : '';
|
||||
list.innerHTML = uEmptyHtml + uhtml;
|
||||
const uNewEmpty = document.getElementById('adl-empty');
|
||||
if (uNewEmpty) uNewEmpty.style.display = uhtml ? 'none' : '';
|
||||
return;
|
||||
}
|
||||
|
||||
if (filtered.length === 0) {
|
||||
if (empty) empty.style.display = '';
|
||||
// Clear any existing rows but keep the empty message
|
||||
|
|
|
|||
Loading…
Reference in a new issue