8a70f478c3
Add a right-side track info drawer that sits to the right of the queue panel when both are open. Shows a large cover, title/artist/album links, a Play/Queue/Edit actions row, and Status/General/File/Identifiers sections (empty rows omitted). Opens from the track context menu, the player now-playing tile, and the queue now-playing card. - ui slice: trackInfoId + open/closeTrackInfo - TrackInfoDrawer rendered after QueuePanel in AppShell; overlays content on narrow viewports - map source/createdAt/enrichedAt from the wire (were unmapped) - formatDateTime helper, info icon, i18n (en/ru) - drop orphaned toggleNowPlaying/isNowPlayingOpen from player slice Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
164 lines
3.5 KiB
TypeScript
164 lines
3.5 KiB
TypeScript
export type TrackAvailability = 'server' | 'downloading' | 'error' | 'missing';
|
|
|
|
/**
|
|
* Metadata-enrichment state, distinct from file `availability`. `pending` = the
|
|
* worker hasn't finished (or hasn't started); `enriched` = identity found;
|
|
* `failed` = no match / a worker error (see `metadataError`); `manual` = user-
|
|
* edited and never auto-overwritten.
|
|
*/
|
|
export type MetadataStatus = 'pending' | 'enriched' | 'failed' | 'manual';
|
|
|
|
export interface Track {
|
|
id: string;
|
|
title: string;
|
|
artistId: string;
|
|
artistName: string;
|
|
albumId: string;
|
|
albumTitle: string;
|
|
albumArtUrl?: string;
|
|
hasCover: boolean;
|
|
durationMs: number;
|
|
trackNumber?: number;
|
|
discNumber?: number;
|
|
year?: number;
|
|
genre?: string;
|
|
availability: TrackAvailability;
|
|
metadataStatus: MetadataStatus;
|
|
/** Human-readable reason the last enrichment run set `failed`; else undefined. */
|
|
metadataError?: string;
|
|
fileSize?: number;
|
|
format?: string;
|
|
bitrate?: number;
|
|
liked: boolean;
|
|
/** Where the track entered the library (e.g. `upload`, `local_folder`). */
|
|
source?: string;
|
|
/** ISO timestamp the track was added to the library. */
|
|
createdAt?: string;
|
|
/** ISO timestamp the last successful enrichment ran; undefined if never. */
|
|
enrichedAt?: string;
|
|
}
|
|
|
|
export interface Album {
|
|
id: string;
|
|
title: string;
|
|
artistId: string;
|
|
artistName: string;
|
|
artUrl?: string;
|
|
year?: number;
|
|
trackCount: number;
|
|
totalDurationMs: number;
|
|
genre?: string;
|
|
}
|
|
|
|
export interface Artist {
|
|
id: string;
|
|
name: string;
|
|
artUrl?: string;
|
|
albumCount: number;
|
|
trackCount: number;
|
|
}
|
|
|
|
export interface Playlist {
|
|
id: string;
|
|
name: string;
|
|
description?: string;
|
|
ownerId: string;
|
|
trackCount: number;
|
|
totalDurationMs: number;
|
|
artUrl?: string;
|
|
isPublic: boolean;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface PlaylistTrack extends Track {
|
|
position: number;
|
|
addedAt: string;
|
|
}
|
|
|
|
export interface DownloadJob {
|
|
id: string;
|
|
url: string;
|
|
title?: string;
|
|
artist?: string;
|
|
album?: string;
|
|
status: 'queued' | 'downloading' | 'processing' | 'done' | 'error';
|
|
progress: number;
|
|
errorMessage?: string;
|
|
trackId?: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface UploadResponse {
|
|
track_id: string;
|
|
title: string;
|
|
already_exists: boolean;
|
|
}
|
|
|
|
export interface StorageStats {
|
|
totalBytes: number;
|
|
usedBytes: number;
|
|
trackCount: number;
|
|
albumCount: number;
|
|
artistCount: number;
|
|
}
|
|
|
|
export interface User {
|
|
id: string;
|
|
username: string;
|
|
email?: string;
|
|
role: 'admin' | 'user';
|
|
createdAt: string;
|
|
lastActiveAt?: string;
|
|
}
|
|
|
|
export interface AuthTokens {
|
|
accessToken: string;
|
|
refreshToken: string;
|
|
// Optional: the backend's TokenResponse carries no TTL — expiry is driven by
|
|
// 401→refresh, not a client-side clock. Present only if a backend supplies it.
|
|
expiresIn?: number;
|
|
}
|
|
|
|
export interface LoginRequest {
|
|
username: string;
|
|
password: string;
|
|
}
|
|
|
|
export interface LoginResponse {
|
|
user: User;
|
|
tokens: AuthTokens;
|
|
}
|
|
|
|
export interface RegisterRequest {
|
|
username: string;
|
|
password: string;
|
|
}
|
|
|
|
export interface PaginatedResponse<T> {
|
|
items: T[];
|
|
total: number;
|
|
page: number;
|
|
pageSize: number;
|
|
hasMore: boolean;
|
|
}
|
|
|
|
export interface LibraryFilters {
|
|
search?: string;
|
|
genre?: string;
|
|
artistId?: string;
|
|
albumId?: string;
|
|
liked?: boolean;
|
|
page?: number;
|
|
pageSize?: number;
|
|
sortBy?: 'title' | 'artist' | 'album' | 'year' | 'dateAdded';
|
|
sortOrder?: 'asc' | 'desc';
|
|
}
|
|
|
|
export interface ApiError {
|
|
status: number;
|
|
message: string;
|
|
code?: string;
|
|
}
|