From fba82d1f5590493ae0f41e67dcdcdf8a02cab047 Mon Sep 17 00:00:00 2001 From: JakubRN Date: Tue, 2 Feb 2021 18:31:35 +0100 Subject: [PATCH] Add browser geolocation --- src/components/App.js | 96 +++++++++++++++++++++++++++++++------- src/components/App.test.js | 6 ++- 2 files changed, 83 insertions(+), 19 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 50dfaa6..82b39b5 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -6,33 +6,93 @@ import CssBaseline from "@material-ui/core/CssBaseline"; import Weather from "./Weather"; import NavBar from "./NavBar"; +const geolocationOptions = { + enableHighAccuracy: false, + timeout: 5000, + maximumAge: 0 +}; + export default function App() { - const [city, setCity] = useState("Eldoret"); + const [geoInitialized, setGeoInitialized] = useState(false); + const [city, setCity] = useState(localStorage.getItem("city") || "Eldoret"); const [error, setError] = useState(null); - const [currentWeather, setCurrentWeather] = useState({}); - const [forecast, setForecast] = useState([]); + const [currentWeather, setCurrentWeather] = useState( + JSON.parse(localStorage.getItem("weather")) || {} + ); + const [forecast, setForecast] = useState( + JSON.parse(localStorage.getItem("forecast")) || [] + ); - useEffect(() => { - getWeather(city) + function geolocationGranted(pos) { + fetch( + `${process.env.REACT_APP_API_URL}/weather/?lat=${pos.coords.latitude}&lon=${pos.coords.longitude}&units=metric&APPID=${process.env.REACT_APP_API_KEY}` + ) + .then(res => handleResponse(res)) .then(weather => { - setCurrentWeather(weather); + setCity(weather.name); setError(null); + setGeoInitialized(true); }) .catch(err => { - setError(err.message); + setGeoInitialized(true); }); - }, [city, error]); + } + function geolocationDenied(err) { + console.warn(`ERROR(${err.code}): ${err.message}`); + } useEffect(() => { - getForecast(city) - .then(data => { - setForecast(data); - setError(null); - }) - .catch(err => { - setError(err.message); - }); - }, [city, error]); + if ("geolocation" in navigator) { + navigator.permissions + .query({ name: "geolocation" }) + .then(result => { + if (result.state === "granted" || result.state === "prompt") { + navigator.geolocation.getCurrentPosition( + geolocationGranted, + geolocationDenied, + geolocationOptions + ); + } else { + setGeoInitialized(true); + } + }) + .catch(err => { + setGeoInitialized(true); + }); + } else { + setGeoInitialized(true); + } + }, []); + + useEffect(() => { + localStorage.setItem("weather", JSON.stringify(currentWeather)); + }, [currentWeather]); + + useEffect(() => { + localStorage.setItem("forecast", JSON.stringify(forecast)); + }, [forecast]); + + useEffect(() => { + if (geoInitialized && city !== localStorage.getItem("city")) { + getWeather(city) + .then(weather => { + setCurrentWeather(weather); + setError(null); + }) + .catch(err => { + setError(err.message); + }); + getForecast(city) + .then(data => { + setForecast(data); + setError(null); + }) + .catch(err => { + setError(err.message); + }); + localStorage.setItem("city", city); + } + }, [city, error, geoInitialized]); const handleCityChange = city => { setCity(city); @@ -91,7 +151,7 @@ function handleResponse(response) { if (response.ok) { return response.json(); } else { - throw new Error("Error: Location " + response.statusText.toLowerCase()); + throw new Error("Error: " + response.statusText.toLowerCase()); } } diff --git a/src/components/App.test.js b/src/components/App.test.js index 082cd9b..aa8f29c 100644 --- a/src/components/App.test.js +++ b/src/components/App.test.js @@ -15,8 +15,12 @@ describe("", () => { beforeAll(() => { process.env.REACT_APP_API_URL = "https://api.openweathermap.org/data/2.5"; process.env.REACT_APP_API_KEY = "some-api-key"; + localStorage.clear(); + }); + beforeEach(() => { + localStorage.clear(); + jest.spyOn(window, "fetch"); }); - beforeEach(() => jest.spyOn(window, "fetch")); afterEach(() => jest.restoreAllMocks()); test("fetches and then renders the current weather and forecast", async () => {