cygnal/src/views/Map.vue

242 lines
8.4 KiB
Vue
Raw Normal View History

2018-06-25 18:29:57 +02:00
<template>
<v-container fluid fill-height class="no-padding">
<v-layout row wrap fill-height>
<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"
@click.native.stop="() => showReportDialog()"
2018-06-25 18:29:57 +02:00
>
<v-icon>report_problem</v-icon>
</v-btn>
<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">
<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>
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';
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();
this.listenForFirstInteraction();
this.$store.dispatch('fetchReports');
window.addEventListener('keydown', this.hideReportDialogOnEsc);
2018-06-25 18:29:57 +02:00
},
beforeDestroy() {
this.disableNoSleep();
2018-06-25 18:29:57 +02:00
this.disablePositionWatching();
window.removeEventListener('keydown', this.hideReportDialogOnEsc);
2018-06-25 18:29:57 +02:00
},
2018-06-26 11:04:23 +02:00
computed: {
reportsMarkers() {
return this.$store.state.reports.map(report => ({
2018-06-26 11:04:23 +02:00
id: report.id,
type: report.attributes.type,
2018-06-26 11:04:23 +02:00
latLng: [report.attributes.lat, report.attributes.lng],
}));
},
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 {
accuracy: null,
centering: false,
2018-06-25 18:29:57 +02:00
dialog: false,
error: null,
hasMapMoved: false,
2018-06-25 18:29:57 +02:00
heading: null,
latLng: null,
mapMovedLatLng: null,
noSleep: null,
reportLat: null,
reportLng: null,
2018-06-25 18:29:57 +02:00
watchID: null,
};
},
methods: {
initializePositionWatching() {
this.error = null; // Reset any error
2018-06-25 18:29:57 +02:00
this.disablePositionWatching(); // Ensure at most one at the same time
if (constants.MOCK_LOCATION) {
2018-06-26 11:04:23 +02:00
this.setPosition(mockLocation());
this.watchID = setInterval(
() => this.setPosition(mockLocation()),
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) {
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) {
if (this.latLng) {
2018-06-26 11:04:23 +02:00
const distanceFromPreviousPoint = distance(
[this.latLng[0], this.latLng[1]],
2018-06-26 11:04:23 +02:00
[position.coords.latitude, position.coords.longitude],
);
if (distanceFromPreviousPoint > constants.UPDATE_REPORTS_DISTANCE_THRESHOLD) {
this.$store.dispatch('fetchReports');
2018-06-26 11:04:23 +02:00
}
}
this.latLng = [position.coords.latitude, position.coords.longitude];
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
},
setNoSleep() {
this.noSleep = new NoSleep();
this.noSleep.enable();
},
disableNoSleep() {
if (this.noSleep) {
this.noSleep.disable();
}
},
showReportDialog(latlng) {
if (latlng) {
this.reportLat = latlng.lat;
this.reportLng = latlng.lng;
} else {
this.reportLat = this.latLng[0];
this.reportLng = this.latLng[1];
}
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;
}
},
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);
},
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>