Add Geolocation for Weather (#25)

* add location mode ui option

* toggleable locator

* toggleable locator with working update

* update and cleanup

* readme

* cleanup
This commit is contained in:
Gerome Matilla 2020-06-12 18:26:39 +08:00 committed by GitHub
parent 57d8884baf
commit 1ca7c94480
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 225 additions and 22 deletions

View File

@ -28,7 +28,7 @@
+ Web Search Suggestions + Web Search Suggestions
+ Mobile Support with Swipe Gestures + Mobile Support with Swipe Gestures
+ Theme Settings - Change colors on-the-fly + Theme Settings - Change colors on-the-fly
+ Weather Forecast - OpenWeatherMap Integration + Weather Forecast - OpenWeatherMap and Geolocation Integration
+ Search Engine Selection + Search Engine Selection
+ Dynamic Background + Dynamic Background
+ Web Menu with Fuzzy Search + 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/). - 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. - 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. + After this you can choose two locator modes - `Geolocation` and `City`.
+ Put your API key and City ID in the `Weather Settings`. + 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. + 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 #### 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: 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 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. + 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. + Found a bug, error or do you have a suggestion? Feel free to open an issue or pull request.

View File

@ -46,6 +46,8 @@
height: auto; height: auto;
width: 100%; width: 100%;
margin-top: 5px; margin-top: 5px;
transition: all var(--transition-speed);
} }
.weatherSettingsLabels { .weatherSettingsLabels {
@ -55,6 +57,14 @@
text-align: left; text-align: left;
} }
.hideWeatherSettings {
height: 0;
opacity: 0;
pointer-events: none;
transition: height var(--transition-speed),
opacity var(--transition-speed);
}
.weatherSettingsInputs { .weatherSettingsInputs {
height: 32px; height: 32px;
width: 100%; width: 100%;
@ -76,7 +86,8 @@
align-items: center; align-items: center;
} }
#weatherSelectUnits { #weatherSelectUnits,
#weatherSelectLocator {
background: var(--base-container); background: var(--base-container);
color: var(--base-color); color: var(--base-color);
@ -85,23 +96,27 @@
appearance: none; appearance: none;
} }
#weatherSelectUnits:hover { #weatherSelectUnits:hover,
#weatherSelectLocator:hover {
outline: none !important; outline: none !important;
cursor: pointer; cursor: pointer;
background: var(--base-hover-bg) !important; background: var(--base-hover-bg) !important;
} }
#weatherSelectUnits:focus { #weatherSelectUnits:focus,
#weatherSelectLocator:focus {
outline: none !important; outline: none !important;
background: var(--base-focus-bg) !important; background: var(--base-focus-bg) !important;
} }
#weatherSelectUnits:active { #weatherSelectUnits:active,
#weatherSelectLocator:active {
outline: none !important; outline: none !important;
background: var(--base-active-bg) !important; background: var(--base-active-bg) !important;
} }
#weatherSelectUnits option { #weatherSelectUnits option,
#weatherSelectLocator option {
color: initial; color: initial;
border: none; border: none;
} }

View File

@ -101,6 +101,14 @@
<input type='text' class='weatherSettingsInputs' id='apiKeySet' autocomplete='off' placeholder='API KEY'/> <input type='text' class='weatherSettingsInputs' id='apiKeySet' autocomplete='off' placeholder='API KEY'/>
</div> </div>
<div class='weatherSettingsGroups' id='weatherSettingsUnitsSelect'>
<label for='weatherSelectUnits' class='weatherSettingsLabels'>Locator Mode</label>
<select class='weatherSettingsInputs' id='weatherSelectLocator'>
<option value='geolocation'>Geolocation</option>
<option value='city'>City</option>
</select>
</div>
<div class='weatherSettingsGroups' id='weatherSettingsCityID'> <div class='weatherSettingsGroups' id='weatherSettingsCityID'>
<label for='cityIDSet' class='weatherSettingsLabels'>City ID</label> <label for='cityIDSet' class='weatherSettingsLabels'>City ID</label>
<input type='text' class='weatherSettingsInputs' id='cityIDSet' autocomplete='off' placeholder='City ID'/> <input type='text' class='weatherSettingsInputs' id='cityIDSet' autocomplete='off' placeholder='City ID'/>

View File

