mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-04 03:27:14 -05:00 
			
		
		
		
	Add random genres list to the home page
This commit is contained in:
		
							parent
							
								
									d21e4ffba2
								
							
						
					
					
						commit
						5a618a8db2
					
				@ -20,6 +20,7 @@
 | 
				
			|||||||
		"@material-symbols/svg-400": "^0.10.3",
 | 
							"@material-symbols/svg-400": "^0.10.3",
 | 
				
			||||||
		"@shopify/flash-list": "1.4.3",
 | 
							"@shopify/flash-list": "1.4.3",
 | 
				
			||||||
		"@tanstack/react-query": "^4.32.6",
 | 
							"@tanstack/react-query": "^4.32.6",
 | 
				
			||||||
 | 
							"array-shuffle": "^3.0.0",
 | 
				
			||||||
		"babel-plugin-transform-inline-environment-variables": "^0.4.4",
 | 
							"babel-plugin-transform-inline-environment-variables": "^0.4.4",
 | 
				
			||||||
		"expo": "^49.0.6",
 | 
							"expo": "^49.0.6",
 | 
				
			||||||
		"expo-build-properties": "~0.8.3",
 | 
							"expo-build-properties": "~0.8.3",
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,7 @@
 | 
				
			|||||||
		"@material-symbols/svg-400": "^0.10.3",
 | 
							"@material-symbols/svg-400": "^0.10.3",
 | 
				
			||||||
		"@radix-ui/react-dropdown-menu": "^2.0.5",
 | 
							"@radix-ui/react-dropdown-menu": "^2.0.5",
 | 
				
			||||||
		"@tanstack/react-query": "^4.32.6",
 | 
							"@tanstack/react-query": "^4.32.6",
 | 
				
			||||||
 | 
							"array-shuffle": "^3.0.0",
 | 
				
			||||||
		"expo-linear-gradient": "^12.4.0",
 | 
							"expo-linear-gradient": "^12.4.0",
 | 
				
			||||||
		"expo-modules-core": "^1.5.9",
 | 
							"expo-modules-core": "^1.5.9",
 | 
				
			||||||
		"hls.js": "^1.4.10",
 | 
							"hls.js": "^1.4.10",
 | 
				
			||||||
 | 
				
			|||||||
@ -38,6 +38,7 @@ import { useTheme, useMobileHover, useStyleRegistry, StyleRegistryProvider } fro
 | 
				
			|||||||
import superjson from "superjson";
 | 
					import superjson from "superjson";
 | 
				
			||||||
import Head from "next/head";
 | 
					import Head from "next/head";
 | 
				
			||||||
