Properly implement refresh on the home page

This commit is contained in:
Zoe Roux 2025-12-20 22:34:15 +01:00
parent f8d935becd
commit 5e83752ec4
No known key found for this signature in database
3 changed files with 62 additions and 60 deletions

View File

@ -6,7 +6,7 @@ import {
useQueryClient,
useMutation as useRQMutation,
} from "@tanstack/react-query";
import { useContext } from "react";
import { useCallback, useContext, useState } from "react";
import { Platform } from "react-native";
import type { z } from "zod/v4";
import { type KyooError, type Page, Paged } from "~/models";
@ -189,6 +189,32 @@ export const useFetch = <Data,>(query: QueryIdentifier<Data>) => {
});
};
export const useRefresh = (queries: QueryIdentifier<unknown>[]) => {
const [refreshing, setRefreshing] = useState(false);
const queryClient = useQueryClient();
const { apiUrl } = useContext(AccountContext);
const refresh = useCallback(async () => {
setRefreshing(true);
await Promise.all(
queries.map((query) =>
queryClient.refetchQueries({
queryKey: toQueryKey({
apiUrl: query.options?.apiUrl ?? apiUrl,
path: query.path,
params: query.params,
}),
type: "active",
exact: true,
}),
),
);
setRefreshing(false);
}, [queries, apiUrl, queryClient]);
return [refreshing, refresh] as const;
};
export const useInfiniteFetch = <Data,>(query: QueryIdentifier<Data>) => {
let { apiUrl, authToken } = useContext(AccountContext);
if (query.options?.apiUrl) apiUrl = query.options.apiUrl;

View File

@ -1,27 +1,7 @@
/*
* 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 { useState } from "react";
import { RefreshControl, ScrollView } from "react-native";
import { Genre } from "~/models";
import { Fetch, prefetch } from "~/query";
import { Fetch, useRefresh } from "~/query";
import { shuffle } from "~/utils";
import { GenreGrid } from "./genre";
import { Header } from "./header";
import { NewsList } from "./news";
@ -29,69 +9,39 @@ import { Recommended } from "./recommended";
import { VerticalRecommended } from "./vertical";
import { WatchlistList } from "./watchlist";
export async function loader() {
const randomItems = [...Object.values(Genre)];
await Promise.all([
prefetch(Header.query()),
prefetch(WatchlistList.query()),
prefetch(NewsList.query()),
...randomItems.filter((_, i) => i < 6).map((x) => prefetch(GenreGrid.query(x))),
prefetch(Recommended.query()),
prefetch(VerticalRecommended.query()),
]);
}
export const HomePage = () => {
const [refreshing, setRefreshing] = useState(false);
const randomItems = [...Object.values(Genre)];
const genres = shuffle(Object.values(Genre.enum));
const [isRefreshing, refresh] = useRefresh(HomePage.queries(genres));
return (
<ScrollView
refreshControl={
<RefreshControl
onRefresh={async () => {
setRefreshing(true);
await loader();
setRefreshing(false);
}}
refreshing={refreshing}
/>
<RefreshControl onRefresh={refresh} refreshing={isRefreshing} />
}
>
<Fetch
query={Header.query()}
Render={(x) => (
<Header
isLoading={false}
name={x.name}
tagline={x.kind !== "collection" && "tagline" in x ? x.tagline : null}
tagline={x.kind !== "collection" ? x.tagline : null}
description={x.description}
thumbnail={x.thumbnail}
link={x.kind !== "collection" ? x.playHref : null}
infoLink={x.href}
/>
)}
Loader={() => (
<Header
isLoading={true}
name=""
tagline={null}
description={null}
thumbnail={null}
link={null}
infoLink="#"
/>
)}
Loader={Header.Loader}
/>
<WatchlistList />
<NewsList />
{randomItems
{genres
.filter((_, i) => i < 2)
.map((x) => (
<GenreGrid key={x} genre={x} />
))}
<Recommended />
{randomItems
{genres
.filter((_, i) => i >= 2 && i < 6)
.map((x) => (
<GenreGrid key={x} genre={x} />
@ -104,3 +54,12 @@ export const HomePage = () => {
</ScrollView>
);
};
HomePage.queries = (randomItems: Genre[]) => [
Header.query(),
WatchlistList.query(),
NewsList.query(),
...randomItems.filter((_, i) => i < 6).map((x) => GenreGrid.query(x)),
Recommended.query(),
VerticalRecommended.query(),
];

View File

@ -48,3 +48,20 @@ export const displayRuntime = (runtime: number | null) => {
if (runtime < 60) return `${runtime}min`;
return `${Math.floor(runtime / 60)}h${runtime % 60}`;
};
// shuffle an array in place, stolen from https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
export function shuffle<T>(array: T[]): T[] {
let currentIndex = array.length;
while (currentIndex !== 0) {
const randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
[array[currentIndex], array[randomIndex]] = [
array[randomIndex],
array[currentIndex],
];
}
return array;
}