From 5fea0f77695936ab9e4206e13fd18cbaa1022170 Mon Sep 17 00:00:00 2001 From: Mugunth140 Date: Sat, 19 Oct 2024 07:03:19 +0530 Subject: [PATCH] Added search bar on navbar with optimized code --- src/Components/WeatherCard.jsx | 40 ++-- src/Context/index.jsx | 338 ++++++++++++++++----------------- 2 files changed, 186 insertions(+), 192 deletions(-) diff --git a/src/Components/WeatherCard.jsx b/src/Components/WeatherCard.jsx index 2cead6a..5499b51 100644 --- a/src/Components/WeatherCard.jsx +++ b/src/Components/WeatherCard.jsx @@ -22,42 +22,40 @@ const WeatherCard = ({ const [icon, setIcon] = useState(sun); const { time } = useDate(); + const weatherIcons = { + cloud: cloud, + rain: rain, + clear: sun, + thunder: storm, + fog: fog, + snow: snow, + wind: wind, + }; + useEffect(() => { - if (iconString) { - if (iconString.toLowerCase().includes('cloud')) { - setIcon(cloud); - } else if (iconString.toLowerCase().includes('rain')) { - setIcon(rain); - } else if (iconString.toLowerCase().includes('clear')) { - setIcon(sun); - } else if (iconString.toLowerCase().includes('thunder')) { - setIcon(storm); - } else if (iconString.toLowerCase().includes('fog')) { - setIcon(fog); - } else if (iconString.toLowerCase().includes('snow')) { - setIcon(snow); - } else if (iconString.toLowerCase().includes('wind')) { - setIcon(wind); - } - } + const lowerCaseIconString = iconString?.toLowerCase(); + const matchedIcon = Object.keys(weatherIcons).find((key) => + lowerCaseIconString?.includes(key) + ); + setIcon(matchedIcon ? weatherIcons[matchedIcon] : sun); }, [iconString]); const convertTemperature = (temp) => { - return isCelsius ? temp : Math.round((temp * 1.8) + 32); + return isCelsius ? temp : Math.round(temp * 1.8 + 32); }; return (
- weather_icon + weather_icon

{convertTemperature(temperature)} °{isCelsius ? 'C' : 'F'}

{place}
-

{new Date().toDateString()}

-

{time}

+

{new Date().toDateString()}

+

{time}

diff --git a/src/Context/index.jsx b/src/Context/index.jsx index ff084f4..6da6866 100644 --- a/src/Context/index.jsx +++ b/src/Context/index.jsx @@ -1,186 +1,182 @@ import { useContext, createContext, useState, useEffect } from "react"; -import axios from 'axios'; +import axios from "axios"; import toast from "react-hot-toast"; const StateContext = createContext(); export const StateContextProvider = ({ children }) => { - const [weather, setWeather] = useState({}); - const [values, setValues] = useState([]); - const [place, setPlace] = useState('Jaipur'); - const [thisLocation, setLocation] = useState(''); - const [isToastVisible, setToastVisible] = useState(false); - - const fetchWeather = async () => { - const options = { - method: 'GET', - url: 'https://visual-crossing-weather.p.rapidapi.com/forecast', - params: { - aggregateHours: '24', - location: place, - contentType: 'json', - unitGroup: 'metric', - shortColumnNames: 0, - }, - headers: { - 'X-RapidAPI-Key': import.meta.env.VITE_API_KEY, - 'X-RapidAPI-Host': 'visual-crossing-weather.p.rapidapi.com' - } - }; - - try { - const response = await axios.request(options); - const thisData = Object.values(response.data.locations)[0]; - setLocation(thisData.address); - setValues(thisData.values); - setWeather(thisData.values[0]); - localStorage.setItem('weatherData', JSON.stringify({ - location: thisData.address, - weather: thisData.values[0], - values: thisData.values - })); - setToastVisible(false); - } catch (e) { - console.error(e); - if (!isToastVisible) { - toast((t) => ( -

- This place does not exist - -
- ), { - id: 'weather-error', - duration: Infinity, - }); - setToastVisible(true); - } - } + const [weather, setWeather] = useState({}); + const [values, setValues] = useState([]); + const [place, setPlace] = useState("Jaipur"); + const [thisLocation, setLocation] = useState(""); + const [isLoading, setIsLoading] = useState(false); + + const fetchWeather = async () => { + if (place === thisLocation) return; // Avoid unnecessary API calls + + setIsLoading(true); + + const options = { + method: "GET", + url: "https://visual-crossing-weather.p.rapidapi.com/forecast", + params: { + aggregateHours: "24", + location: place, + contentType: "json", + unitGroup: "metric", + shortColumnNames: 0, + }, + headers: { + "X-RapidAPI-Key": import.meta.env.VITE_API_KEY, + "X-RapidAPI-Host": "visual-crossing-weather.p.rapidapi.com", + }, }; - const fetchWeatherByCoords = async (latitude, longitude) => { - const options = { - method: 'GET', - url: 'https://visual-crossing-weather.p.rapidapi.com/forecast', - params: { - aggregateHours: '24', - location: `${latitude},${longitude}`, - contentType: 'json', - unitGroup: 'metric', - shortColumnNames: 0, - }, - headers: { - 'X-RapidAPI-Key': import.meta.env.VITE_API_KEY, - 'X-RapidAPI-Host': 'visual-crossing-weather.p.rapidapi.com', - }, - }; - - try { - const response = await axios.request(options); - const thisData = Object.values(response.data.locations)[0]; - - const geoResponse = await axios.get(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude}&lon=${longitude}`, { - headers: { - 'User-Agent': `Propacity/1.0 (${import.meta.env.VITE_EX_EMAIL})`, - } - }); - const placeName = geoResponse.data.address.city || geoResponse.data.address.town || geoResponse.data.address.village || `${latitude}, ${longitude}`; - - setLocation(placeName); - setValues(thisData.values); - setWeather(thisData.values[0]); - setPlace(placeName); - - localStorage.setItem('weatherData', JSON.stringify({ - location: placeName, - weather: thisData.values[0], - values: thisData.values, - })); - setToastVisible(false); - } catch (e) { - console.error(e); - if (!isToastVisible) { - toast((t) => ( -
- Failed to fetch weather data for your location - -
- ), { - id: 'coords-error', - duration: Infinity, - }); - setToastVisible(true); - } - } + try { + const response = await axios.request(options); + const thisData = Object.values(response.data.locations)[0]; + setLocation(thisData.address); + setValues(thisData.values); + setWeather(thisData.values[0]); + localStorage.setItem( + "weatherData", + JSON.stringify({ + location: thisData.address, + weather: thisData.values[0], + values: thisData.values, + }) + ); + toast.dismiss("weather-error"); + } catch (e) { + console.error(e); + showToast("This place does not exist", "weather-error"); + } finally { + setIsLoading(false); + } + }; + + const fetchWeatherByCoords = async (latitude, longitude) => { + setIsLoading(true); + + const options = { + method: "GET", + url: "https://visual-crossing-weather.p.rapidapi.com/forecast", + params: { + aggregateHours: "24", + location: `${latitude},${longitude}`, + contentType: "json", + unitGroup: "metric", + shortColumnNames: 0, + }, + headers: { + "X-RapidAPI-Key": import.meta.env.VITE_API_KEY, + "X-RapidAPI-Host": "visual-crossing-weather.p.rapidapi.com", + }, }; - useEffect(() => { - fetchWeather(); - }, [place]); - - useEffect(() => { - const cachedData = localStorage.getItem('weatherData'); - if (cachedData) { - const { location, weather, values } = JSON.parse(cachedData); - setLocation(location); - setWeather(weather); - setValues(values); - setPlace(location); + try { + const response = await axios.request(options); + const thisData = Object.values(response.data.locations)[0]; + + const geoResponse = await axios.get( + `https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude}&lon=${longitude}`, + { + headers: { + "User-Agent": `Propacity/1.0 (${import.meta.env.VITE_EX_EMAIL})`, + }, } - }, []); - - return ( - - {children} - + ); + const placeName = + geoResponse.data.address.city || + geoResponse.data.address.town || + geoResponse.data.address.village || + `${latitude}, ${longitude}`; + + setLocation(placeName); + setValues(thisData.values); + setWeather(thisData.values[0]); + setPlace(placeName); + + localStorage.setItem( + "weatherData", + JSON.stringify({ + location: placeName, + weather: thisData.values[0], + values: thisData.values, + }) + ); + toast.dismiss("coords-error"); + } catch (e) { + console.error(e); + showToast("Failed to fetch weather data for your location", "coords-error"); + } finally { + setIsLoading(false); + } + }; + + const showToast = (message, toastId) => { + if (toast.isActive(toastId)) return; // Only show if no other active toast + + toast( + (t) => ( +
+ {message} + +
+ ), + { + id: toastId, + duration: Infinity, + } ); + }; + + useEffect(() => { + const cachedData = localStorage.getItem("weatherData"); + if (cachedData) { + const { location, weather, values } = JSON.parse(cachedData); + setLocation(location); + setWeather(weather); + setValues(values); + setPlace(location); + } else { + fetchWeather(); // Fetch if no cache + } + }, []); + + return ( + + {children} + + ); }; export const useStateContext = () => useContext(StateContext);