import { withTranslations } from "../i18n";
 | 
					import { withTranslations } from "../i18n";
 | 
				
			||||||
 | 
					import arrayShuffle from "array-shuffle";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const font = Poppins({ weight: ["300", "400", "900"], subsets: ["latin"], display: "swap" });
 | 
					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 App = ({ Component, pageProps }: AppProps) => {
 | 
				
			||||||
	const [queryClient] = useState(() => createQueryClient());
 | 
						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 layoutInfo = (Component as QueryPage).getLayout ?? (({ page }) => page);
 | 
				
			||||||
	const { Layout, props: layoutProps } =
 | 
						const { Layout, props: layoutProps } =
 | 
				
			||||||
		typeof layoutInfo === "function" ? { Layout: layoutInfo, props: {} } : layoutInfo;
 | 
							typeof layoutInfo === "function" ? { Layout: layoutInfo, props: {} } : layoutInfo;
 | 
				
			||||||
@ -122,7 +125,19 @@ const App = ({ Component, pageProps }: AppProps) => {
 | 
				
			|||||||
					<Hydrate state={queryState}>
 | 
										<Hydrate state={queryState}>
 | 
				
			||||||
						<ThemeSelector theme="auto" font={{ normal: "inherit" }}>
 | 
											<ThemeSelector theme="auto" font={{ normal: "inherit" }}>
 | 
				
			||||||
							<GlobalCssTheme />
 | 
												<GlobalCssTheme />
 | 
				
			||||||
							<Layout page={<Component {...props} />} {...layoutProps} />
 | 
												<Layout
 | 
				
			||||||
 | 
													page={
 | 
				
			||||||
 | 
														<Component
 | 
				
			||||||
 | 
															randomItems={
 | 
				
			||||||
 | 
																randomItems[Component.displayName!] ??
 | 
				
			||||||
 | 
																arrayShuffle((Component as QueryPage).randomItems ?? [])
 | 
				
			||||||
 | 
															}
 | 
				
			||||||
 | 
															{...props}
 | 
				
			||||||
 | 
														/>
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
													randomItems={[]}
 | 
				
			||||||
 | 
													{...layoutProps}
 | 
				
			||||||
 | 
												/>
 | 
				
			||||||
						</ThemeSelector>
 | 
											</ThemeSelector>
 | 
				
			||||||
					</Hydrate>
 | 
										</Hydrate>
 | 
				
			||||||
				</QueryClientProvider>
 | 
									</QueryClientProvider>
 | 
				
			||||||
@ -148,6 +163,9 @@ App.getInitialProps = async (ctx: AppContext) => {
 | 
				
			|||||||
	const [authToken, token] = await getTokenWJ(ctx.ctx.req?.headers.cookie);
 | 
						const [authToken, token] = await getTokenWJ(ctx.ctx.req?.headers.cookie);
 | 
				
			||||||
	appProps.pageProps.queryState = await fetchQuery(urls, authToken);
 | 
						appProps.pageProps.queryState = await fetchQuery(urls, authToken);
 | 
				
			||||||
	appProps.pageProps.token = token;
 | 
						appProps.pageProps.token = token;
 | 
				
			||||||
 | 
						appProps.pageProps.randomItems = {
 | 
				
			||||||
 | 
							[Component.displayName!]: arrayShuffle(Component.randomItems ?? []),
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return { pageProps: superjson.serialize(appProps.pageProps) };
 | 
						return { pageProps: superjson.serialize(appProps.pageProps) };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -162,11 +162,12 @@ export type QueryIdentifier<T = unknown, Ret = T> = {
 | 
				
			|||||||
	getNext?: (item: unknown) => string | undefined;
 | 
						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>[];
 | 
						getFetchUrls?: (route: { [key: string]: string }) => QueryIdentifier<any>[];
 | 
				
			||||||
	getLayout?:
 | 
						getLayout?:
 | 
				
			||||||
		| QueryPage<{ page: ReactElement }>
 | 
							| QueryPage<{ page: ReactElement }>
 | 
				
			||||||
		| { Layout: QueryPage<{ page: ReactElement }>; props: object };
 | 
							| { Layout: QueryPage<{ page: ReactElement }>; props: object };
 | 
				
			||||||
 | 
						randomItems?: Items[]
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const toQueryKey = <Data, Ret>(query: QueryIdentifier<Data, Ret>) => {
 | 
					const toQueryKey = <Data, Ret>(query: QueryIdentifier<Data, Ret>) => {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										86
									
								
								front/packages/ui/src/home/genre.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								front/packages/ui/src/home/genre.tsx
									
									
									
									
									
										Normal 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",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -18,29 +18,41 @@
 | 
				
			|||||||
 * along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
					 * 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 { Fetch } from "../fetch";
 | 
				
			||||||
import { Header } from "./header";
 | 
					import { Header } from "./header";
 | 
				
			||||||
import { DefaultLayout } from "../layout";
 | 
					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 (
 | 
						return (
 | 
				
			||||||
		<Fetch query={Header.query()}>
 | 
							<ScrollView>
 | 
				
			||||||
			{(x) => (
 | 
								<Fetch query={Header.query()}>
 | 
				
			||||||
				<Header
 | 
									{(x) => (
 | 
				
			||||||
					isLoading={x.isLoading as any}
 | 
										<Header
 | 
				
			||||||
					name={x.name}
 | 
											isLoading={x.isLoading as any}
 | 
				
			||||||
					tagline={"tagline" in x ? x.tagline : null}
 | 
											name={x.name}
 | 
				
			||||||
					overview={x.overview}
 | 
											tagline={"tagline" in x ? x.tagline : null}
 | 
				
			||||||
					thumbnail={x.thumbnail}
 | 
											overview={x.overview}
 | 
				
			||||||
					link={x.kind === ItemKind.Show ? `/watch/${x.slug}-s1e1` : `/movie/${x.slug}/watch`}
 | 
											thumbnail={x.thumbnail}
 | 
				
			||||||
					infoLink={x.href}
 | 
											link={x.kind === ItemKind.Show ? `/watch/${x.slug}-s1e1` : `/movie/${x.slug}/watch`}
 | 
				
			||||||
				/>
 | 
											infoLink={x.href}
 | 
				
			||||||
			)}
 | 
										/>
 | 
				
			||||||
		</Fetch>
 | 
									)}
 | 
				
			||||||
 | 
								</Fetch>
 | 
				
			||||||
 | 
								{randomItems.map((x) => (
 | 
				
			||||||
 | 
									<GenreGrid key={x} genre={x} />
 | 
				
			||||||
 | 
								))}
 | 
				
			||||||
 | 
							</ScrollView>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HomePage.randomItems = [...Object.values(Genre)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
HomePage.getLayout = { Layout: DefaultLayout, props: { transparent: true } };
 | 
					HomePage.getLayout = { Layout: DefaultLayout, props: { transparent: true } };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
HomePage.getFetchUrls = () => [Header.query()];
 | 
					HomePage.getFetchUrls = () => [
 | 
				
			||||||
 | 
						Header.query(),
 | 
				
			||||||
 | 
						...Object.values(Genre).map((x) => GenreGrid.query(x)),
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
				
			|||||||
@ -4746,6 +4746,13 @@ __metadata:
 | 
				
			|||||||
  languageName: node
 | 
					  languageName: node
 | 
				
			||||||
  linkType: hard
 | 
					  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":
 | 
					"array-union@npm:^2.1.0":
 | 
				
			||||||
  version: 2.1.0
 | 
					  version: 2.1.0
 | 
				
			||||||
  resolution: "array-union@npm:2.1.0"
 | 
					  resolution: "array-union@npm:2.1.0"
 | 
				
			||||||
@ -10198,6 +10205,7 @@ __metadata:
 | 
				
			|||||||
    "@shopify/flash-list": 1.4.3
 | 
					    "@shopify/flash-list": 1.4.3
 | 
				
			||||||
    "@tanstack/react-query": ^4.32.6
 | 
					    "@tanstack/react-query": ^4.32.6
 | 
				
			||||||
    "@types/react": 18.2.0
 | 
					    "@types/react": 18.2.0
 | 
				
			||||||
 | 
					    array-shuffle: ^3.0.0
 | 
				
			||||||
    babel-plugin-transform-inline-environment-variables: ^0.4.4
 | 
					    babel-plugin-transform-inline-environment-variables: ^0.4.4
 | 
				
			||||||
    expo: ^49.0.6
 | 
					    expo: ^49.0.6
 | 
				
			||||||
    expo-build-properties: ~0.8.3
 | 
					    expo-build-properties: ~0.8.3
 | 
				
			||||||
@ -13864,6 +13872,7 @@ __metadata:
 | 
				
			|||||||
    "@types/node": 20.4.8
 | 
					    "@types/node": 20.4.8
 | 
				
			||||||
    "@types/react": 18.2.0
 | 
					    "@types/react": 18.2.0
 | 
				
			||||||
    "@types/react-dom": 18.2.0
 | 
					    "@types/react-dom": 18.2.0
 | 
				
			||||||
 | 
					    array-shuffle: ^3.0.0
 | 
				
			||||||
    copy-webpack-plugin: ^11.0.0
 | 
					    copy-webpack-plugin: ^11.0.0
 | 
				
			||||||
    eslint: ^8.46.0
 | 
					    eslint: ^8.46.0
 | 
				
			||||||
    eslint-config-next: 13.4.13
 | 
					    eslint-config-next: 13.4.13
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user