ac703eecd2
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).
79 lines
2.1 KiB
TypeScript
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);
|
|
},
|
|
});
|
|
}
|