Add the ability to add a report anywhere through long press + preferences section to set the locale

This commit is contained in:
Lucas Verney 2018-06-28 14:40:56 +02:00
parent e682c9818e
commit a4564ad053
10 changed files with 169 additions and 22 deletions

View File

@ -12,8 +12,14 @@
<v-icon>more_vert</v-icon> <v-icon>more_vert</v-icon>
</v-btn> </v-btn>
<v-list> <v-list>
<v-list-tile @click=""> <v-list-tile @click="goToMap">
<v-list-tile-title @click="goToAbout">{{ $t("menu.About") }}</v-list-tile-title> <v-list-tile-title>{{ $t("menu.Map") }}</v-list-tile-title>
</v-list-tile>
<v-list-tile @click="goToAbout">
<v-list-tile-title>{{ $t("menu.About") }}</v-list-tile-title>
</v-list-tile>
<v-list-tile @click="goToSettings">
<v-list-tile-title>{{ $t("menu.Settings") }}</v-list-tile-title>
</v-list-tile> </v-list-tile>
</v-list> </v-list>
</v-menu> </v-menu>
@ -36,6 +42,12 @@ export default {
goToAbout() { goToAbout() {
this.$router.push({ name: 'About' }); this.$router.push({ name: 'About' });
}, },
goToMap() {
this.$router.push({ name: 'Map' });
},
goToSettings() {
this.$router.push({ name: 'Settings' });
},
}, },
}; };
</script> </script>

View File

@ -1,9 +1,12 @@
<template> <template>
<v-container fluid> <v-container fluid>
<v-layout row> <v-layout row>
<v-flex xs6 offset-xs3> <v-flex xs12 sm6 offset-sm3>
<p>{{ $t('about.summary') }}</p> <p>{{ $t('about.summary') }}</p>
<h2 class="body-2">{{ $t('about.usage') }}</h2>
<p>{{ $t('about.usageDescription') }}</p>
<h2 class="body-2">{{ $t('about.availableReportsTitle') }}</h2> <h2 class="body-2">{{ $t('about.availableReportsTitle') }}</h2>
<ul class="ml-3"> <ul class="ml-3">
<li><strong>{{ $t('reportLabels.gcum') }}</strong>{{ $t('misc.spaceBeforeDoublePunctuations') }}: {{ $t('about.gcumDescription') }}</li> <li><strong>{{ $t('reportLabels.gcum') }}</strong>{{ $t('misc.spaceBeforeDoublePunctuations') }}: {{ $t('about.gcumDescription') }}</li>

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="fill-height fill-width"> <div class="fill-height fill-width">
<v-lmap :center="latlng" :zoom="this.zoom" :minZoom="this.minZoom" :maxZoom="this.maxZoom" :options="{ zoomControl: false }"> <v-lmap :center="latlng" :zoom="this.zoom" :minZoom="this.minZoom" :maxZoom="this.maxZoom" :options="{ zoomControl: false }" @contextmenu="handleLongPress">
<v-ltilelayer :url="tileServer" :attribution="attribution"></v-ltilelayer> <v-ltilelayer :url="tileServer" :attribution="attribution"></v-ltilelayer>
<v-lts v-if="heading" :lat-lng="latlng" :options="markerOptions"></v-lts> <v-lts v-if="heading" :lat-lng="latlng" :options="markerOptions"></v-lts>
@ -44,6 +44,7 @@ export default {
lat: Number, lat: Number,
lng: Number, lng: Number,
markers: Array, markers: Array,
onLongPress: Function,
}, },
computed: { computed: {
radiusFromAccuracy() { radiusFromAccuracy() {
@ -84,6 +85,13 @@ export default {
tileServer: constants.TILE_SERVER, tileServer: constants.TILE_SERVER,
}; };
}, },
methods: {
handleLongPress(event) {
if (this.onLongPress) {
this.onLongPress(event.latlng);
}
},
},
}; };
</script> </script>

View File

@ -7,13 +7,17 @@ export default {
license: 'It is released under an <a href="https://opensource.org/licenses/MIT">MIT license</a>. The map background is using tiles from <a href="https://www.opencyclemap.org/docs/">OpenCycleMap</a>, thanks to <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a> and <a href="http://leafletjs.com/">Leaflet</a>.', license: 'It is released under an <a href="https://opensource.org/licenses/MIT">MIT license</a>. The map background is using tiles from <a href="https://www.opencyclemap.org/docs/">OpenCycleMap</a>, thanks to <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a> and <a href="http://leafletjs.com/">Leaflet</a>.',
potholeDescription: 'A pothole in the ground.', potholeDescription: 'A pothole in the ground.',
summary: 'This app lets you track and share issues with bike lanes.', summary: 'This app lets you track and share issues with bike lanes.',
usage: 'How to use',
usageDescription: 'Use the button in the lower right corner to add a new report at your current location. To add a report elsewhere, do a long press (or right click) where you want the report to be shown.',
}, },
geolocation: { geolocation: {
fetching: 'Fetching current position…', fetching: 'Fetching current position…',
unavailable: 'Sorry, geolocation is not available in your browser.', unavailable: 'Sorry, geolocation is not available in your browser.',
}, },
menu: { menu: {
About: 'About', About: 'Help',
Map: 'Map',
Settings: 'Settings',
}, },
misc: { misc: {
spaceBeforeDoublePunctuations: '', spaceBeforeDoublePunctuations: '',
@ -23,4 +27,8 @@ export default {
interrupt: 'Interruption', interrupt: 'Interruption',
pothole: 'Pothole', pothole: 'Pothole',
}, },
settings: {
locale: 'Language',
save: 'Save',
},
}; };

