Highlight the playlist in the left panel on play (#2025)

* Fixed bad smart playlist field s

* first try to add playlist highlight

* Simplified calls

* Now works for grids too.

* Derive the playlist highlight from the currently-playing track's origin instead of a stale global field.

* addressed comments
This commit is contained in:
Norman
2026-06-23 20:18:02 -07:00
committed by GitHub
parent b6519e9839
commit 5ddbfcbfee
6 changed files with 89 additions and 10 deletions
@@ -39,7 +39,12 @@ import {
import { Play, PlayerRepeat, PlayerShuffle } from '/@/shared/types/types';
export interface PlayerContext {
addToQueueByData: (data: Song[], type: AddToQueueType, playSongId?: string) => void;
addToQueueByData: (
data: Song[],
type: AddToQueueType,
playSongId?: string,
contextPlaylistId?: null | string,
) => void;
addToQueueByFetch: (
serverId: string,
id: string[],
@@ -137,6 +142,23 @@ const getRootQueryKey = (itemType: LibraryItem, serverId: string) => {
}
};
const isReplaceQueueType = (type: AddToQueueType): boolean => {
if (typeof type === 'object') return false;
return type === Play.NOW || type === Play.SHUFFLE;
};
// HashRouter puts the route in location.hash, not pathname.
const inferPlaylistContextFromUrl = (): null | string => {
const route = window.location.hash.replace(/^#/, '');
const match = route.match(/^\/playlists\/([^/]+)/);
return match ? match[1] : null;
};
// Stamps each song with the playlist it was queued from, so the sidebar highlight
// can be derived from whichever song is currently playing (see useCurrentPlaylistContextId).
const tagPlaylistContext = (songs: Song[], contextPlaylistId: string): Song[] =>
songs.map((song) => ({ ...song, _contextPlaylistId: contextPlaylistId }));
export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const { t } = useTranslation();
const queryClient = useQueryClient();
@@ -187,9 +209,20 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
}, [doNotShowAgain, setDoNotShowAgain, t]);
const addToQueueByData = useCallback(
(data: Song[], type: AddToQueueType, playSongId?: string) => {
(
data: Song[],
type: AddToQueueType,
playSongId?: string,
contextPlaylistId?: null | string,
) => {
const filters = useSettingsStore.getState().playback.filters;
const filteredData = filterSongsByPlayerFilters(data, filters);
let filteredData = filterSongsByPlayerFilters(data, filters);
const resolvedContextId =
contextPlaylistId ??
(isReplaceQueueType(type) ? inferPlaylistContextFromUrl() : null);
if (resolvedContextId) {
filteredData = tagPlaylistContext(filteredData, resolvedContextId);
}
if (typeof type === 'object' && 'edge' in type && type.edge !== null) {
const edge = type.edge === 'top' ? 'top' : 'bottom';
@@ -279,7 +312,21 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
}
const filters = useSettingsStore.getState().playback.filters;
const filteredSongs = filterSongsByPlayerFilters(sortedSongs, filters);
let filteredSongs = filterSongsByPlayerFilters(sortedSongs, filters);
// Songs from multiple playlists are merged together, so there is no single
// playlist to attribute them to: skip tagging (and URL inference) entirely.
const isMultiPlaylist = itemType === LibraryItem.PLAYLIST && id.length > 1;
const explicitId =
itemType === LibraryItem.PLAYLIST && id.length === 1 ? id[0] : null;
const resolvedContextId =
explicitId ??
(!isMultiPlaylist && isReplaceQueueType(type)
? inferPlaylistContextFromUrl()
: null);
if (resolvedContextId) {
filteredSongs = tagPlaylistContext(filteredSongs, resolvedContextId);
}
if (typeof type === 'object' && 'edge' in type && type.edge !== null) {
const edge = type.edge === 'top' ? 'top' : 'bottom';