Let user pick a location manually, fix for #3.
This commit is contained in:
parent
a67462fab0
commit
edadbd6393
114
src/components/AddressInput.vue
Normal file
114
src/components/AddressInput.vue
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<template>
|
||||||
|
<v-combobox
|
||||||
|
:loading="loading"
|
||||||
|
required
|
||||||
|
:items="items"
|
||||||
|
:search-input.sync="search"
|
||||||
|
:filter="filter"
|
||||||
|
v-model="select"
|
||||||
|
:label="label"
|
||||||
|
:hint="hint"
|
||||||
|
persistent-hint
|
||||||
|
clearable
|
||||||
|
append-icon="my_location"
|
||||||
|
:rules="rules"
|
||||||
|
v-on:input="onInputHandler"
|
||||||
|
></v-combobox>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import fetch from 'isomorphic-fetch';
|
||||||
|
|
||||||
|
const API_ENDPOINT = 'https://api-adresse.data.gouv.fr/search/';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
label: String,
|
||||||
|
hint: String,
|
||||||
|
onInput: Function,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
APIItems: [],
|
||||||
|
search: null,
|
||||||
|
select: null,
|
||||||
|
selectedItem: null,
|
||||||
|
rules: [
|
||||||
|
this.checkValue,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
search(value) {
|
||||||
|
if (value) {
|
||||||
|
this.queryAPI(value);
|
||||||
|
} else {
|
||||||
|
this.APIItems = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
checkValue() {
|
||||||
|
return (this.selectedItem != null) || 'Invalid selection';
|
||||||
|
},
|
||||||
|
queryAPI(value) {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
fetch(`${API_ENDPOINT}?q=${value}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => data.features)
|
||||||
|
.then((data) => {
|
||||||
|
this.loading = false;
|
||||||
|
this.APIItems = [];
|
||||||
|
data.forEach(
|
||||||
|
item => this.APIItems.push(item),
|
||||||
|
);
|
||||||
|
this.APIItems.sort((a, b) => (b.properties.score - a.properties.score));
|
||||||
|
this.APIItems = Array.concat([], this.APIItems);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onInputHandler(value) {
|
||||||
|
if (!value) {
|
||||||
|
this.APIItems = [];
|
||||||
|
} else {
|
||||||
|
// Check against location string
|
||||||
|
const APIItem = this.APIItems.find(item => (
|
||||||
|
item.properties && item.properties.label === value),
|
||||||
|
);
|
||||||
|
if (APIItem) {
|
||||||
|
this.selectedItem = {
|
||||||
|
latlng: {
|
||||||
|
lat: APIItem.geometry.coordinates[1],
|
||||||
|
lng: APIItem.geometry.coordinates[0],
|
||||||
|
},
|
||||||
|
label: APIItem.properties.label,
|
||||||
|
};
|
||||||
|
this.onInput(this.selectedItem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No matched position
|
||||||
|
this.selectedItem = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filter(items) {
|
||||||
|
// No filtering, already handled by the API endpoint.
|
||||||
|
return items;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
items() {
|
||||||
|
if (this.APIItems.length > 0) {
|
||||||
|
return this.APIItems.map((item) => {
|
||||||
|
if (item.properties) {
|
||||||
|
return item.properties.label;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
@ -2,7 +2,7 @@
|
|||||||
export default {
|
export default {
|
||||||
about: {
|
about: {
|
||||||
availableReportsTitle: 'The available reports so far are:',
|
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 <a href="https://carto.com/location-data-services/basemaps/">Carto.com</a> and they have then access to an estimate of the displayed position.',
|
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 <a href="https://carto.com/location-data-services/basemaps/">Carto.com</a> and they have 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 <a href="https://opensource.org/licenses/MIT">MIT license</a> (<a href="https://framagit.org/phyks/cyclassist">source code</a>). Icons are based on creations from Wikimedia and Vecteezy. The map background is using tiles from <a href="https://carto.com/location-data-services/basemaps/">Carto.com</a>, thanks to <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a> and <a href="http://leafletjs.com/">Leaflet</a>. Collected reports are available under <a href="https://opendatacommons.org/licenses/odbl/">ODbL license</a>.',
|
license: 'It is released under an <a href="https://opensource.org/licenses/MIT">MIT license</a> (<a href="https://framagit.org/phyks/cyclassist">source code</a>). Icons are based on creations from Wikimedia and Vecteezy. The map background is using tiles from <a href="https://carto.com/location-data-services/basemaps/">Carto.com</a>, thanks to <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a> and <a href="http://leafletjs.com/">Leaflet</a>. Collected reports are available under <a href="https://opendatacommons.org/licenses/odbl/">ODbL license</a>.',
|
||||||
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',
|
usage: 'How to use',
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
export default {
|
export default {
|
||||||
about: {
|
about: {
|
||||||
availableReportsTitle: "Les signalements disponibles pour l'instant sont :",
|
availableReportsTitle: "Les signalements disponibles pour l'instant sont :",
|
||||||
geolocationDescription: "Dans la version actuelle, votre position est traitée directement par votre appareil et n'est jamais envoyée à un service externe. Le fond de carte est téléchargé à la demande depuis <a href='https://carto.com/location-data-services/basemaps/'>Carto.com</a> et ils ont donc accès à une estimation de la position affichée.",
|
geolocationDescription: "Dans la version actuelle, votre position est traitée directement par votre appareil et n'est jamais envoyée à un service externe. Le fond de carte est téléchargé à la demande depuis <a href='https://carto.com/location-data-services/basemaps/'>Carto.com</a> et ils ont donc accès à une estimation de la position affichée. Si vous refusez le partage de votre géolocalisation, vous pourrez saisir une adresse manuellement à la place mais vous perdrez les fonctionnalités avancées qui reposent sur la géolocalisation.",
|
||||||
license: "Le code source est sous <a href='https://opensource.org/licenses/MIT'>licence MIT license</a> (<a href='https://framagit.org/phyks/cyclassist'>code source</a>). Les icones sont basées sur des travaux de Wikimedia et Vecteezy. Les tuiles de fond de carte proviennent de chez <a href='https://carto.com/location-data-services/basemaps/'>Carto.com</a>, grace aux <a href='https://www.openstreetmap.org/copyright'>contributeurs OpenStreetMap</a> et à <a href='http://leafletjs.com/'>Leaflet</a>. Les signalements sont disponibles sous <a href='https://opendatacommons.org/licenses/odbl/'>licence ODbL</a>.",
|
license: "Le code source est sous <a href='https://opensource.org/licenses/MIT'>licence MIT license</a> (<a href='https://framagit.org/phyks/cyclassist'>code source</a>). Les icones sont basées sur des travaux de Wikimedia et Vecteezy. Les tuiles de fond de carte proviennent de chez <a href='https://carto.com/location-data-services/basemaps/'>Carto.com</a>, grace aux <a href='https://www.openstreetmap.org/copyright'>contributeurs OpenStreetMap</a> et à <a href='http://leafletjs.com/'>Leaflet</a>. Les signalements sont disponibles sous <a href='https://opendatacommons.org/licenses/odbl/'>licence ODbL</a>.",
|
||||||
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',
|
usage: 'Utilisation',
|
||||||
|
@ -21,12 +21,16 @@
|
|||||||
</v-btn>
|
</v-btn>
|
||||||
<ReportDialog v-model="dialog" :lat="reportLat" :lng="reportLng"></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 sm6 offset-sm3 md4 offset-md4 fill-height v-else class="pa-3">
|
||||||
<template v-if="error">
|
<template v-if="error">
|
||||||
<p class="text-xs-center">{{ error }}</p>
|
<p class="text-xs-center">{{ error }}</p>
|
||||||
<p class="text-xs-center">
|
<p class="text-xs-center">
|
||||||
<v-btn role="button" color="blue" dark @click="initializePositionWatching">Retry</v-btn>
|
<v-btn role="button" color="blue" dark @click="initializePositionWatching">Retry</v-btn>
|
||||||
</p>
|
</p>
|
||||||
|
<p>or</p>
|
||||||
|
<p>
|
||||||
|
<AddressInput label="pick a location manually" :onInput="onManualLocationPicker" v-model="manualLocation"></AddressInput>
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<p class="text-xs-center">{{ $t('geolocation.fetching') }}</p>
|
<p class="text-xs-center">{{ $t('geolocation.fetching') }}</p>
|
||||||
@ -39,6 +43,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import NoSleep from 'nosleep.js';
|
import NoSleep from 'nosleep.js';
|
||||||
|
|
||||||
|
import AddressInput from '@/components/AddressInput.vue';
|
||||||
import Map from '@/components/Map.vue';
|
import Map from '@/components/Map.vue';
|
||||||
import ReportCard from '@/components/ReportCard.vue';
|
import ReportCard from '@/components/ReportCard.vue';
|
||||||
import ReportDialog from '@/components/ReportDialog/index.vue';
|
import ReportDialog from '@/components/ReportDialog/index.vue';
|
||||||
@ -47,6 +52,7 @@ import { distance, mockLocation } from '@/tools';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
AddressInput,
|
||||||
Map,
|
Map,
|
||||||
ReportCard,
|
ReportCard,
|
||||||
ReportDialog,
|
ReportDialog,
|
||||||
@ -85,6 +91,7 @@ export default {
|
|||||||
error: null,
|
error: null,
|
||||||
heading: null,
|
heading: null,
|
||||||
latLng: null,
|
latLng: null,
|
||||||
|
manualLocation: null,
|
||||||
noSleep: null,
|
noSleep: null,
|
||||||
positionHistory: [],
|
positionHistory: [],
|
||||||
reportLat: null,
|
reportLat: null,
|
||||||
@ -181,6 +188,9 @@ export default {
|
|||||||
this.dialog = false;
|
this.dialog = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onManualLocationPicker(value) {
|
||||||
|
this.latLng = [value.latlng.lat, value.latlng.lng];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.setNoSleep();
|
this.setNoSleep();
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
|
|
||||||
<v-btn :disabled="!hasGeolocationPermission" role="button" round color="green" dark @click="step += 1">{{ $t('intro.next') }}</v-btn>
|
<v-btn role="button" round color="green" dark @click="step += 1">{{ $t('intro.next') }}</v-btn>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
<v-layout row wrap class="text-xs-center blue lighten-2" v-if="step == 3">
|
<v-layout row wrap class="text-xs-center blue lighten-2" v-if="step == 3">
|
||||||
|
Loading…
Reference in New Issue
Block a user