list: prototype with combined list field
This commit is contained in:
36
src/lib/utils/list/index.ts
Normal file
36
src/lib/utils/list/index.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
export const MAL_URL = "https://myanimelist.net";
|
||||||
|
export const ANILIST_URL = "https://anilist.co";
|
||||||
|
export const KITSU_URL = "https://kitsu.io";
|
||||||
|
|
||||||
|
export const AnimeListKind = z.enum([
|
||||||
|
"mal",
|
||||||
|
"anilist",
|
||||||
|
"kitsu",
|
||||||
|
])
|
||||||
|
|
||||||
|
export const AnimeListWatchStatus = z.enum({
|
||||||
|
"completed": "c",
|
||||||
|
"watching": "w",
|
||||||
|
"plan_to_watch": "p",
|
||||||
|
"on_hold": "h",
|
||||||
|
"dropped": "d",
|
||||||
|
} as const)
|
||||||
|
|
||||||
|
export const AnimeList = z.object({
|
||||||
|
kind: AnimeListKind,
|
||||||
|
username: z.string(),
|
||||||
|
status: z.array(AnimeListWatchStatus).default([]),
|
||||||
|
});
|
||||||
|
|
||||||
|
export function listExternalUrl(list: z.infer<typeof AnimeList>) {
|
||||||
|
switch (list.kind) {
|
||||||
|
case "mal":
|
||||||
|
return `${MAL_URL}/profile/${encodeURIComponent(list.username)}`;
|
||||||
|
case "anilist":
|
||||||
|
return `${ANILIST_URL}/user/${encodeURIComponent(list.username)}`;
|
||||||
|
case "kitsu":
|
||||||
|
return `${KITSU_URL}/username/${encodeURIComponent(list.username)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
<nav class="flex items-center gap-2 text-sm">
|
<nav class="flex items-center gap-2 text-sm">
|
||||||
<a href={resolve("/")}>Anime</a>
|
<a href={resolve("/")}>Anime</a>
|
||||||
<a href={resolve("/songs")}>Songs</a>
|
<a href={resolve("/songs")}>Songs</a>
|
||||||
|
<a href={resolve("/list")}>List</a>
|
||||||
<a href={resolve("/mal")}>MAL</a>
|
<a href={resolve("/mal")}>MAL</a>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
35
src/routes/list/+page.svelte
Normal file
35
src/routes/list/+page.svelte
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { useSearchParams } from "runed/kit";
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
import { SearchParamsSchema } from "./schema";
|
||||||
|
import { ChipGroup } from "$lib/components/ui/chip-group";
|
||||||
|
import { AnimeListWatchStatus } from "$lib/utils/list";
|
||||||
|
|
||||||
|
let { data }: { data: PageData } = $props();
|
||||||
|
|
||||||
|
const params = useSearchParams(SearchParamsSchema, {
|
||||||
|
pushHistory: false,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1 class="text-2xl font-semibold">List Search WIP</h1>
|
||||||
|
|
||||||
|
<form>
|
||||||
|
<label for="list-kind">Kind</label>
|
||||||
|
<!-- nested stuff won't work with useSearchParams -->
|
||||||
|
<select id="list-kind" bind:value={params.list.kind}>
|
||||||
|
<option value="mal">MAL</option>
|
||||||
|
<option value="anilist">AniList</option>
|
||||||
|
<option value="kitsu">Kitsu</option>
|
||||||
|
</select>
|
||||||
|
<label for="list-username">Username</label>
|
||||||
|
<input id="list-username" bind:value={params.list.username} />
|
||||||
|
<ChipGroup
|
||||||
|
label="Status"
|
||||||
|
items={AnimeListWatchStatus.options.map((v) => ({
|
||||||
|
label: v.toUpperCase(),
|
||||||
|
value: v,
|
||||||
|
}))}
|
||||||
|
bind:value={params.list.status}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
7
src/routes/list/+page.ts
Normal file
7
src/routes/list/+page.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { PageLoad } from "./$types";
|
||||||
|
import { SearchParamsSchema } from "./schema";
|
||||||
|
|
||||||
|
export const load: PageLoad = async ({ url, fetch, depends }) => {
|
||||||
|
const parsed = SearchParamsSchema.safeParse(url.searchParams);
|
||||||
|
console.log(parsed);
|
||||||
|
}
|
||||||
19
src/routes/list/schema.ts
Normal file
19
src/routes/list/schema.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { AnimeList, AnimeListWatchStatus } from "$lib/utils/list";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const fieldSep = ":";
|
||||||
|
const valueSep = ",";
|
||||||
|
|
||||||
|
const listCodec = z.codec(z.string(), AnimeList, {
|
||||||
|
decode: (s) => {
|
||||||
|
const [kind, ...rest] = decodeURIComponent(s).split(fieldSep);
|
||||||
|
const status = rest.pop()?.split(valueSep).map((v) => AnimeListWatchStatus.parse(v)) ?? [];
|
||||||
|
const username = rest.join("");
|
||||||
|
return AnimeList.parse({ kind, username, status });
|
||||||
|
},
|
||||||
|
encode: (list) => encodeURIComponent(`${list.kind}${fieldSep}${list.username}${fieldSep}${list.status?.join(valueSep)}`),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SearchParamsSchema = z.object({
|
||||||
|
list: listCodec.default({ kind: "mal", username: "", status: [] }),
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user