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:
parent
6f82de4866
commit
bfcffc983d
5 changed files with 198 additions and 2 deletions
|
@ -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
1
frontend/.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
combine.css
|
||||
combine.js
|
||||
fontawesome
|
||||
leaflet
|
||||
|
|
6
frontend/app.css
Normal file
6
frontend/app.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
html, body, #app, #map {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
117
frontend/app.js
Normal file
117
frontend/app.js
Normal 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: '© <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
60
frontend/index.html
Normal 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>
|
Loading…
Reference in a new issue