View File

@ -7,13 +7,17 @@ export default {
license: "Le code source est sous <a href='https://opensource.org/licenses/MIT'>licence MIT license</a>. Les tuiles de fond de carte proviennent de chez <a href='https://www.opencyclemap.org/docs/'>OpenCycleMap</a>, grace aux <a href='https://www.openstreetmap.org/copyright'>contributeurs OpenStreetMap</a> et à <a href='http://leafletjs.com/'>Leaflet</a>.", license: "Le code source est sous <a href='https://opensource.org/licenses/MIT'>licence MIT license</a>. Les tuiles de fond de carte proviennent de chez <a href='https://www.opencyclemap.org/docs/'>OpenCycleMap</a>, grace aux <a href='https://www.openstreetmap.org/copyright'>contributeurs OpenStreetMap</a> et à <a href='http://leafletjs.com/'>Leaflet</a>.",
potholeDescription: 'Un nid de poule dans la route.', potholeDescription: 'Un nid de poule dans la route.',
summary: 'Cette application vous permet de signaler et de partager des problèmes avec les itinéraires cyclables.', summary: 'Cette application vous permet de signaler et de partager des problèmes avec les itinéraires cyclables.',
usage: 'Utilisation',
usageDescription: "Utilisez le bouton en bas à droite pour ajouter un signalement à votre emplacement actuel. Pour ajouter un signalement ailleurs, faites un appui long (ou clic droit) à l'emplacement souhaité sur la carte.",
}, },
geolocation: { geolocation: {
fetching: 'En attente de votre position…', fetching: 'En attente de votre position…',
unavailable: "Désolé, la géolocalisation n'est pas disponible dans votre navigateur.", unavailable: "Désolé, la géolocalisation n'est pas disponible dans votre navigateur.",
}, },
menu: { menu: {
About: 'À-propos', About: 'Aide',
Map: 'Carte',
Settings: 'Préférences',
}, },
misc: { misc: {
spaceBeforeDoublePunctuations: ' ', spaceBeforeDoublePunctuations: ' ',
@ -23,4 +27,8 @@ export default {
interrupt: 'Interruption', interrupt: 'Interruption',
pothole: 'Nid de poule', pothole: 'Nid de poule',
}, },
settings: {
locale: 'Langue',
save: 'Sauver',
},
}; };

View File

