kaas
This commit is contained in:
parent
35213e1add
commit
6451b8cfb4
14 changed files with 158 additions and 130 deletions
|
@ -15,7 +15,7 @@
|
||||||
inset: 0;
|
inset: 0;
|
||||||
display: block;
|
display: block;
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
calc(atan(var(--ratio))),
|
atan(var(--ratio, .2)),
|
||||||
var(--surface-2) 20em,
|
var(--surface-2) 20em,
|
||||||
transparent 90%
|
transparent 90%
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,13 +11,10 @@ export const Details: Component<DetailsProps> = (props) => {
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const observer = new ResizeObserver(([entry]) => {
|
const observer = new ResizeObserver(([entry]) => {
|
||||||
const _20em = 20 * 16;
|
|
||||||
const { inlineSize, blockSize } = entry.contentBoxSize[0];
|
const { inlineSize, blockSize } = entry.contentBoxSize[0];
|
||||||
console.log(blockSize / inlineSize);
|
|
||||||
|
|
||||||
(entry.target as HTMLElement).style.setProperty(
|
(entry.target as HTMLElement).style.setProperty(
|
||||||
"--ratio",
|
"--ratio",
|
||||||
String(_20em / inlineSize),
|
String((blockSize * 0.2) / inlineSize)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,6 @@ type HeroProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Hero(props: HeroProps) {
|
export function Hero(props: HeroProps) {
|
||||||
const entry = createMemo(() => props.entries.at(0)!);
|
|
||||||
const slug = createMemo(() => createSlug(entry()));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={`${css.container} ${props.class ?? ""}`}>
|
<div class={`${css.container} ${props.class ?? ""}`}>
|
||||||
<For each={props.entries}>{(entry) => <Page entry={entry} />}</For>
|
<For each={props.entries}>{(entry) => <Page entry={entry} />}</For>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { paths } from "./jellyfin.generated"; // generated by openapi-typescript
|
import type { components, paths } from "./jellyfin.generated"; // generated by openapi-typescript
|
||||||
import createClient from "openapi-fetch";
|
import createClient from "openapi-fetch";
|
||||||
import { query } from "@solidjs/router";
|
import { query } from "@solidjs/router";
|
||||||
import { Entry } from "../types";
|
import { Entry } from "../types";
|
||||||
|
@ -39,7 +39,7 @@ const getClient = () => {
|
||||||
export const getCurrentUser = query(async () => {
|
export const getCurrentUser = query(async () => {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
const { data, error, response } = await getClient().GET("/Users/Public", {
|
const { data } = await getClient().GET("/Users/Public", {
|
||||||
params: {},
|
params: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ export const listItemIds = query(
|
||||||
|
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
data.Items?.map((item) => ([
|
data.Items?.map((item) => ([
|
||||||
item.ProviderIds!["Tmdb"]!,
|
`${item.MediaType as any}-${item.ProviderIds!["Tmdb"]!}`,
|
||||||
{ jellyfin: item.Id! },
|
{ jellyfin: item.Id! },
|
||||||
])) ?? []
|
])) ?? []
|
||||||
);
|
);
|
||||||
|
@ -118,12 +118,7 @@ export const listItems = query(
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
data.Items?.map((item) => ({
|
data.Items?.map((item) => mapToEntry(item)) ?? []
|
||||||
// id: item.Id!,
|
|
||||||
id: item.ProviderIds!["Tmdb"]!,
|
|
||||||
title: item.Name!,
|
|
||||||
thumbnail: new URL(`/Items/${item.Id!}/Images/Primary`, getBaseUrl()), //await getItemImage(data.Id!, 'Primary'),
|
|
||||||
})) ?? []
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
"jellyfin.listItems",
|
"jellyfin.listItems",
|
||||||
|
@ -164,13 +159,7 @@ export const getRandomItems = query(
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
data?.Items?.map((item) => ({
|
data?.Items?.map((item) => mapToEntry(item)) ?? []
|
||||||
// id: item.Id!,
|
|
||||||
id: item.ProviderIds!["Tmdb"]!,
|
|
||||||
title: item.Name!,
|
|
||||||
thumbnail: new URL(`/Items/${item.Id!}/Images/Primary`, getBaseUrl()), //await getItemImage(data.Id!, 'Primary'),
|
|
||||||
image: new URL(`/Items/${item.Id!}/Images/Backdrop`, getBaseUrl()), //await getItemImage(data.Id!, 'Primary'),
|
|
||||||
})) ?? []
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
"jellyfin.listRandomItems",
|
"jellyfin.listRandomItems",
|
||||||
|
@ -205,18 +194,7 @@ export const getItem = query(
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return mapToEntry(data);
|
||||||
// id: data.Id!,
|
|
||||||
id: data.ProviderIds!["Tmdb"]!,
|
|
||||||
title: data.Name!,
|
|
||||||
overview: data.Overview!,
|
|
||||||
thumbnail: new URL(`/Items/${itemId}/Images/Primary`, getBaseUrl()), //await getItemImage(data.Id!, 'Primary'),
|
|
||||||
image: new URL(`/Items/${itemId}/Images/Backdrop`, getBaseUrl()),
|
|
||||||
providers: {
|
|
||||||
jellyfin: data.Id
|
|
||||||
}
|
|
||||||
// ...data,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
"jellyfin.getItem",
|
"jellyfin.getItem",
|
||||||
);
|
);
|
||||||
|
@ -235,7 +213,7 @@ export const getItemStream = query(
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
static: true,
|
static: true,
|
||||||
container: 'mkv',
|
// container: 'mkv',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
parseAs: 'stream',
|
parseAs: 'stream',
|
||||||
|
@ -315,8 +293,6 @@ export const queryItems = query(async () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(data);
|
|
||||||
}, "jellyfin.queryItems");
|
}, "jellyfin.queryItems");
|
||||||
|
|
||||||
export const getContinueWatching = query(
|
export const getContinueWatching = query(
|
||||||
|
@ -373,3 +349,22 @@ function assertNoErrors<T>(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapToEntry = (item: components['schemas']['BaseItemDto']): Entry => {
|
||||||
|
const type = {
|
||||||
|
Movie: 'movie',
|
||||||
|
Series: 'tv',
|
||||||
|
}[item.Type as string] as any;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type,
|
||||||
|
id: item.ProviderIds!["Tmdb"]!,
|
||||||
|
title: item.Name!,
|
||||||
|
overview: item.Overview!,
|
||||||
|
thumbnail: new URL(`/Items/${item.Id}/Images/Primary`, getBaseUrl()), //await getItemImage(data.Id!, 'Primary'),
|
||||||
|
image: new URL(`/Items/${item.Id}/Images/Backdrop`, getBaseUrl()),
|
||||||
|
providers: {
|
||||||
|
jellyfin: item.Id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -28,16 +28,24 @@ const getClients = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getEntry = query(
|
export const getEntry = query(
|
||||||
async (id: string): Promise<Entry | undefined> => {
|
async (type: Entry['type'], id: string): Promise<Entry | undefined> => {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
const [ clientV3 ] = getClients();
|
const [ clientV3 ] = getClients();
|
||||||
|
|
||||||
const { data } = await clientV3.GET("/movie/{movie_id}", {
|
const endpoint = ({
|
||||||
|
movie: "/movie/{movie_id}",
|
||||||
|
tv: '/tv/{series_id}',
|
||||||
|
} as const)[type];
|
||||||
|
|
||||||
|
const params = ({
|
||||||
|
movie: { movie_id: Number.parseInt(id) },
|
||||||
|
tv: { series_id: Number.parseInt(id) },
|
||||||
|
} as const)[type];
|
||||||
|
|
||||||
|
const { data } = await clientV3.GET(endpoint, {
|
||||||
params: {
|
params: {
|
||||||
path: {
|
path: params,
|
||||||
movie_id: Number.parseInt(id),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -46,6 +54,7 @@ export const getEntry = query(
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
type,
|
||||||
id: String(data.id ?? -1),
|
id: String(data.id ?? -1),
|
||||||
title: data.title!,
|
title: data.title!,
|
||||||
overview: data.overview,
|
overview: data.overview,
|
||||||
|
@ -78,6 +87,7 @@ export const getRecommendations = query(async (): Promise<Entry[]> => {
|
||||||
|
|
||||||
return data?.results.map(
|
return data?.results.map(
|
||||||
({ id, title, overview, poster_path, backdrop_path }) => ({
|
({ id, title, overview, poster_path, backdrop_path }) => ({
|
||||||
|
type: 'movie',
|
||||||
id: String(id ?? -1),
|
id: String(id ?? -1),
|
||||||
title: title!,
|
title: title!,
|
||||||
overview,
|
overview,
|
||||||
|
@ -102,7 +112,8 @@ export const getDiscovery = query(async (): Promise<Entry[]> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const movieEntries = movies?.results?.slice(0, 10)
|
const movieEntries = movies?.results?.slice(0, 10)
|
||||||
.map(({ id, title, overview, poster_path, backdrop_path }) => ({
|
.map(({ id, title, overview, poster_path, backdrop_path }): Entry => ({
|
||||||
|
type: 'movie',
|
||||||
id: String(id ?? -1),
|
id: String(id ?? -1),
|
||||||
title: title!,
|
title: title!,
|
||||||
overview,
|
overview,
|
||||||
|
@ -111,7 +122,8 @@ export const getDiscovery = query(async (): Promise<Entry[]> => {
|
||||||
})) ?? []
|
})) ?? []
|
||||||
|
|
||||||
const seriesEntries = series?.results?.slice(0, 10)
|
const seriesEntries = series?.results?.slice(0, 10)
|
||||||
.map(({ id, name, overview, poster_path, backdrop_path }) => ({
|
.map(({ id, name, overview, poster_path, backdrop_path }): Entry => ({
|
||||||
|
type: 'tv',
|
||||||
id: String(id ?? -1),
|
id: String(id ?? -1),
|
||||||
title: name!,
|
title: name!,
|
||||||
overview,
|
overview,
|
||||||
|
@ -137,7 +149,7 @@ export const searchMulti = query(async (query: string, page: number = 1): Promis
|
||||||
query,
|
query,
|
||||||
page,
|
page,
|
||||||
include_adult: false,
|
include_adult: false,
|
||||||
language: 'en-US'
|
language: 'en-US',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -147,8 +159,13 @@ export const searchMulti = query(async (query: string, page: number = 1): Promis
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`loaded page ${page}, found ${data.results?.length} results`);
|
console.log(`loaded page ${page}, found ${data.results?.length} results`);
|
||||||
|
console.log(data.results[0]);
|
||||||
|
|
||||||
return { count: data.total_results!, pages: data.total_pages!, results: data.results?.map(({ id, name, title, media_type, overview, backdrop_path, poster_path }) => ({
|
return { count: data.total_results!, pages: data.total_pages!, results: data.results?.filter(({ media_type }) => media_type === 'movie' || media_type === 'tv').map(({ id, name, title, media_type, overview, backdrop_path, poster_path }): Entry => ({
|
||||||
|
type: ({
|
||||||
|
movie: 'movie',
|
||||||
|
tv: 'tv',
|
||||||
|
}[media_type ?? '']) as any,
|
||||||
id: String(id),
|
id: String(id),
|
||||||
title: `${name ?? title ?? ''} (${media_type})`,
|
title: `${name ?? title ?? ''} (${media_type})`,
|
||||||
overview,
|
overview,
|
||||||
|
|
|
@ -1,33 +1,6 @@
|
||||||
import { toSlug } from "~/utilities";
|
import { toSlug } from "~/utilities";
|
||||||
import type { Entry } from "./types";
|
import type { Entry } from "./types";
|
||||||
|
|
||||||
export const entries = new Map<string, Entry>([
|
export const emptyEntry = Object.freeze<Entry>({ type: 'movie', id: '0', title: '' });
|
||||||
{ id: '1', title: 'Realtime with Bill Maher', thumbnail: 'https://www.themoviedb.org/t/p/w342/pbpoLLp4kvnYVfnEGiEhagpJuVZ.jpg' },
|
|
||||||
{ id: '2', title: 'Binnelanders', thumbnail: 'https://www.themoviedb.org/t/p/w342/v9nGSRx5lFz6KEgfmgHJMSgaARC.jpg' },
|
|
||||||
{ id: '3', title: 'Family guy', thumbnail: 'https://www.themoviedb.org/t/p/w342/y0HUz4eUNUe3TeEd8fQWYazPaC7.jpg' },
|
|
||||||
{ id: '4', title: 'The Simpsons', thumbnail: 'https://www.themoviedb.org/t/p/w342/vHqeLzYl3dEAutojCO26g0LIkom.jpg' },
|
|
||||||
{ id: '5', title: 'Breaking Bad', thumbnail: 'https://www.themoviedb.org/t/p/w342/ztkUQFLlC19CCMYHW9o1zWhJRNq.jpg' },
|
|
||||||
{ id: '6', title: 'Loki', thumbnail: 'https://www.themoviedb.org/t/p/w342/oJdVHUYrjdS2IqiNztVIP4GPB1p.jpg' },
|
|
||||||
{ id: '7', title: 'Dark desire', thumbnail: 'https://www.themoviedb.org/t/p/w342/uxFNAo2A6ZRcgNASLk02hJUbybn.jpg' },
|
|
||||||
{ id: '8', title: 'Bridgerton', thumbnail: 'https://www.themoviedb.org/t/p/w342/luoKpgVwi1E5nQsi7W0UuKHu2Rq.jpg' },
|
|
||||||
{ id: '9', title: 'Naruto', thumbnail: 'https://www.themoviedb.org/t/p/w342/xppeysfvDKVx775MFuH8Z9BlpMk.jpg' },
|
|
||||||
{ id: '11', title: 'Teenwolf', thumbnail: 'https://www.themoviedb.org/t/p/w342/fmlMmxSBgPEunHS5gjokIej048g.jpg' },
|
|
||||||
{ id: '12', title: 'Record of Ragnarok', thumbnail: 'https://www.themoviedb.org/t/p/w342/kTs2WNZOukpWdNhoRlH94pSJ3xf.jpg' },
|
|
||||||
{ id: '13', title: 'The Mandalorian', thumbnail: 'https://www.themoviedb.org/t/p/w342/eU1i6eHXlzMOlEq0ku1Rzq7Y4wA.jpg' },
|
|
||||||
{
|
|
||||||
id: '14',
|
|
||||||
title: 'Wednesday',
|
|
||||||
thumbnail: 'https://www.themoviedb.org/t/p/w342/9PFonBhy4cQy7Jz20NpMygczOkv.jpg',
|
|
||||||
image: 'https://www.themoviedb.org/t/p/original/iHSwvRVsRyxpX7FE7GbviaDvgGZ.jpg',
|
|
||||||
summary: 'Wednesday Addams is sent to Nevermore Academy, a bizarre boarding school where she attempts to master her psychic powers, stop a monstrous killing spree of the town citizens, and solve the supernatural mystery that affected her family',
|
|
||||||
releaseDate: '2022',
|
|
||||||
sources: [
|
|
||||||
{ label: 'TMDB', url: 'https://themoviedb.org/tv/119051', rating: { score: 8.5, max: 10 } }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
].map((entry) => [entry.id, entry]));
|
|
||||||
|
|
||||||
|
export const createSlug = (entry: Entry) => toSlug(`${entry.title}-${entry.type}-${entry.id}`);
|
||||||
export const emptyEntry = Object.freeze<Entry>({ id: '0', title: '' });
|
|
||||||
|
|
||||||
export const createSlug = (entry: Entry) => toSlug(`${entry.title}-${entry.id}`);
|
|
||||||
|
|
|
@ -14,12 +14,11 @@ const jellyfinUserId = "a9c51af84bf54578a99ab4dd0ebf0763";
|
||||||
|
|
||||||
const lookupTable = query(async () => listItemIds(), 'content.lookupTable');
|
const lookupTable = query(async () => listItemIds(), 'content.lookupTable');
|
||||||
|
|
||||||
// export const getHighlights = () => getRandomItems(jellyfinUserId);
|
|
||||||
export const getHighlights = () => getContinueWatching(jellyfinUserId);
|
export const getHighlights = () => getContinueWatching(jellyfinUserId);
|
||||||
export const getStream = query(async (id: string, range: string) => {
|
export const getStream = query(async (type: Entry['type'], id: string, range: string) => {
|
||||||
const table = await lookupTable();
|
const table = await lookupTable();
|
||||||
|
|
||||||
return getItemStream(table[id].jellyfin, range);
|
return getItemStream(table[`${type}-${id}`].jellyfin, range);
|
||||||
}, 'content.stream');
|
}, 'content.stream');
|
||||||
|
|
||||||
export const listCategories = query(async (): Promise<Category[]> => {
|
export const listCategories = query(async (): Promise<Category[]> => {
|
||||||
|
@ -34,9 +33,18 @@ export const listCategories = query(async (): Promise<Category[]> => {
|
||||||
];
|
];
|
||||||
}, "content.categories.list");
|
}, "content.categories.list");
|
||||||
|
|
||||||
|
export const getEntryFromSlug = query(
|
||||||
|
async (slug: string): Promise<Entry | undefined> => {
|
||||||
|
const { type, id } = slug.match(/^.+-(?<type>\w+)-(?<id>\w+)$/)?.groups ?? {};
|
||||||
|
|
||||||
|
return getTmdbEntry(type as any, id);
|
||||||
|
},
|
||||||
|
"content.getFromSlug",
|
||||||
|
);
|
||||||
|
|
||||||
export const getEntry = query(
|
export const getEntry = query(
|
||||||
async (id: Entry["id"]): Promise<Entry | undefined> => {
|
async (type: Entry['type'], id: Entry["id"]): Promise<Entry | undefined> => {
|
||||||
return getTmdbEntry(id);
|
return getTmdbEntry(type, id);
|
||||||
},
|
},
|
||||||
"content.get",
|
"content.get",
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,6 +4,7 @@ export interface Category {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Entry {
|
export interface Entry {
|
||||||
|
type: 'movie'|'tv';
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
overview?: string;
|
overview?: string;
|
||||||
|
|
|
@ -169,6 +169,9 @@ export const [VideoProvider, useVideo] = createContextProvider<
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
canplay() {
|
canplay() {
|
||||||
|
// setStore("loading", false);
|
||||||
|
},
|
||||||
|
canplaythrough() {
|
||||||
setStore("loading", false);
|
setStore("loading", false);
|
||||||
},
|
},
|
||||||
waiting() {
|
waiting() {
|
||||||
|
|
|
@ -34,8 +34,7 @@
|
||||||
max-inline-size: none;
|
max-inline-size: none;
|
||||||
align-content: end;
|
align-content: end;
|
||||||
|
|
||||||
gap: var(--size-2);
|
background: linear-gradient(to bottom, black, transparent) top left / 100% 20% no-repeat;
|
||||||
padding: var(--size-2);
|
|
||||||
|
|
||||||
& > header {
|
& > header {
|
||||||
display: block grid;
|
display: block grid;
|
||||||
|
@ -60,16 +59,30 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
& > footer {
|
& > footer {
|
||||||
|
position: relative;
|
||||||
display: block grid;
|
display: block grid;
|
||||||
grid: auto auto / auto auto auto;
|
grid: auto auto / auto auto auto;
|
||||||
place-content: space-between;
|
place-content: space-between;
|
||||||
gap: var(--size-2);
|
|
||||||
|
|
||||||
& > :nth-child(1) {
|
gap: var(--size-2);
|
||||||
grid-column: 1 / -1;
|
padding: var(--size-2);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
inset-block-start: -5rem;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
mask-image: linear-gradient(to bottom, transparent, black 5rem);
|
||||||
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > section {
|
& > section {
|
||||||
|
z-index: 1;
|
||||||
|
&:nth-child(1) {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
& > button {
|
& > button {
|
||||||
font-size: var(--size-7);
|
font-size: var(--size-7);
|
||||||
text-shadow: 0 0 .5rem #000;
|
text-shadow: 0 0 .5rem #000;
|
||||||
|
|
|
@ -121,7 +121,9 @@ export const Player: Component<PlayerProps> = (props) => {
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
<section>
|
||||||
<SeekBar />
|
<SeekBar />
|
||||||
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<Volume />
|
<Volume />
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -10,7 +10,12 @@ import {
|
||||||
} from "@solidjs/router";
|
} from "@solidjs/router";
|
||||||
import { Show } from "solid-js";
|
import { Show } from "solid-js";
|
||||||
import { Details } from "~/components/details";
|
import { Details } from "~/components/details";
|
||||||
import { createSlug, Entry, getEntry } from "~/features/content";
|
import {
|
||||||
|
createSlug,
|
||||||
|
Entry,
|
||||||
|
getEntry,
|
||||||
|
getEntryFromSlug,
|
||||||
|
} from "~/features/content";
|
||||||
|
|
||||||
const healUrl = async (slug: string, entry: Entry) => {
|
const healUrl = async (slug: string, entry: Entry) => {
|
||||||
const actualSlug = createSlug(entry);
|
const actualSlug = createSlug(entry);
|
||||||
|
@ -47,8 +52,7 @@ export const route = {
|
||||||
|
|
||||||
export default function Item() {
|
export default function Item() {
|
||||||
const { slug } = useParams<ItemParams>();
|
const { slug } = useParams<ItemParams>();
|
||||||
const id = slug.slice(slug.lastIndexOf("-") + 1);
|
const entry = createAsync(() => getEntryFromSlug(slug));
|
||||||
const entry = createAsync(() => getEntry(id));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -1,55 +1,73 @@
|
||||||
import { createInfiniteScroll } from "@solid-primitives/pagination";
|
import { createInfiniteScroll } from "@solid-primitives/pagination";
|
||||||
import { Title } from "@solidjs/meta";
|
import { Title } from "@solidjs/meta";
|
||||||
import { createEffect, createSignal, For, on, onMount, Show, createComputed, batch, createMemo, untrack } from "solid-js";
|
import {
|
||||||
|
createEffect,
|
||||||
|
createSignal,
|
||||||
|
For,
|
||||||
|
Show,
|
||||||
|
createMemo,
|
||||||
|
untrack,
|
||||||
|
} from "solid-js";
|
||||||
import { createSlug, search } from "~/features/content";
|
import { createSlug, search } from "~/features/content";
|
||||||
import { AiOutlineLoading } from "solid-icons/ai";
|
import { AiOutlineLoading } from "solid-icons/ai";
|
||||||
import css from './index.module.css';
|
|
||||||
import { debounce } from "@solid-primitives/scheduled";
|
import { debounce } from "@solid-primitives/scheduled";
|
||||||
|
import css from "./index.module.css";
|
||||||
|
|
||||||
const getResults = async (query: string, page: number) => {
|
const getResults = async (query: string, page: number) => {
|
||||||
const { results } = await search(query, page + 1);
|
const { results } = await search(query, page + 1);
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
const [ query, setQuery ] = createSignal(""); // lord of the rings
|
const [query, setQuery] = createSignal(""); // lord of the rings
|
||||||
const [ ref, setRef ] = createSignal<HTMLInputElement>();
|
const [ref, setRef] = createSignal<HTMLInputElement>();
|
||||||
|
|
||||||
const KAAS = createMemo(() => {
|
const results = createMemo(() => {
|
||||||
const q = query();
|
const q = query();
|
||||||
const [pages, setEl, { end }] = createInfiniteScroll((page) => getResults(q, page));
|
const [pages, setEl, { end }] = createInfiniteScroll((page) =>
|
||||||
|
getResults(q, page)
|
||||||
|
);
|
||||||
|
|
||||||
return { pages, setEl, end };
|
return { pages, setEl, end };
|
||||||
});
|
});
|
||||||
// const result = createAsync(() => search(query()), { initialValue: { count: 0, pages: 0, results: [] } });
|
// const result = createAsync(() => search(query()), { initialValue: { count: 0, pages: 0, results: [] } });
|
||||||
|
|
||||||
const title = 'Search';
|
const title = "Search";
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
KAAS();
|
results();
|
||||||
|
|
||||||
untrack(ref)?.focus();
|
untrack(ref)?.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
return <div class={css.container}>
|
return (
|
||||||
|
<div class={css.container}>
|
||||||
<Title>{title}</Title>
|
<Title>{title}</Title>
|
||||||
|
|
||||||
<header class={css.header}>
|
<header class={css.header}>
|
||||||
<input ref={setRef} type="search" placeholder={title} value={query()} oninput={debounce(e => setQuery(e.target.value), 300)} />
|
<input
|
||||||
|
ref={setRef}
|
||||||
|
type="search"
|
||||||
|
placeholder={title}
|
||||||
|
value={query()}
|
||||||
|
oninput={debounce((e) => setQuery(e.target.value), 300)}
|
||||||
|
/>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<ul class={css.grid}>
|
<ul class={css.grid}>
|
||||||
<For each={KAAS().pages()}>{
|
<For each={results().pages()}>
|
||||||
item => <a id={`item:${item.id}`} href={`/details/${createSlug(item)}`}>
|
{(item) => (
|
||||||
|
<a id={`item:${item.id}`} href={`/details/${createSlug(item)}`}>
|
||||||
<img class={css.item} src={item.thumbnail} title={item.title} />
|
<img class={css.item} src={item.thumbnail} title={item.title} />
|
||||||
</a>
|
</a>
|
||||||
}</For>
|
)}
|
||||||
|
</For>
|
||||||
|
|
||||||
<Show when={!KAAS().end()}>
|
<Show when={!results().end()}>
|
||||||
<AiOutlineLoading ref={KAAS().setEl} />
|
<AiOutlineLoading ref={results().setEl} />
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<Show when={KAAS().pages().length === 0}>
|
<Show when={results().pages().length === 0}>
|
||||||
<p>No results</p>
|
<p>No results</p>
|
||||||
</Show>
|
</Show>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -63,5 +81,6 @@ export default function Index() {
|
||||||
}</For>
|
}</For>
|
||||||
</ul>
|
</ul>
|
||||||
</output> */}
|
</output> */}
|
||||||
</div>;
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,13 @@ import {
|
||||||
useParams,
|
useParams,
|
||||||
} from "@solidjs/router";
|
} from "@solidjs/router";
|
||||||
import { Show } from "solid-js";
|
import { Show } from "solid-js";
|
||||||
import { createSlug, getEntry } from "~/features/content";
|
import { createSlug, getEntryFromSlug } from "~/features/content";
|
||||||
import { Player } from "~/features/player";
|
import { Player } from "~/features/player";
|
||||||
import css from "./slug.module.css";
|
|
||||||
import { Title } from "@solidjs/meta";
|
import { Title } from "@solidjs/meta";
|
||||||
|
import css from "./slug.module.css";
|
||||||
|
|
||||||
const healUrl = query(async (slug: string) => {
|
const healUrl = query(async (slug: string) => {
|
||||||
const entry = await getEntry(slug.slice(slug.lastIndexOf("-") + 1));
|
const entry = await getEntryFromSlug(slug);
|
||||||
|
|
||||||
if (entry === undefined) {
|
if (entry === undefined) {
|
||||||
return json(null, { status: 404 });
|
return json(null, { status: 404 });
|
||||||
|
@ -44,14 +44,13 @@ export const route = {
|
||||||
|
|
||||||
await healUrl(slug);
|
await healUrl(slug);
|
||||||
|
|
||||||
return getEntry(slug.slice(slug.lastIndexOf("-") + 1));
|
return getEntryFromSlug(slug);
|
||||||
},
|
},
|
||||||
} satisfies RouteDefinition;
|
} satisfies RouteDefinition;
|
||||||
|
|
||||||
export default function Item() {
|
export default function Item() {
|
||||||
const { slug } = useParams<ItemParams>();
|
const { slug } = useParams<ItemParams>();
|
||||||
const id = slug.slice(slug.lastIndexOf("-") + 1);
|
const entry = createAsync(() => getEntryFromSlug(slug));
|
||||||
const entry = createAsync(() => getEntry(id));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={css.page}>
|
<div class={css.page}>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue