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;
|
||||
display: block;
|
||||
background: linear-gradient(
|
||||
calc(atan(var(--ratio))),
|
||||
atan(var(--ratio, .2)),
|
||||
var(--surface-2) 20em,
|
||||
transparent 90%
|
||||
);
|
||||
|
|
|
@ -11,13 +11,10 @@ export const Details: Component<DetailsProps> = (props) => {
|
|||
|
||||
onMount(() => {
|
||||
const observer = new ResizeObserver(([entry]) => {
|
||||
const _20em = 20 * 16;
|
||||
const { inlineSize, blockSize } = entry.contentBoxSize[0];
|
||||
console.log(blockSize / inlineSize);
|
||||
|
||||
(entry.target as HTMLElement).style.setProperty(
|
||||
"--ratio",
|
||||
String(_20em / inlineSize),
|
||||
String((blockSize * 0.2) / inlineSize)
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -8,9 +8,6 @@ type HeroProps = {
|
|||
};
|
||||
|
||||
export function Hero(props: HeroProps) {
|
||||
const entry = createMemo(() => props.entries.at(0)!);
|
||||
const slug = createMemo(() => createSlug(entry()));
|
||||
|
||||
return (
|
||||
<div class={`${css.container} ${props.class ?? ""}`}>
|
||||
<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 { query } from "@solidjs/router";
|
||||
import { Entry } from "../types";
|
||||
|
@ -39,7 +39,7 @@ const getClient = () => {
|
|||
export const getCurrentUser = query(async () => {
|
||||
"use server";
|
||||
|
||||
const { data, error, response } = await getClient().GET("/Users/Public", {
|
||||
const { data } = await getClient().GET("/Users/Public", {
|
||||
params: {},
|
||||
});
|
||||
|
||||
|
@ -83,7 +83,7 @@ export const listItemIds = query(
|
|||
|
||||
return Object.fromEntries(
|
||||
data.Items?.map((item) => ([
|
||||
item.ProviderIds!["Tmdb"]!,
|
||||
`${item.MediaType as any}-${item.ProviderIds!["Tmdb"]!}`,
|
||||
{ jellyfin: item.Id! },
|
||||
])) ?? []
|
||||
);
|
||||
|
@ -118,12 +118,7 @@ export const listItems = query(
|
|||
}
|
||||
|
||||
return (
|
||||
data.Items?.map((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'),
|
||||
})) ?? []
|
||||
data.Items?.map((item) => mapToEntry(item)) ?? []
|
||||
);
|
||||
},
|
||||
"jellyfin.listItems",
|
||||
|
@ -164,13 +159,7 @@ export const getRandomItems = query(
|
|||
});
|
||||
|
||||
return (
|
||||
data?.Items?.map((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'),
|
||||
})) ?? []
|
||||
data?.Items?.map((item) => mapToEntry(item)) ?? []
|
||||
);
|
||||
},
|
||||
"jellyfin.listRandomItems",
|
||||
|
@ -205,18 +194,7 @@ export const getItem = query(
|
|||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
// 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,
|
||||
};
|
||||
return mapToEntry(data);
|
||||
},
|
||||
"jellyfin.getItem",
|
||||
);
|
||||
|
@ -235,7 +213,7 @@ export const getItemStream = query(
|
|||
},
|
||||
query: {
|
||||
static: true,
|
||||
container: 'mkv',
|
||||
// container: 'mkv',
|
||||
},
|
||||
},
|
||||
parseAs: 'stream',
|
||||
|
@ -315,8 +293,6 @@ export const queryItems = query(async () => {
|
|||
},
|
||||
},
|
||||
});
|
||||
|
||||
console.log(data);
|
||||
}, "jellyfin.queryItems");
|
||||
|
||||
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(
|
||||
async (id: string): Promise<Entry | undefined> => {
|
||||
async (type: Entry['type'], id: string): Promise<Entry | undefined> => {
|
||||
"use server";
|
||||
|
||||
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: {
|
||||
path: {
|
||||
movie_id: Number.parseInt(id),
|
||||
},
|
||||
path: params,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -46,6 +54,7 @@ export const getEntry = query(
|
|||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
id: String(data.id ?? -1),
|
||||
title: data.title!,
|
||||
overview: data.overview,
|
||||
|
@ -78,6 +87,7 @@ export const getRecommendations = query(async (): Promise<Entry[]> => {
|
|||
|
||||
return data?.results.map(
|
||||
({ id, title, overview, poster_path, backdrop_path }) => ({
|
||||
type: 'movie',
|
||||
id: String(id ?? -1),
|
||||
title: title!,
|
||||
overview,
|
||||
|
@ -102,7 +112,8 @@ export const getDiscovery = query(async (): Promise<Entry[]> => {
|
|||
}
|
||||
|
||||
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),
|
||||
title: title!,
|
||||
overview,
|
||||
|
@ -111,7 +122,8 @@ export const getDiscovery = query(async (): Promise<Entry[]> => {
|
|||
})) ?? []
|
||||
|
||||
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),
|
||||
title: name!,
|
||||
overview,
|
||||
|
@ -137,7 +149,7 @@ export const searchMulti = query(async (query: string, page: number = 1): Promis
|
|||
query,
|
||||
page,
|
||||
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(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),
|
||||
title: `${name ?? title ?? ''} (${media_type})`,
|
||||
overview,
|
||||
|
|
|
@ -1,33 +1,6 @@
|
|||
import { toSlug } from "~/utilities";
|
||||
import type { Entry } from "./types";
|
||||
|
||||
export const entries = new Map<string, Entry>([
|
||||
{ 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 emptyEntry = Object.freeze<Entry>({ type: 'movie', id: '0', title: '' });
|
||||
|
||||
|
||||
export const emptyEntry = Object.freeze<Entry>({ id: '0', title: '' });
|
||||
|
||||
export const createSlug = (entry: Entry) => toSlug(`${entry.title}-${entry.id}`);
|
||||
export const createSlug = (entry: Entry) => toSlug(`${entry.title}-${entry.type}-${entry.id}`);
|
||||
|
|
|
@ -14,12 +14,11 @@ const jellyfinUserId = "a9c51af84bf54578a99ab4dd0ebf0763";
|
|||
|
||||
const lookupTable = query(async () => listItemIds(), 'content.lookupTable');
|
||||
|
||||
// export const getHighlights = () => getRandomItems(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();
|
||||
|
||||
return getItemStream(table[id].jellyfin, range);
|
||||
return getItemStream(table[`${type}-${id}`].jellyfin, range);
|
||||
}, 'content.stream');
|
||||
|
||||
export const listCategories = query(async (): Promise<Category[]> => {
|
||||
|
@ -34,9 +33,18 @@ export const listCategories = query(async (): Promise<Category[]> => {
|
|||
];
|
||||
}, "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(
|
||||
async (id: Entry["id"]): Promise<Entry | undefined> => {
|
||||
return getTmdbEntry(id);
|
||||
async (type: Entry['type'], id: Entry["id"]): Promise<Entry | undefined> => {
|
||||
return getTmdbEntry(type, id);
|
||||
},
|
||||
"content.get",
|
||||
);
|
||||
|
|
|
@ -4,6 +4,7 @@ export interface Category {
|
|||
}
|
||||
|
||||
export interface Entry {
|
||||
type: 'movie'|'tv';
|
||||
id: string;
|
||||
title: string;
|
||||
overview?: string;
|
||||
|
|
|
@ -169,6 +169,9 @@ export const [VideoProvider, useVideo] = createContextProvider<
|
|||
);
|
||||
},
|
||||
canplay() {
|
||||
// setStore("loading", false);
|
||||
},
|
||||
canplaythrough() {
|
||||
setStore("loading", false);
|
||||
},
|
||||
waiting() {
|
||||
|
|
|
@ -34,8 +34,7 @@
|
|||
max-inline-size: none;
|
||||
align-content: end;
|
||||
|
||||
gap: var(--size-2);
|
||||
padding: var(--size-2);
|
||||
background: linear-gradient(to bottom, black, transparent) top left / 100% 20% no-repeat;
|
||||
|
||||
& > header {
|
||||
display: block grid;
|
||||
|
@ -60,16 +59,30 @@
|
|||
}
|
||||
|
||||
& > footer {
|
||||
position: relative;
|
||||
display: block grid;
|
||||
grid: auto auto / auto auto auto;
|
||||
place-content: space-between;
|
||||
gap: var(--size-2);
|
||||
|
||||
& > :nth-child(1) {
|
||||
grid-column: 1 / -1;
|
||||
gap: var(--size-2);
|
||||
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 {
|
||||
z-index: 1;
|
||||
&:nth-child(1) {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
& > button {
|
||||
font-size: var(--size-7);
|
||||
text-shadow: 0 0 .5rem #000;
|
||||
|
|
|
@ -121,7 +121,9 @@ export const Player: Component<PlayerProps> = (props) => {
|
|||
</section>
|
||||
|
||||
<footer>
|
||||
<section>
|
||||
<SeekBar />
|
||||
</section>
|
||||
<section>
|
||||
<Volume />
|
||||
</section>
|
||||
|
|
|
@ -10,7 +10,12 @@ import {
|
|||
} from "@solidjs/router";
|
||||
import { Show } from "solid-js";
|
||||
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 actualSlug = createSlug(entry);
|
||||
|
@ -47,8 +52,7 @@ export const route = {
|
|||
|
||||
export default function Item() {
|
||||
const { slug } = useParams<ItemParams>();
|
||||
const id = slug.slice(slug.lastIndexOf("-") + 1);
|
||||
const entry = createAsync(() => getEntry(id));
|
||||
const entry = createAsync(() => getEntryFromSlug(slug));
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -1,55 +1,73 @@
|
|||
import { createInfiniteScroll } from "@solid-primitives/pagination";
|
||||
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 { AiOutlineLoading } from "solid-icons/ai";
|
||||
import css from './index.module.css';
|
||||
import { debounce } from "@solid-primitives/scheduled";
|
||||
import css from "./index.module.css";
|
||||
|
||||
const getResults = async (query: string, page: number) => {
|
||||
const { results } = await search(query, page + 1);
|
||||
return results;
|
||||
};
|
||||
};
|
||||
|
||||
export default function Index() {
|
||||
const [ query, setQuery ] = createSignal(""); // lord of the rings
|
||||
const [ ref, setRef ] = createSignal<HTMLInputElement>();
|
||||
const [query, setQuery] = createSignal(""); // lord of the rings
|
||||
const [ref, setRef] = createSignal<HTMLInputElement>();
|
||||
|
||||
const KAAS = createMemo(() => {
|
||||
const results = createMemo(() => {
|
||||
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 };
|
||||
});
|
||||
// const result = createAsync(() => search(query()), { initialValue: { count: 0, pages: 0, results: [] } });
|
||||
|
||||
const title = 'Search';
|
||||
const title = "Search";
|
||||
|
||||
createEffect(() => {
|
||||
KAAS();
|
||||
results();
|
||||
|
||||
untrack(ref)?.focus();
|
||||
});
|
||||
|
||||
return <div class={css.container}>
|
||||
return (
|
||||
<div class={css.container}>
|
||||
<Title>{title}</Title>
|
||||
|
||||
<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>
|
||||
|
||||
<ul class={css.grid}>
|
||||
<For each={KAAS().pages()}>{
|
||||
item => <a id={`item:${item.id}`} href={`/details/${createSlug(item)}`}>
|
||||
<For each={results().pages()}>
|
||||
{(item) => (
|
||||
<a id={`item:${item.id}`} href={`/details/${createSlug(item)}`}>
|
||||
<img class={css.item} src={item.thumbnail} title={item.title} />
|
||||
</a>
|
||||
}</For>
|
||||
)}
|
||||
</For>
|
||||
|
||||
<Show when={!KAAS().end()}>
|
||||
<AiOutlineLoading ref={KAAS().setEl} />
|
||||
<Show when={!results().end()}>
|
||||
<AiOutlineLoading ref={results().setEl} />
|
||||
</Show>
|
||||
|
||||
<Show when={KAAS().pages().length === 0}>
|
||||
<Show when={results().pages().length === 0}>
|
||||
<p>No results</p>
|
||||
</Show>
|
||||
</ul>
|
||||
|
@ -63,5 +81,6 @@ export default function Index() {
|
|||
}</For>
|
||||
</ul>
|
||||
</output> */}
|
||||
</div>;
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,13 +8,13 @@ import {
|
|||
useParams,
|
||||
} from "@solidjs/router";
|
||||
import { Show } from "solid-js";
|
||||
import { createSlug, getEntry } from "~/features/content";
|
||||
import { createSlug, getEntryFromSlug } from "~/features/content";
|
||||
import { Player } from "~/features/player";
|
||||
import css from "./slug.module.css";
|
||||
import { Title } from "@solidjs/meta";
|
||||
import css from "./slug.module.css";
|
||||
|
||||
const healUrl = query(async (slug: string) => {
|
||||
const entry = await getEntry(slug.slice(slug.lastIndexOf("-") + 1));
|
||||
const entry = await getEntryFromSlug(slug);
|
||||
|
||||
if (entry === undefined) {
|
||||
return json(null, { status: 404 });
|
||||
|
@ -44,14 +44,13 @@ export const route = {
|
|||
|
||||
await healUrl(slug);
|
||||
|
||||
return getEntry(slug.slice(slug.lastIndexOf("-") + 1));
|
||||
return getEntryFromSlug(slug);
|
||||
},
|
||||
} satisfies RouteDefinition;
|
||||
|
||||
export default function Item() {
|
||||
const { slug } = useParams<ItemParams>();
|
||||
const id = slug.slice(slug.lastIndexOf("-") + 1);
|
||||
const entry = createAsync(() => getEntry(id));
|
||||
const entry = createAsync(() => getEntryFromSlug(slug));
|
||||
|
||||
return (
|
||||
<div class={css.page}>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue