mirror of
https://github.com/Luzifer/mapshare.git
synced 2024-12-20 22:51: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
|
npm/bootstrap-vue@2/dist/bootstrap-vue.min.css
|
||||||
|
|
||||||
# Other packages
|
# Other packages
|
||||||
npm/leaflet@1.5.1/dist/leaflet.min.css
|
npm/leaflet@1.6.0/dist/leaflet.min.css
|
||||||
)
|
)
|
||||||
|
|
||||||
js_deps=(
|
js_deps=(
|
||||||
|
@ -18,10 +18,22 @@ js_deps=(
|
||||||
|
|
||||||
# Other packages
|
# Other packages
|
||||||
npm/axios@0.19.0/dist/axios.min.js
|
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
|
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=','
|
IFS=','
|
||||||
|
|
||||||
curl -sSfLo frontend/combine.js "https://cdn.jsdelivr.net/combine/${js_deps[*]}"
|
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.css
|
||||||
combine.js
|
combine.js
|
||||||
fontawesome
|
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