@ -1,6 +1,8 @@
import Vue from 'vue'; import Vue from 'vue';
import VueI18n from 'vue-i18n'; import VueI18n from 'vue-i18n';
import { storageAvailable } from '@/tools';
import en from './en'; import en from './en';
import fr from './fr'; import fr from './fr';
@ -31,22 +33,30 @@ export function getBrowserLocales() {
return locales; return locales;
} }
const messages = { export const messages = {
en, en,
fr, fr,
}; };
let locale = null;
if (storageAvailable('localStorage')) {
locale = localStorage.getItem('i18nSetting');
if (!messages[locale]) {
locale = null;
}
} else {
// Get best matching locale from browser
const locales = getBrowserLocales(); const locales = getBrowserLocales();
let locale = 'en'; // Safe default
// Get best matching locale
for (let i = 0; i < locales.length; i += 1) { for (let i = 0; i < locales.length; i += 1) {
if (messages[locales[i]]) { if (messages[locales[i]]) {
locale = locales[i]; locale = locales[i];
break; // Break at first matching locale break; // Break at first matching locale
} }
} }
}
if (!locale) {
locale = 'en'; // Safe default
}
export default new VueI18n({ export default new VueI18n({
locale, locale,

View File

@ -2,20 +2,26 @@ import Vue from 'vue';
import Router from 'vue-router'; import Router from 'vue-router';
import About from '@/components/About.vue'; import About from '@/components/About.vue';
import Map from '@/views/Map.vue'; import Map from '@/views/Map.vue';
import Settings from '@/views/Settings.vue';
Vue.use(Router); Vue.use(Router);
export default new Router({ export default new Router({
routes: [ routes: [
{
path: '/about',
name: 'About',
component: About,
},
{ {
path: '/', path: '/',
name: 'Map', name: 'Map',
component: Map, component: Map,
}, },
{ {
path: '/about', path: '/settings',
name: 'About', name: 'Settings',
component: About, component: Settings,
}, },
], ],
}); });

View File

@ -30,3 +30,27 @@ export function mockLocation() {
}, },
}; };
} }
export function storageAvailable(type) {
let storage;
try {
storage = window[type];
const x = '__storage_test__';
storage.setItem(x, x);
storage.removeItem(x);
return true;
} catch (e) {
return e instanceof DOMException && (
// everything except Firefox
e.code === 22 ||
// Firefox
e.code === 1014 ||
// test name field too, because code might not be present
// everything except Firefox
e.name === 'QuotaExceededError' ||
// Firefox
e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
// acknowledge QuotaExceededError only if there's something already stored
storage.length !== 0;
}
}

View File

@ -2,7 +2,7 @@
<v-container fluid fill-height class="no-padding"> <v-container fluid fill-height class="no-padding">
<v-layout row wrap fill-height> <v-layout row wrap fill-height>
<v-flex xs12 fill-height v-if="lat && lng"> <v-flex xs12 fill-height v-if="lat && lng">
<Map :lat="lat" :lng="lng" :heading="heading" :accuracy="accuracy" :markers="reportsMarkers"></Map> <Map :lat="lat" :lng="lng" :heading="heading" :accuracy="accuracy" :markers="reportsMarkers" :onLongPress="showReportDialog"></Map>
<v-btn <v-btn
fixed fixed
dark dark
@ -11,11 +11,11 @@
right right
color="orange" color="orange"
class="overlayButton" class="overlayButton"
@click.native.stop="dialog = !dialog" @click.native.stop="() => showReportDialog()"
> >
<v-icon>report_problem</v-icon> <v-icon>report_problem</v-icon>
</v-btn> </v-btn>
<ReportDialog v-model="dialog" :lat="lat" :lng="lng"></ReportDialog> <ReportDialog v-model="dialog" :lat="reportLat" :lng="reportLng"></ReportDialog>
</v-flex> </v-flex>
<v-flex xs12 fill-height v-else class="pa-3"> <v-flex xs12 fill-height v-else class="pa-3">
<template v-if="error"> <template v-if="error">
@ -49,10 +49,12 @@ export default {
this.initializePositionWatching(); this.initializePositionWatching();
this.setNoSleep(); this.setNoSleep();
this.$store.dispatch('fetchReports'); this.$store.dispatch('fetchReports');
window.addEventListener('keydown', this.hideReportDialogOnEsc);
}, },
beforeDestroy() { beforeDestroy() {
this.disableNoSleep(); this.disableNoSleep();
this.disablePositionWatching(); this.disablePositionWatching();
window.removeEventListener('keydown', this.hideReportDialogOnEsc);
}, },
computed: { computed: {
reportsMarkers() { reportsMarkers() {
@ -72,6 +74,8 @@ export default {
lat: null, lat: null,
lng: null, lng: null,
noSleep: null, noSleep: null,
reportLat: null,
reportLng: null,
watchID: null, watchID: null,
}; };
}, },
@ -137,6 +141,27 @@ export default {
this.noSleep.disable(); this.noSleep.disable();
} }
}, },
showReportDialog(latlng) {
if (latlng) {
this.reportLat = latlng.lat;
this.reportLng = latlng.lng;
} else {
this.reportLat = this.lat;
this.reportLng = this.lng;
}
this.dialog = !this.dialog;
},
hideReportDialogOnEsc(event) {
let isEscape = false;
if ('key' in event) {
isEscape = (event.key === 'Escape' || event.key === 'Esc');
} else {
isEscape = (event.keyCode === 27);
}
if (isEscape) {
this.dialog = false;
}
},
}, },
}; };
</script> </script>

43
src/views/Settings.vue Normal file
View File

@ -0,0 +1,43 @@
<template>
<v-container fluid class="no-padding">
<v-layout row wrap>
<v-flex xs12 class="text-xs-center">
<h2>{{ $t('menu.Settings') }}</h2>
<form>
<v-select
:items="i18nItems"
v-model="i18nSelect"
:label="$t('settings.locale')"
required
></v-select>
<v-btn @click="submit">{{ $t('settings.save') }}</v-btn>
</form>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
import Vue from 'vue';
import { messages } from '@/i18n';
import { storageAvailable } from '@/tools';
export default {
data() {
return {
i18nItems: Object.keys(messages),
i18nSelect: this.$i18n.locale,
};
},
methods: {
submit() {
if (storageAvailable('localStorage')) {
localStorage.setItem('i18nSetting', this.i18nSelect);
}
this.$i18n.locale = this.i18nSelect;
},
},
};
</script>