diff --git a/index.html b/index.html
index a564724..0cc4e9d 100644
--- a/index.html
+++ b/index.html
@@ -3108,6 +3108,19 @@
+
+
+ Intercept Back to Close Modals
+ When pressing back, close open modals/panels first without navigating.
+ Press back again to actually go back.
+
+
+
diff --git a/js/app.js b/js/app.js
index 5507f3b..5af1126 100644
--- a/js/app.js
+++ b/js/app.js
@@ -2117,6 +2117,14 @@ document.addEventListener('DOMContentLoaded', async () => {
return;
}
+ // Intercept back navigation to close modals first if setting is enabled
+ if (event && modalSettings.shouldInterceptBackToClose() && modalSettings.hasOpenModalsOrPanels()) {
+ sidePanelManager.close();
+ modalSettings.closeAllModals();
+ history.pushState(history.state || { app: true }, '', window.location.pathname);
+ return;
+ }
+
// Close side panel (queue/lyrics) and modals on navigation if setting is enabled
if (modalSettings.shouldCloseOnNavigation()) {
sidePanelManager.close();
@@ -2293,6 +2301,12 @@ function showUpdateNotification(updateCallback) {
});
}
+function escapeHtml(text) {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+}
+
function showMissingTracksNotification(missingTracks) {
const modal = document.getElementById('missing-tracks-modal');
const listUl = document.getElementById('missing-tracks-list-ul');
@@ -2301,7 +2315,7 @@ function showMissingTracksNotification(missingTracks) {
.map((track) => {
const text =
typeof track === 'string' ? track : `${track.artist ? track.artist + ' - ' : ''}${track.title}`;
- return `${text}`;
+ return `${escapeHtml(text)}`;
})
.join('');
diff --git a/js/player.js b/js/player.js
index 4839f4e..a094d17 100644
--- a/js/player.js
+++ b/js/player.js
@@ -8,6 +8,7 @@ import {
getTrackArtistsHTML,
getTrackYearDisplay,
createQualityBadgeHTML,
+ escapeHtml,
} from './utils.js';
import {
queueManager,
@@ -166,7 +167,7 @@ export class Player {
if (coverEl) coverEl.src = this.api.getCoverUrl(track.album?.cover);
if (titleEl) {
const qualityBadge = createQualityBadgeHTML(track);
- titleEl.innerHTML = `${trackTitle} ${qualityBadge}`;
+ titleEl.innerHTML = `${escapeHtml(trackTitle)} ${qualityBadge}`;
}
if (albumEl) {
const albumTitle = track.album?.title || '';
@@ -356,7 +357,8 @@ export class Player {
const yearDisplay = getTrackYearDisplay(track);
document.querySelector('.now-playing-bar .cover').src = this.api.getCoverUrl(track.album?.cover);
- document.querySelector('.now-playing-bar .title').innerHTML = `${trackTitle} ${createQualityBadgeHTML(track)}`;
+ document.querySelector('.now-playing-bar .title').innerHTML =
+ `${escapeHtml(trackTitle)} ${createQualityBadgeHTML(track)}`;
const albumEl = document.querySelector('.now-playing-bar .album');
if (albumEl) {
const albumTitle = track.album?.title || '';
diff --git a/js/settings.js b/js/settings.js
index 88caab0..a4a03b5 100644
--- a/js/settings.js
+++ b/js/settings.js
@@ -1919,6 +1919,15 @@ export function initializeSettings(scrobbler, player, api, ui) {
});
}
+ // Intercept Back to Close Modals Toggle
+ const interceptBackToCloseToggle = document.getElementById('intercept-back-to-close-modals-toggle');
+ if (interceptBackToCloseToggle) {
+ interceptBackToCloseToggle.checked = modalSettings.shouldInterceptBackToClose();
+ interceptBackToCloseToggle.addEventListener('change', (e) => {
+ modalSettings.setInterceptBackToClose(e.target.checked);
+ });
+ }
+
// Compact Artist Toggle
const compactArtistToggle = document.getElementById('compact-artist-toggle');
if (compactArtistToggle) {
diff --git a/js/storage.js b/js/storage.js
index 20b28ad..97fc89f 100644
--- a/js/storage.js
+++ b/js/storage.js
@@ -2210,10 +2210,10 @@ export const musicProviderSettings = {
export const modalSettings = {
STORAGE_KEY: 'close-modals-on-navigation',
+ INTERCEPT_BACK_KEY: 'intercept-back-to-close-modals',
shouldCloseOnNavigation() {
try {
- // Default to false to preserve existing behavior
const saved = localStorage.getItem(this.STORAGE_KEY);
if (saved === null) {
return false;
@@ -2228,6 +2228,54 @@ export const modalSettings = {
localStorage.setItem(this.STORAGE_KEY, enabled ? 'true' : 'false');
},
+ shouldInterceptBackToClose() {
+ try {
+ const saved = localStorage.getItem(this.INTERCEPT_BACK_KEY);
+ if (saved === null) {
+ return false;
+ }
+ return saved === 'true';
+ } catch {
+ return false;
+ }
+ },
+
+ setInterceptBackToClose(enabled) {
+ localStorage.setItem(this.INTERCEPT_BACK_KEY, enabled ? 'true' : 'false');
+ },
+
+ hasOpenModalsOrPanels() {
+ const sidePanel = document.getElementById('side-panel');
+ if (sidePanel && sidePanel.classList.contains('active')) {
+ return true;
+ }
+ if (document.querySelector('.modal.active')) {
+ return true;
+ }
+ if (document.querySelector('.modal-overlay')) {
+ return true;
+ }
+ const modalIds = [
+ 'playlist-modal',
+ 'folder-modal',
+ 'playlist-select-modal',
+ 'shortcuts-modal',
+ 'missing-tracks-modal',
+ 'sleep-timer-modal',
+ 'discography-download-modal',
+ 'custom-db-modal',
+ 'tracker-modal',
+ 'epilepsy-warning-modal',
+ ];
+ for (const id of modalIds) {
+ const modal = document.getElementById(id);
+ if (modal && modal.classList.contains('active')) {
+ return true;
+ }
+ }
+ return false;
+ },
+
closeAllModals() {
// Close all modal overlays
document.querySelectorAll('.modal-overlay').forEach((modal) => {
diff --git a/js/utils.js b/js/utils.js
index 10be675..d95c626 100644
--- a/js/utils.js
+++ b/js/utils.js
@@ -285,14 +285,17 @@ export const getTrackArtistsHTML = (track = {}, { fallback = 'Unknown Artist' }
if (track?.artists?.length) {
return track.artists
.map((artist) => {
+ const escapedName = escapeHtml(artist.name || 'Unknown Artist');
+ const escapedId = escapeHtml(artist.id || '');
// Check if this is a tracker/unreleased track
const isTracker = track.isTracker || (track.id && String(track.id).startsWith('tracker-'));
if (isTracker && track.trackerInfo?.sheetId) {
+ const escapedSheetId = escapeHtml(track.trackerInfo.sheetId);
// For tracker tracks, link to the tracker artist page
- return `${artist.name}`;
+ return `${escapedName}`;
}
// For normal tracks, use the artist ID
- return `${artist.name}`;
+ return `${escapedName}`;
})
.join(', ');
}