Add random genres list to the home page

This commit is contained in:
Zoe Roux 2023-10-16 19:01:59 +02:00
parent d21e4ffba2
commit 5a618a8db2
7 changed files with 147 additions and 19 deletions

View File

@ -20,6 +20,7 @@
"@material-symbols/svg-400": "^0.10.3",
"@shopify/flash-list": "1.4.3",
"@tanstack/react-query": "^4.32.6",
"array-shuffle": "^3.0.0",
"babel-plugin-transform-inline-environment-variables": "^0.4.4",
"expo": "^49.0.6",
"expo-build-properties": "~0.8.3",

View File

@ -17,6 +17,7 @@
"@material-symbols/svg-400": "^0.10.3",
"@radix-ui/react-dropdown-menu": "^2.0.5",
"@tanstack/react-query": "^4.32.6",
"array-shuffle": "^3.0.0",
"expo-linear-gradient": "^12.4.0",
"expo-modules-core": "^1.5.9",
"hls.js": "^1.4.10",

View File

@ -38,6 +38,7 @@ import { useTheme, useMobileHover, useStyleRegistry, StyleRegistryProvider } fro
import superjson from "superjson";
import Head from "next/head";
import { withTranslations } from "../i18n";
import arrayShuffle from "array-shuffle";
const font = Poppins({ weight: ["300", "400", "900"], subsets: ["latin"], display: "swap" });
@ -102,7 +103,9 @@ const YoshikiDebug = ({ children }: { children: JSX.Element }) => {
const App = ({ Component, pageProps }: AppProps) => {
const [queryClient] = useState(() => createQueryClient());
const { queryState, token, ...props } = superjson.deserialize<any>(pageProps ?? { json: {} });
const { queryState, token, randomItems, ...props } = superjson.deserialize<any>(
pageProps ?? { json: {} },
);
const layoutInfo = (Component as QueryPage).getLayout ?? (({ page }) => page);
const { Layout, props: layoutProps } =
typeof layoutInfo === "function" ? { Layout: layoutInfo, props: {} } : layoutInfo;
@ -122,7 +125,19 @@ const App = ({ Component, pageProps }: AppProps) => {
<Hydrate state={queryState}>
<ThemeSelector theme="auto" font={{ normal: "inherit" }}>
<GlobalCssTheme />
<Layout page={<Component {...props} />} {...layoutProps} />
<Layout
page={
<Component
randomItems={
randomItems[Component.displayName!] ??
arrayShuffle((Component as QueryPage).randomItems ?? [])
}
{...props}
/>
}
randomItems={[]}
{...layoutProps}
/>
</ThemeSelector>
</Hydrate>
</QueryClientProvider>
@ -148,6 +163,9 @@ App.getInitialProps = async (ctx: AppContext) => {
const [authToken, token] = await getTokenWJ(ctx.ctx.req?.headers.cookie);
appProps.pageProps.queryState = await fetchQuery(urls, authToken);
appProps.pageProps.token = token;
appProps.pageProps.randomItems = {
[Component.displayName!]: arrayShuffle(Component.randomItems ?? []),
};
return { pageProps: superjson.serialize(appProps.pageProps) };
};

View File

@ -162,11 +162,12 @@ export type QueryIdentifier<T = unknown, Ret = T> = {
getNext?: (item: unknown) => string | undefined;
};
export type QueryPage<Props = {}> = ComponentType<Props> & {
export type QueryPage<Props = {}, Items = unknown> = ComponentType<Props & { randomItems: Items[]}> & {
getFetchUrls?: (route: { [key: string]: string }) => QueryIdentifier<any>[];
getLayout?:
| QueryPage<{ page: ReactElement }>
| { Layout: QueryPage<{ page: ReactElement }>; props: object };
randomItems?: Items[]
};
const toQueryKey = <Data, Ret>(query: QueryIdentifier<Data, Ret>) => {

View File

@ -0,0 +1,86 @@
/*
* Kyoo - A portable and vast media library solution.
* Copyright (c) Kyoo.
*
* See AUTHORS.md and LICENSE file in the project root for full license information.
*
* Kyoo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Kyoo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
import {
Genre,
ItemKind,
LibraryItem,
LibraryItemP,
Page,
Paged,
QueryIdentifier,
getDisplayDate,
} from "@kyoo/models";
import { H3, IconButton, ts } from "@kyoo/primitives";
import { useRef } from "react";
import { ScrollView, View } from "react-native";
import { useYoshiki } from "yoshiki/native";
import { Fetch } from "../fetch";
import { ItemGrid } from "../browse/grid";
import ChevronLeft from "@material-symbols/svg-400/rounded/chevron_left-fill.svg";
import ChevronRight from "@material-symbols/svg-400/rounded/chevron_right-fill.svg";
export const GenreGrid = ({ genre }: { genre: Genre }) => {
const ref = useRef<ScrollView>(null);
const { css } = useYoshiki();
return (
<View>
<View {...css({ marginX: ts(1), flexDirection: "row", justifyContent: "space-between" })}>
<H3>{genre}</H3>
<View {...css({ flexDirection: "row" })}>
<IconButton
icon={ChevronLeft}
onPress={() => ref.current?.scrollTo({ x: 0, animated: true })}
/>
<IconButton
icon={ChevronRight}
onPress={() => ref.current?.scrollTo({ x: 0, animated: true })}
/>
</View>
</View>
<ScrollView ref={ref} horizontal>
<Fetch query={GenreGrid.query(genre)}>
{(x, i) => (
<ItemGrid
key={x.id ?? i}
isLoading={x.isLoading as any}
href={x.href}
name={x.name}
subtitle={
x.kind !== ItemKind.Collection && !x.isLoading ? getDisplayDate(x) : undefined
}
poster={x.poster}
/>
)}
</Fetch>
</ScrollView>
</View>
);
};
GenreGrid.query = (genre: Genre): QueryIdentifier<Page<LibraryItem>> => ({
parser: Paged(LibraryItemP) as any,
path: ["items"],
params: {
genres: genre,
sortBy: "random",
},
});

View File

@ -18,29 +18,41 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
import { ItemKind, QueryPage } from "@kyoo/models";
import { Genre, ItemKind, QueryPage } from "@kyoo/models";
import { Fetch } from "../fetch";
import { Header } from "./header";
import { DefaultLayout } from "../layout";
import { ScrollView, View } from "react-native";
import { GenreGrid } from "./genre";
export const HomePage: QueryPage = () => {
export const HomePage: QueryPage<{}, Genre> = ({ randomItems }) => {
return (
<Fetch query={Header.query()}>
{(x) => (
<Header
isLoading={x.isLoading as any}
name={x.name}
tagline={"tagline" in x ? x.tagline : null}
overview={x.overview}
thumbnail={x.thumbnail}
link={x.kind === ItemKind.Show ? `/watch/${x.slug}-s1e1` : `/movie/${x.slug}/watch`}
infoLink={x.href}
/>
)}
</Fetch>
<ScrollView>
<Fetch query={Header.query()}>
{(x) => (
<Header
isLoading={x.isLoading as any}
name={x.name}
tagline={"tagline" in x ? x.tagline : null}
overview={x.overview}
thumbnail={x.thumbnail}
link={x.kind === ItemKind.Show ? `/watch/${x.slug}-s1e1` : `/movie/${x.slug}/watch`}
infoLink={x.href}
/>
)}
</Fetch>
{randomItems.map((x) => (
<GenreGrid key={x} genre={x} />
))}
</ScrollView>
);
};
HomePage.randomItems = [...Object.values(Genre)];
HomePage.getLayout = { Layout: DefaultLayout, props: { transparent: true } };
HomePage.getFetchUrls = () => [Header.query()];
HomePage.getFetchUrls = () => [
Header.query(),
...Object.values(Genre).map((x) => GenreGrid.query(x)),
];

View File

@ -4746,6 +4746,13 @@ __metadata:
languageName: node
linkType: hard
"array-shuffle@npm:^3.0.0":
version: 3.0.0
resolution: "array-shuffle@npm:3.0.0"
checksum: 003d813f10d6a57174d134d46d9465e0ca76a802a8c2d21c4b42513af914d4a25b3c51a3004c3d170c3a7b2caab356108c34455c327b21537978a6417cfde1d7
languageName: node
linkType: hard
"array-union@npm:^2.1.0":
version: 2.1.0
resolution: "array-union@npm:2.1.0"
@ -10198,6 +10205,7 @@ __metadata:
"@shopify/flash-list": 1.4.3
"@tanstack/react-query": ^4.32.6
"@types/react": 18.2.0
array-shuffle: ^3.0.0
babel-plugin-transform-inline-environment-variables: ^0.4.4
expo: ^49.0.6
expo-build-properties: ~0.8.3
@ -13864,6 +13872,7 @@ __metadata:
"@types/node": 20.4.8
"@types/react": 18.2.0
"@types/react-dom": 18.2.0
array-shuffle: ^3.0.0
copy-webpack-plugin: ^11.0.0
eslint: ^8.46.0
eslint-config-next: 13.4.13