feat(web): player bar, playable track lists and Collection (M3)
- player bar with cover, like and dislike, previous / play / next, shuffle, repeat, seek bar and volume; PlayerProvider lives with the signed-in shell and restores the last track and position paused - clicking a row (or Enter) plays the list from that track with its context (album, search, history, collection); the current track is highlighted and rows have a like button; a reaction refreshes lists - Collection: history grouped by day with clearing, likes with "play all" and dislikes, all with paging; sidebar section - events and playback state are flushed when the page is hidden; player timers survive StrictMode remounts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ec3c2810c8
commit
976ffb574b
15 files changed
+803
-49
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { createContext, useContext, useEffect, useState, useSyncExternalStore, type ReactNode } from 'react';
|
||||
|
||||
import { useSession } from '../app/session';
|
||||
@@ -8,9 +9,12 @@ const PlayerContext = createContext<Player | null>(null);
|
||||
/** Плеер живёт, пока пользователь вошёл; горячие клавиши и сохранение при уходе со страницы. */
|
||||
export function PlayerProvider({ children }: { children: ReactNode }) {
|
||||
const session = useSession();
|
||||
const queryClient = useQueryClient();
|
||||
const [player] = useState(() => new Player(session));
|
||||
player.onReaction = () => void queryClient.invalidateQueries();
|
||||
|
||||
useEffect(() => {
|
||||
player.start();
|
||||
void player.restore().catch(() => undefined);
|
||||
const onHide = () => player.onHide();
|
||||
const onVisibility = () => document.visibilityState === 'hidden' && onHide();
|
||||
|
||||
Reference in new issue
Block a user