From c19bd601742c7d2962697e05e21de1e506392c9c Mon Sep 17 00:00:00 2001 From: "Phyks (Lucas Verney)" Date: Fri, 27 Jul 2018 16:13:16 +0200 Subject: [PATCH] Better management of permissions * Finish the ability to use custom tile server. * Store settings whenever they are changed. * Support new settings for audio/vibrate permission. --- src/components/Map.vue | 9 ++- src/components/PermissionsSwitches.vue | 79 +++++++++++++++++++++++ src/constants.js | 2 + src/i18n/en.json | 14 ++++- src/store/mutations.js | 46 ++++++++++++-- src/views/About.vue | 2 +- src/views/Map.vue | 9 ++- src/views/Onboarding.vue | 43 ++----------- src/views/Settings.vue | 86 ++++++++++++++++++++------ 9 files changed, 221 insertions(+), 69 deletions(-) create mode 100644 src/components/PermissionsSwitches.vue diff --git a/src/components/Map.vue b/src/components/Map.vue index 332ba74..500f292 100644 --- a/src/components/Map.vue +++ b/src/components/Map.vue @@ -107,6 +107,14 @@ export default { this.radiusFromAccuracy > this.markerRadius ); }, + tileServer() { + const tileServerSetting = this.$store.state.settings.tileServer; + if (tileServerSetting in constants.TILE_SERVERS) { + return constants.TILE_SERVERS[tileServerSetting]; + } + const firstColon = tileServerSetting.indexOf(':'); + return tileServerSetting.substring(firstColon + 1); + }, }, data() { return { @@ -118,7 +126,6 @@ export default { maxZoom: constants.MAX_ZOOM, minZoom: constants.MIN_ZOOM, isRecenterButtonShown: false, - tileServer: constants.TILE_SERVERS[this.$store.state.settings.tileServer], unknownMarkerIcon: L.icon({ iconAnchor: [20, 40], iconSize: [40, 40], diff --git a/src/components/PermissionsSwitches.vue b/src/components/PermissionsSwitches.vue new file mode 100644 index 0000000..22019c4 --- /dev/null +++ b/src/components/PermissionsSwitches.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/src/constants.js b/src/constants.js index 158ddbc..eaca419 100644 --- a/src/constants.js +++ b/src/constants.js @@ -13,6 +13,8 @@ import miscIcon from '@/assets/misc.svg'; import obstacleIcon from '@/assets/obstacle.svg'; import potholeIcon from '@/assets/pothole.svg'; +export const VERSION = '0.1'; + export const REPORT_TYPES = { accident: { description: 'reportLabels.accidentDescription', diff --git a/src/i18n/en.json b/src/i18n/en.json index e7df162..607628f 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -1,7 +1,6 @@ { "about": { "availableReportsTitle": "The available reports so far are:", - "geolocationDescription": "As of current version, your precise geolocation is handled within your device and never sent from it to any external service. The map background is downloaded on demand from the tile provider and it has then access to an estimate of the displayed position. If you refuse to share your geolocation, you can still pick a location manually but you will miss some geolocation dependent features.", "license": "It is released under an MIT license (source code). Icons are based on creations from Wikimedia, Vecteezy, Pixabay or Flaticon. The map background is using tiles from Carto.com or Thunderforest, thanks to OpenStreetMap contributors and Leaflet. Collected reports are available under ODbL license. Manual location picking uses the awesome API from adresse.data.gouv.fr.", "summary": "This app lets you track and share issues with bike lanes.", "usage": "How to use", @@ -53,6 +52,14 @@ "retry": "Retry", "spaceBeforeDoublePunctuations": "" }, + "permissions": { + "geolocation": "Geolocation", + "geolocationDescription": "As of current version, your precise geolocation is handled within your device and never sent from it to any external service. The map background is downloaded on demand from the tile provider and it has then access to an estimate of the displayed position. If you refuse to share your geolocation, you can still pick a location manually but you will miss some geolocation dependent features.", + "playSound": "Play sound", + "preventSuspend": "Prevent device from going to sleep", + "preventSuspendDescription": "When the map is displayed, the device will be prevented from going to sleep.", + "vibrate": "Vibrate" + }, "reportCard": { "Reported": "Reported" }, @@ -75,9 +82,10 @@ "potholeDescription": "A pothole in the ground." }, "settings": { + "customTileServer": "Custom tile server", + "customTileServerURL": "Custom tile server URL", + "customTileServerURLHint": "For example: http://tile.thunderforest.com/cycle/{z}/{x}/{y}.png", "locale": "Language", - "preventSuspend": "Prevent device from going to sleep", - "save": "Save", "skipOnboarding": "Skip onboarding", "tileServer": "Map tiles server" }, diff --git a/src/store/mutations.js b/src/store/mutations.js index 628bb43..25412a9 100644 --- a/src/store/mutations.js +++ b/src/store/mutations.js @@ -3,7 +3,7 @@ import Vue from 'vue'; import { messages, getBrowserLocales } from '@/i18n'; import { storageAvailable } from '@/tools'; -import { TILE_SERVERS, DEFAULT_TILE_SERVER } from '@/constants'; +import { DEFAULT_TILE_SERVER, TILE_SERVERS, VERSION } from '@/constants'; import * as types from './mutations-types'; function loadDataFromStorage(name) { @@ -19,6 +19,23 @@ function loadDataFromStorage(name) { } } +function handleMigrations() { + if (!storageAvailable('localStorage')) { + return; + } + const version = loadDataFromStorage('version'); + + // Migration from pre-0.1 to 0.1 + if (version === null) { + const preventSuspend = loadDataFromStorage('preventSuspend'); + if (preventSuspend !== null) { + localStorage.setItem('hasPreventSuspendPermission', JSON.stringify(preventSuspend)); + } + localStorage.removeItem('preventSuspend'); + localStorage.setItem('version', JSON.stringify(VERSION)); + } +} + // Load unsent reports from storage let unsentReports = null; if (storageAvailable('localStorage')) { @@ -27,15 +44,23 @@ if (storageAvailable('localStorage')) { // Load settings from storage let locale = null; -let preventSuspend = null; +let hasGeolocationPermission = null; +let hasPlaySoundPermission = null; +let hasPreventSuspendPermission = null; +let hasVibratePermission = null; let skipOnboarding = null; let tileServer = null; if (storageAvailable('localStorage')) { - preventSuspend = loadDataFromStorage('preventSuspend'); + handleMigrations(); + + hasGeolocationPermission = loadDataFromStorage('hasGeolocationPermission'); + hasPlaySoundPermission = loadDataFromStorage('hasPlaySoundPermission'); + hasPreventSuspendPermission = loadDataFromStorage('hasPreventSuspendPermission'); + hasVibratePermission = loadDataFromStorage('hasVibratePermission'); skipOnboarding = loadDataFromStorage('skipOnboarding'); tileServer = loadDataFromStorage('tileServer'); - if (!TILE_SERVERS[tileServer]) { + if (tileServer && !TILE_SERVERS[tileServer] && !tileServer.startsWith('custom:')) { tileServer = null; } @@ -80,7 +105,18 @@ export const initialState = { unsentReports: unsentReports || [], settings: { locale: locale || 'en', - preventSuspend: preventSuspend || true, + hasGeolocationPermission: ( + hasGeolocationPermission !== null ? hasGeolocationPermission : true + ), + hasPlaySoundPermission: ( + hasPlaySoundPermission !== null ? hasPlaySoundPermission : true + ), + hasPreventSuspendPermission: ( + hasPreventSuspendPermission !== null ? hasPreventSuspendPermission : true + ), + hasVibratePermission: ( + hasVibratePermission !== null ? hasVibratePermission : true + ), skipOnboarding: skipOnboarding || false, tileServer: tileServer || DEFAULT_TILE_SERVER, }, diff --git a/src/views/About.vue b/src/views/About.vue index 44184ee..ba57f75 100644 --- a/src/views/About.vue +++ b/src/views/About.vue @@ -2,7 +2,7 @@ -

{{ $t('about.summary') }}

+

{{ $t('about.summary') }}

{{ $t('about.usage') }}

{{ $t('about.usageDescription') }}

diff --git a/src/views/Map.vue b/src/views/Map.vue index f283b0d..44a4685 100644 --- a/src/views/Map.vue +++ b/src/views/Map.vue @@ -57,7 +57,6 @@ import i18n from '@/i18n'; import store from '@/store'; function handlePositionError(error) { - // TODO: Not translated when changing locale store.dispatch('setLocationError', { error: error.code }); } @@ -220,7 +219,7 @@ export default { this.reportLatLng = null; }, setNoSleep() { - if (this.$store.state.settings.preventSuspend) { + if (this.$store.state.settings.hasPreventSuspendPermission) { this.noSleep = new NoSleep(); this.noSleep.enable(); } @@ -238,7 +237,11 @@ export default { if (this.$route.name !== 'SharedMap') { // Only enable NoSleep in normal map view (with position tracking). this.setNoSleep(); - this.initializePositionWatching(); + if (this.$store.state.settings.hasGeolocationPermission) { + this.initializePositionWatching(); + } else { + this.$store.state.location.error = 1; + } } this.$store.dispatch('fetchReports'); }, diff --git a/src/views/Onboarding.vue b/src/views/Onboarding.vue index 27105a7..03404f8 100644 --- a/src/views/Onboarding.vue +++ b/src/views/Onboarding.vue @@ -23,9 +23,9 @@

{{ $t('intro.checkingPermissions') }}

- - - + + + @@ -44,17 +44,14 @@