ui: list: add song type filter
This commit is contained in:
@@ -161,6 +161,7 @@ export async function getAnimeWithSongsByAnnId(db: ClientDb, annId: number) {
|
|||||||
export async function getSongsForMalAnimeIds(
|
export async function getSongsForMalAnimeIds(
|
||||||
db: ClientDb,
|
db: ClientDb,
|
||||||
malAnimeIds: number[],
|
malAnimeIds: number[],
|
||||||
|
songTypes?: number[],
|
||||||
) {
|
) {
|
||||||
const ids = malAnimeIds.filter((n) => Number.isFinite(n));
|
const ids = malAnimeIds.filter((n) => Number.isFinite(n));
|
||||||
if (ids.length === 0) return [];
|
if (ids.length === 0) return [];
|
||||||
@@ -192,7 +193,14 @@ export async function getSongsForMalAnimeIds(
|
|||||||
.innerJoin(songs, eq(songs.annSongId, animeSongLinks.annSongId))
|
.innerJoin(songs, eq(songs.annSongId, animeSongLinks.annSongId))
|
||||||
.leftJoin(artists, eq(artists.songArtistId, songs.songArtistId))
|
.leftJoin(artists, eq(artists.songArtistId, songs.songArtistId))
|
||||||
.leftJoin(groups, eq(groups.songGroupId, songs.songGroupId))
|
.leftJoin(groups, eq(groups.songGroupId, songs.songGroupId))
|
||||||
.where(inArray(anime.malId, ids))
|
.where(
|
||||||
|
songTypes && songTypes.length > 0
|
||||||
|
? and(
|
||||||
|
inArray(anime.malId, ids),
|
||||||
|
inArray(animeSongLinks.type, songTypes),
|
||||||
|
)
|
||||||
|
: inArray(anime.malId, ids),
|
||||||
|
)
|
||||||
.orderBy(
|
.orderBy(
|
||||||
desc(anime.year),
|
desc(anime.year),
|
||||||
desc(anime.seasonId),
|
desc(anime.seasonId),
|
||||||
|
|||||||
@@ -5,13 +5,16 @@
|
|||||||
import { browser } from "$app/environment";
|
import { browser } from "$app/environment";
|
||||||
import { invalidate } from "$app/navigation";
|
import { invalidate } from "$app/navigation";
|
||||||
import SongEntry from "$lib/components/SongEntry.svelte";
|
import SongEntry from "$lib/components/SongEntry.svelte";
|
||||||
|
import { ChipGroup } from "$lib/components/ui/chip-group";
|
||||||
import { db as clientDb } from "$lib/db/client-db";
|
import { db as clientDb } from "$lib/db/client-db";
|
||||||
import { player } from "$lib/player/store.svelte";
|
import { player } from "$lib/player/store.svelte";
|
||||||
import { trackFromSongRow } from "$lib/player/types";
|
import { trackFromSongRow } from "$lib/player/types";
|
||||||
|
import { AmqSongLinkTypeMap } from "$lib/types/amq";
|
||||||
import {
|
import {
|
||||||
MalAnimeListQuerySchema,
|
MalAnimeListQuerySchema,
|
||||||
MalAnimeListStatusEnum,
|
MalAnimeListStatusEnum,
|
||||||
} from "$lib/types/mal";
|
} from "$lib/types/mal";
|
||||||
|
import { songTypesCodec } from "../songs/schema";
|
||||||
import type { PageData } from "./$types";
|
import type { PageData } from "./$types";
|
||||||
|
|
||||||
const ListSearchSchema = MalAnimeListQuerySchema.extend({
|
const ListSearchSchema = MalAnimeListQuerySchema.extend({
|
||||||
@@ -19,6 +22,7 @@
|
|||||||
status: MalAnimeListStatusEnum.or(z.literal("")).default(""),
|
status: MalAnimeListStatusEnum.or(z.literal("")).default(""),
|
||||||
// URL param `mal` is updated only on Search
|
// URL param `mal` is updated only on Search
|
||||||
mal: z.string().default(""),
|
mal: z.string().default(""),
|
||||||
|
type: songTypesCodec.default([]),
|
||||||
}).strict();
|
}).strict();
|
||||||
|
|
||||||
const params = useSearchParams(ListSearchSchema, {
|
const params = useSearchParams(ListSearchSchema, {
|
||||||
@@ -130,6 +134,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-2">
|
||||||
|
<ChipGroup
|
||||||
|
label="Song Type"
|
||||||
|
items={Object.keys(AmqSongLinkTypeMap).map((type) => ({
|
||||||
|
label: type,
|
||||||
|
value: AmqSongLinkTypeMap[type as keyof typeof AmqSongLinkTypeMap],
|
||||||
|
}))}
|
||||||
|
bind:value={params.type}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="text-sm text-muted-foreground">
|
<div class="text-sm text-muted-foreground">
|
||||||
{#if data.username}
|
{#if data.username}
|
||||||
MAL entries: {data.malResponse?.data.length ?? 0} (limited to {data.LIST_QUERY_LIMIT})
|
MAL entries: {data.malResponse?.data.length ?? 0} (limited to {data.LIST_QUERY_LIMIT})
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
MalAnimeListResponseSchema,
|
MalAnimeListResponseSchema,
|
||||||
MalAnimeListStatusEnum,
|
MalAnimeListStatusEnum,
|
||||||
} from "$lib/types/mal";
|
} from "$lib/types/mal";
|
||||||
|
import { songTypesCodec } from "../songs/schema";
|
||||||
import type { PageLoad } from "./$types";
|
import type { PageLoad } from "./$types";
|
||||||
|
|
||||||
const LIST_QUERY_LIMIT = 1000;
|
const LIST_QUERY_LIMIT = 1000;
|
||||||
@@ -17,6 +18,7 @@ const SearchSchema = MalAnimeListQuerySchema.extend({
|
|||||||
|
|
||||||
// Allow empty string to mean "All"
|
// Allow empty string to mean "All"
|
||||||
status: MalAnimeListStatusEnum.or(z.literal("")).optional(),
|
status: MalAnimeListStatusEnum.or(z.literal("")).optional(),
|
||||||
|
type: songTypesCodec.optional(),
|
||||||
}).strict();
|
}).strict();
|
||||||
|
|
||||||
type StatusParam = z.infer<typeof SearchSchema>["status"];
|
type StatusParam = z.infer<typeof SearchSchema>["status"];
|
||||||
@@ -39,6 +41,7 @@ export const load: PageLoad = async ({ url, fetch, depends }) => {
|
|||||||
const status = parsed.success
|
const status = parsed.success
|
||||||
? normalizeStatus(parsed.data.status)
|
? normalizeStatus(parsed.data.status)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const types = parsed.success ? parsed.data.type : undefined;
|
||||||
|
|
||||||
const username = (mal ?? "").trim();
|
const username = (mal ?? "").trim();
|
||||||
|
|
||||||
@@ -88,7 +91,7 @@ export const load: PageLoad = async ({ url, fetch, depends }) => {
|
|||||||
await ensureSeeded({ fetch });
|
await ensureSeeded({ fetch });
|
||||||
|
|
||||||
const malIds = malResponse.data.map((e) => e.node.id);
|
const malIds = malResponse.data.map((e) => e.node.id);
|
||||||
const songRows = await getSongsForMalAnimeIds(db, malIds);
|
const songRows = await getSongsForMalAnimeIds(db, malIds, types);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
LIST_QUERY_LIMIT,
|
LIST_QUERY_LIMIT,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
|
|
||||||
const SEP = ",";
|
const SEP = ",";
|
||||||
|
|
||||||
const songTypesCodec = z.codec(z.string(), z.array(AmqSongLinkType), {
|
export const songTypesCodec = z.codec(z.string(), z.array(AmqSongLinkType), {
|
||||||
decode: (str) =>
|
decode: (str) =>
|
||||||
str
|
str
|
||||||
? decodeURIComponent(str)
|
? decodeURIComponent(str)
|
||||||
|
|||||||
Reference in New Issue
Block a user