From 5e83752ec4706684dab861ccd73db023a7e98d1f Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Sat, 20 Dec 2025 22:34:15 +0100 Subject: [PATCH] Properly implement refresh on the home page --- front/src/query/query.tsx | 28 +++++++++++++- front/src/ui/home/index.tsx | 77 +++++++++---------------------------- front/src/utils.ts | 17 ++++++++ 3 files changed, 62 insertions(+), 60 deletions(-) diff --git a/front/src/query/query.tsx b/front/src/query/query.tsx index ec5ae269..23ac45d6 100644 --- a/front/src/query/query.tsx +++ b/front/src/query/query.tsx @@ -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 = (query: QueryIdentifier) => { }); }; +export const useRefresh = (queries: QueryIdentifier[]) => { + 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 = (query: QueryIdentifier) => { let { apiUrl, authToken } = useContext(AccountContext); if (query.options?.apiUrl) apiUrl = query.options.apiUrl; diff --git a/front/src/ui/home/index.tsx b/front/src/ui/home/index.tsx index ad18b675..86e89eff 100644 --- a/front/src/ui/home/index.tsx +++ b/front/src/ui/home/index.tsx @@ -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 . - */ - -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 ( { - setRefreshing(true); - await loader(); - setRefreshing(false); - }} - refreshing={refreshing} - /> + } > (
)} - Loader={() => ( -
- )} + Loader={Header.Loader} /> - {randomItems + {genres .filter((_, i) => i < 2) .map((x) => ( ))} - {randomItems + {genres .filter((_, i) => i >= 2 && i < 6) .map((x) => ( @@ -104,3 +54,12 @@ export const HomePage = () => { ); }; + +HomePage.queries = (randomItems: Genre[]) => [ + Header.query(), + WatchlistList.query(), + NewsList.query(), + ...randomItems.filter((_, i) => i < 6).map((x) => GenreGrid.query(x)), + Recommended.query(), + VerticalRecommended.query(), +]; diff --git a/front/src/utils.ts b/front/src/utils.ts index fc93627d..98839f29 100644 --- a/front/src/utils.ts +++ b/front/src/utils.ts @@ -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(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; +}