1
0
Fork 0
mirror of https://github.com/Luzifer/mapshare.git synced 2024-12-20 14:41:19 +00:00

Initial implementation of frontend

This commit is contained in:
Knut Ahlers 2020-01-07 00:19:11 +01:00
parent 6f82de4866
commit bfcffc983d
Signed by: luzifer
GPG key ID: DC2729FDD34BE99E
5 changed files with 198 additions and 2 deletions

View file

@ -8,7 +8,7 @@ css_deps=(
npm/bootstrap-vue@2/dist/bootstrap-vue.min.css
# Other packages
npm/leaflet@1.5.1/dist/leaflet.min.css
npm/leaflet@1.6.0/dist/leaflet.min.css
)
js_deps=(
@ -18,10 +18,22 @@ js_deps=(
# Other packages
npm/axios@0.19.0/dist/axios.min.js
npm/leaflet@1.5.1/dist/leaflet.min.js
npm/leaflet@1.6.0/dist/leaflet.min.js
npm/moment@2.24.0/min/moment.min.js
)
leaflet_images=(
marker-icon.png
marker-icon-2x.png
marker-shadow.png
)
rm -rf frontend/leaflet
mkdir -p frontend/leaflet
for img in "${leaflet_images[@]}"; do
curl -sSfLo "frontend/leaflet/${img}" "https://cdn.jsdelivr.net/npm/leaflet@1.5.1/dist/images/${img}"
done
IFS=','
curl -sSfLo frontend/combine.js "https://cdn.jsdelivr.net/combine/${js_deps[*]}"

1
frontend/.gitignore vendored
View file

@ -1,3 +1,4 @@
combine.css
combine.js
fontawesome
leaflet

6
frontend/app.css Normal file
View file

@ -0,0 +1,6 @@
html, body, #app, #map {
height: 100%;
margin: 0;
padding: 0;
width: 100%;
}

117
frontend/app.js Normal file
View file

@ -0,0 +1,117 @@
const app = new Vue({
created() {
// Use defaults with custom icon paths
this.icon = L.icon({
...L.Icon.Default.prototype.options,
iconUrl: '/asset/leaflet/marker-icon.png',
iconRetinaUrl: '/asset/leaflet/marker-icon-2x.png',
shadowUrl: '/asset/leaflet/marker-shadow.png',
})
// This is only to detect another user updated the location
// therefore this is NOT cryptographically safe!
this.browserID = localStorage.getItem('browserID')
if (!this.browserID) {
this.browserID = Math.random().toString(16).substr(2)
localStorage.setItem('browserID', this.browserID)
}
},
data: {
browserID: null,
icon: null,
loc: null,
map: null,
marker: null,
shareActive: false,
shareSettings: {
continuous: true,
retained: false,
},
shareSettingsOpen: false,
socket: null,
},
el: '#app',
methods: {
initMap() {
this.map = L.map('map')
.setView([0,0], 13)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(this.map)
},
shareLocation() {
this.shareActive = true
const opts = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0,
}
if (!this.shareSettings.continuous) {
navigator.geolocation.getCurrentPosition(this.updateLocation, err => console.error(err), opts)
return
}
navigator.geolocation.watchPosition(this.updateLocation, err => console.error(err), opts)
},
subscribe() {
if (this.socket) {
// Dispose old socket
this.socket.close()
this.socket = null
}
this.socket = new WebSocket(`${window.location.href.split('#')[0].replace(/^http/, 'ws')}/ws`)
this.socket.onclose = () => window.setTimeout(this.subscribe, 1000) // Restart socket
this.socket.onmessage = evt => {
let loc= JSON.parse(evt.data)
loc.time = new Date(loc.time)
this.loc= loc
}
},
updateLocation(pos) {
const data = {
lat: pos.coords.latitude,
lon: pos.coords.longitude,
retained: this.shareSettings.retained,
sender_id: this.browserID,
}
return axios.put(window.location.href.split('#')[0], data)
.catch(err => console.error(err))
},
updateMap() {
const center = [this.loc.lat, this.loc.lon]
if (!this.marker) {
this.marker = L.marker(center, {icon:this.icon})
.addTo(this.map)
}
this.map.panTo(center)
this.marker.setLatLng(center)
},
},
mounted() {
this.initMap()
this.subscribe()
},
watch: {
loc() {
this.updateMap()
},
},
})

60
frontend/index.html Normal file
View file

@ -0,0 +1,60 @@
<html>
<title>MapShare</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/asset/combine.css">
<link rel="stylesheet" href="/asset/fontawesome/css/all.min.css">
<link rel="stylesheet" href="/asset/app.css">
<div id="app">
<div id="map"></div>
<b-modal
centered
hide-footer
hide-header
no-close-on-backdrop
no-close-on-esc
size="sm"
:visible="(!loc || loc.sender_id === browserID) && !shareActive"
>
<div class="text-center">
<p class="mb-0">
<i class="fa fa-spinner fa-pulse fa-4x mb-3"></i><br>
Waiting for location...
</p>
<p class="mt-3" v-if="navigator.geolocation">
<b-button-group>
<b-btn
@click="shareLocation"
variant="primary"
>
Share my location!
</b-btn>
<b-btn @click="shareSettingsOpen = !shareSettingsOpen">
<i class="fa fa-cog"></i>
</b-btn>
</b-button-group>
</p>
</div>
</b-modal>
<b-modal
centered
title="Share-Settings"
@hidden="shareSettingsOpen = false"
ok-only
:visible="shareSettingsOpen"
>
<b-form-checkbox v-model="shareSettings.continuous" switch>
Keep sending location<br><small>(when enabled location is updated as long as this window is open)</small>
</b-form-checkbox>
<b-form-checkbox class="mt-3" v-model="shareSettings.retained" switch>
Retain location on server<br><small>(new viewers instantly see your location)</small>
</b-form-checkbox>
</b-modal>
</div>
<script src="/asset/combine.js"></script>
<script src="/asset/app.js"></script>
</html>