diff --git a/package.json b/package.json index 1782863..8451f13 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,12 @@ "dev": "vite", "build": "tsc -b && vite build", "lint": "eslint .", - "preview": "vite preview" + "preview": "vite preview", + "fmt": "npx prettier . --write" }, "dependencies": { "@tanstack/react-query": "^5.102.8", + "country-codes-list": "^3.2.0", "react": "^19.2.8", "react-dom": "^19.2.8" }, diff --git a/src/App.css b/src/App.css index f460279..a4bc79d 100644 --- a/src/App.css +++ b/src/App.css @@ -61,7 +61,6 @@ flex-direction: column; gap: 25px; place-content: center; - place-items: center; flex-grow: 1; @media (max-width: 1024px) { diff --git a/src/components/listItem.tsx b/src/components/listItem.tsx index b55799b..352c3c8 100644 --- a/src/components/listItem.tsx +++ b/src/components/listItem.tsx @@ -1,5 +1,5 @@ type cityWeather = { - name: string + name?: string temperature?: number precipitation?: number nextDays?: { time: string; temperature: number | undefined }[] diff --git a/src/components/location-list.tsx b/src/components/location-list.tsx index 9476553..0b5f007 100644 --- a/src/components/location-list.tsx +++ b/src/components/location-list.tsx @@ -1,13 +1,7 @@ -import type { WeatherApiResponse } from "../hooks/fetchWeather/types" -import useGetWeather from "../hooks/fetchWeather/useGetWeather" +import type { WeatherApiResponse, City } from "../hooks/fetchWeather/types" +import { useGetWeatherForCities } from "../hooks/fetchWeather/useGetWeather" import ListItem from "./listItem" -type City = { - name: string - lat: number - lon: number -} - const cities: City[] = [ { name: "Oslo", lat: 59.91273, lon: 10.74609 }, { name: "Sørumsand", lat: 59.98621, lon: 11.24154 }, @@ -31,7 +25,12 @@ const getNextDays = (data?: WeatherApiResponse) => { return ( date >= now && date.getUTCHours() === 12 && - date < new Date(now.getTime() + 3 * 24 * 60 * 60 * 1000) + date < + new Date( + now.getTime() - + now.getHours() * 60 * 60 * 1000 + + 4 * 24 * 60 * 60 * 1000 + ) ) }) .map((entry) => ({ @@ -43,23 +42,17 @@ const getNextDays = (data?: WeatherApiResponse) => { } const LocationList = () => { - const weatherResults = cities.map((city) => useGetWeather(city.lat, city.lon)) - - console.log(weatherResults) - - if (weatherResults.some((result) => result.isLoading)) { - return
Loading weather…
- } + const { isLoading, results } = useGetWeatherForCities(cities) - if (weatherResults.some((result) => result.error)) { - return
Could not load weather data.
+ if (isLoading) { + return
Loading...
} const cityWeather = cities.map((city, index) => ({ name: city.name, - temperature: getTemperature(weatherResults[index]?.result), - precipitation: getPrecipitation(weatherResults[index]?.result), - nextDays: getNextDays(weatherResults[index]?.result), + temperature: getTemperature(results[index]), + precipitation: getPrecipitation(results[index]), + nextDays: getNextDays(results[index]), })) return ( @@ -98,10 +91,6 @@ const LocationList = () => { > {cityWeather.map((item) => ( - //
  • - // {item.name}: {typeof item.temperature === 'number' ? `${item.temperature}°` : '—'} - // {typeof item.precipitation === 'number' ? `, ${item.precipitation} mm` : ''} - //
  • ))} diff --git a/src/hooks/fetchWeather/types.ts b/src/hooks/fetchWeather/types.ts index 1c008ba..74b5b75 100644 --- a/src/hooks/fetchWeather/types.ts +++ b/src/hooks/fetchWeather/types.ts @@ -175,4 +175,11 @@ export type METJSONForecast = { type: "Feature" } +export type City = { + name?: string + lat: number + lon: number + alt?: number +} + export type WeatherApiResponse = METJSONForecast diff --git a/src/hooks/fetchWeather/useGetWeather.ts b/src/hooks/fetchWeather/useGetWeather.ts index 163650e..e6cc108 100644 --- a/src/hooks/fetchWeather/useGetWeather.ts +++ b/src/hooks/fetchWeather/useGetWeather.ts @@ -1,12 +1,13 @@ -import { useQuery } from "@tanstack/react-query" -import { type WeatherApiResponse } from "./types" +import { useQuery, useQueries } from "@tanstack/react-query" +import { type City, type WeatherApiResponse } from "./types" -const useGetWeather = (lat: number, lon: number, alt?: number) => { - let sanitized_alt = alt - if (alt != undefined) { - sanitized_alt = alt > 9000 ? 9000 : alt - sanitized_alt = sanitized_alt < -500 ? -500 : sanitized_alt - } +const sanitizeAltitude = (alt?: number) => { + if (alt === undefined) return undefined + return Math.min(9000, Math.max(-500, alt)) +} + +export const useGetWeather = (lat: number, lon: number, alt?: number) => { + const sanitized_alt = sanitizeAltitude(alt) const { isLoading, error, data } = useQuery({ queryKey: ["citySearch", `${lat}-${lon}`], @@ -18,5 +19,33 @@ const useGetWeather = (lat: number, lon: number, alt?: number) => { return { isLoading, error, result: data } } - export default useGetWeather + +const fetchWeather = ( + lat: number, + lon: number, + alt?: number +): Promise => { + const sanitizedAlt = sanitizeAltitude(alt) + const url = `https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=${lat}&lon=${lon}${ + sanitizedAlt !== undefined ? `&altitude=${sanitizedAlt}` : "" + }` + return fetch(url).then((res) => res.json() as Promise) +} + +export const useGetWeatherForCities = (cities: City[]) => { + const queries = useQueries({ + queries: cities.map((city) => ({ + queryKey: ["citySearch", `${city.lat}-${city.lon}`], + queryFn: () => fetchWeather(city.lat, city.lon, city.alt), + })), + }) + + const isLoading = queries.some((q) => q.isLoading) + const errors = queries.filter((q) => q.error).map((q) => q.error) + const results = queries + .map((q) => q.data) + .filter((d): d is WeatherApiResponse => d !== undefined) + + return { isLoading, errors, results } +}