diff --git a/web/src/hooks/queries/keys.ts b/web/src/hooks/queries/keys.ts index ad7325f0..87d94fcf 100644 --- a/web/src/hooks/queries/keys.ts +++ b/web/src/hooks/queries/keys.ts @@ -121,6 +121,26 @@ export const requestKeys = { discovery: () => ["requests", "discovery"] as const, discoverySection: (section: string, page: number) => ["requests", "discovery", section, page] as const, + discoverStudios: () => ["requests", "discover", "studios"] as const, + discoverNetworks: () => ["requests", "discover", "networks"] as const, + discoverGenres: () => ["requests", "discover", "genres"] as const, + discoverBrowse: ( + kind: "studio" | "network" | "genre", + slug: string, + mediaType: string | undefined, + sort: string, + page: number, + ) => + [ + "requests", + "discover", + "browse", + kind, + slug, + mediaType ?? "", + sort, + page, + ] as const, search: (mediaType: string, query: string, page: number) => ["requests", "search", mediaType, query, page] as const, detail: (mediaType: string, tmdbID: number) => ["requests", "detail", mediaType, tmdbID] as const, diff --git a/web/src/hooks/queries/requests.ts b/web/src/hooks/queries/requests.ts index eab55ed0..b4422946 100644 --- a/web/src/hooks/queries/requests.ts +++ b/web/src/hooks/queries/requests.ts @@ -3,6 +3,11 @@ import { toast } from "sonner"; import { api } from "@/api/client"; import type { CreateMediaRequestInput, + DiscoverBrowseKind, + DiscoverBrowseResponse, + DiscoverGenresResponse, + DiscoverNetworksResponse, + DiscoverStudiosResponse, LoadRequestIntegrationOptionsRequest, MediaRequest, MediaRequestsListResponse, @@ -21,6 +26,8 @@ import type { import { adminKeys, requestKeys } from "./keys"; const REQUESTS_STALE_TIME = 30_000; +const DISCOVER_BRAND_STALE_TIME = 24 * 60 * 60 * 1000; +const BROWSE_STALE_TIME = 60 * 1000; function listParamsKey(params: RequestListParams) { return { @@ -67,6 +74,62 @@ export function useRequestDiscoverySection(section: string, page = 1) { }); } +export function useDiscoverStudios() { + return useQuery({ + queryKey: requestKeys.discoverStudios(), + queryFn: () => + api("/requests/discover/studios").then( + (data) => data.studios ?? [], + ), + staleTime: DISCOVER_BRAND_STALE_TIME, + }); +} + +export function useDiscoverNetworks() { + return useQuery({ + queryKey: requestKeys.discoverNetworks(), + queryFn: () => + api("/requests/discover/networks").then( + (data) => data.networks ?? [], + ), + staleTime: DISCOVER_BRAND_STALE_TIME, + }); +} + +export function useDiscoverGenres() { + return useQuery({ + queryKey: requestKeys.discoverGenres(), + queryFn: () => + api("/requests/discover/genres").then( + (data) => data.genres ?? [], + ), + staleTime: DISCOVER_BRAND_STALE_TIME, + }); +} + +export interface UseRequestBrowseArgs { + kind: DiscoverBrowseKind; + slug: string; + mediaType?: RequestMediaType; + sort: "popularity" | "vote_average" | "release_date"; + page: number; +} + +export function useRequestBrowse({ kind, slug, mediaType, sort, page }: UseRequestBrowseArgs) { + return useQuery({ + queryKey: requestKeys.discoverBrowse(kind, slug, mediaType, sort, page), + queryFn: () => { + const params = new URLSearchParams({ sort, page: String(page) }); + if (mediaType) params.set("media_type", mediaType); + return api( + `/requests/discover/browse/${kind}/${encodeURIComponent(slug)}?${params}`, + ); + }, + enabled: slug.trim().length > 0 && (kind !== "genre" || Boolean(mediaType)), + staleTime: BROWSE_STALE_TIME, + }); +} + export function useRequestMediaDetail(mediaType: RequestMediaType, tmdbID: number) { return useQuery({ queryKey: requestKeys.detail(mediaType, tmdbID),