userplaylists in editor picks + refresh button for playlist suggestions
This commit is contained in:
parent
562e8aa6b2
commit
49c054e64a
4 changed files with 85 additions and 6 deletions
27
index.html
27
index.html
|
|
@ -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>
|
||||
|
|
|
|||
46
js/ui.js
46
js/ui.js
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
11
styles.css
11
styles.css
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue