diff --git a/front/apps/mobile/package.json b/front/apps/mobile/package.json index 5212af2d..4f434d8a 100644 --- a/front/apps/mobile/package.json +++ b/front/apps/mobile/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@kyoo/ui": "workspace:^", + "@shopify/flash-list": "1.3.1", "@tanstack/react-query": "^4.19.1", "babel-plugin-transform-inline-environment-variables": "^0.4.4", "expo": "^47.0.0", diff --git a/front/packages/ui/package.json b/front/packages/ui/package.json index d8a4ea5b..d709c386 100644 --- a/front/packages/ui/package.json +++ b/front/packages/ui/package.json @@ -13,6 +13,7 @@ "typescript": "^4.9.3" }, "peerDependencies": { + "@shopify/flash-list": "^1.4.0", "@tanstack/react-query": "*", "expo-linear-gradient": "*", "i18next": "*", diff --git a/front/packages/ui/src/fetch-infinite.tsx b/front/packages/ui/src/fetch-infinite.tsx new file mode 100644 index 00000000..d09adf4e --- /dev/null +++ b/front/packages/ui/src/fetch-infinite.tsx @@ -0,0 +1,78 @@ +/* + * 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 . + */ + +import { Page, QueryIdentifier, useInfiniteFetch } from "@kyoo/models"; +import { FlashList } from "@shopify/flash-list"; +import { ReactElement } from "react"; +import { ErrorView, WithLoading } from "./fetch"; + +export const InfiniteFetch = ({ + query, + placeholderCount = 15, + children, + size, + numColumns, + ...props +}: { + query: QueryIdentifier; + placeholderCount?: number; + numColumns: number; + children: ( + item: Data extends Page ? WithLoading : WithLoading, + key: string | undefined, + i: number, + ) => ReactElement | null; + size: number; +}): JSX.Element | null => { + if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch."); + + const { items, error, fetchNextPage, hasNextPage, refetch, isRefetching } = + useInfiniteFetch(query); + + if (error) return ; + + return ( + + children({ isLoading: false, ...item } as any, undefined, index) + } + data={ + hasNextPage + ? [ + ...(items || []), + ...[ + ...Array( + items ? numColumns - (items.length % numColumns) + numColumns : placeholderCount, + ), + ].map((_, i) => ({ id: `gen${i}`, isLoading: true } as Data)), + ] + : items + } + keyExtractor={(item: any) => item.id?.toString()} + numColumns={numColumns} + estimatedItemSize={size} + onEndReached={fetchNextPage} + onEndReachedThreshold={0.5} + onRefresh={refetch} + refreshing={isRefetching} + {...props} + /> + ); +}; diff --git a/front/packages/ui/src/fetch-infinite.web.tsx b/front/packages/ui/src/fetch-infinite.web.tsx new file mode 100644 index 00000000..5a0c6999 --- /dev/null +++ b/front/packages/ui/src/fetch-infinite.web.tsx @@ -0,0 +1,69 @@ +/* + * 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 . + */ + +import { Page, QueryIdentifier, useInfiniteFetch } from "@kyoo/models"; +import { ReactElement } from "react"; +import InfiniteScroll from "react-infinite-scroll-component"; +import { useYoshiki } from "yoshiki"; +import { ErrorView, WithLoading } from "./fetch"; + +export const InfiniteFetch = ({ + query, + placeholderCount = 15, + children, + ...props +}: { + query: QueryIdentifier; + placeholderCount?: number; + children: ( + item: Data extends Page ? WithLoading : WithLoading, + key: string | undefined, + i: number, + ) => ReactElement | null; +}): JSX.Element | null => { + if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch."); + + const { items, error, fetchNextPage, hasNextPage } = useInfiniteFetch(query); + const { css } = useYoshiki(); + + if (error) return ; + + return ( + children({ isLoading: true } as any, i.toString(), i))} + {...css( + { + display: "flex", + flexWrap: "wrap", + alignItems: "flex-start", + justifyContent: "center", + }, + props, + )} + > + {items?.map((item, i) => + children({ ...item, isLoading: false } as any, (item as any).id?.toString(), i), + )} + + ); +}; diff --git a/front/packages/ui/src/fetch.tsx b/front/packages/ui/src/fetch.tsx index 925547ec..6ec4a2ca 100644 --- a/front/packages/ui/src/fetch.tsx +++ b/front/packages/ui/src/fetch.tsx @@ -18,7 +18,7 @@ * along with Kyoo. If not, see . */ -import { Page, QueryIdentifier, useFetch, KyooErrors, useInfiniteFetch } from "@kyoo/models"; +import { Page, QueryIdentifier, useFetch, KyooErrors } from "@kyoo/models"; import { P } from "@kyoo/primitives"; import { View } from "react-native"; import { useYoshiki } from "yoshiki/native"; @@ -54,29 +54,6 @@ export const Fetch = ({ return <>{data.items.map((item, i) => children({ ...item, isLoading: false } as any, i))}; }; -export const InfiniteFetch = ({ - query, - placeholderCount = 15, - children, -}: { - query: QueryIdentifier; - placeholderCount?: number; - children: ( - item: Data extends Page ? WithLoading : WithLoading, - i: number, - ) => JSX.Element | null; -}): JSX.Element | null => { - if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch."); - const { items, error } = useInfiniteFetch(query); - - if (error) return ; - if (!items) - return ( - <>{[...Array(placeholderCount)].map((_, i) => children({ isLoading: true } as any, i))} - ); - return <>{items.map((item, i) => children({ ...item, isLoading: false } as any, i))}; -}; - export const ErrorView = ({ error }: { error: KyooErrors }) => { const { css } = useYoshiki(); diff --git a/front/yarn.lock b/front/yarn.lock index 50908e38..b00e603a 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -2326,6 +2326,7 @@ __metadata: react-native-svg: ^13.6.0 typescript: ^4.9.3 peerDependencies: + "@shopify/flash-list": ^1.4.0 "@tanstack/react-query": "*" expo-linear-gradient: "*" i18next: "*" @@ -3141,6 +3142,20 @@ __metadata: languageName: node linkType: hard +"@shopify/flash-list@npm:1.3.1": + version: 1.3.1 + resolution: "@shopify/flash-list@npm:1.3.1" + dependencies: + recyclerlistview: 4.1.2 + tslib: 2.4.0 + peerDependencies: + "@babel/runtime": "*" + react: "*" + react-native: "*" + checksum: 49a82512bf2b3622e1d7f1a86dc2837f7ee8809131fde0ffe0ced41ede001f77bf81741ea5d65b803ad76d669f146f8cd53702daf3387c9f89402b32f9667110 + languageName: node + linkType: hard + "@sideway/address@npm:^4.1.3": version: 4.1.4 resolution: "@sideway/address@npm:4.1.4" @@ -4280,6 +4295,13 @@ __metadata: languageName: node linkType: hard +"babel-plugin-transform-inline-environment-variables@npm:^0.4.4": + version: 0.4.4 + resolution: "babel-plugin-transform-inline-environment-variables@npm:0.4.4" + checksum: fa361287411301237fd8ce332aff4f8e8ccb8db30e87a2ddc7224c8bf7cd792eda47aca24dc2e09e70bce4c027bc8cbe22f4999056be37a25d2472945df21ef5 + languageName: node + linkType: hard + "babel-preset-expo@npm:~9.2.2": version: 9.2.2 resolution: "babel-preset-expo@npm:9.2.2" @@ -8600,7 +8622,7 @@ __metadata: languageName: node linkType: hard -"lodash.debounce@npm:^4.0.8": +"lodash.debounce@npm:4.0.8, lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "lodash.debounce@npm:4.0.8" checksum: a3f527d22c548f43ae31c861ada88b2637eb48ac6aa3eb56e82d44917971b8aa96fbb37aa60efea674dc4ee8c42074f90f7b1f772e9db375435f6c83a19b3bc6 @@ -9636,9 +9658,11 @@ __metadata: dependencies: "@babel/core": ^7.19.3 "@kyoo/ui": "workspace:^" + "@shopify/flash-list": 1.3.1 "@tanstack/react-query": ^4.19.1 "@types/react": ~18.0.24 "@types/react-native": ~0.70.6 + babel-plugin-transform-inline-environment-variables: ^0.4.4 expo: ^47.0.0 expo-constants: ~14.0.2 expo-linear-gradient: ~12.0.1 @@ -10708,7 +10732,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.6.2, prop-types@npm:^15.8.1": +"prop-types@npm:15.8.1, prop-types@npm:^15.6.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -11196,6 +11220,20 @@ __metadata: languageName: node linkType: hard +"recyclerlistview@npm:4.1.2": + version: 4.1.2 + resolution: "recyclerlistview@npm:4.1.2" + dependencies: + lodash.debounce: 4.0.8 + prop-types: 15.8.1 + ts-object-utils: 0.0.5 + peerDependencies: + react: ">= 15.2.1" + react-native: ">= 0.30.0" + checksum: e18e36c0783b528b5244ed0b7eabf231c49ec7b0a97c947a6f596900165a4d7bbc3216b23084a8a2cb8d33b98fb38fc4a64254cf0b6431a0d0fa3ca2e7129fbc + languageName: node + linkType: hard + "regenerate-unicode-properties@npm:^10.1.0": version: 10.1.0 resolution: "regenerate-unicode-properties@npm:10.1.0" @@ -12718,6 +12756,13 @@ __metadata: languageName: node linkType: hard +"ts-object-utils@npm:0.0.5": + version: 0.0.5 + resolution: "ts-object-utils@npm:0.0.5" + checksum: 83c48fbdaba392fb2c01cea53b267ed5538d2bb44fc6c3eecc10bcfabc1780bfa6ec8569b52bbf0140d9b521d9049d5f15884e12286918244d463d854dbc73cb + languageName: node + linkType: hard + "tsconfig-paths@npm:^3.14.1": version: 3.14.1 resolution: "tsconfig-paths@npm:3.14.1" @@ -12730,6 +12775,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:2.4.0": + version: 2.4.0 + resolution: "tslib@npm:2.4.0" + checksum: 8c4aa6a3c5a754bf76aefc38026134180c053b7bd2f81338cb5e5ebf96fefa0f417bff221592bf801077f5bf990562f6264fecbc42cd3309b33872cb6fc3b113 + languageName: node + linkType: hard + "tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1"