mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 04:04:21 -04: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