From 4e40cfbdde6c4603f4be9d20b6bd54d12b61d961 Mon Sep 17 00:00:00 2001 From: Richard Chapman Date: Wed, 25 Oct 2023 16:36:09 +0100 Subject: [PATCH 1/6] Add react-query --- package-lock.json | 35 +++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 36 insertions(+) diff --git a/package-lock.json b/package-lock.json index f352b72..b582b18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "@heroicons/react": "^2.0.18", + "@tanstack/react-query": "^5.0.5", "date-fns": "^2.30.0", "md5": "^2.3.0", "moviedb-promise": "^4.0.3", @@ -1004,6 +1005,40 @@ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" } }, + "node_modules/@tanstack/query-core": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.0.5.tgz", + "integrity": "sha512-MThCETMkHDHTnFZHp71L+SqTtD5d6XHftFCVR1xRJdWM3qGrlQ2VCXaj0SKVcyJej2e1Opa2c7iknu1llxCDNQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.0.5.tgz", + "integrity": "sha512-ZG0Q4HZ0iuI8mWiZ2/MdVYPHbrmAVhMn7+gLOkxJh6zLIgCL4luSZlohzN5Xt4MjxfxxWioO1nemwpudaTsmQg==", + "dependencies": { + "@tanstack/query-core": "5.0.5" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/@types/babel__core": { "version": "7.20.3", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.3.tgz", diff --git a/package.json b/package.json index 352c193..37c58e7 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@heroicons/react": "^2.0.18", + "@tanstack/react-query": "^5.0.5", "date-fns": "^2.30.0", "md5": "^2.3.0", "moviedb-promise": "^4.0.3", From 310dd4eb033e4892210827079eadcb12dbabe5b8 Mon Sep 17 00:00:00 2001 From: Richard Chapman Date: Wed, 25 Oct 2023 16:36:22 +0100 Subject: [PATCH 2/6] New listing query hooks --- src/hooks/useGetListing/index.ts | 2 + src/hooks/useGetListing/types.ts | 13 ++ src/hooks/useGetListing/useGetListing.ts | 116 ++++++++++++++++++ .../useGetListing/useGetTrending/index.ts | 1 + .../useGetTrending/useGetTrending.ts | 23 ++++ src/hooks/useGetListing/useSearchAll/index.ts | 1 + .../useSearchAll/useSearchAll.ts | 26 ++++ .../useGetListing/useSearchMovies/index.ts | 1 + .../useSearchMovies/useSearchMovies.ts | 26 ++++ .../useGetListing/useSearchPeople/index.ts | 1 + .../useSearchPeople/useSearchPeople.ts | 26 ++++ src/hooks/useGetListing/useSearchTv/index.ts | 1 + .../useGetListing/useSearchTv/useSearchTv.ts | 26 ++++ 13 files changed, 263 insertions(+) create mode 100644 src/hooks/useGetListing/index.ts create mode 100644 src/hooks/useGetListing/types.ts create mode 100644 src/hooks/useGetListing/useGetListing.ts create mode 100644 src/hooks/useGetListing/useGetTrending/index.ts create mode 100644 src/hooks/useGetListing/useGetTrending/useGetTrending.ts create mode 100644 src/hooks/useGetListing/useSearchAll/index.ts create mode 100644 src/hooks/useGetListing/useSearchAll/useSearchAll.ts create mode 100644 src/hooks/useGetListing/useSearchMovies/index.ts create mode 100644 src/hooks/useGetListing/useSearchMovies/useSearchMovies.ts create mode 100644 src/hooks/useGetListing/useSearchPeople/index.ts create mode 100644 src/hooks/useGetListing/useSearchPeople/useSearchPeople.ts create mode 100644 src/hooks/useGetListing/useSearchTv/index.ts create mode 100644 src/hooks/useGetListing/useSearchTv/useSearchTv.ts diff --git a/src/hooks/useGetListing/index.ts b/src/hooks/useGetListing/index.ts new file mode 100644 index 0000000..f282cdc --- /dev/null +++ b/src/hooks/useGetListing/index.ts @@ -0,0 +1,2 @@ +export * from "./useGetListing"; +export * from "./types"; diff --git a/src/hooks/useGetListing/types.ts b/src/hooks/useGetListing/types.ts new file mode 100644 index 0000000..539d937 --- /dev/null +++ b/src/hooks/useGetListing/types.ts @@ -0,0 +1,13 @@ +import { ListItem } from "../../lib/format"; + +export type SearchType = "all" | "movie" | "tv" | "person"; + +export type SearchArgs = { + query: string; + page: number; +}; + +export type ListingResponse = { + items: ListItem[]; + total: number; +}; diff --git a/src/hooks/useGetListing/useGetListing.ts b/src/hooks/useGetListing/useGetListing.ts new file mode 100644 index 0000000..9402b4e --- /dev/null +++ b/src/hooks/useGetListing/useGetListing.ts @@ -0,0 +1,116 @@ +import { useMemo } from "react"; +import { useGetTrending } from "./useGetTrending"; +import { SearchArgs, SearchType } from "./types"; +import { useSearchAll } from "./useSearchAll"; +import { useSearchMovies } from "./useSearchMovies"; +import { useSearchPeople } from "./useSearchPeople"; +import { useSearchTv } from "./useSearchTv"; + +type Trending = { + type: "trending"; +}; + +type Search = { + type: SearchType; + args: SearchArgs; +}; + +type SearchProps = Trending | Search; + +const FALLBACK_ARGS: SearchArgs = { + query: "", + page: 1, +}; + +export const useGetListing = (props: SearchProps) => { + const { type } = props; + + const { + isTrending, + isSearchAll, + isSearchMovies, + isSearchPeople, + isSearchTv, + } = useMemo( + () => ({ + isTrending: type === "trending", + isSearchAll: type === "all", + isSearchMovies: type === "movie", + isSearchTv: type === "tv", + isSearchPeople: type === "person", + }), + [type], + ); + + const args = type !== "trending" ? props.args : FALLBACK_ARGS; + + const { isLoading: isTrendingLoading, data: trendingData } = + useGetTrending(isTrending); + + const { isLoading: isSearchAllLoading, data: searchAllData } = useSearchAll( + args, + isSearchAll, + ); + + const { isLoading: isSearchMoviesLoading, data: moviesData } = + useSearchMovies(args, isSearchMovies); + + const { isLoading: isSearchTvLoading, data: tvData } = useSearchTv( + args, + isSearchTv, + ); + + const { isLoading: isSearchPeopleLoading, data: peopleData } = + useSearchPeople(args, isSearchPeople); + + const isLoading = useMemo( + () => + isTrendingLoading || + isSearchAllLoading || + isSearchMoviesLoading || + isSearchTvLoading || + isSearchPeopleLoading, + [ + isSearchAllLoading, + isSearchMoviesLoading, + isSearchPeopleLoading, + isSearchTvLoading, + isTrendingLoading, + ], + ); + + const results = useMemo(() => { + if (isSearchAll) { + return searchAllData; + } + + if (isSearchMovies) { + return moviesData; + } + + if (isSearchTv) { + return tvData; + } + + if (isSearchPeople) { + return peopleData; + } + + return trendingData; + }, [ + isSearchAll, + isSearchMovies, + isSearchPeople, + isSearchTv, + moviesData, + peopleData, + searchAllData, + trendingData, + tvData, + ]); + + return { + isLoading, + results, + }; +}; diff --git a/src/hooks/useGetListing/useGetTrending/index.ts b/src/hooks/useGetListing/useGetTrending/index.ts new file mode 100644 index 0000000..8b23677 --- /dev/null +++ b/src/hooks/useGetListing/useGetTrending/index.ts @@ -0,0 +1 @@ +export * from "./useGetTrending"; diff --git a/src/hooks/useGetListing/useGetTrending/useGetTrending.ts b/src/hooks/useGetListing/useGetTrending/useGetTrending.ts new file mode 100644 index 0000000..b774435 --- /dev/null +++ b/src/hooks/useGetListing/useGetTrending/useGetTrending.ts @@ -0,0 +1,23 @@ +import { QueryFunction, useQuery } from "@tanstack/react-query"; +import { apiFetch } from "../../../lib/api"; +import { TrendingResponse } from "moviedb-promise/dist/request-types"; +import { formatSearchAll } from "../../../lib/format"; +import { ListingResponse } from "../types"; + +export const useGetTrending = (enabled = true) => { + const queryFn: QueryFunction = async () => { + const data = await apiFetch(`/trending`); + + return { + items: formatSearchAll(data.results), + // There may be more results but we're only showing one trending page + total: 1, + }; + }; + + return useQuery({ + enabled, + queryKey: ["trending"], + queryFn, + }); +}; diff --git a/src/hooks/useGetListing/useSearchAll/index.ts b/src/hooks/useGetListing/useSearchAll/index.ts new file mode 100644 index 0000000..173fbb6 --- /dev/null +++ b/src/hooks/useGetListing/useSearchAll/index.ts @@ -0,0 +1 @@ +export * from "./useSearchAll"; diff --git a/src/hooks/useGetListing/useSearchAll/useSearchAll.ts b/src/hooks/useGetListing/useSearchAll/useSearchAll.ts new file mode 100644 index 0000000..4ba9b0e --- /dev/null +++ b/src/hooks/useGetListing/useSearchAll/useSearchAll.ts @@ -0,0 +1,26 @@ +import { QueryFunction, useQuery } from "@tanstack/react-query"; + +import { SearchMultiResponse } from "moviedb-promise/dist/request-types"; +import { ListingResponse, SearchArgs } from "../types"; +import { apiFetch } from "../../../lib/api"; +import { formatSearchAll } from "../../../lib/format"; + +export const useSearchAll = (args: SearchArgs, enabled = true) => { + const queryFn: QueryFunction = async () => { + const { query, page } = args; + const data = await apiFetch( + `/search?query=${query}&page=${page}`, + ); + + return { + items: formatSearchAll(data.results), + total: data.total_pages || 1, + }; + }; + + return useQuery({ + enabled, + queryKey: ["searchAll", args], + queryFn, + }); +}; diff --git a/src/hooks/useGetListing/useSearchMovies/index.ts b/src/hooks/useGetListing/useSearchMovies/index.ts new file mode 100644 index 0000000..cf17713 --- /dev/null +++ b/src/hooks/useGetListing/useSearchMovies/index.ts @@ -0,0 +1 @@ +export * from "./useSearchMovies"; diff --git a/src/hooks/useGetListing/useSearchMovies/useSearchMovies.ts b/src/hooks/useGetListing/useSearchMovies/useSearchMovies.ts new file mode 100644 index 0000000..13135f9 --- /dev/null +++ b/src/hooks/useGetListing/useSearchMovies/useSearchMovies.ts @@ -0,0 +1,26 @@ +import { QueryFunction, useQuery } from "@tanstack/react-query"; + +import { MovieResultsResponse } from "moviedb-promise/dist/request-types"; +import { ListingResponse, SearchArgs } from "../types"; +import { apiFetch } from "../../../lib/api"; +import { formatSearchMovie } from "../../../lib/format"; + +export const useSearchMovies = (args: SearchArgs, enabled = true) => { + const queryFn: QueryFunction = async () => { + const { query, page } = args; + const data = await apiFetch( + `/movie/search?query=${query}&page=${page}`, + ); + + return { + items: formatSearchMovie(data.results), + total: data.total_pages || 1, + }; + }; + + return useQuery({ + enabled, + queryKey: ["searchMovie", args], + queryFn, + }); +}; diff --git a/src/hooks/useGetListing/useSearchPeople/index.ts b/src/hooks/useGetListing/useSearchPeople/index.ts new file mode 100644 index 0000000..e7e7822 --- /dev/null +++ b/src/hooks/useGetListing/useSearchPeople/index.ts @@ -0,0 +1 @@ +export * from "./useSearchPeople"; diff --git a/src/hooks/useGetListing/useSearchPeople/useSearchPeople.ts b/src/hooks/useGetListing/useSearchPeople/useSearchPeople.ts new file mode 100644 index 0000000..cd43c0d --- /dev/null +++ b/src/hooks/useGetListing/useSearchPeople/useSearchPeople.ts @@ -0,0 +1,26 @@ +import { QueryFunction, useQuery } from "@tanstack/react-query"; + +import { SearchPersonResponse } from "moviedb-promise/dist/request-types"; +import { ListingResponse, SearchArgs } from "../types"; +import { apiFetch } from "../../../lib/api"; +import { formatSearchPerson } from "../../../lib/format"; + +export const useSearchPeople = (args: SearchArgs, enabled = true) => { + const queryFn: QueryFunction = async () => { + const { query, page } = args; + const data = await apiFetch( + `/person/search?query=${query}&page=${page}`, + ); + + return { + items: formatSearchPerson(data.results), + total: data.total_pages || 1, + }; + }; + + return useQuery({ + enabled, + queryKey: ["searchPeople", args], + queryFn, + }); +}; diff --git a/src/hooks/useGetListing/useSearchTv/index.ts b/src/hooks/useGetListing/useSearchTv/index.ts new file mode 100644 index 0000000..5d057b5 --- /dev/null +++ b/src/hooks/useGetListing/useSearchTv/index.ts @@ -0,0 +1 @@ +export * from "./useSearchTv"; diff --git a/src/hooks/useGetListing/useSearchTv/useSearchTv.ts b/src/hooks/useGetListing/useSearchTv/useSearchTv.ts new file mode 100644 index 0000000..949d3a4 --- /dev/null +++ b/src/hooks/useGetListing/useSearchTv/useSearchTv.ts @@ -0,0 +1,26 @@ +import { QueryFunction, useQuery } from "@tanstack/react-query"; + +import { TvResultsResponse } from "moviedb-promise/dist/request-types"; +import { ListingResponse, SearchArgs } from "../types"; +import { apiFetch } from "../../../lib/api"; +import { formatSearchTvShow } from "../../../lib/format"; + +export const useSearchTv = (args: SearchArgs, enabled = true) => { + const queryFn: QueryFunction = async () => { + const { query, page } = args; + const data = await apiFetch( + `/tv/search?query=${query}&page=${page}`, + ); + + return { + items: formatSearchTvShow(data.results), + total: data.total_pages || 1, + }; + }; + + return useQuery({ + enabled, + queryKey: ["searchTv", args], + queryFn, + }); +}; From a732984281804aeaf10d44d990a2aa51053e1abe Mon Sep 17 00:00:00 2001 From: Richard Chapman Date: Wed, 25 Oct 2023 16:36:48 +0100 Subject: [PATCH 3/6] Tweak formatting libs --- src/lib/format.ts | 123 +++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 73 deletions(-) diff --git a/src/lib/format.ts b/src/lib/format.ts index 4b7631e..27ccd72 100644 --- a/src/lib/format.ts +++ b/src/lib/format.ts @@ -8,67 +8,60 @@ import type { SearchMultiResponse, SearchPersonResponse, ShowResponse, + TrendingResponse, TvResult, TvResultsResponse, -} from 'moviedb-promise/dist/request-types'; -import parseISO from 'date-fns/parseISO'; -import compareDesc from 'date-fns/compareDesc'; +} from "moviedb-promise/dist/request-types"; +import parseISO from "date-fns/parseISO"; +import compareDesc from "date-fns/compareDesc"; -import { formatShortMonthDate } from './dates'; +import { formatShortMonthDate } from "./dates"; -// Types export type ListItem = { dbId?: string; tmdbId: number; - type: 'movie' | 'tv' | 'person'; + type: "movie" | "tv" | "person"; poster?: string; title: string; subTitle?: string; }; -// Helpers -export const formatMovie = (movie: MovieResult | MovieResponse): ListItem => { - return { - tmdbId: movie.id ?? 0, - type: 'movie', - poster: movie.poster_path, - title: movie.title || 'Unknown title', - subTitle: formatShortMonthDate(movie.release_date), - }; -}; - -export const formatTvShow = (tvShow: TvResult | ShowResponse): ListItem => { - return { - tmdbId: tvShow.id ?? 0, - type: 'tv', - poster: tvShow.poster_path ?? undefined, - title: tvShow.name || 'Unknown name', - subTitle: formatShortMonthDate(tvShow.first_air_date), - }; -}; - -export const formatPerson = (person: PersonResult | Person): ListItem => { - return { - tmdbId: person.id ?? 0, - type: 'person', - poster: person.profile_path ?? undefined, - title: person.name || 'Unknown name', - }; -}; - -export const formatSearchAll = (data: SearchMultiResponse): ListItem[] => { - const { results } = data; - +export const formatMovie = (movie: MovieResult | MovieResponse): ListItem => ({ + tmdbId: movie.id ?? 0, + type: "movie", + poster: movie.poster_path, + title: movie.title || "Unknown title", + subTitle: formatShortMonthDate(movie.release_date), +}); + +export const formatTvShow = (tvShow: TvResult | ShowResponse): ListItem => ({ + tmdbId: tvShow.id ?? 0, + type: "tv", + poster: tvShow.poster_path ?? undefined, + title: tvShow.name || "Unknown name", + subTitle: formatShortMonthDate(tvShow.first_air_date), +}); + +export const formatPerson = (person: PersonResult | Person): ListItem => ({ + tmdbId: person.id ?? 0, + type: "person", + poster: person.profile_path ?? undefined, + title: person.name || "Unknown name", +}); + +export const formatSearchAll = ( + results: SearchMultiResponse["results"] | TrendingResponse["results"], +): ListItem[] => { if (!results) { return []; } return results.map((result) => { - if ('media_type' in result) { + if ("media_type" in result) { switch (result.media_type) { - case 'movie': + case "movie": return formatMovie(result); - case 'tv': + case "tv": return formatTvShow(result); default: // Force typing as data matches but types don't @@ -81,37 +74,21 @@ export const formatSearchAll = (data: SearchMultiResponse): ListItem[] => { }); }; -export const formatSearchMovie = (data: MovieResultsResponse): ListItem[] => { - const { results } = data; - - if (!results) { - return []; - } - - return results.map((result) => formatMovie(result)); -}; +export const formatSearchMovie = ( + results: MovieResultsResponse["results"], +): ListItem[] => results?.map((result) => formatMovie(result)) ?? []; -export const formatSearchTvShow = (data: TvResultsResponse): ListItem[] => { - const { results } = data; +export const formatSearchTvShow = ( + results: TvResultsResponse["results"], +): ListItem[] => results?.map((result) => formatTvShow(result)) ?? []; - if (!results) { - return []; - } - - return results.map((result) => formatTvShow(result)); -}; - -export const formatSearchPerson = (data: SearchPersonResponse): ListItem[] => { - const { results } = data; - - if (!results) { - return []; - } - - return results.map((result) => formatPerson(result)); -}; +export const formatSearchPerson = ( + results: SearchPersonResponse["results"], +): ListItem[] => results?.map((result) => formatPerson(result)) ?? []; -export const formatPersonCredits = (data: PersonCombinedCreditsResponse): ListItem[] => { +export const formatPersonCredits = ( + data: PersonCombinedCreditsResponse, +): ListItem[] => { const { cast } = data; if (!cast) { @@ -138,10 +115,10 @@ export const formatPersonCredits = (data: PersonCombinedCreditsResponse): ListIt return sortedData .map((result): ListItem | undefined => { - if ('media_type' in result) { - if (result.media_type === 'movie') { + if ("media_type" in result) { + if (result.media_type === "movie") { return formatMovie(result as MovieResult); - } else if (result.media_type === 'tv') { + } else if (result.media_type === "tv") { return formatTvShow(result as TvResult); } else { // No matching type From 6880861802764ede56d0cf161244af283c26b916 Mon Sep 17 00:00:00 2001 From: Richard Chapman Date: Wed, 25 Oct 2023 16:37:03 +0100 Subject: [PATCH 4/6] Remove redundant api calls --- src/lib/api/movie.ts | 16 ++-------------- src/lib/api/person.ts | 16 ++-------------- src/lib/api/search.ts | 18 ------------------ src/lib/api/tvShow.ts | 15 ++------------- 4 files changed, 6 insertions(+), 59 deletions(-) delete mode 100644 src/lib/api/search.ts diff --git a/src/lib/api/movie.ts b/src/lib/api/movie.ts index f9b79c6..579c921 100644 --- a/src/lib/api/movie.ts +++ b/src/lib/api/movie.ts @@ -1,26 +1,14 @@ import type { CreditsResponse, MovieResponse, - MovieResultsResponse, -} from 'moviedb-promise/dist/request-types'; +} from "moviedb-promise/dist/request-types"; -import { apiFetch } from '../api'; - -// Types -type SearchArgs = { - query: string; - page: number; -}; +import { apiFetch } from "../api"; type GetArgs = { id: number; }; -// Exports -export const searchMovie = async ({ query, page }: SearchArgs) => { - return apiFetch(`/movie/search?query=${query}&page=${page}`); -}; - export const getMovie = async ({ id }: GetArgs) => { return apiFetch(`/movie/${id}`); }; diff --git a/src/lib/api/person.ts b/src/lib/api/person.ts index 7593049..5e126c7 100644 --- a/src/lib/api/person.ts +++ b/src/lib/api/person.ts @@ -1,26 +1,14 @@ import type { Person, PersonCombinedCreditsResponse, - SearchPersonResponse, -} from 'moviedb-promise/dist/request-types'; +} from "moviedb-promise/dist/request-types"; -import { apiFetch } from '../api'; - -// Types -type SearchArgs = { - query: string; - page: number; -}; +import { apiFetch } from "../api"; type GetArgs = { id: number; }; -// Exports -export const searchPerson = async ({ query, page }: SearchArgs) => { - return apiFetch(`/person/search?query=${query}&page=${page}`); -}; - export const getPerson = async ({ id }: GetArgs) => { return apiFetch(`/person/${id}`); }; diff --git a/src/lib/api/search.ts b/src/lib/api/search.ts deleted file mode 100644 index e39daeb..0000000 --- a/src/lib/api/search.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { SearchMultiResponse, TrendingResponse } from 'moviedb-promise/dist/request-types'; - -import { apiFetch } from '../api'; - -// Types -type SearchArgs = { - query: string; - page: number; -}; - -// Exports -export const getTrending = async () => { - return apiFetch(`/trending`); -}; - -export const searchAll = async ({ query, page }: SearchArgs) => { - return apiFetch(`/search?query=${query}&page=${page}`); -}; diff --git a/src/lib/api/tvShow.ts b/src/lib/api/tvShow.ts index 42e7d1e..878c00e 100644 --- a/src/lib/api/tvShow.ts +++ b/src/lib/api/tvShow.ts @@ -1,22 +1,11 @@ -import type { CreditsResponse, TvResultsResponse } from 'moviedb-promise/dist/request-types'; +import type { CreditsResponse } from "moviedb-promise/dist/request-types"; -import { apiFetch, ExtShowResponse } from '../api'; - -// Types -type SearchArgs = { - query: string; - page: number; -}; +import { apiFetch, ExtShowResponse } from "../api"; type GetArgs = { id: number; }; -// Exports -export const searchTv = async ({ query, page }: SearchArgs) => { - return apiFetch(`/tv/search?query=${query}&page=${page}`); -}; - export const getTvShow = async ({ id }: GetArgs) => { return apiFetch(`/tv/${id}`); }; From 16b9c0c8dfac8ca2539bfa87d3de0542947cef64 Mon Sep 17 00:00:00 2001 From: Richard Chapman Date: Wed, 25 Oct 2023 16:37:13 +0100 Subject: [PATCH 5/6] Use react-query on Home --- src/components/Providers.tsx | 15 ++- src/components/pages/Home.tsx | 242 +++++++++------------------------- 2 files changed, 71 insertions(+), 186 deletions(-) diff --git a/src/components/Providers.tsx b/src/components/Providers.tsx index 75be80c..dd6f94c 100644 --- a/src/components/Providers.tsx +++ b/src/components/Providers.tsx @@ -1,17 +1,22 @@ import { HelmetProvider } from "react-helmet-async"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { UserProvider } from "../hooks/useUser"; import { ListProvider } from "../hooks/useList"; import { ListModalProvider } from "../hooks/useListModal"; +const queryClient = new QueryClient(); + const Providers = ({ children }: { children: React.ReactNode }) => { return ( - - - {children} - - + + + + {children} + + + ); }; diff --git a/src/components/pages/Home.tsx b/src/components/pages/Home.tsx index 993e23e..0108dbf 100644 --- a/src/components/pages/Home.tsx +++ b/src/components/pages/Home.tsx @@ -1,161 +1,48 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useMemo, useRef } from "react"; import { Helmet } from "react-helmet-async"; import { useSearchParams } from "react-router-dom"; -import type { SearchMultiResponse } from "moviedb-promise/dist/request-types"; import Pagination from "../search/Pagination"; import TabButton from "../search/TabButton"; import ListItem from "../lists/ListItem"; -import { - formatSearchAll, - formatSearchMovie, - formatSearchPerson, - formatSearchTvShow, - ListItem as ListItemType, -} from "../../lib/format"; -import type { ApiError, ApiResponse } from "../../lib/api"; -import { searchMovie } from "../../lib/api/movie"; -import { searchPerson } from "../../lib/api/person"; -import { searchAll, getTrending } from "../../lib/api/search"; -import { searchTv } from "../../lib/api/tvShow"; +import { useGetListing } from "../../hooks/useGetListing"; type Tab = "all" | "movie" | "tv" | "person"; const Home = () => { const [searchParams, setSearchParams] = useSearchParams(); - const [searchResults, setSearchResults] = useState< - ApiResponse | undefined - >(undefined); - const [formattedResults, setFormattedResults] = useState([]); - const tabRef = useRef(null); - const search = searchParams.get("search") || ""; - const tab = (searchParams.get("tab") || "all") as Tab; - const page = parseInt(searchParams.get("page") || "1"); - - useEffect(() => { - let isCancelled = false; - - setSearchResults({ status: "pending" }); - - if (search && search.trim() !== "") { - if (tabRef.current) { - tabRef.current.scrollIntoView({ behavior: "smooth" }); - } + const { page, search, tab } = useMemo(() => { + const search = searchParams.get("search") || ""; + const tab = (searchParams.get("tab") || "all") as Tab; + const page = parseInt(searchParams.get("page") || "1"); - switch (tab) { - case "all": - searchAll({ - query: search, - page, - }) - .then((data) => { - if (!isCancelled) { - setSearchResults({ status: "resolved", data }); - setFormattedResults(formatSearchAll(data)); - } - }) - .catch((error: ApiError) => { - if (!isCancelled) { - // TODO: Handle error - setSearchResults({ status: "rejected", error }); - setFormattedResults([]); - } - }); - - break; - case "movie": - searchMovie({ - query: search, - page, - }) - .then((data) => { - if (!isCancelled) { - setSearchResults({ status: "resolved", data }); - setFormattedResults(formatSearchMovie(data)); - } - }) - .catch((error: ApiError) => { - if (!isCancelled) { - // TODO: Handle error - setSearchResults({ status: "rejected", error }); - setFormattedResults([]); - } - }); - - break; - case "tv": - searchTv({ - query: search, - page, - }) - .then((data) => { - if (!isCancelled) { - setSearchResults({ status: "resolved", data }); - setFormattedResults(formatSearchTvShow(data)); - } - }) - .catch((error: ApiError) => { - if (!isCancelled) { - // TODO: Handle error - setSearchResults({ status: "rejected", error }); - setFormattedResults([]); - } - }); + return { + search, + tab, + page, + }; + }, [searchParams]); - break; - case "person": - searchPerson({ - query: search, - page, - }) - .then((data) => { - if (!isCancelled) { - setSearchResults({ status: "resolved", data }); - setFormattedResults(formatSearchPerson(data)); - } - }) - .catch((error: ApiError) => { - if (!isCancelled) { - // TODO: Handle error - setSearchResults({ status: "rejected", error }); - setFormattedResults([]); - } - }); + const hasSearchTerm = search.trim() !== ""; - break; - default: - // Something went wrong! - setSearchResults({ status: "rejected" }); - setFormattedResults([]); + const { isLoading, results } = useGetListing({ + type: hasSearchTerm ? tab : "trending", + args: { + page, + query: search, + }, + }); - break; - } - } else { - // Default to trending today - getTrending() - .then((data) => { - if (!isCancelled) { - setSearchResults({ status: "resolved", data }); - setFormattedResults(formatSearchAll(data)); - } - }) - .catch((error: ApiError) => { - if (!isCancelled) { - // TODO: Handle error - setSearchResults({ status: "rejected", error }); - setFormattedResults([]); - } - }); + useEffect(() => { + // Scroll back to the top on page or query change + if (hasSearchTerm && tabRef.current) { + tabRef.current.scrollIntoView({ behavior: "smooth" }); } - - return () => { - isCancelled = true; - }; - }, [search, page, tab]); + }, [hasSearchTerm, page, tab]); const handleSearch = (event: React.FormEvent) => { event.preventDefault(); @@ -236,9 +123,9 @@ const Home = () => { - {searchResults ? ( + {results ? (
- {!search ? ( + {!hasSearchTerm ? (

Trending today @@ -303,47 +190,40 @@ const Home = () => { )} - {searchResults.status !== "rejected" ? ( - <> -
    - {searchResults.status === "pending" ? ( - <> - {Array(20) - .fill(null) - .map((_, index) => ( -
  • -
    -
    -
    -
  • - ))} - - ) : ( - <> - {formattedResults.map((result) => ( -
  • - -
  • - ))} - - )} -
- - {search && - searchResults.status === "resolved" && - searchResults.data.total_pages && - searchResults.data.total_pages > 1 ? ( - { - searchParams.set("page", newPage.toString()); - - setSearchParams(searchParams); - }} - /> - ) : null} - +
    + {isLoading ? ( + <> + {Array(20) + .fill(null) + .map((_, index) => ( +
  • +
    +
    +
    +
  • + ))} + + ) : ( + <> + {results.items.map((item) => ( +
  • + +
  • + ))} + + )} +
+ + {results.total > 1 ? ( + { + searchParams.set("page", newPage.toString()); + + setSearchParams(searchParams); + }} + /> ) : null}

) : null} From 9a65433786fcbfe7394425db10e0eb3601f1fe50 Mon Sep 17 00:00:00 2001 From: Richard Chapman Date: Wed, 25 Oct 2023 16:45:40 +0100 Subject: [PATCH 6/6] Rename useTrending --- src/hooks/useGetListing/useGetListing.ts | 4 ++-- src/hooks/useGetListing/useGetTrending/index.ts | 1 - src/hooks/useGetListing/useTrending/index.ts | 1 + .../useGetTrending.ts => useTrending/useTrending.ts} | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 src/hooks/useGetListing/useGetTrending/index.ts create mode 100644 src/hooks/useGetListing/useTrending/index.ts rename src/hooks/useGetListing/{useGetTrending/useGetTrending.ts => useTrending/useTrending.ts} (92%) diff --git a/src/hooks/useGetListing/useGetListing.ts b/src/hooks/useGetListing/useGetListing.ts index 9402b4e..9ea40b2 100644 --- a/src/hooks/useGetListing/useGetListing.ts +++ b/src/hooks/useGetListing/useGetListing.ts @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { useGetTrending } from "./useGetTrending"; +import { useTrending } from "./useTrending"; import { SearchArgs, SearchType } from "./types"; import { useSearchAll } from "./useSearchAll"; import { useSearchMovies } from "./useSearchMovies"; @@ -45,7 +45,7 @@ export const useGetListing = (props: SearchProps) => { const args = type !== "trending" ? props.args : FALLBACK_ARGS; const { isLoading: isTrendingLoading, data: trendingData } = - useGetTrending(isTrending); + useTrending(isTrending); const { isLoading: isSearchAllLoading, data: searchAllData } = useSearchAll( args, diff --git a/src/hooks/useGetListing/useGetTrending/index.ts b/src/hooks/useGetListing/useGetTrending/index.ts deleted file mode 100644 index 8b23677..0000000 --- a/src/hooks/useGetListing/useGetTrending/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./useGetTrending"; diff --git a/src/hooks/useGetListing/useTrending/index.ts b/src/hooks/useGetListing/useTrending/index.ts new file mode 100644 index 0000000..28e4787 --- /dev/null +++ b/src/hooks/useGetListing/useTrending/index.ts @@ -0,0 +1 @@ +export * from "./useTrending"; diff --git a/src/hooks/useGetListing/useGetTrending/useGetTrending.ts b/src/hooks/useGetListing/useTrending/useTrending.ts similarity index 92% rename from src/hooks/useGetListing/useGetTrending/useGetTrending.ts rename to src/hooks/useGetListing/useTrending/useTrending.ts index b774435..c2d6231 100644 --- a/src/hooks/useGetListing/useGetTrending/useGetTrending.ts +++ b/src/hooks/useGetListing/useTrending/useTrending.ts @@ -4,7 +4,7 @@ import { TrendingResponse } from "moviedb-promise/dist/request-types"; import { formatSearchAll } from "../../../lib/format"; import { ListingResponse } from "../types"; -export const useGetTrending = (enabled = true) => { +export const useTrending = (enabled = true) => { const queryFn: QueryFunction = async () => { const data = await apiFetch(`/trending`);