Add shared pagination, projections and ownership helpers; simplify query and command handlers

This commit is contained in:
2026-09-24 11:59:34 +02:00
parent 5a0f82b77b
commit 4b147d9ee3
84 changed files with 547 additions and 767 deletions
+11 -38
View File
@@ -763,22 +763,6 @@ export interface components {
expiresInSeconds: number | string;
uploadUrl: string;
};
AlbumSearchItemResponse: {
album: components["schemas"]["AlbumApplicationResponse"];
};
AlbumsSearchResponse: {
items: components["schemas"]["AlbumSearchItemResponse"][];
/** Format: int32 */
pageNumber: number | string;
/** Format: int32 */
pageSize: number | string;
/** Format: int32 */
pageCount: number | string;
/** Format: int32 */
totalItemCount: number | string;
hasPreviousPage: boolean;
hasNextPage: boolean;
};
AvailableGenreResponse: {
genre: components["schemas"]["Genre"];
incompatibleWith: components["schemas"]["Genre"][];
@@ -796,7 +780,7 @@ export interface components {
description: null | string;
/** Format: uuid */
pictureIntentId: null | string;
visibility?: components["schemas"]["PlaylistVisibility"];
visibility?: components["schemas"]["PlayListVisibility"];
};
CreateTrackRequest: {
title: string;
@@ -918,12 +902,17 @@ export interface components {
smallImageKeyName: null | string;
mediumImageKeyName: null | string;
largeImageKeyName: null | string;
visibility: components["schemas"]["PlaylistVisibility"];
visibility: components["schemas"]["PlayListVisibility"];
/** Format: date-time */
createdAt: string;
/** Format: date-time */
updatedAt: string;
coverTrackIds: string[];
ownerUserId: string;
/** Format: int32 */
trackCount: number | string;
/** Format: double */
durationSeconds: number | string;
};
PlayListPictureUploadResponse: {
/** Format: uuid */
@@ -937,7 +926,7 @@ export interface components {
uploadUrl: string;
};
/** @enum {unknown} */
PlaylistVisibility: "Private" | "Public";
PlayListVisibility: "Private" | "Public";
/** @enum {unknown} */
ProcessingStatus: "Pending" | "Processing" | "Completed" | "Failed";
RecordListeningProgressRequest: {
@@ -988,22 +977,6 @@ export interface components {
tags: components["schemas"]["Genre"][];
isExplicit: boolean;
};
TrackSearchItemResponse: {
track: components["schemas"]["TrackApplicationResponse"];
};
TracksSearchResponse: {
items: components["schemas"]["TrackSearchItemResponse"][];
/** Format: int32 */
pageNumber: number | string;
/** Format: int32 */
pageSize: number | string;
/** Format: int32 */
pageCount: number | string;
/** Format: int32 */
totalItemCount: number | string;
hasPreviousPage: boolean;
hasNextPage: boolean;
};
TrackStreamResponse: {
manifestUrl: string;
ticket: string;
@@ -1042,7 +1015,7 @@ export interface components {
newDescription: null | string;
/** Format: uuid */
newPictureIntentId: null | string;
newVisibility?: null | components["schemas"]["PlaylistVisibility"];
newVisibility?: null | components["schemas"]["PlayListVisibility"];
};
UserApplicationResponse: {
id: string;
@@ -1106,7 +1079,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["AlbumsSearchResponse"];
"application/json": components["schemas"]["PaginatedResponseOfAlbumApplicationResponse"];
};
};
};
@@ -2158,7 +2131,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["TracksSearchResponse"];
"application/json": components["schemas"]["PaginatedResponseOfTrackApplicationResponse"];
};
};
/** @description Bad Request */
@@ -63,7 +63,7 @@ export const load: PageServerLoad = async ({ url, locals, fetch }) => {
genre,
genres,
tracks,
albums: (albumsRes?.data?.items ?? []).map((item) => item.album),
albums: albumsRes?.data?.items ?? [],
users: usersRes?.data?.items ?? [],
viewerId: user?.sub ?? null,
playlists: playlistsRes?.data?.items ?? []
@@ -49,7 +49,7 @@
let albumMenu = $state<AlbumMenuState | null>(null);
let sfilter = $state<SearchFilter>('Todo');
type LocalTrack = (typeof data.tracks.items)[number]['track'];
type LocalTrack = (typeof data.tracks.items)[number];
let localItems = $state<LocalTrack[]>([]);
let localPage = $state(1);
@@ -60,7 +60,7 @@
trackMenu.close();
albumMenu = null;
sfilter = data.genre ? 'Canciones' : 'Todo';
localItems = data.tracks.items.map((item) => item.track);
localItems = data.tracks.items;
localPage = Number(data.tracks.pageNumber);
hasMore = data.tracks.hasNextPage;
});
@@ -177,11 +177,7 @@
const res = await fetch(`/api/tracks?${params}`);
if (!res.ok) throw new Error(String(res.status));
const next = (await res.json()) as typeof data.tracks;
localItems = appendUnique(
localItems,
next.items.map((item) => item.track),
(track) => track.id
);
localItems = appendUnique(localItems, next.items, (track) => track.id);
localPage = Number(next.pageNumber);
hasMore = next.hasNextPage;
} catch {
+1 -1
View File
@@ -40,7 +40,7 @@ export const load: PageServerLoad = async ({ locals, url, fetch }) => {
recentlyPlayedPromise
]);
const playlistItems = playlists.data?.items ?? [];
const latestItems = (latest.data?.items ?? []).map((item) => item.track);
const latestItems = latest.data?.items ?? [];
const spotlightPlaylist = playlistItems[0] ?? null;
const spotlightTracksPromise = spotlightPlaylist