@ -163,7 +163,7 @@ class WeatherScreen {
request.send(); 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}`; 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}`; 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); 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 => { _processForecastData = data => {
// Empty forecast container to avoid duplication // Empty forecast container to avoid duplication

View File

@ -7,17 +7,35 @@ class WeatherSettings {
this._appID = ''; this._appID = '';
this._cityID = ''; this._cityID = '';
this._units = ''; 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._apiKeySet = document.querySelector('#apiKeySet');
this._cityIDSet = document.querySelector('#cityIDSet'); this._cityIDSet = document.querySelector('#cityIDSet');
this._weatherSelectLocator = document.querySelector('#weatherSelectLocator');
this._weatherSelectUnits = document.querySelector('#weatherSelectUnits'); this._weatherSelectUnits = document.querySelector('#weatherSelectUnits');
this._weatherSettingsReset = document.querySelector('#weatherSettingsReset'); this._weatherSettingsReset = document.querySelector('#weatherSettingsReset');
this._weatherSettingsApply = document.querySelector('#weatherSettingsApply'); this._weatherSettingsApply = document.querySelector('#weatherSettingsApply');
this.getWeatherData = weatherScreen.getWeatherData; this._weatherSettingsCityIDGroup = document.querySelector('#weatherSettingsCityID');
this.getForecastData = weatherScreen.getForecastData;
this._getWeatherDataViaCity = weatherScreen.getWeatherDataViaCity;
this._getForecastDataViaCity = weatherScreen.getForecastDataViaCity;
this._getWeatherDataViaGeo = weatherScreen.getWeatherDataViaGeo;
this._getForecastDataViaGeo = weatherScreen.getForecastDataViaGeo;
this._init(); this._init();
} }
@ -26,6 +44,7 @@ class WeatherSettings {
this._updateWeatherSettings(); this._updateWeatherSettings();
this._registerWeatherResetOnClickEvent(); this._registerWeatherResetOnClickEvent();
this._registerWeatherApplyOnClickEvent(); this._registerWeatherApplyOnClickEvent();
this._registerWeatherSelectLocatorOnChangeEvent();
} }
// Clear credentials // Clear credentials
@ -33,6 +52,7 @@ class WeatherSettings {
this._localStorage.removeItem('apiKey'); this._localStorage.removeItem('apiKey');
this._localStorage.removeItem('cityID'); this._localStorage.removeItem('cityID');
this._localStorage.removeItem('units'); this._localStorage.removeItem('units');
this._localStorage.removeItem('locatorMode');
} }
// Reset textboxes // Reset textboxes
@ -40,20 +60,23 @@ class WeatherSettings {
this._apiKeySet.value = ''; this._apiKeySet.value = '';
this._cityIDSet.value = ''; this._cityIDSet.value = '';
this._weatherSelectUnits.value = 'metric'; this._weatherSelectUnits.value = 'metric';
this._weatherSelectLocator.value = 'geolocation';
} }
// Apply credentials // Apply credentials
_applyWeatherSettings = (key, city, units) => { _applyWeatherSettings = (key, city, units, locator) => {
this._localStorage.setItem('apiKey', key); this._localStorage.setItem('apiKey', key);
this._localStorage.setItem('cityID', city); this._localStorage.setItem('cityID', city);
this._localStorage.setItem('units', units); this._localStorage.setItem('units', units);
this._localStorage.setItem('locatorMode', locator);
} }
// Update credential variables // Update credential variables
_updateCredentialVariables = () => { _updateCredentialVariables = () => {
this._appID = localStorage.getItem('apiKey') || 'API Key'; this._appID = this._localStorage.getItem('apiKey') || 'API Key';
this._cityID = localStorage.getItem('cityID') || 'City ID'; this._cityID = this._localStorage.getItem('cityID') || 'City ID';
this._units = localStorage.getItem('units') || 'metric'; this._units = this._localStorage.getItem('units') || 'metric';
this._locatorMode = this._localStorage.getItem('locatorMode') || 'geolocation';
} }
// Update textbox placeholders // Update textbox placeholders
@ -61,21 +84,145 @@ class WeatherSettings {
this._apiKeySet.placeholder = this._appID; this._apiKeySet.placeholder = this._appID;
this._cityIDSet.placeholder = this._cityID; this._cityIDSet.placeholder = this._cityID;
this._weatherSelectUnits.value = this._units; 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 // Update weather settings
_updateWeatherSettings = () => { _updateWeatherSettings = () => {
// Update cred vars // Update cred vars
this._updateCredentialVariables(); this._updateCredentialVariables();
// Update weather forecast elements if (this._locatorMode === 'geolocation') {
this.getWeatherData(this._appID, this._cityID, this._units);
this.getForecastData(this._appID, this._cityID, this._units); 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._deleteWeatherSettingsValue();
this._updateWeatherSettingsPlaceholder(); this._updateWeatherSettingsPlaceholder();
} }
// Reset // Reset
@ -83,6 +230,10 @@ class WeatherSettings {
// Reset keys // Reset keys
this._clearWeatherCredentials(); this._clearWeatherCredentials();
// Stop geolocating
this._stopGeolocating();
// Update
this._updateCredentialVariables(); this._updateCredentialVariables();
this._deleteWeatherSettingsValue(); this._deleteWeatherSettingsValue();
this._updateWeatherSettingsPlaceholder(); this._updateWeatherSettingsPlaceholder();
@ -96,7 +247,8 @@ class WeatherSettings {
this._applyWeatherSettings( this._applyWeatherSettings(
this._apiKeySet.value || this._apiKeySet.placeholder, this._apiKeySet.value || this._apiKeySet.placeholder,
this._cityIDSet.value || this._cityIDSet.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(); this._updateWeatherSettings();