feat(web): player bar, playable track lists and Collection (M3)
ci / ts / ts (packages + web) (push) Successful in 53s
release / release (версия и заметки) (push) Successful in 6s
release / android (APK в релиз) (push) Successful in 2m33s

- 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:
mrleo1nidandClaude Opus 5.5 committed 2026-10-20 12:11:12 +03:00
1 parent ec3c2810c8
commit 976ffb574b
15 files changed
+803 -49

No files matched your search

+4
View File
@@ -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();