diff --git a/package.json b/package.json index 779b2bb..68f54b2 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@nextui-org/react": "^2.2.10", "@tanstack/react-query": "^5.27.5", + "country-region-data": "^3.0.0", "framer-motion": "^11.0.12", "ky": "^1.2.2", "luxon": "^3.4.4", diff --git a/src/app/results/SearchPage.tsx b/src/app/results/SearchPage.tsx index 95ca165..ad86e71 100644 --- a/src/app/results/SearchPage.tsx +++ b/src/app/results/SearchPage.tsx @@ -11,7 +11,9 @@ export const SearchPage: Component = () => { return ( <> - +
+ +
); }; diff --git a/src/app/trending/RegionSwitcher.tsx b/src/app/trending/RegionSwitcher.tsx new file mode 100644 index 0000000..44dffe3 --- /dev/null +++ b/src/app/trending/RegionSwitcher.tsx @@ -0,0 +1,33 @@ +"use client"; + +import { Component } from "@/typings/component"; +import { Region } from "@/utils/getRegionCodes"; + +import { Autocomplete, AutocompleteItem } from "@nextui-org/autocomplete"; +import { useRouter } from "next/navigation"; + +export const RegionSwitcher: Component<{ + regions: Region[]; + currentRegion: Region | null; +}> = ({ currentRegion, regions }) => { + const router = useRouter(); + + return ( + { + if (typeof key === "string" && key.length != 0) + return router.push(`/trending?region=${key}`); + }} + className="max-w-xs" + > + {(item) => ( + {item.name} + )} + + ); +}; diff --git a/src/app/trending/Trending.tsx b/src/app/trending/Trending.tsx index b0e20fe..6bc8c50 100644 --- a/src/app/trending/Trending.tsx +++ b/src/app/trending/Trending.tsx @@ -5,47 +5,95 @@ import { useClient } from "@/hooks/useClient"; import { useQuery } from "@tanstack/react-query"; import { Button } from "@nextui-org/button"; -import { CircularProgress } from "@nextui-org/progress"; import { Spacer } from "@nextui-org/spacer"; import { VideoCard } from "./VideoCard"; +import { LoadingPage } from "@/components/LoadingPage"; +import { useSearchParams } from "next/navigation"; +import { useMemo } from "react"; +import getRegionCodes from "@/utils/getRegionCodes"; +import { RegionSwitcher } from "./RegionSwitcher"; +import { defaultRegion } from "@/constants"; export const Trending: Component = ({}) => { const client = useClient(); - const { isLoading, error, refetch, data } = useQuery({ - queryKey: ["trending"], - queryFn: () => client.getTrending("NL") + const searchParams = useSearchParams(); + const validRegions = useMemo(() => getRegionCodes(), []); + + const specifiedRegion = + searchParams.get("region")?.toUpperCase() ?? defaultRegion; + + const [region, regionError] = useMemo(() => { + const foundRegion = validRegions.find( + (validRegion) => validRegion.code === specifiedRegion + ); + + if (foundRegion === undefined) + return [null, new Error(`Region \`${specifiedRegion}\` is invalid`)]; + + return [foundRegion, null]; + }, [specifiedRegion, validRegions]); + + const { + isLoading, + error: fetchError, + refetch, + data + } = useQuery({ + queryKey: ["trending", region], + queryFn: () => { + if (region === null) return; + + return client.getTrending(region.code); + }, + enabled: regionError === null }); + const noDataError = useMemo(() => { + if (data && data.length === 0) + return new Error( + `Could not find any trending video's in region \`${region?.name}\`` + ); + + return null; + }, [data]); + + const error: Error | null = regionError ?? fetchError ?? noDataError ?? null; + return ( -
- {isLoading && !data && ( -
- -
- )} - {error && ( -
-
-

- An error occurred loading the trending page -

-

{error.toString()}

- - + <> + {isLoading && !data && } + {!isLoading && ( +
+
+ + +

Trending

+ {error && ( +
+
+

+ An error occurred loading the trending page +

+

{error.toString()}

+ + +
+
+ )} + {data && error === null && ( +
+ {data.map((video) => ( + + ))} +
+ )}
)} - {data && ( -
- {data.map((video) => ( - - ))} -
- )} -
+ ); }; diff --git a/src/app/trending/VideoCard.tsx b/src/app/trending/VideoCard.tsx index b0d1290..da7800c 100644 --- a/src/app/trending/VideoCard.tsx +++ b/src/app/trending/VideoCard.tsx @@ -10,9 +10,17 @@ import formatDuration from "@/utils/formatDuration"; export const VideoCard: Component<{ data: TrendingVideo }> = ({ data: video }) => { + const handleContextMenu = () => {}; + return ( - + { + e.preventDefault(); + handleContextMenu(); + }} + > {video.title} - + }> + + ); } diff --git a/src/components/LoadingPage.tsx b/src/components/LoadingPage.tsx new file mode 100644 index 0000000..d0f9187 --- /dev/null +++ b/src/components/LoadingPage.tsx @@ -0,0 +1,12 @@ +"use client"; + +import { Component } from "@/typings/component"; +import { CircularProgress } from "@nextui-org/progress"; + +export const LoadingPage: Component = () => { + return ( +
+ +
+ ); +}; diff --git a/src/components/Search.tsx b/src/components/Search.tsx index e799786..212713b 100644 --- a/src/components/Search.tsx +++ b/src/components/Search.tsx @@ -12,7 +12,7 @@ export const Search: Component<{ initialQueryValue?: string }> = ({ }) => { const client = useClient(); - const [searchQuery, setSearchQuery] = useState(initialQueryValue ?? ""); + const [searchQuery, setSearchQuery] = useState(""); const [searchQueryDebounced] = useDebounce(searchQuery, 500); @@ -23,8 +23,6 @@ export const Search: Component<{ initialQueryValue?: string }> = ({ }); const handleSubmit: FormEventHandler = (e) => { - // e.preventDefault(); - console.log(searchQuery); }; diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..b302473 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1 @@ +export const defaultRegion = "US" as const; diff --git a/src/utils/getRegionCodes.ts b/src/utils/getRegionCodes.ts new file mode 100644 index 0000000..1bfa20f --- /dev/null +++ b/src/utils/getRegionCodes.ts @@ -0,0 +1,15 @@ +import { allCountries } from "country-region-data"; + +export interface Region { + code: string; + name: string; +} + +const getRegionCodes = (): Region[] => { + return allCountries.map((country) => ({ + name: country[0], + code: country[1] + })); +}; + +export default getRegionCodes; diff --git a/yarn.lock b/yarn.lock index 7eddb01..81b82cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3676,6 +3676,11 @@ core-js-compat@^3.31.0, core-js-compat@^3.34.0: dependencies: browserslist "^4.22.3" +country-region-data@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/country-region-data/-/country-region-data-3.0.0.tgz#9251cff57c22e450cbe96a7e50a3a23362d4304a" + integrity sha512-jpZwc6coXayi3aAv2HHTC9vhwRJB2zdur+coBlIZo1IVMonzylRR4Asf5j7evtUzdZPODdHrJ8CzEFK6MGUAgg== + cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"