fix: refresh data on open page in background replace cached data

This commit is contained in:
Fredrik Burmester
2026-01-05 21:28:00 +01:00
parent 24d04c1003
commit 090ed98233
15 changed files with 136 additions and 32 deletions

View File

@@ -1,12 +1,13 @@
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useMutation } from "@tanstack/react-query";
import { useAtom } from "jotai";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useNetworkAwareQueryClient } from "@/hooks/useNetworkAwareQueryClient";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
export const useFavorite = (item: BaseItemDto) => {
const queryClient = useQueryClient();
const queryClient = useNetworkAwareQueryClient();
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
const [isFavorite, setIsFavorite] = useState<boolean | undefined>(

View File

@@ -10,10 +10,10 @@ import type {
} from "@/utils/jellyseerr/server/models/Search";
import { storage } from "@/utils/mmkv";
import "@/augmentations";
import { useQueryClient } from "@tanstack/react-query";
import { t } from "i18next";
import { useCallback, useMemo } from "react";
import { toast } from "sonner-native";
import { useNetworkAwareQueryClient } from "@/hooks/useNetworkAwareQueryClient";
import { useSettings } from "@/utils/atoms/settings";
import type { RTRating } from "@/utils/jellyseerr/server/api/rating/rottentomatoes";
import {
@@ -436,7 +436,7 @@ const jellyseerrUserAtom = atom(storage.get<JellyseerrUser>(JELLYSEERR_USER));
export const useJellyseerr = () => {
const { settings, updateSettings } = useSettings();
const [jellyseerrUser, setJellyseerrUser] = useAtom(jellyseerrUserAtom);
const queryClient = useQueryClient();
const queryClient = useNetworkAwareQueryClient();
const jellyseerrApi = useMemo(() => {
const cookies = storage.get<string[]>(JELLYSEERR_COOKIES);

View File

@@ -1,12 +1,12 @@
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { useQueryClient } from "@tanstack/react-query";
import { useCallback } from "react";
import { useNetworkAwareQueryClient } from "@/hooks/useNetworkAwareQueryClient";
import { useHaptic } from "./useHaptic";
import { usePlaybackManager } from "./usePlaybackManager";
import { useInvalidatePlaybackProgressCache } from "./useRevalidatePlaybackProgressCache";
export const useMarkAsPlayed = (items: BaseItemDto[]) => {
const queryClient = useQueryClient();
const queryClient = useNetworkAwareQueryClient();
const lightHapticFeedback = useHaptic("light");
const { markItemPlayed, markItemUnplayed } = usePlaybackManager();
const invalidatePlaybackProgressCache = useInvalidatePlaybackProgressCache();

View File

@@ -0,0 +1,47 @@
import type {
InvalidateOptions,
InvalidateQueryFilters,
QueryClient,
QueryKey,
} from "@tanstack/react-query";
import { useQueryClient } from "@tanstack/react-query";
import { useCallback, useMemo } from "react";
import { invalidateQueriesWhenOnline } from "@/utils/query/networkAwareInvalidate";
type NetworkAwareQueryClient = QueryClient & {
forceInvalidateQueries: QueryClient["invalidateQueries"];
};
/**
* Returns a queryClient wrapper with network-aware invalidation.
* Use this instead of useQueryClient when you need to invalidate queries.
*
* - invalidateQueries: Only invalidates when online (preserves offline cache)
* - forceInvalidateQueries: Always invalidates (use sparingly)
*/
export function useNetworkAwareQueryClient(): NetworkAwareQueryClient {
const queryClient = useQueryClient();
const networkAwareInvalidate = useCallback(
<TTaggedQueryKey extends QueryKey = QueryKey>(
filters?: InvalidateQueryFilters<TTaggedQueryKey>,
options?: InvalidateOptions,
): Promise<void> => {
if (!filters) {
return Promise.resolve();
}
return invalidateQueriesWhenOnline(queryClient, filters, options);
},
[queryClient],
);
return useMemo(() => {
// Create a proxy-like object that inherits from queryClient
// but overrides invalidateQueries
const wrapped = Object.create(queryClient) as NetworkAwareQueryClient;
wrapped.invalidateQueries = networkAwareInvalidate;
wrapped.forceInvalidateQueries =
queryClient.invalidateQueries.bind(queryClient);
return wrapped;
}, [queryClient, networkAwareInvalidate]);
}

View File

@@ -1,8 +1,9 @@
import { getLibraryApi, getPlaylistsApi } from "@jellyfin/sdk/lib/utils/api";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useMutation } from "@tanstack/react-query";
import { useAtomValue } from "jotai";
import { useTranslation } from "react-i18next";
import { toast } from "sonner-native";
import { useNetworkAwareQueryClient } from "@/hooks/useNetworkAwareQueryClient";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
/**
@@ -11,7 +12,7 @@ import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
export const useCreatePlaylist = () => {
const api = useAtomValue(apiAtom);
const user = useAtomValue(userAtom);
const queryClient = useQueryClient();
const queryClient = useNetworkAwareQueryClient();
const { t } = useTranslation();
const mutation = useMutation({
@@ -58,7 +59,7 @@ export const useCreatePlaylist = () => {
export const useAddToPlaylist = () => {
const api = useAtomValue(apiAtom);
const user = useAtomValue(userAtom);
const queryClient = useQueryClient();
const queryClient = useNetworkAwareQueryClient();
const { t } = useTranslation();
const mutation = useMutation({
@@ -108,7 +109,7 @@ export const useAddToPlaylist = () => {
*/
export const useRemoveFromPlaylist = () => {
const api = useAtomValue(apiAtom);
const queryClient = useQueryClient();
const queryClient = useNetworkAwareQueryClient();
const { t } = useTranslation();
const mutation = useMutation({
@@ -160,7 +161,7 @@ export const useRemoveFromPlaylist = () => {
*/
export const useDeletePlaylist = () => {
const api = useAtomValue(apiAtom);
const queryClient = useQueryClient();
const queryClient = useNetworkAwareQueryClient();
const { t } = useTranslation();
const mutation = useMutation({

View File

@@ -1,4 +1,4 @@
import { useQueryClient } from "@tanstack/react-query";
import { useNetworkAwareQueryClient } from "@/hooks/useNetworkAwareQueryClient";
import { useDownload } from "@/providers/DownloadProvider";
import { useTwoWaySync } from "./useTwoWaySync";
@@ -6,7 +6,7 @@ import { useTwoWaySync } from "./useTwoWaySync";
* useRevalidatePlaybackProgressCache invalidates queries related to playback progress.
*/
export function useInvalidatePlaybackProgressCache() {
const queryClient = useQueryClient();
const queryClient = useNetworkAwareQueryClient();
const { getDownloadedItems } = useDownload();
const { syncPlaybackState } = useTwoWaySync();

View File

@@ -1,7 +1,8 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useMutation } from "@tanstack/react-query";
import { useAtomValue } from "jotai";
import { useCallback } from "react";
import { toast } from "sonner-native";
import { useNetworkAwareQueryClient } from "@/hooks/useNetworkAwareQueryClient";
import { apiAtom } from "@/providers/JellyfinProvider";
import { useSettings } from "@/utils/atoms/settings";
import { createStreamystatsApi } from "@/utils/streamystats/api";
@@ -17,7 +18,7 @@ import type {
export const useCreateWatchlist = () => {
const api = useAtomValue(apiAtom);
const { settings } = useSettings();
const queryClient = useQueryClient();
const queryClient = useNetworkAwareQueryClient();
const mutation = useMutation({
mutationFn: async (
@@ -58,7 +59,7 @@ export const useCreateWatchlist = () => {
export const useUpdateWatchlist = () => {
const api = useAtomValue(apiAtom);
const { settings } = useSettings();
const queryClient = useQueryClient();
const queryClient = useNetworkAwareQueryClient();
const mutation = useMutation({
mutationFn: async ({
@@ -106,7 +107,7 @@ export const useUpdateWatchlist = () => {
export const useDeleteWatchlist = () => {
const api = useAtomValue(apiAtom);
const { settings } = useSettings();
const queryClient = useQueryClient();
const queryClient = useNetworkAwareQueryClient();
const mutation = useMutation({
mutationFn: async (watchlistId: number): Promise<void> => {
@@ -147,7 +148,7 @@ export const useDeleteWatchlist = () => {
export const useAddToWatchlist = () => {
const api = useAtomValue(apiAtom);
const { settings } = useSettings();
const queryClient = useQueryClient();
const queryClient = useNetworkAwareQueryClient();
const mutation = useMutation({
mutationFn: async ({
@@ -205,7 +206,7 @@ export const useAddToWatchlist = () => {
export const useRemoveFromWatchlist = () => {
const api = useAtomValue(apiAtom);
const { settings } = useSettings();
const queryClient = useQueryClient();
const queryClient = useNetworkAwareQueryClient();
const mutation = useMutation({
mutationFn: async ({