music (web): click the dock track info to open its album in /music
The bottom player dock's cover + title/artist is now a button — clicking it sets the music:cwd channel to the playing album and navigates to /music, landing on that album's tracklist (current track highlighted). Uses the global panel channel + react-router navigate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { useClient } from 'hooks/useClient';
|
||||
import { usePanelChannel } from 'hooks/usePanelChannel';
|
||||
import { Play, Pause, SkipBack, SkipForward, X, Volume2, VolumeX } from 'lucide-react';
|
||||
import { useSeekBar, SeekBar } from '../apps/FileViewer/renderers/SeekBar';
|
||||
import { MusicHeart } from '../apps/Music/MusicHeart';
|
||||
import { sortTracks, trackHomePath, type AlbumMeta, type NowPlaying } from '../apps/Music/shared';
|
||||
import { MUSIC_ROOT, MUSIC_CWD_CHANNEL, sortTracks, trackHomePath, type AlbumMeta, type NowPlaying } from '../apps/Music/shared';
|
||||
import { useMusicPlayer, type PlayerTrack } from './useMusicPlayer';
|
||||
|
||||
// Mounted once in the persistent DashboardLayout (outside <Routes>), so it owns the single <audio>
|
||||
@@ -15,6 +17,8 @@ const fmt = (s: number): string =>
|
||||
|
||||
export const MusicPlayerHost = () => {
|
||||
const { token, get, put } = useClient();
|
||||
const navigate = useNavigate();
|
||||
const [, setCwd] = usePanelChannel<string | null>(MUSIC_CWD_CHANNEL, null);
|
||||
const { current, index, queue, playing, toggle, next, prev, setPlaying, close, loadQueue } = useMusicPlayer();
|
||||
const audioRef = useRef<HTMLAudioElement>(null);
|
||||
const [position, setPosition] = useState(0);
|
||||
@@ -128,6 +132,13 @@ export const MusicPlayerHost = () => {
|
||||
const subtitle = current?.artist ?? current?.albumRel.split('/').slice(-2, -1)[0] ?? '';
|
||||
const pct = duration ? (position / duration) * 100 : 0;
|
||||
|
||||
// Clicking the track info jumps the /music library to the playing album (and navigates there).
|
||||
const openCurrentAlbum = () => {
|
||||
if (!current) return;
|
||||
setCwd(`${MUSIC_ROOT}/${current.albumRel}`);
|
||||
navigate('/music');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<audio
|
||||
@@ -148,21 +159,28 @@ export const MusicPlayerHost = () => {
|
||||
/>
|
||||
{current && (
|
||||
<div className="fixed inset-x-0 bottom-0 z-50 flex items-center gap-3 border-t border-border bg-card/95 px-4 py-2 backdrop-blur">
|
||||
{/* cover + info */}
|
||||
<div className="flex h-11 w-11 shrink-0 items-center justify-center overflow-hidden rounded bg-muted">
|
||||
<img
|
||||
src={coverUrl(current.albumRel)}
|
||||
alt=""
|
||||
className="h-full w-full object-cover"
|
||||
onError={(e) => {
|
||||
(e.currentTarget as HTMLImageElement).style.visibility = 'hidden';
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="hidden w-44 shrink-0 sm:block">
|
||||
<p className="truncate text-sm font-medium text-foreground">{current.title ?? current.file}</p>
|
||||
<p className="truncate text-xs text-muted-foreground">{subtitle}</p>
|
||||
</div>
|
||||
{/* cover + info — click to open this album in /music */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={openCurrentAlbum}
|
||||
title="Show in library"
|
||||
className="flex min-w-0 items-center gap-3 rounded text-left transition-opacity hover:opacity-80"
|
||||
>
|
||||
<div className="flex h-11 w-11 shrink-0 items-center justify-center overflow-hidden rounded bg-muted">
|
||||
<img
|
||||
src={coverUrl(current.albumRel)}
|
||||
alt=""
|
||||
className="h-full w-full object-cover"
|
||||
onError={(e) => {
|
||||
(e.currentTarget as HTMLImageElement).style.visibility = 'hidden';
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="hidden w-44 shrink-0 sm:block">
|
||||
<p className="truncate text-sm font-medium text-foreground">{current.title ?? current.file}</p>
|
||||
<p className="truncate text-xs text-muted-foreground">{subtitle}</p>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{/* transport */}
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
|
||||
Reference in New Issue
Block a user