editor's picks and open in new tab

This commit is contained in:
Eduard Prigoana 2026-02-08 22:59:21 +00:00
parent 2522e0e5be
commit 8bb3b285b1
7 changed files with 121 additions and 2 deletions

View file

@ -45,6 +45,7 @@
<li data-action="go-to-artist" data-type-filter="track,album">Go to artist</li>
<li data-action="go-to-album" data-type-filter="track">Go to album</li>
<li data-action="copy-link">Copy link</li>
<li data-action="open-in-new-tab">Open in new tab</li>
<li data-action="track-info" data-type-filter="track">Track info</li>
<li data-action="open-original-url" data-type-filter="track">Open original URL</li>
<li data-action="download">Download</li>
@ -1164,6 +1165,19 @@
</div>
<div class="card-grid" id="home-recommended-albums"></div>
</section>
<section class="content-section" id="home-editors-picks-section">
<div
style="
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1rem;
"
>
<h2 class="section-title" style="margin-bottom: 0">Editor's Picks</h2>
</div>
<div class="card-grid" id="home-editors-picks"></div>
</section>
<section class="content-section">
<div
style="
@ -2267,6 +2281,18 @@
<span class="slider"></span>
</label>
</div>
<div class="setting-item">
<div class="info">
<span class="label">Show Editor's Picks</span>
<span class="description"
>Display curated album selections on the home page</span
>
</div>
<label class="toggle-switch">
<input type="checkbox" id="show-editors-picks-toggle" checked />
<span class="slider"></span>
</label>
</div>
</div>
<div class="settings-group">

View file

@ -1067,6 +1067,15 @@ export async function handleTrackAction(
navigator.clipboard.writeText(url).then(() => {
showNotification('Link copied to clipboard!');
});
} else if (action === 'open-in-new-tab') {
// Use stored href from card if available, otherwise construct URL
const contextMenu = document.getElementById('context-menu');
const storedHref = contextMenu?._contextHref;
const url = storedHref
? `${window.location.origin}${storedHref}`
: `${window.location.origin}/track/${item.id || item.uuid}`;
window.open(url, '_blank');
} else if (action === 'track-info') {
// Show detailed track info modal
const isTracker = item.isTracker;

View file

@ -919,6 +919,14 @@ export function initializeSettings(scrobbler, player, api, ui) {
});
}
const showEditorsPicksToggle = document.getElementById('show-editors-picks-toggle');
if (showEditorsPicksToggle) {
showEditorsPicksToggle.checked = homePageSettings.shouldShowEditorsPicks();
showEditorsPicksToggle.addEventListener('change', (e) => {
homePageSettings.setShowEditorsPicks(e.target.checked);
});
}
// Sidebar Section Toggles
const sidebarShowHomeToggle = document.getElementById('sidebar-show-home-toggle');
if (sidebarShowHomeToggle) {

View file

@ -969,6 +969,21 @@ export const homePageSettings = {
setShowJumpBackIn(enabled) {
localStorage.setItem(this.SHOW_JUMP_BACK_IN_KEY, enabled ? 'true' : 'false');
},
SHOW_EDITORS_PICKS_KEY: 'home-show-editors-picks',
shouldShowEditorsPicks() {
try {
const val = localStorage.getItem(this.SHOW_EDITORS_PICKS_KEY);
return val === null ? true : val === 'true';
} catch {
return true;
}
},
setShowEditorsPicks(enabled) {
localStorage.setItem(this.SHOW_EDITORS_PICKS_KEY, enabled ? 'true' : 'false');
},
};
export const sidebarSectionSettings = {

View file

@ -1414,6 +1414,7 @@ export class UIRenderer {
this.renderHomeSongs();
this.renderHomeAlbums();
this.renderHomeEditorsPicks();
this.renderHomeArtists();
this.renderHomeRecent();
}
@ -1518,6 +1519,63 @@ export class UIRenderer {
}
}
async renderHomeEditorsPicks(forceRefresh = false) {
const picksContainer = document.getElementById('home-editors-picks');
const section = document.getElementById('home-editors-picks-section');
if (!homePageSettings.shouldShowEditorsPicks()) {
if (section) section.style.display = 'none';
return;
}
if (section) section.style.display = '';
if (picksContainer) {
if (forceRefresh) picksContainer.innerHTML = this.createSkeletonCards(6);
else if (picksContainer.children.length > 0 && !picksContainer.querySelector('.skeleton')) return;
try {
const response = await fetch('/editors-picks.json');
if (!response.ok) throw new Error("Failed to load editor's picks");
const data = await response.json();
const albumIds = data.albums || [];
if (albumIds.length === 0) {
picksContainer.innerHTML = createPlaceholder("No editor's picks available.");
return;
}
// Fetch album details
const albums = [];
for (const albumId of albumIds.slice(0, 12)) {
try {
const result = await this.api.getAlbum(albumId);
if (result && result.album) albums.push(result.album);
} catch (e) {
console.warn(`Failed to load album ${albumId}:`, e);
}
}
if (albums.length > 0) {
picksContainer.innerHTML = albums.map((a) => this.createAlbumCardHTML(a)).join('');
albums.forEach((a) => {
const el = picksContainer.querySelector(`[data-album-id="${a.id}"]`);
if (el) {
trackDataStore.set(el, a);
this.updateLikeState(el, 'album', a.id);
}
});
} else {
picksContainer.innerHTML = createPlaceholder("No editor's picks available.");
}
} catch (e) {
console.error("Failed to load editor's picks:", e);
picksContainer.innerHTML = createPlaceholder("Failed to load editor's picks.");
}
}
}
async renderHomeArtists(forceRefresh = false) {
const artistsContainer = document.getElementById('home-recommended-artists');
const section = artistsContainer?.closest('.content-section');

View file

@ -0,0 +1,5 @@
{
"name": "Editor's Picks",
"description": "Curated selection of standout albums handpicked by the monochrome team",
"albums": ["4527433", "90502209"]
}

View file

@ -2,8 +2,6 @@
Sorted by ease of implementation (easiest to hardest):
- [ ] Editor's Picks: Create a JSON file of curated album IDs with metadata for the home screen. Include an option to disable in settings to avoid extra API calls.
- [ ] Update notifications: Add ability to show the update popup in settings, with an option to automatically update (enabled by default)
- [ ] Volume curve option: Add setting to switch between exponential and linear volume scaling
- [ ] Custom fonts: Allow users to change fonts in settings or add custom fonts from Google Fonts or direct links