web-player: P13 — páginas grandes (Explorar, Inicio) y limpieza de tracks.ts
Se aborda junto con esta parte la tarea 5 diferida desde P5: se
elimina el envoltorio TrackTarget de tracks.ts y sus 8 accesores
(targetId/targetTitle/targetArtist/targetOwnerUserId/targetExplicit/
targetListensCount/isTargetCurrent/queueItemForTarget/
targetForQueueItem). Explorar, Inicio, Mezcla, menus.svelte.ts y
mixes.ts pasan a usar los objetos de pista directamente, con
toQueueItem/trackFromQueueItem (nuevo, en player.svelte.ts) como
únicos conversores. TypeScript reveló en el proceso que el campo
isExplicit no existe en el schema real de /tracks: el badge
"explicit" de Explorar ya era un no-op antes del refactor (siempre
undefined ?? false); se retira el binding en vez de fingir soporte
que nunca existió.
Explorar: GENRE_INFO/genreTiles se extraen a src/lib/genres.ts; la
lógica de búsqueda (showGroup/capped/matchPlaylists/searchCounts/
searchChips/findTopResult) se extrae a src/lib/search.ts como
funciones puras parametrizadas por sfilter/query en vez de cerrar
sobre el estado del componente. "Mejor resultado" unifica el botón/
enlace duplicado con <svelte:element>. Decisión tomada sin bloquear:
no se colapsan los 4 bloques de grupo (Canciones/Álbumes/Usuarios/
Playlists) en un único {#each groups} — cada grupo tiene snippets,
menú contextual y props de ListRow genuinamente distintos, y una
configuración genérica habría añadido más indirección de la que
elimina.
Inicio: usa Page en vez de page-x a mano; las 3 estadísticas pasan a
{#each}; los fondos de hero/spotlight (style= con variables CSS) se
convierten en las utilities hero-surface/spotlight-surface, y
gap-11.5 se sustituye por el token --spacing-section (2.875rem) vía
gap-section.
Verificado con npm run check (0 errores) y npm run build. /explore
sin sesión redirige a /login porque +layout.server.ts exige
allowAnonymousListening desde el backend (inalcanzable sin Docker en
esta sesión) — comportamiento preexistente, no una regresión. Sin QA
visual autenticada; smoke test de /login sin errores de consola.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
export const SEARCH_FILTERS = ['Todo', 'Canciones', 'Álbumes', 'Playlists', 'Usuarios'] as const;
|
||||
export type SearchFilter = (typeof SEARCH_FILTERS)[number];
|
||||
export const SEARCH_GROUP_PREVIEW = 4;
|
||||
|
||||
export function showGroup(filter: SearchFilter, label: Exclude<SearchFilter, 'Todo'>) {
|
||||
return filter === 'Todo' || filter === label;
|
||||
}
|
||||
|
||||
export function capped<T>(filter: SearchFilter, list: T[]) {
|
||||
return filter === 'Todo' ? list.slice(0, SEARCH_GROUP_PREVIEW) : list;
|
||||
}
|
||||
|
||||
export function matchPlaylists<T extends { name: string }>(playlists: T[], query: string): T[] {
|
||||
return query ? playlists.filter((p) => p.name.toLowerCase().includes(query.toLowerCase())) : [];
|
||||
}
|
||||
|
||||
export function searchCounts(counts: {
|
||||
tracks: number;
|
||||
albums: number;
|
||||
playlists: number;
|
||||
users: number;
|
||||
}) {
|
||||
return {
|
||||
Canciones: counts.tracks,
|
||||
Álbumes: counts.albums,
|
||||
Playlists: counts.playlists,
|
||||
Usuarios: counts.users
|
||||
};
|
||||
}
|
||||
|
||||
export function searchChips(counts: ReturnType<typeof searchCounts>) {
|
||||
const total = counts.Canciones + counts.Álbumes + counts.Playlists + counts.Usuarios;
|
||||
return SEARCH_FILTERS.map((label) => ({
|
||||
label,
|
||||
count: label === 'Todo' ? total : counts[label]
|
||||
}));
|
||||
}
|
||||
|
||||
export type TopResult<Track, Album, Playlist, User> =
|
||||
| { kind: 'track'; track: Track }
|
||||
| { kind: 'album'; album: Album }
|
||||
| { kind: 'playlist'; playlist: Playlist }
|
||||
| { kind: 'user'; user: User };
|
||||
|
||||
export function findTopResult<
|
||||
Track extends { title: string },
|
||||
Album extends { title: string },
|
||||
Playlist extends { name: string },
|
||||
User extends { name: string }
|
||||
>(params: {
|
||||
query: string;
|
||||
tracks: Track[];
|
||||
albums: Album[];
|
||||
playlists: Playlist[];
|
||||
users: User[];
|
||||
}): TopResult<Track, Album, Playlist, User> | null {
|
||||
const q = params.query.trim().toLowerCase();
|
||||
const startsWithQuery = (value: string) => value.toLowerCase().startsWith(q);
|
||||
|
||||
const userHit = params.users.find((u) => startsWithQuery(u.name));
|
||||
if (userHit) return { kind: 'user', user: userHit };
|
||||
const trackHit = params.tracks.find((track) => startsWithQuery(track.title));
|
||||
if (trackHit) return { kind: 'track', track: trackHit };
|
||||
const albumHit = params.albums.find((album) => startsWithQuery(album.title));
|
||||
if (albumHit) return { kind: 'album', album: albumHit };
|
||||
const playlistHit = params.playlists.find((p) => startsWithQuery(p.name));
|
||||
if (playlistHit) return { kind: 'playlist', playlist: playlistHit };
|
||||
|
||||
if (params.users.length) return { kind: 'user', user: params.users[0] };
|
||||
if (params.tracks.length) return { kind: 'track', track: params.tracks[0] };
|
||||
if (params.albums.length) return { kind: 'album', album: params.albums[0] };
|
||||
if (params.playlists.length) return { kind: 'playlist', playlist: params.playlists[0] };
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user