From 1ca7c9448007fde99bdb73d07c6396029c61f343 Mon Sep 17 00:00:00 2001 From: Gerome Matilla Date: Fri, 12 Jun 2020 18:26:39 +0800 Subject: [PATCH] Add Geolocation for Weather (#25) * add location mode ui option * toggleable locator * toggleable locator with working update * update and cleanup * readme * cleanup --- README.md | 15 +++- css/weather-settings.css | 25 ++++-- index.html | 8 ++ js/weather-screen.js | 23 ++++- js/weather-settings.js | 176 ++++++++++++++++++++++++++++++++++++--- 5 files changed, 225 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 518ca1c..34d4d9f 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ + Web Search Suggestions + Mobile Support with Swipe Gestures + Theme Settings - Change colors on-the-fly -+ Weather Forecast - OpenWeatherMap Integration ++ Weather Forecast - OpenWeatherMap and Geolocation Integration + Search Engine Selection + Dynamic Background + Web Menu with Fuzzy Search @@ -117,10 +117,17 @@ Setting up your OpenWeatherMap credential is a breeze. - OpenWeatherMap is the weather provider, so go to OpenWeatherMap's [website](https://home.openweathermap.org/). - Register, log-in, and then go [here](https://home.openweathermap.org/api_keys) to generate your very own API keys. -+ After getting you API key, you have to get your City ID. -+ Put your API key and City ID in the `Weather Settings`. ++ After this you can choose two locator modes - `Geolocation` and `City`. ++ In City Mode, you have to get your City ID in OpenWeatherMap website. ++ While `Geolocation` mode offers GPS tracking. You don't need to get an ID. Note that you must allow the location permission request. ++ Put your API key in the `Weather Settings`. ++ It's recommended to still put your City ID if you plan to use the `geolocation` mode. + Apply. +**Note:** + ++ If you're using firefox and you're planning to use the `geolocation`, make sure to set the value of `geo.provider.network.url` to `https://location.services.mozilla.com/v1/geolocate?key=test` in `about:config`. **Google changed its policies, so now it requires a valid API key when accessing their geolocation service. This tells us that you need a valid API key in place of** `%GOOGLE_LOCATION_SERVICE_API_KEY%`. - [Citation](https://stackoverflow.com/questions/61032115/unknown-error-acquiring-position-geolocationpositionerror-code-2-firefox-linux). + #### Changing the default search engine Google is the default search engine of the search bar, if you want to change it to DuckDuckGo or something: @@ -151,6 +158,8 @@ The background image changes based on time. + If you're using firefox and blur effect is not enabled, open `about:config`, accept the risks, find `layout.css.backdrop-filter.enabled`, and set it to true to enable it. Refresh the startpage. ++ If you're using firefox and planning to use the geolocation, set the value of `geo.provider.network.url` to `https://location.services.mozilla.com/v1/geolocate?key=test` in `about:config`. + + The code could be better, this is my first time writing a startpage from the ground up. I will improve this from time to time. + Found a bug, error or do you have a suggestion? Feel free to open an issue or pull request. diff --git a/css/weather-settings.css b/css/weather-settings.css index f5dd179..3d9767b 100644 --- a/css/weather-settings.css +++ b/css/weather-settings.css @@ -46,6 +46,8 @@ height: auto; width: 100%; margin-top: 5px; + + transition: all var(--transition-speed); } .weatherSettingsLabels { @@ -55,6 +57,14 @@ text-align: left; } +.hideWeatherSettings { + height: 0; + opacity: 0; + pointer-events: none; + transition: height var(--transition-speed), + opacity var(--transition-speed); +} + .weatherSettingsInputs { height: 32px; width: 100%; @@ -76,7 +86,8 @@ align-items: center; } -#weatherSelectUnits { +#weatherSelectUnits, +#weatherSelectLocator { background: var(--base-container); color: var(--base-color); @@ -85,23 +96,27 @@ appearance: none; } -#weatherSelectUnits:hover { +#weatherSelectUnits:hover, +#weatherSelectLocator:hover { outline: none !important; cursor: pointer; background: var(--base-hover-bg) !important; } -#weatherSelectUnits:focus { +#weatherSelectUnits:focus, +#weatherSelectLocator:focus { outline: none !important; background: var(--base-focus-bg) !important; } -#weatherSelectUnits:active { +#weatherSelectUnits:active, +#weatherSelectLocator:active { outline: none !important; background: var(--base-active-bg) !important; } -#weatherSelectUnits option { +#weatherSelectUnits option, +#weatherSelectLocator option { color: initial; border: none; } diff --git a/index.html b/index.html index e0146fa..75001fe 100644 --- a/index.html +++ b/index.html @@ -101,6 +101,14 @@ +
+ + +
+
diff --git a/js/weather-screen.js b/js/weather-screen.js index 7b5611f..fb16236 100644 --- a/js/weather-screen.js +++ b/js/weather-screen.js @@ -163,7 +163,7 @@ class WeatherScreen { request.send(); } - getWeatherData = (appID, cityID, units) => { + getWeatherDataViaCity = (appID, cityID, units) => { const requestString = `https://api.openweathermap.org/data/2.5/weather?APPID=${appID}&id=${cityID}&units=${units}`; @@ -173,7 +173,7 @@ class WeatherScreen { }; - getForecastData = (appID, cityID, units) => { + getForecastDataViaCity = (appID, cityID, units) => { const requestString = `https://api.openweathermap.org/data/2.5/forecast?APPID=${appID}&id=${cityID}&units=${units}`; @@ -182,6 +182,25 @@ class WeatherScreen { this._fetchOpenWeatherMapDate(requestString, this._processForecastData); } + getWeatherDataViaGeo = (appID, units, lon, lat) => { + + const requestString = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&APPID=${appID}&units=${units}`; + + this._tempSymbol = (units === 'metric') ? '°C' : '°F'; + + this._fetchOpenWeatherMapDate(requestString, this._processWeatherData); + }; + + + getForecastDataViaGeo = (appID, units, lon, lat) => { + + const requestString = `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&APPID=${appID}&units=${units}`; + + this._tempSymbol = (units === 'metric') ? '°C' : '°F'; + + this._fetchOpenWeatherMapDate(requestString, this._processForecastData); + } + _processForecastData = data => { // Empty forecast container to avoid duplication diff --git a/js/weather-settings.js b/js/weather-settings.js index a0470e3..ebf3bab 100644 --- a/js/weather-settings.js +++ b/js/weather-settings.js @@ -7,17 +7,35 @@ class WeatherSettings { this._appID = ''; this._cityID = ''; this._units = ''; + this._locatorMode = ''; + + // Geolocation data + this._origLongitude = 0; + this._origLatitude = 0; + this._watchPositionID = 0; + + this._watchGeoOptions = { + enableHighAccuracy: false, + timeout: 5000, + maximumAge: 0 + }; this._apiKeySet = document.querySelector('#apiKeySet'); this._cityIDSet = document.querySelector('#cityIDSet'); + this._weatherSelectLocator = document.querySelector('#weatherSelectLocator'); this._weatherSelectUnits = document.querySelector('#weatherSelectUnits'); this._weatherSettingsReset = document.querySelector('#weatherSettingsReset'); this._weatherSettingsApply = document.querySelector('#weatherSettingsApply'); - this.getWeatherData = weatherScreen.getWeatherData; - this.getForecastData = weatherScreen.getForecastData; + this._weatherSettingsCityIDGroup = document.querySelector('#weatherSettingsCityID'); + + this._getWeatherDataViaCity = weatherScreen.getWeatherDataViaCity; + this._getForecastDataViaCity = weatherScreen.getForecastDataViaCity; + + this._getWeatherDataViaGeo = weatherScreen.getWeatherDataViaGeo; + this._getForecastDataViaGeo = weatherScreen.getForecastDataViaGeo; this._init(); } @@ -26,6 +44,7 @@ class WeatherSettings { this._updateWeatherSettings(); this._registerWeatherResetOnClickEvent(); this._registerWeatherApplyOnClickEvent(); + this._registerWeatherSelectLocatorOnChangeEvent(); } // Clear credentials @@ -33,6 +52,7 @@ class WeatherSettings { this._localStorage.removeItem('apiKey'); this._localStorage.removeItem('cityID'); this._localStorage.removeItem('units'); + this._localStorage.removeItem('locatorMode'); } // Reset textboxes @@ -40,20 +60,23 @@ class WeatherSettings { this._apiKeySet.value = ''; this._cityIDSet.value = ''; this._weatherSelectUnits.value = 'metric'; + this._weatherSelectLocator.value = 'geolocation'; } // Apply credentials - _applyWeatherSettings = (key, city, units) => { + _applyWeatherSettings = (key, city, units, locator) => { this._localStorage.setItem('apiKey', key); this._localStorage.setItem('cityID', city); this._localStorage.setItem('units', units); + this._localStorage.setItem('locatorMode', locator); } // Update credential variables _updateCredentialVariables = () => { - this._appID = localStorage.getItem('apiKey') || 'API Key'; - this._cityID = localStorage.getItem('cityID') || 'City ID'; - this._units = localStorage.getItem('units') || 'metric'; + this._appID = this._localStorage.getItem('apiKey') || 'API Key'; + this._cityID = this._localStorage.getItem('cityID') || 'City ID'; + this._units = this._localStorage.getItem('units') || 'metric'; + this._locatorMode = this._localStorage.getItem('locatorMode') || 'geolocation'; } // Update textbox placeholders @@ -61,21 +84,145 @@ class WeatherSettings { this._apiKeySet.placeholder = this._appID; this._cityIDSet.placeholder = this._cityID; this._weatherSelectUnits.value = this._units; + this._weatherSelectLocator.value = this._locatorMode; + } + + // Stop geolocating + _stopGeolocating = () => { + + // Unregister the handler + navigator.geolocation.clearWatch(this._watchPositionID); + + // Reset positions + this._origLongitude = 0; + this._origLatitude = 0; + } + + // You denied the permission request + _deniedGeolocation = () => { + + alert(`You denied the request to access your location. As a consequence for your action, ` + + `you need to allow it on your browser's settings if you want to use the geolocation functionality. You can just use the City Mode, though.`); + + } + + // Watch + _watchGeoSuccess = pos => { + + const currentCoord = pos.coords; + + if ((this._origLongitude !== currentCoord.longitude) || (this._origLatitude !== currentCoord.latitude)) { + + console.log('update current position'); + + // Update origPositions + this._origLongitude = currentCoord.longitude; + this._origLatitude = currentCoord.latitude; + + // fetch and update widget + this._getWeatherDataViaGeo(this._appID, this._units, this._origLongitude, this._origLatitude); + this._getForecastDataViaGeo(this._appID, this._units, this._origLongitude, this._origLatitude); + } + } + + // Error + _watchGeoError = err => { + + console.warn('ERROR(' + err.code + '): ' + err.message); + + if (err.code == err.PERMISSION_DENIED) { + + this._deniedGeolocation(); + + } + } + + // Start watching location + _watchGeoPosition = () => { + this._watchPositionID = navigator.geolocation.watchPosition(this._watchGeoSuccess, this._watchGeoError, this._watchGeoOptions); + } + + // Check permission + _checkGeoPermission = () => { + + navigator.permissions.query({name:'geolocation'}).then(result => { + + if ((result.state === 'prompt') || (result.state == 'granted')) { + + this._watchGeoPosition(); + + } else if (result.state === 'denied') { + + alert('Manually enable the geolocation in your browser settings. How? Who knows?'); + + } + + }); + } + + // Locator mode on change event + _weatherSelectLocatorOnChangeEvent = e => { + + this._locatorMode = this._weatherSelectLocator.options[this._weatherSelectLocator.selectedIndex].value; + + if (this._locatorMode === 'geolocation') { + + console.log('geolocation'); + + this._weatherSettingsCityIDGroup.classList.add('hideWeatherSettings'); + + } else if (this._locatorMode === 'city') { + + console.log('city'); + + this._weatherSettingsCityIDGroup.classList.remove('hideWeatherSettings'); + + } + + } + + // Register on change event + _registerWeatherSelectLocatorOnChangeEvent = () => { + + this._weatherSelectLocator.onchange = this._weatherSelectLocatorOnChangeEvent; + } // Update weather settings _updateWeatherSettings = () => { - + // Update cred vars this._updateCredentialVariables(); - // Update weather forecast elements - this.getWeatherData(this._appID, this._cityID, this._units); - this.getForecastData(this._appID, this._cityID, this._units); + if (this._locatorMode === 'geolocation') { + + this._weatherSettingsCityIDGroup.classList.add('hideWeatherSettings'); + + if (navigator.geolocation) { + + this._checkGeoPermission(); + + } else { + + alert(`Oof! It seems your browser doesn't support geolocation.`); + + } + + } else if (this._locatorMode === 'city') { + + this._weatherSettingsCityIDGroup.classList.remove('hideWeatherSettings'); + + // Stop geolocating + this._stopGeolocating(); + + // Update weather forecast elements + this._getWeatherDataViaCity(this._appID, this._cityID, this._units); + this._getForecastDataViaCity(this._appID, this._cityID, this._units); + + } this._deleteWeatherSettingsValue(); this._updateWeatherSettingsPlaceholder(); - } // Reset @@ -83,6 +230,10 @@ class WeatherSettings { // Reset keys this._clearWeatherCredentials(); + // Stop geolocating + this._stopGeolocating(); + + // Update this._updateCredentialVariables(); this._deleteWeatherSettingsValue(); this._updateWeatherSettingsPlaceholder(); @@ -96,7 +247,8 @@ class WeatherSettings { this._applyWeatherSettings( this._apiKeySet.value || this._apiKeySet.placeholder, this._cityIDSet.value || this._cityIDSet.placeholder, - this._weatherSelectUnits.options[this._weatherSelectUnits.selectedIndex].value + this._weatherSelectUnits.options[this._weatherSelectUnits.selectedIndex].value, + this._weatherSelectLocator.options[this._weatherSelectLocator.selectedIndex].value ); this._updateWeatherSettings();