2018-06-25 18:29:57 +02:00
|
|
|
<template>
|
|
|
|
<v-container fluid fill-height class="no-padding">
|
|
|
|
<v-layout row wrap fill-height>
|
2018-07-01 22:02:54 +02:00
|
|
|
<v-flex xs12 fill-height v-if="latLng">
|
|
|
|
<Map :latLng="mapLatLng" :positionLatLng="latLng" :heading="heading" :accuracy="accuracy" :markers="reportsMarkers" :onLongPress="showReportDialog" :onMoveEnd="onMapMoveEnd" :onMoveStart="onMapMoveStart"></Map>
|
|
|
|
<v-btn
|
|
|
|
fixed
|
|
|
|
dark
|
|
|
|
fab
|
|
|
|
bottom
|
|
|
|
left
|
|
|
|
color="blue"
|
|
|
|
class="overlayButton"
|
|
|
|
v-if="hasMapMoved"
|
|
|
|
@click.native.stop="recenterMap"
|
|
|
|
>
|
|
|
|
<v-icon>my_location</v-icon>
|
|
|
|
</v-btn>
|
2018-06-25 18:29:57 +02:00
|
|
|
<v-btn
|
|
|
|
fixed
|
|
|
|
dark
|
|
|
|
fab
|
|
|
|
bottom
|
|
|
|
right
|
|
|
|
color="orange"
|
|
|
|
class="overlayButton"
|
2018-06-28 14:40:56 +02:00
|
|
|
@click.native.stop="() => showReportDialog()"
|
2018-06-25 18:29:57 +02:00
|
|
|
>
|
|
|
|
<v-icon>report_problem</v-icon>
|
|
|
|
</v-btn>
|
2018-06-28 14:40:56 +02:00
|
|
|
<ReportDialog v-model="dialog" :lat="reportLat" :lng="reportLng"></ReportDialog>
|
2018-06-25 18:29:57 +02:00
|
|
|
</v-flex>
|
|
|
|
<v-flex xs12 fill-height v-else class="pa-3">
|
2018-06-27 11:17:38 +02:00
|
|
|
<template v-if="error">
|
|
|
|
<p class="text-xs-center">{{ error }}</p>
|
|
|
|
<p class="text-xs-center">
|
|
|
|
<v-btn color="blue" dark @click="initializePositionWatching">Retry</v-btn>
|
|
|
|
</p>
|
|
|
|
</template>
|
|
|
|
<template v-else>
|
|
|
|
<p class="text-xs-center">{{ $t('geolocation.fetching') }}</p>
|
|
|
|
</template>
|
2018-06-25 18:29:57 +02:00
|
|
|
</v-flex>
|
|
|
|
</v-layout>
|
|
|
|
</v-container>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2018-06-25 23:57:33 +02:00
|
|
|
import NoSleep from 'nosleep.js';
|
|
|
|
|
2018-06-25 18:29:57 +02:00
|
|
|
import Map from '@/components/Map.vue';
|
|
|
|
import ReportDialog from '@/components/ReportDialog/index.vue';
|
2018-06-27 11:17:38 +02:00
|
|
|
import * as constants from '@/constants';
|
2018-06-26 11:04:23 +02:00
|
|
|
import { distance, mockLocation } from '@/tools';
|
|
|
|
|
2018-06-25 18:29:57 +02:00
|
|
|
export default {
|
|
|
|
components: {
|
|
|
|
Map,
|
|
|
|
ReportDialog,
|
|
|
|
},
|
|
|
|
created() {
|
|
|
|
this.initializePositionWatching();
|
2018-06-28 15:08:56 +02:00
|
|
|
this.listenForFirstInteraction();
|
2018-06-26 11:39:43 +02:00
|
|
|
this.$store.dispatch('fetchReports');
|
2018-06-28 14:40:56 +02:00
|
|
|
window.addEventListener('keydown', this.hideReportDialogOnEsc);
|
2018-06-25 18:29:57 +02:00
|
|
|
},
|
|
|
|
beforeDestroy() {
|
2018-06-25 23:57:33 +02:00
|
|
|
this.disableNoSleep();
|
2018-06-25 18:29:57 +02:00
|
|
|
this.disablePositionWatching();
|
2018-06-28 14:40:56 +02:00
|
|
|
window.removeEventListener('keydown', this.hideReportDialogOnEsc);
|
2018-06-25 18:29:57 +02:00
|
|
|
},
|
2018-06-26 11:04:23 +02:00
|
|
|
computed: {
|
|
|
|
reportsMarkers() {
|
2018-06-26 11:39:43 +02:00
|
|
|
return this.$store.state.reports.map(report => ({
|
2018-06-26 11:04:23 +02:00
|
|
|
id: report.id,
|
2018-06-27 14:59:45 +02:00
|
|
|
type: report.attributes.type,
|
2018-06-26 11:04:23 +02:00
|
|
|
latLng: [report.attributes.lat, report.attributes.lng],
|
|
|
|
}));
|
|
|
|
},
|
2018-07-01 22:02:54 +02:00
|
|
|
mapLatLng() {
|
|
|
|
if (this.hasMapMoved) {
|
|
|
|
return this.mapMovedLatLng;
|
|
|
|
}
|
|
|
|
return this.latLng;
|
|
|
|
},
|
2018-06-26 11:04:23 +02:00
|
|
|
},
|
2018-06-25 18:29:57 +02:00
|
|
|
data() {
|
|
|
|
return {
|
2018-06-27 11:17:38 +02:00
|
|
|
accuracy: null,
|
2018-07-01 22:02:54 +02:00
|
|
|
centering: false,
|
2018-06-25 18:29:57 +02:00
|
|
|
dialog: false,
|
2018-06-27 11:17:38 +02:00
|
|
|
error: null,
|
2018-07-01 22:02:54 +02:00
|
|
|
hasMapMoved: false,
|
2018-06-25 18:29:57 +02:00
|
|
|
heading: null,
|
2018-07-01 22:02:54 +02:00
|
|
|
latLng: null,
|
|
|
|
mapMovedLatLng: null,
|
2018-06-25 23:57:33 +02:00
|
|
|
noSleep: null,
|
2018-06-28 14:40:56 +02:00
|
|
|
reportLat: null,
|
|
|
|
reportLng: null,
|
2018-06-25 18:29:57 +02:00
|
|
|
watchID: null,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
initializePositionWatching() {
|
2018-06-29 15:09:39 +02:00
|
|
|
this.error = null; // Reset any error
|
2018-06-25 18:29:57 +02:00
|
|
|
this.disablePositionWatching(); // Ensure at most one at the same time
|
|
|
|
|
2018-06-27 11:17:38 +02:00
|
|
|
if (constants.MOCK_LOCATION) {
|
2018-06-26 11:04:23 +02:00
|
|
|
this.setPosition(mockLocation());
|
|
|
|
this.watchID = setInterval(
|
|
|
|
() => this.setPosition(mockLocation()),
|
2018-06-27 11:17:38 +02:00
|
|
|
constants.MOCK_LOCATION_UPDATE_INTERVAL,
|
2018-06-26 11:04:23 +02:00
|
|
|
);
|
2018-06-25 18:29:57 +02:00
|
|
|
} else {
|
2018-06-26 11:04:23 +02:00
|
|
|
if (!('geolocation' in navigator)) {
|
|
|
|
this.error = this.$t('geolocation.unavailable');
|
|
|
|
}
|
|
|
|
|
2018-06-25 18:29:57 +02:00
|
|
|
this.watchID = navigator.geolocation.watchPosition(
|
|
|
|
this.setPosition,
|
|
|
|
this.handlePositionError,
|
|
|
|
{
|
|
|
|
enableHighAccuracy: true,
|
|
|
|
maximumAge: 30000,
|
|
|
|
timeout: 27000,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
disablePositionWatching() {
|
|
|
|
if (this.watchID !== null) {
|
2018-06-27 11:17:38 +02:00
|
|
|
if (constants.MOCK_LOCATION) {
|
2018-06-26 11:04:23 +02:00
|
|
|
clearInterval(this.watchID);
|
|
|
|
} else {
|
|
|
|
navigator.geolocation.clearWatch(this.watchID);
|
|
|
|
}
|
2018-06-25 18:29:57 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
handlePositionError(error) {
|
|
|
|
this.error = `Error ${error.code}: ${error.message}`;
|
|
|
|
},
|
|
|
|
setPosition(position) {
|
2018-07-01 22:02:54 +02:00
|
|
|
if (this.latLng) {
|
2018-06-26 11:04:23 +02:00
|
|
|
const distanceFromPreviousPoint = distance(
|
2018-07-01 22:02:54 +02:00
|
|
|
[this.latLng[0], this.latLng[1]],
|
2018-06-26 11:04:23 +02:00
|
|
|
[position.coords.latitude, position.coords.longitude],
|
|
|
|
);
|
2018-06-27 11:17:38 +02:00
|
|
|
if (distanceFromPreviousPoint > constants.UPDATE_REPORTS_DISTANCE_THRESHOLD) {
|
2018-06-26 11:39:43 +02:00
|
|
|
this.$store.dispatch('fetchReports');
|
2018-06-26 11:04:23 +02:00
|
|
|
}
|
|
|
|
}
|
2018-07-01 22:02:54 +02:00
|
|
|
this.latLng = [position.coords.latitude, position.coords.longitude];
|
2018-06-27 11:17:38 +02:00
|
|
|
this.heading = position.coords.heading ? position.coords.heading : null;
|
|
|
|
this.accuracy = position.coords.accuracy ? position.coords.accuracy : null;
|
2018-06-25 18:29:57 +02:00
|
|
|
},
|
2018-06-25 23:57:33 +02:00
|
|
|
setNoSleep() {
|
|
|
|
this.noSleep = new NoSleep();
|
|
|
|
this.noSleep.enable();
|
|
|
|
},
|
|
|
|
disableNoSleep() {
|
|
|
|
if (this.noSleep) {
|
|
|
|
this.noSleep.disable();
|
|
|
|
}
|
|
|
|
},
|
2018-06-28 14:40:56 +02:00
|
|
|
showReportDialog(latlng) {
|
|
|
|
if (latlng) {
|
|
|
|
this.reportLat = latlng.lat;
|
|
|
|
this.reportLng = latlng.lng;
|
|
|
|
} else {
|
2018-07-01 22:02:54 +02:00
|
|
|
this.reportLat = this.latLng[0];
|
|
|
|
this.reportLng = this.latLng[1];
|
2018-06-28 14:40:56 +02:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
},
|
2018-06-28 15:08:56 +02:00
|
|
|
handleFirstUserInteraction() {
|
|
|
|
this.setNoSleep();
|
|
|
|
|
|
|
|
window.removeEventListener('mousemove', this.handleFirstUserInteraction, false);
|
|
|
|
window.removeEventListener('mousedown', this.handleFirstUserInteraction, false);
|
|
|
|
window.removeEventListener('keypress', this.handleFirstUserInteraction, false);
|
|
|
|
window.removeEventListener('DOMMouseScroll', this.handleFirstUserInteraction, false);
|
|
|
|
window.removeEventListener('mousewheel', this.handleFirstUserInteraction, false);
|
|
|
|
window.removeEventListener('touchmove', this.handleFirstUserInteraction, false);
|
|
|
|
window.removeEventListener('MSPointerMove', this.handleFirstUserInteraction, false);
|
|
|
|
},
|
|
|
|
listenForFirstInteraction() {
|
|
|
|
window.addEventListener('mousemove', this.handleFirstUserInteraction, false);
|
|
|
|
window.addEventListener('mousedown', this.handleFirstUserInteraction, false);
|
|
|
|
window.addEventListener('keypress', this.handleFirstUserInteraction, false);
|
|
|
|
window.addEventListener('DOMMouseScroll', this.handleFirstUserInteraction, false);
|
|
|
|
window.addEventListener('mousewheel', this.handleFirstUserInteraction, false);
|
|
|
|
window.addEventListener('touchmove', this.handleFirstUserInteraction, false);
|
|
|
|
window.addEventListener('MSPointerMove', this.handleFirstUserInteraction, false);
|
|
|
|
},
|
2018-07-01 22:02:54 +02:00
|
|
|
recenterMap() {
|
|
|
|
this.mapMovedLatLng = null;
|
|
|
|
this.centering = true;
|
|
|
|
this.hasMapMoved = false;
|
|
|
|
},
|
|
|
|
onMapMoveStart() {
|
|
|
|
if (!this.centering) {
|
|
|
|
this.hasMapMoved = true;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
onMapMoveEnd(ev) {
|
|
|
|
const latLng = ev.target.getCenter();
|
|
|
|
if (!this.hasMapMoved) {
|
|
|
|
this.mapMovedLatLng = [latLng.lat, latLng.lng];
|
|
|
|
}
|
|
|
|
this.centering = false;
|
|
|
|
},
|
2018-06-25 18:29:57 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
.no-padding {
|
|
|
|
padding: 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
.overlayButton {
|
|
|
|
z-index: 1000 !important;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
|
|
|
|
<style>
|
|
|
|
.overlay {
|
|
|
|
z-index: 1002 !important;
|
|
|
|
}
|
|
|
|
</style>
|