Files
manage/frontend/src/hooks/useMedia.ts
T
Developer ac703eecd2 Pause TanStack interval refetches when the tab is hidden (D8)
Set refetchIntervalInBackground: false as a QueryClient default so all
interval-based polls (widgets ~30s, message-queue 5s, media build progress
1s) pause when document.visibilityState === 'hidden'. Battery-friendly on
mobile -- the dashboard is the page most likely to be left open on a phone.

The media build-progress poll previously forced refetchIntervalInBackground:
true; that override is removed so it inherits the default. The build keeps
running server-side; the poll resumes and catches up when the user returns
to the tab.

122 tests pass; lint/build green.

Refs openspec/changes/mobile-responsive-parity/verify-report.md residual
risk #3 (D8 battery follow-up).
2026-06-26 15:48:43 +00:00

79 lines
2.1 KiB
TypeScript

import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import {
fetchMediaStatus,
buildMediaIndex,
queryMedia,
stopMediaIndexBuild,
forceStopMediaIndexBuild,
} from "../api/client";
export function useMediaStatus(jellyfinServiceId?: string) {
return useQuery({
queryKey: ["media", "status", jellyfinServiceId ?? "default"],
queryFn: () => fetchMediaStatus(jellyfinServiceId),
staleTime: 5_000,
refetchInterval: (query) =>
query.state.data?.build_running ? 1000 : false,
// Inherit the default refetchIntervalInBackground: false — pause the
// 1s build-progress poll when the tab is hidden. The build keeps
// running server-side; the poll resumes and catches up on return.
// Battery-friendly (D8 follow-up).
});
}
export function useMediaQuery(params: {
libraries?: string;
types?: string;
search?: string;
hdr_filter?: string;
sort_key?: string;
sort_order?: string;
limit?: number;
offset?: number;
jellyfinServiceId?: string;
enabled?: boolean;
}) {
const { enabled = true, ...queryParams } = params;
return useQuery({
queryKey: ["media", "query", queryParams],
queryFn: () => queryMedia(queryParams),
enabled,
staleTime: 30_000,
});
}
function invalidateMedia(queryClient: ReturnType<typeof useQueryClient>) {
queryClient.invalidateQueries({ queryKey: ["media"] });
}
export function useBuildIndex(jellyfinServiceId?: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => buildMediaIndex(jellyfinServiceId),
onSuccess: () => {
invalidateMedia(queryClient);
},
});
}
export function useStopBuildIndex(jellyfinServiceId?: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => stopMediaIndexBuild(jellyfinServiceId),
onSuccess: () => {
invalidateMedia(queryClient);
},
});
}
export function useForceStopBuildIndex(jellyfinServiceId?: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => forceStopMediaIndexBuild(jellyfinServiceId),
onSuccess: () => {
invalidateMedia(queryClient);
},
});
}