userplaylists in editor picks + refresh button for playlist suggestions

This commit is contained in:
Eduard Prigoana 2026-02-23 17:41:45 +00:00
parent 562e8aa6b2
commit 49c054e64a
4 changed files with 85 additions and 6 deletions

View file

@ -2477,8 +2477,31 @@
id="playlist-section-recommended"
style="display: none; margin-top: 3rem"
>
<h2 class="section-title">Recommended Songs</h2>
<p style="color: grey; margin-bottom: 15px">Suggested Songs From Your Playlist</p>
<div class="section-header-row">
<div>
<h2 class="section-title">Recommended Songs</h2>
<p style="color: grey; margin-bottom: 15px">Suggested Songs From Your Playlist</p>
</div>
<button
id="refresh-recommended-songs-btn"
class="btn-secondary"
title="Refresh Recommendations"
>
<svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" />
<path d="M21 3v5h-5" />
</svg>
</button>
</div>
<div class="track-list" id="playlist-detail-recommended"></div>
</section>
</section>

View file

@ -492,7 +492,7 @@ export class UIRenderer {
});
}
createUserPlaylistCardHTML(playlist) {
createUserPlaylistCardHTML(playlist, customSubtitle = null) {
let imageHTML = '';
if (playlist.cover) {
imageHTML = `<img src="${playlist.cover}" alt="${playlist.name}" class="card-image" loading="lazy">`;
@ -529,6 +529,8 @@ export class UIRenderer {
}
const isCompact = cardSettings.isCompactAlbum();
const subtitle =
customSubtitle || `${playlist.tracks ? playlist.tracks.length : playlist.numberOfTracks || 0} tracks`;
return this.createBaseCardHTML({
type: 'user-playlist', // Note: data-type logic in base might need adjustment if it uses this for buttons.
@ -536,7 +538,7 @@ export class UIRenderer {
id: playlist.id,
href: `/userplaylist/${playlist.id}`,
title: escapeHtml(playlist.name),
subtitle: `${playlist.tracks ? playlist.tracks.length : playlist.numberOfTracks || 0} tracks`,
subtitle,
imageHTML: imageHTML,
actionButtonsHTML: `
<button class="edit-playlist-btn" data-action="edit-playlist" title="Edit Playlist">
@ -1782,6 +1784,26 @@ export class UIRenderer {
itemsToStore.push({ el: null, data: track, type: 'track' });
}
}
} else if (item.type === 'user-playlist') {
if (item.id && item.name) {
const playlist = {
id: item.id,
name: item.name,
cover: item.cover,
tracks: item.tracks || [],
numberOfTracks: item.numberOfTracks || (item.tracks ? item.tracks.length : 0),
};
const subtitle = item.username ? `by ${item.username}` : null;
cardsHTML.push(this.createUserPlaylistCardHTML(playlist, subtitle));
itemsToStore.push({ el: null, data: playlist, type: 'user-playlist' });
} else {
const playlist = await syncManager.getPublicPlaylist(item.id);
if (playlist) {
const subtitle = item.username ? `by ${item.username}` : null;
cardsHTML.push(this.createUserPlaylistCardHTML(playlist, subtitle));
itemsToStore.push({ el: null, data: playlist, type: 'user-playlist' });
}
}
}
} catch (e) {
console.warn(`Failed to load ${item.type} ${item.id}:`, e);
@ -2404,7 +2426,7 @@ export class UIRenderer {
}
}
async loadRecommendedSongsForPlaylist(tracks) {
async loadRecommendedSongsForPlaylist(tracks, forceRefresh = false) {
const recommendedSection = document.getElementById('playlist-section-recommended');
const recommendedContainer = document.getElementById('playlist-detail-recommended');
@ -2413,8 +2435,12 @@ export class UIRenderer {
return;
}
if (forceRefresh) {
recommendedContainer.innerHTML = this.createSkeletonTracks(5, true);
}
try {
let recommendedTracks = await this.api.getRecommendedTracksForPlaylist(tracks, 20);
let recommendedTracks = await this.api.getRecommendedTracksForPlaylist(tracks, 20, forceRefresh);
// Filter out blocked tracks
const { contentBlockingSettings } = await import('./storage.js');
@ -2680,6 +2706,18 @@ export class UIRenderer {
// Load recommended songs thingy
if (ownedPlaylist) {
this.loadRecommendedSongsForPlaylist(tracks);
const refreshBtn = document.getElementById('refresh-recommended-songs-btn');
if (refreshBtn) {
refreshBtn.onclick = async () => {
const icon = refreshBtn.querySelector('svg');
if (icon) icon.style.animation = 'spin 1s linear infinite';
refreshBtn.disabled = true;
await this.loadRecommendedSongsForPlaylist(tracks, true);
if (icon) icon.style.animation = '';
refreshBtn.disabled = false;
};
}
}
// Render Actions (Sort, Shuffle, Edit, Delete, Share)

View file

@ -1,4 +1,11 @@
[
{
"type": "user-playlist",
"id": "e64ed040-57ab-4583-b047-8fb590b04750",
"name": "2edi",
"cover": "https://i.imgur.gg/jgYh3K6-favicon_(2).png",
"username": "edideaur"
},
{
"type": "album",
"id": 89313048,

View file

@ -1627,6 +1627,17 @@ input[type='search']::-webkit-search-cancel-button {
margin-bottom: var(--spacing-lg);
}
.section-header-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--spacing-md);
}
.section-header-row .section-title {
margin-bottom: 0;
}
.search-tabs {
display: flex;
gap: var(--spacing-xs);