mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 04:04:21 -04:00
Add an infinite scroll on web and native
This commit is contained in:
parent
1cd418991c
commit
47ca25fe1c
@ -10,6 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kyoo/ui": "workspace:^",
|
"@kyoo/ui": "workspace:^",
|
||||||
|
"@shopify/flash-list": "1.3.1",
|
||||||
"@tanstack/react-query": "^4.19.1",
|
"@tanstack/react-query": "^4.19.1",
|
||||||
"babel-plugin-transform-inline-environment-variables": "^0.4.4",
|
"babel-plugin-transform-inline-environment-variables": "^0.4.4",
|
||||||
"expo": "^47.0.0",
|
"expo": "^47.0.0",
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
"typescript": "^4.9.3"
|
"typescript": "^4.9.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
"@shopify/flash-list": "^1.4.0",
|
||||||
"@tanstack/react-query": "*",
|
"@tanstack/react-query": "*",
|
||||||
"expo-linear-gradient": "*",
|
"expo-linear-gradient": "*",
|
||||||
"i18next": "*",
|
"i18next": "*",
|
||||||
|
78
front/packages/ui/src/fetch-infinite.tsx
Normal file
78
front/packages/ui/src/fetch-infinite.tsx
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 = <Data,>({
|
||||||
|
query,
|
||||||
|
placeholderCount = 15,
|
||||||
|
children,
|
||||||
|
size,
|
||||||
|
numColumns,
|
||||||
|
...props
|
||||||
|
}: {
|
||||||
|
query: QueryIdentifier<Data>;
|
||||||
|
placeholderCount?: number;
|
||||||
|
numColumns: number;
|
||||||
|
children: (
|
||||||
|
item: Data extends Page<infer Item> ? WithLoading<Item> : WithLoading<Data>,
|
||||||
|
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 <ErrorView error={error} />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FlashList
|
||||||
|
renderItem={({ item, index }) =>
|
||||||
|
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}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
69
front/packages/ui/src/fetch-infinite.web.tsx
Normal file
69
front/packages/ui/src/fetch-infinite.web.tsx
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 = <Data,>({
|
||||||
|
query,
|
||||||
|
placeholderCount = 15,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: {
|
||||||
|
query: QueryIdentifier<Data>;
|
||||||
|
placeholderCount?: number;
|
||||||
|
children: (
|
||||||
|
item: Data extends Page<infer Item> ? WithLoading<Item> : WithLoading<Data>,
|
||||||
|
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 <ErrorView error={error} />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InfiniteScroll
|
||||||
|
dataLength={items?.length ?? 0}
|
||||||
|
next={fetchNextPage}
|
||||||
|
hasMore={hasNextPage!}
|
||||||
|
loader={[...Array(12)].map((_, i) => 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),
|
||||||
|
)}
|
||||||
|
</InfiniteScroll>
|
||||||
|
);
|
||||||
|
};
|
@ -18,7 +18,7 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Page, QueryIdentifier, useFetch, KyooErrors, useInfiniteFetch } from "@kyoo/models";
|
import { Page, QueryIdentifier, useFetch, KyooErrors } from "@kyoo/models";
|
||||||
import { P } from "@kyoo/primitives";
|
import { P } from "@kyoo/primitives";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import { useYoshiki } from "yoshiki/native";
|
import { useYoshiki } from "yoshiki/native";
|
||||||
@ -54,29 +54,6 @@ export const Fetch = <Data,>({
|
|||||||
return <>{data.items.map((item, i) => children({ ...item, isLoading: false } as any, i))}</>;
|
return <>{data.items.map((item, i) => children({ ...item, isLoading: false } as any, i))}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InfiniteFetch = <Data,>({
|
|
||||||
query,
|
|
||||||
placeholderCount = 15,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
query: QueryIdentifier<Data>;
|
|
||||||
placeholderCount?: number;
|
|
||||||
children: (
|
|
||||||
item: Data extends Page<infer Item> ? WithLoading<Item> : WithLoading<Data>,
|
|
||||||
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 <ErrorView error={error} />;
|
|
||||||
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 }) => {
|
export const ErrorView = ({ error }: { error: KyooErrors }) => {
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
|
|
||||||
|
@ -2326,6 +2326,7 @@ __metadata:
|
|||||||
react-native-svg: ^13.6.0
|
react-native-svg: ^13.6.0
|
||||||
typescript: ^4.9.3
|
typescript: ^4.9.3
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
"@shopify/flash-list": ^1.4.0
|
||||||
"@tanstack/react-query": "*"
|
"@tanstack/react-query": "*"
|
||||||
expo-linear-gradient: "*"
|
expo-linear-gradient: "*"
|
||||||
i18next: "*"
|
i18next: "*"
|
||||||
@ -3141,6 +3142,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"@sideway/address@npm:^4.1.3":
|
||||||
version: 4.1.4
|
version: 4.1.4
|
||||||
resolution: "@sideway/address@npm:4.1.4"
|
resolution: "@sideway/address@npm:4.1.4"
|
||||||
@ -4280,6 +4295,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"babel-preset-expo@npm:~9.2.2":
|
||||||
version: 9.2.2
|
version: 9.2.2
|
||||||
resolution: "babel-preset-expo@npm:9.2.2"
|
resolution: "babel-preset-expo@npm:9.2.2"
|
||||||
@ -8600,7 +8622,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"lodash.debounce@npm:^4.0.8":
|
"lodash.debounce@npm:4.0.8, lodash.debounce@npm:^4.0.8":
|
||||||
version: 4.0.8
|
version: 4.0.8
|
||||||
resolution: "lodash.debounce@npm:4.0.8"
|
resolution: "lodash.debounce@npm:4.0.8"
|
||||||
checksum: a3f527d22c548f43ae31c861ada88b2637eb48ac6aa3eb56e82d44917971b8aa96fbb37aa60efea674dc4ee8c42074f90f7b1f772e9db375435f6c83a19b3bc6
|
checksum: a3f527d22c548f43ae31c861ada88b2637eb48ac6aa3eb56e82d44917971b8aa96fbb37aa60efea674dc4ee8c42074f90f7b1f772e9db375435f6c83a19b3bc6
|
||||||
@ -9636,9 +9658,11 @@ __metadata:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/core": ^7.19.3
|
"@babel/core": ^7.19.3
|
||||||
"@kyoo/ui": "workspace:^"
|
"@kyoo/ui": "workspace:^"
|
||||||
|
"@shopify/flash-list": 1.3.1
|
||||||
"@tanstack/react-query": ^4.19.1
|
"@tanstack/react-query": ^4.19.1
|
||||||
"@types/react": ~18.0.24
|
"@types/react": ~18.0.24
|
||||||
"@types/react-native": ~0.70.6
|
"@types/react-native": ~0.70.6
|
||||||
|
babel-plugin-transform-inline-environment-variables: ^0.4.4
|
||||||
expo: ^47.0.0
|
expo: ^47.0.0
|
||||||
expo-constants: ~14.0.2
|
expo-constants: ~14.0.2
|
||||||
expo-linear-gradient: ~12.0.1
|
expo-linear-gradient: ~12.0.1
|
||||||
@ -10708,7 +10732,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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
|
version: 15.8.1
|
||||||
resolution: "prop-types@npm:15.8.1"
|
resolution: "prop-types@npm:15.8.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -11196,6 +11220,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"regenerate-unicode-properties@npm:^10.1.0":
|
||||||
version: 10.1.0
|
version: 10.1.0
|
||||||
resolution: "regenerate-unicode-properties@npm:10.1.0"
|
resolution: "regenerate-unicode-properties@npm:10.1.0"
|
||||||
@ -12718,6 +12756,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"tsconfig-paths@npm:^3.14.1":
|
||||||
version: 3.14.1
|
version: 3.14.1
|
||||||
resolution: "tsconfig-paths@npm:3.14.1"
|
resolution: "tsconfig-paths@npm:3.14.1"
|
||||||
@ -12730,6 +12775,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"tslib@npm:^1.8.1":
|
||||||
version: 1.14.1
|
version: 1.14.1
|
||||||
resolution: "tslib@npm:1.14.1"
|
resolution: "tslib@npm:1.14.1"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user