Fix: Refactor playlist loading and sharing logic
Resolves an issue where Tidal playlists with UUIDs were incorrectly identified as missing user playlists. Introduces explicit source (api/user) parameter to renderPlaylistPage for reliable routing. Restricts the Share button to user playlists only and fixes npm deprecation warnings. # Conflicts: # package.json
This commit is contained in:
parent
c9f639ba6c
commit
ff166b27ca
5 changed files with 63 additions and 106 deletions
|
|
@ -378,7 +378,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
ui.renderLibraryPage();
|
||||
// Also update current page if we are on it
|
||||
if (window.location.hash === `#userplaylist/${editingId}`) {
|
||||
ui.renderPlaylistPage(editingId);
|
||||
ui.renderPlaylistPage(editingId, 'user');
|
||||
}
|
||||
modal.style.display = 'none';
|
||||
delete modal.dataset.editingId;
|
||||
|
|
@ -553,7 +553,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
const trackId = playlist.tracks[index].id;
|
||||
const updatedPlaylist = await db.removeTrackFromPlaylist(playlistId, trackId);
|
||||
syncManager.syncUserPlaylist(updatedPlaylist, 'update');
|
||||
ui.renderPlaylistPage(playlistId);
|
||||
ui.renderPlaylistPage(playlistId, 'user');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ export function createRouter(ui) {
|
|||
ui.renderArtistPage(param);
|
||||
break;
|
||||
case 'playlist':
|
||||
ui.renderPlaylistPage(param);
|
||||
ui.renderPlaylistPage(param, 'api');
|
||||
break;
|
||||
case 'userplaylist':
|
||||
ui.renderPlaylistPage(param);
|
||||
ui.renderPlaylistPage(param, 'user');
|
||||
break;
|
||||
case 'mix':
|
||||
ui.renderMixPage(param);
|
||||
|
|
|
|||
57
js/ui.js
57
js/ui.js
|
|
@ -1103,7 +1103,7 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) {
|
|||
}
|
||||
}
|
||||
|
||||
async renderPlaylistPage(playlistId) {
|
||||
async renderPlaylistPage(playlistId, source = null) {
|
||||
this.showPage('playlist');
|
||||
const imageEl = document.getElementById('playlist-detail-image');
|
||||
const titleEl = document.getElementById('playlist-detail-title');
|
||||
|
|
@ -1133,15 +1133,25 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) {
|
|||
// Check if it's a user playlist (UUID format)
|
||||
const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(playlistId);
|
||||
|
||||
const ownedPlaylist = await db.getPlaylist(playlistId);
|
||||
let playlistData = ownedPlaylist;
|
||||
|
||||
// If not in local DB, check if it's a public Firebase playlist
|
||||
if (!playlistData) {
|
||||
try {
|
||||
playlistData = await syncManager.getPublicPlaylist(playlistId);
|
||||
} catch (e) {
|
||||
console.warn('Failed to check public Firebase playlists:', e);
|
||||
let playlistData = null;
|
||||
let ownedPlaylist = null;
|
||||
|
||||
// Priority:
|
||||
// 1. If source is 'user', check DB/Sync.
|
||||
// 2. If source is 'api', check API.
|
||||
// 3. If no source, check DB if UUID, then API.
|
||||
|
||||
if (source === 'user' || (!source && isUUID)) {
|
||||
ownedPlaylist = await db.getPlaylist(playlistId);
|
||||
playlistData = ownedPlaylist;
|
||||
|
||||
// If not in local DB, check if it's a public Firebase playlist
|
||||
if (!playlistData) {
|
||||
try {
|
||||
playlistData = await syncManager.getPublicPlaylist(playlistId);
|
||||
} catch (e) {
|
||||
console.warn('Failed to check public Firebase playlists:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1221,9 +1231,9 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) {
|
|||
});
|
||||
document.title = `${playlistData.name || playlistData.title} - Monochrome`;
|
||||
} else {
|
||||
// If it is a UUID, we know it won't be in the API.
|
||||
if (isUUID) {
|
||||
throw new Error('Playlist not found. If this is a custom playlist, make sure it is set to Public.');
|
||||
// If source was explicitly 'user' and we didn't find it, fail.
|
||||
if (source === 'user') {
|
||||
throw new Error('Playlist not found. If this is a custom playlist, make sure it is set to Public.');
|
||||
}
|
||||
|
||||
// Render API playlist
|
||||
|
|
@ -1282,7 +1292,7 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) {
|
|||
}
|
||||
|
||||
// Render Actions (Shuffle + Share)
|
||||
this.updatePlaylistHeaderActions(playlist, false, tracks, true);
|
||||
this.updatePlaylistHeaderActions(playlist, false, tracks, false);
|
||||
|
||||
recentActivityManager.addPlaylist(playlist);
|
||||
document.title = playlist.title || 'Artist Mix';
|
||||
|
|
@ -1608,28 +1618,15 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) {
|
|||
fragment.appendChild(deleteBtn);
|
||||
}
|
||||
|
||||
// Share
|
||||
if (showShare || playlist.isPublic) {
|
||||
// Share (User Playlists Only)
|
||||
if (showShare || (isOwned && playlist.isPublic)) {
|
||||
const shareBtn = document.createElement('button');
|
||||
shareBtn.id = 'share-playlist-btn';
|
||||
shareBtn.className = 'btn-secondary';
|
||||
shareBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/><polyline points="16 6 12 2 8 6"/><line x1="12" y1="2" x2="12" y2="15"/></svg><span>Share</span>';
|
||||
|
||||
// Determine URL based on type (User Playlist or API Playlist)
|
||||
// User Playlists use #userplaylist/ID, API use #playlist/UUID
|
||||
// But we don't have isOwned strictly here for that decision?
|
||||
// Actually, if it's owned it's #userplaylist.
|
||||
// If it's public firebase, it's also #userplaylist.
|
||||
// If it's API, it's #playlist.
|
||||
|
||||
// Heuristic: If it has `isPublic` property (even if false), it's likely a User/Firebase playlist structure.
|
||||
// API playlists don't usually have `isPublic`.
|
||||
|
||||
const isUserType = 'isPublic' in playlist || isOwned;
|
||||
const prefix = isUserType ? 'userplaylist' : 'playlist';
|
||||
|
||||
shareBtn.onclick = () => {
|
||||
const url = `${window.location.origin}${window.location.pathname}#${prefix}/${playlist.id || playlist.uuid}`;
|
||||
const url = `${window.location.origin}${window.location.pathname}#userplaylist/${playlist.id || playlist.uuid}`;
|
||||
navigator.clipboard.writeText(url).then(() => alert('Link copied to clipboard!'));
|
||||
};
|
||||
fragment.appendChild(shareBtn);
|
||||
|
|
|
|||
100
package-lock.json
generated
100
package-lock.json
generated
|
|
@ -62,6 +62,7 @@
|
|||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
|
|
@ -2542,6 +2543,7 @@
|
|||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
|
|
@ -2750,6 +2752,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
|
|
@ -4296,13 +4299,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.sortby": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
|
||||
"integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
|
|
@ -4323,6 +4319,14 @@
|
|||
"sourcemap-codec": "^1.4.8"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string/node_modules/sourcemap-codec": {
|
||||
"name": "@jridgewell/sourcemap-codec",
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
|
|
@ -4577,16 +4581,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
|
|
@ -5053,20 +5047,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.8.0-beta.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz",
|
||||
"integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==",
|
||||
"deprecated": "The work that was done in this beta branch won't be included in future versions",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
|
|
@ -5089,23 +5069,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/source-map-support/node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"version": "0.7.6",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
|
||||
"integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/sourcemap-codec": {
|
||||
"version": "1.4.8",
|
||||
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
||||
"deprecated": "Please use @jridgewell/sourcemap-codec instead",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stop-iteration-iterator": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
|
||||
|
|
@ -5384,6 +5356,7 @@
|
|||
"integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.15.0",
|
||||
|
|
@ -5414,16 +5387,6 @@
|
|||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
|
||||
"integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz",
|
||||
|
|
@ -5649,6 +5612,7 @@
|
|||
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
"fdir": "^6.5.0",
|
||||
|
|
@ -5749,25 +5713,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
|
||||
"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
|
||||
"integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash.sortby": "^4.7.0",
|
||||
"tr46": "^1.0.1",
|
||||
"webidl-conversions": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
|
@ -6045,6 +5990,7 @@
|
|||
"integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
|
|
@ -6055,6 +6001,16 @@
|
|||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/workbox-build/node_modules/source-map": {
|
||||
"version": "0.7.6",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
|
||||
"integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/workbox-cacheable-response": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-7.4.0.tgz",
|
||||
|
|
|
|||
|
|
@ -24,5 +24,9 @@
|
|||
"devDependencies": {
|
||||
"vite": "^7.3.0",
|
||||
"vite-plugin-pwa": "^1.2.0"
|
||||
},
|
||||
"overrides": {
|
||||
"sourcemap-codec": "npm:@jridgewell/sourcemap-codec@^1.4.14",
|
||||
"source-map": "^0.7.4"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue