Progressive track list rendering for large playlists
This commit is contained in:
parent
ed184f9bb8
commit
2c56b23c27
2 changed files with 72 additions and 0 deletions
|
|
@ -5601,6 +5601,65 @@ let currentModalPlaylistId = null;
|
||||||
// PHASE 2: Local cancelled track management (GUI PARITY)
|
// PHASE 2: Local cancelled track management (GUI PARITY)
|
||||||
let cancelledTracks = new Set(); // Track cancelled track indices like GUI's cancelled_tracks
|
let cancelledTracks = new Set(); // Track cancelled track indices like GUI's cancelled_tracks
|
||||||
|
|
||||||
|
const TRACK_RENDER_BATCH_SIZE = 100;
|
||||||
|
|
||||||
|
function applyProgressiveTrackRendering(playlistId, totalTrackCount) {
|
||||||
|
if (totalTrackCount <= TRACK_RENDER_BATCH_SIZE) return;
|
||||||
|
|
||||||
|
const modal = document.getElementById(`download-missing-modal-${playlistId}`);
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const tbody = document.getElementById(`download-tracks-tbody-${playlistId}`);
|
||||||
|
if (!tbody) return;
|
||||||
|
|
||||||
|
const rows = tbody.querySelectorAll('tr[data-track-index]');
|
||||||
|
if (rows.length <= TRACK_RENDER_BATCH_SIZE) return;
|
||||||
|
|
||||||
|
// Hide rows beyond first batch
|
||||||
|
for (let i = TRACK_RENDER_BATCH_SIZE; i < rows.length; i++) {
|
||||||
|
rows[i].classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
let revealedCount = TRACK_RENDER_BATCH_SIZE;
|
||||||
|
|
||||||
|
// Append indicator into .download-tracks-title
|
||||||
|
const titleEl = modal.querySelector('.download-tracks-title');
|
||||||
|
if (titleEl) {
|
||||||
|
const indicator = document.createElement('span');
|
||||||
|
indicator.className = 'track-render-indicator';
|
||||||
|
indicator.id = `track-render-indicator-${playlistId}`;
|
||||||
|
indicator.textContent = `Showing ${revealedCount} of ${totalTrackCount} tracks`;
|
||||||
|
titleEl.appendChild(indicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scroll listener on table container
|
||||||
|
const container = modal.querySelector('.download-tracks-table-container');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
container.addEventListener('scroll', function onScroll() {
|
||||||
|
const scrollBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
|
||||||
|
if (scrollBottom > 200) return;
|
||||||
|
if (revealedCount >= rows.length) return;
|
||||||
|
|
||||||
|
const nextEnd = Math.min(revealedCount + TRACK_RENDER_BATCH_SIZE, rows.length);
|
||||||
|
for (let i = revealedCount; i < nextEnd; i++) {
|
||||||
|
rows[i].classList.remove('hidden');
|
||||||
|
}
|
||||||
|
revealedCount = nextEnd;
|
||||||
|
|
||||||
|
const indicator = document.getElementById(`track-render-indicator-${playlistId}`);
|
||||||
|
if (indicator) {
|
||||||
|
indicator.textContent = revealedCount >= rows.length
|
||||||
|
? `Showing all ${totalTrackCount} tracks`
|
||||||
|
: `Showing ${revealedCount} of ${totalTrackCount} tracks`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (revealedCount >= rows.length) {
|
||||||
|
container.removeEventListener('scroll', onScroll);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function openDownloadMissingModal(playlistId) {
|
async function openDownloadMissingModal(playlistId) {
|
||||||
showLoadingOverlay('Loading playlist...');
|
showLoadingOverlay('Loading playlist...');
|
||||||
|
|
||||||
|
|
@ -5765,6 +5824,7 @@ async function openDownloadMissingModal(playlistId) {
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
applyProgressiveTrackRendering(playlistId, tracks.length);
|
||||||
modal.style.display = 'flex';
|
modal.style.display = 'flex';
|
||||||
hideLoadingOverlay();
|
hideLoadingOverlay();
|
||||||
}
|
}
|
||||||
|
|
@ -6123,6 +6183,7 @@ async function openDownloadMissingModalForYouTube(virtualPlaylistId, playlistNam
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
applyProgressiveTrackRendering(virtualPlaylistId, spotifyTracks.length);
|
||||||
modal.style.display = 'flex';
|
modal.style.display = 'flex';
|
||||||
hideLoadingOverlay();
|
hideLoadingOverlay();
|
||||||
}
|
}
|
||||||
|
|
@ -7326,6 +7387,7 @@ async function openDownloadMissingWishlistModal(category = null) {
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
applyProgressiveTrackRendering(playlistId, tracks.length);
|
||||||
modal.style.display = 'flex';
|
modal.style.display = 'flex';
|
||||||
hideLoadingOverlay();
|
hideLoadingOverlay();
|
||||||
WishlistModalState.setVisible(); // Track that new wishlist modal is now visible
|
WishlistModalState.setVisible(); // Track that new wishlist modal is now visible
|
||||||
|
|
@ -14989,6 +15051,7 @@ async function openDownloadMissingModalForTidal(virtualPlaylistId, playlistName,
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
applyProgressiveTrackRendering(virtualPlaylistId, spotifyTracks.length);
|
||||||
modal.style.display = 'flex';
|
modal.style.display = 'flex';
|
||||||
hideLoadingOverlay();
|
hideLoadingOverlay();
|
||||||
}
|
}
|
||||||
|
|
@ -22079,6 +22142,7 @@ async function openDownloadMissingModalForArtistAlbum(virtualPlaylistId, playlis
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
applyProgressiveTrackRendering(virtualPlaylistId, spotifyTracks.length);
|
||||||
modal.style.display = 'flex';
|
modal.style.display = 'flex';
|
||||||
hideLoadingOverlay();
|
hideLoadingOverlay();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10326,6 +10326,14 @@ body {
|
||||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.track-render-indicator {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #888888;
|
||||||
|
margin-left: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.download-tracks-table-container {
|
.download-tracks-table-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue