Properly use nextup in the home

This commit is contained in:
Zoe Roux 2026-02-16 12:29:11 +01:00
parent 637505dde9
commit cef7a64017
No known key found for this signature in database
6 changed files with 96 additions and 104 deletions

View File

@ -135,7 +135,7 @@ const newsSort: Sort = {
],
};
const entryRelations = {
export const entryRelations = {
translations: () => {
const { pk, language, ...trans } = getColumns(entryTranslations);
return db

View File

@ -6,6 +6,7 @@ import { entries } from "~/db/schema";
import { watchlist } from "~/db/schema/watchlist";
import { getColumns } from "~/db/utils";
import { Entry } from "~/models/entry";
import { Show } from "~/models/show";
import {
AcceptLanguage,
createPage,
@ -21,6 +22,7 @@ import { desc } from "~/models/utils/descriptions";
import {
entryFilters,
entryProgressQ,
entryRelations,
entryVideosQ,
getEntryTransQ,
mapProgress,
@ -71,7 +73,7 @@ export const nextup = new Elysia({ tags: ["profiles"] })
query: { sort, filter, query, limit, after },
headers: { "accept-language": languages, ...headers },
request: { url },
jwt: { sub },
jwt: { sub, settings },
}) => {
const langs = processLanguages(languages);
const transQ = getEntryTransQ(langs);
@ -102,6 +104,11 @@ export const nextup = new Elysia({ tags: ["profiles"] })
seasonNumber: sql<number>`${seasonNumber}`,
episodeNumber: sql<number>`${episodeNumber}`,
name: sql<string>`${transQ.name}`,
show: sql`${entryRelations.show({
languages: langs,
preferOriginal: settings.preferOriginal,
})}`,
})
.from(entries)
.innerJoin(watchlist, eq(watchlist.nextEntry, entries.pk))
@ -134,7 +141,7 @@ export const nextup = new Elysia({ tags: ["profiles"] })
"accept-language": AcceptLanguage({ autoFallback: true }),
}),
response: {
200: Page(Entry),
200: Page(t.Intersect([Entry, t.Object({ show: Show })])),
},
},
);

View File

@ -8,9 +8,9 @@ import { HeaderBackground, useScrollNavbar } from "../navbar";
import { GenreGrid } from "./genre";
import { Header } from "./header";
import { NewsList } from "./news";
import { NextupList } from "./nextup";
import { Recommended } from "./recommended";
import { VerticalRecommended } from "./vertical";
import { WatchlistList } from "./watchlist";
export const HomePage = () => {
const genres = shuffle(Object.values(Genre.enum));
@ -50,7 +50,7 @@ export const HomePage = () => {
)}
Loader={Header.Loader}
/>
<WatchlistList />
<NextupList />
<NewsList />
{genres
.filter((_, i) => i < 2)
@ -64,10 +64,11 @@ export const HomePage = () => {
<GenreGrid key={x} genre={x} />
))}
<VerticalRecommended />
{/*
TODO: Lazy load those items
{randomItems.filter((_, i) => i >= 6).map((x) => <GenreGrid key={x} genre={x} />)}
*/}
{genres
.filter((_, i) => i >= 6)
.map((x) => (
<GenreGrid key={x} genre={x} />
))}
</Animated.ScrollView>
</>
);
@ -75,7 +76,7 @@ export const HomePage = () => {
HomePage.queries = (randomItems: Genre[]) => [
Header.query(),
WatchlistList.query(),
NextupList.query(),
NewsList.query(),
...randomItems.filter((_, i) => i < 6).map((x) => GenreGrid.query(x)),
Recommended.query(),

View File

@ -15,20 +15,18 @@ export const NewsList = () => {
query={NewsList.query()}
layout={{ ...EntryBox.layout, layout: "horizontal" }}
Empty={<EmptyView message={t("home.none")} />}
Render={({ item }) => {
return (
<EntryBox
kind={item.kind}
slug={item.slug}
serieSlug={item.show!.slug}
name={`${item.show!.name} ${entryDisplayNumber(item)}`}
description={item.name}
thumbnail={item.thumbnail}
href={item.href ?? "#"}
watchedPercent={item.progress.percent}
/>
);
}}
Render={({ item }) => (
<EntryBox
kind={item.kind}
slug={item.slug}
serieSlug={item.show!.slug}
name={`${item.show!.name} ${entryDisplayNumber(item)}`}
description={item.name}
thumbnail={item.thumbnail ?? item.show!.thumbnail}
href={item.href ?? "#"}
watchedPercent={item.progress.percent}
/>
)}
Loader={EntryBox.Loader}
/>
</>

View File

@ -0,0 +1,66 @@
import { useTranslation } from "react-i18next";
import { View } from "react-native";
import { EntryBox, entryDisplayNumber } from "~/components/entries";
import { ItemGrid } from "~/components/items";
import { Entry } from "~/models";
import { Button, Link, P } from "~/primitives";
import { useAccount } from "~/providers/account-context";
import { InfiniteFetch, type QueryIdentifier } from "~/query";
import { EmptyView } from "~/ui/empty-view";
import { Header } from "./genre";
export const NextupList = () => {
const { t } = useTranslation();
const account = useAccount();
if (!account) {
return (
<>
<Header title={t("home.watchlist")} />
<View className="items-center justify-center">
<P>{t("home.watchlistLogin")}</P>
<Button
as={Link}
href={"/login"}
text={t("login.login")}
className="m-4 min-w-md"
/>
</View>
</>
);
}
return (
<>
<Header title={t("home.watchlist")} />
<InfiniteFetch
query={NextupList.query()}
layout={{ ...ItemGrid.layout, layout: "horizontal" }}
Empty={<EmptyView message={t("home.none")} />}
Render={({ item }) => (
<EntryBox
kind={item.kind}
slug={item.slug}
serieSlug={item.show!.slug}
name={`${item.show!.name} ${entryDisplayNumber(item)}`}
description={item.name}
thumbnail={item.thumbnail ?? item.show!.thumbnail}
href={item.href ?? "#"}
watchedPercent={item.progress.percent}
/>
)}
Loader={EntryBox.Loader}
/>
</>
);
};
NextupList.query = (): QueryIdentifier<Entry> => ({
parser: Entry,
infinite: true,
path: ["api", "profiles", "me", "nextup"],
params: {
limit: 10,
with: ["nextEntry"],
},
});

View File

@ -1,80 +0,0 @@
import { useTranslation } from "react-i18next";
import { View } from "react-native";
import { EntryBox, entryDisplayNumber } from "~/components/entries";
import { ItemGrid, itemMap } from "~/components/items";
import { Show } from "~/models";
import { Button, Link, P } from "~/primitives";
import { useAccount } from "~/providers/account-context";
import { InfiniteFetch, type QueryIdentifier } from "~/query";
import { EmptyView } from "~/ui/empty-view";
import { Header } from "./genre";
export const WatchlistList = () => {
const { t } = useTranslation();
const account = useAccount();
if (!account) {
return (
<>
<Header title={t("home.watchlist")} />
<View className="items-center justify-center">
<P>{t("home.watchlistLogin")}</P>
<Button
as={Link}
href={"/login"}
text={t("login.login")}
className="m-4 min-w-md"
/>
</View>
</>
);
}
return (
<>
<Header title={t("home.watchlist")} />
<InfiniteFetch
query={WatchlistList.query()}
layout={{ ...ItemGrid.layout, layout: "horizontal" }}
getItemType={(x, i) =>
(x?.kind === "serie" && x.nextEntry) || (!x && i % 2)
? "episode"
: "item"
}
getItemSizeMult={(_, __, kind) => (kind === "episode" ? 2 : 1)}
Empty={<EmptyView message={t("home.none")} />}
Render={({ item }) => {
const entry = item.kind === "serie" ? item.nextEntry : null;
if (entry) {
return (
<EntryBox
kind={entry.kind}
slug={entry.slug}
serieSlug={item.slug}
name={`${item.name} ${entryDisplayNumber(entry)}`}
description={entry.name}
thumbnail={entry.thumbnail ?? item.thumbnail}
href={entry.href ?? "#"}
watchedPercent={entry.progress.percent}
/>
);
}
return <ItemGrid {...itemMap(item)} horizontal />;
}}
Loader={({ index }) =>
index % 2 ? <EntryBox.Loader /> : <ItemGrid.Loader horizontal />
}
/>
</>
);
};
WatchlistList.query = (): QueryIdentifier<Show> => ({
parser: Show,
infinite: true,
path: ["api", "profiles", "me", "watchlist"],
params: {
limit: 10,
with: ["nextEntry"],
},
});