Client - add a fullscreen control to workout map - fix #140

This commit is contained in:
Sam 2022-01-15 20:13:25 +01:00
parent 2242525b39
commit 8897659c62
18 changed files with 125 additions and 66 deletions

View File

@ -1 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"><link rel="stylesheet" href="/static/css/leaflet.css"><title>FitTrackee</title><link href="/static/css/admin.e77f8b26.css" rel="prefetch"><link href="/static/css/profile.8b668068.css" rel="prefetch"><link href="/static/css/reset.fc19709e.css" rel="prefetch"><link href="/static/css/statistics.2afdc8a9.css" rel="prefetch"><link href="/static/css/workouts.df61eaf2.css" rel="prefetch"><link href="/static/js/admin.5f46d0fe.js" rel="prefetch"><link href="/static/js/chunk-2d0c9189.c81458cc.js" rel="prefetch"><link href="/static/js/chunk-2d0cf391.020c75ea.js" rel="prefetch"><link href="/static/js/chunk-2d0da8f3.c8c3e7e8.js" rel="prefetch"><link href="/static/js/chunk-2d2248b6.d84473c1.js" rel="prefetch"><link href="/static/js/chunk-2d22523a.4b710d99.js" rel="prefetch"><link href="/static/js/profile.d25975e2.js" rel="prefetch"><link href="/static/js/reset.ca898ebe.js" rel="prefetch"><link href="/static/js/statistics.d03ca304.js" rel="prefetch"><link href="/static/js/workouts.a478f15b.js" rel="prefetch"><link href="/static/css/app.b54fa5fe.css" rel="preload" as="style"><link href="/static/js/app.51ee7901.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.d887e943.js" rel="preload" as="script"><link href="/static/css/app.b54fa5fe.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="fittrackee_client"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but FitTrackee doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.d887e943.js"></script><script src="/static/js/app.51ee7901.js"></script></body></html> <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"><link rel="stylesheet" href="/static/css/leaflet.css"><title>FitTrackee</title><link href="/static/css/admin.e77f8b26.css" rel="prefetch"><link href="/static/css/profile.8b668068.css" rel="prefetch"><link href="/static/css/reset.fc19709e.css" rel="prefetch"><link href="/static/css/statistics.2afdc8a9.css" rel="prefetch"><link href="/static/css/workouts.c5521765.css" rel="prefetch"><link href="/static/js/admin.5f46d0fe.js" rel="prefetch"><link href="/static/js/chunk-2d0c9189.c81458cc.js" rel="prefetch"><link href="/static/js/chunk-2d0cf391.020c75ea.js" rel="prefetch"><link href="/static/js/chunk-2d0da8f3.c8c3e7e8.js" rel="prefetch"><link href="/static/js/chunk-2d2248b6.d84473c1.js" rel="prefetch"><link href="/static/js/chunk-2d22523a.4b710d99.js" rel="prefetch"><link href="/static/js/profile.d25975e2.js" rel="prefetch"><link href="/static/js/reset.ca898ebe.js" rel="prefetch"><link href="/static/js/statistics.d03ca304.js" rel="prefetch"><link href="/static/js/workouts.dcd0b64a.js" rel="prefetch"><link href="/static/css/app.b54fa5fe.css" rel="preload" as="style"><link href="/static/js/app.15c3e6ab.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.6e2f6ef7.js" rel="preload" as="script"><link href="/static/css/app.b54fa5fe.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="fittrackee_client"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but FitTrackee doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.6e2f6ef7.js"></script><script src="/static/js/app.15c3e6ab.js"></script></body></html>

View File

@ -72,7 +72,7 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/img/workouts/start.svg" "url": "/img/workouts/start.svg"
}, },
{ {
"revision": "04ac08405aad030634bae2b25dca6f01", "revision": "c26adc3efe5f26cacdbb91b86e3a5e38",
"url": "/index.html" "url": "/index.html"
}, },
{ {
@ -88,7 +88,7 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/css/admin.e77f8b26.css" "url": "/static/css/admin.e77f8b26.css"
}, },
{ {
"revision": "ae3dd7a64c0096f9d697", "revision": "58739c2038f30839f6ea",
"url": "/static/css/app.b54fa5fe.css" "url": "/static/css/app.b54fa5fe.css"
}, },
{ {
@ -112,8 +112,8 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/css/statistics.2afdc8a9.css" "url": "/static/css/statistics.2afdc8a9.css"
}, },
{ {
"revision": "f29654976a994f39722c", "revision": "af09a25559d1f134383d",
"url": "/static/css/workouts.df61eaf2.css" "url": "/static/css/workouts.c5521765.css"
}, },
{ {
"revision": "e719f9244c69e28e7d00e725ca1e280e", "revision": "e719f9244c69e28e7d00e725ca1e280e",
@ -200,8 +200,8 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/js/admin.5f46d0fe.js" "url": "/static/js/admin.5f46d0fe.js"
}, },
{ {
"revision": "ae3dd7a64c0096f9d697", "revision": "58739c2038f30839f6ea",
"url": "/static/js/app.51ee7901.js" "url": "/static/js/app.15c3e6ab.js"
}, },
{ {
"revision": "bd7d183c9f68e5f4027d", "revision": "bd7d183c9f68e5f4027d",
@ -224,8 +224,8 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/js/chunk-2d22523a.4b710d99.js" "url": "/static/js/chunk-2d22523a.4b710d99.js"
}, },
{ {
"revision": "3f898f17e41e7bdac2f9", "revision": "3e9cb690e510bff131b5",
"url": "/static/js/chunk-vendors.d887e943.js" "url": "/static/js/chunk-vendors.6e2f6ef7.js"
}, },
{ {
"revision": "00382d944a1bc6fca08b", "revision": "00382d944a1bc6fca08b",
@ -240,7 +240,7 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/js/statistics.d03ca304.js" "url": "/static/js/statistics.d03ca304.js"
}, },
{ {
"revision": "f29654976a994f39722c", "revision": "af09a25559d1f134383d",
"url": "/static/js/workouts.a478f15b.js" "url": "/static/js/workouts.dcd0b64a.js"
} }
]); ]);

View File

@ -14,7 +14,7 @@
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"); importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts( importScripts(
"/precache-manifest.6828a0593e300b9de34b9b69d8067b94.js" "/precache-manifest.c7eccdefc201d4db22dbdf20aa0937a3.js"
); );
workbox.core.setCacheNameDetails({prefix: "fittrackee_client"}); workbox.core.setCacheNameDetails({prefix: "fittrackee_client"});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -23,6 +23,7 @@
"register-service-worker": "^1.7.1", "register-service-worker": "^1.7.1",
"vue": "^3.0.0", "vue": "^3.0.0",
"vue-chart-3": "^3.0.7", "vue-chart-3": "^3.0.7",
"vue-fullscreen": "^3.1.1",
"vue-i18n": "^9.1.9", "vue-i18n": "^9.1.9",
"vue-router": "^4.0.0-0", "vue-router": "^4.0.0-0",
"vuex": "^4.0.0-0" "vuex": "^4.0.0-0"

View File

@ -2,47 +2,65 @@
<div id="workout-map"> <div id="workout-map">
<div v-if="workoutData.loading" class="leaflet-container" /> <div v-if="workoutData.loading" class="leaflet-container" />
<div v-else> <div v-else>
<div class="leaflet-container" v-if="workoutData.workout.with_gpx"> <VFullscreen v-if="workoutData.workout.with_gpx" v-model="isFullscreen">
<LMap <div
v-if="geoJson.jsonData && center && bounds.length === 2" class="leaflet-container"
:zoom="13" :class="{ 'fullscreen-map': isFullscreen }"
:maxZoom="19"
:center="center"
:bounds="bounds"
ref="workoutMap"
@ready="fitBounds(bounds)"
> >
<LControlLayers /> <LMap
<LControl position="topleft" class="reset-button" @click="resetZoom"> v-if="geoJson.jsonData && center && bounds.length === 2"
<i class="fa fa-refresh" aria-hidden="true"></i> :zoom="13"
</LControl> :maxZoom="19"
<LTileLayer :center="center"
:url="`${getApiUrl()}workouts/map_tile/{s}/{z}/{x}/{y}.png`"
:attribution="appConfig.map_attribution"
:bounds="bounds" :bounds="bounds"
/> ref="workoutMap"
<LGeoJson :geojson="geoJson.jsonData" /> @ready="fitBounds(bounds)"
<LMarker
v-if="markerCoordinates.latitude"
:lat-lng="[markerCoordinates.latitude, markerCoordinates.longitude]"
/>
<LLayerGroup
:name="$t('workouts.START_AND_FINISH')"
layer-type="overlay"
> >
<CustomMarker <LControlLayers />
v-if="startMarkerCoordinates.latitude" <LControl position="topleft" class="map-control" @click="resetZoom">
:markerCoordinates="startMarkerCoordinates" <i class="fa fa-refresh" aria-hidden="true" />
:isStart="true" </LControl>
<LControl
position="topleft"
class="map-control"
@click="toggleFullscreen"
>
<i
:class="`fa fa-${isFullscreen ? 'compress' : 'arrows-alt'}`"
aria-hidden="true"
/>
</LControl>
<LTileLayer
:url="`${getApiUrl()}workouts/map_tile/{s}/{z}/{x}/{y}.png`"
:attribution="appConfig.map_attribution"
:bounds="bounds"
/> />
<CustomMarker <LGeoJson :geojson="geoJson.jsonData" />
v-if="endMarkerCoordinates.latitude" <LMarker
:markerCoordinates="endMarkerCoordinates" v-if="markerCoordinates.latitude"
:isStart="false" :lat-lng="[
markerCoordinates.latitude,
markerCoordinates.longitude,
]"
/> />
</LLayerGroup> <LLayerGroup
</LMap> :name="$t('workouts.START_AND_FINISH')"
</div> layer-type="overlay"
>
<CustomMarker
v-if="startMarkerCoordinates.latitude"
:markerCoordinates="startMarkerCoordinates"
:isStart="true"
/>
<CustomMarker
v-if="endMarkerCoordinates.latitude"
:markerCoordinates="endMarkerCoordinates"
:isStart="false"
/>
</LLayerGroup>
</LMap>
</div>
</VFullscreen>
<div v-else class="no-map">{{ $t('workouts.NO_MAP') }}</div> <div v-else class="no-map">{{ $t('workouts.NO_MAP') }}</div>
</div> </div>
</div> </div>
@ -114,6 +132,7 @@
} }
: {} : {}
) )
const isFullscreen = ref(false)
function getGeoJson(gpxContent: string): GeoJSONData { function getGeoJson(gpxContent: string): GeoJSONData {
if (!gpxContent || gpxContent !== '') { if (!gpxContent || gpxContent !== '') {
@ -157,6 +176,14 @@
function resetZoom() { function resetZoom() {
workoutMap.value?.leafletObject.fitBounds(getBounds()) workoutMap.value?.leafletObject.fitBounds(getBounds())
} }
function toggleFullscreen() {
isFullscreen.value = !isFullscreen.value
if (!isFullscreen.value) {
setTimeout(() => {
resetZoom()
}, 100)
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -171,13 +198,21 @@
.no-map { .no-map {
line-height: 400px; line-height: 400px;
} }
.reset-button { .map-control {
background: #ffffff; background: #ffffff;
padding: 5px 10px; padding: 5px 10px;
border: 2px solid #bfc0ab; border: 2px solid #bfc0ab;
border-radius: 3px; border-radius: 3px;
color: #000000; color: #000000;
} }
::v-deep(.fullscreen) {
display: flex;
align-items: center;
.fullscreen-map {
height: 100%;
width: 100%;
}
}
@media screen and (max-width: $small-limit) { @media screen and (max-width: $small-limit) {
padding: 0; padding: 0;

View File

@ -14,6 +14,7 @@ import {
} from 'chart.js' } from 'chart.js'
import ChartDataLabels from 'chartjs-plugin-datalabels' import ChartDataLabels from 'chartjs-plugin-datalabels'
import { createApp } from 'vue' import { createApp } from 'vue'
import VueFullscreen from 'vue-fullscreen'
import './registerServiceWorker' import './registerServiceWorker'
import App from './App.vue' import App from './App.vue'
@ -45,6 +46,7 @@ const app = createApp(App)
.use(i18n) .use(i18n)
.use(store) .use(store)
.use(router) .use(router)
.use(VueFullscreen, { name: 'VFullscreen' })
.directive('click-outside', clickOutsideDirective) .directive('click-outside', clickOutsideDirective)
customComponents.forEach((component) => { customComponents.forEach((component) => {

View File

@ -8501,6 +8501,11 @@ schema-utils@^3.0.0:
ajv "^6.12.5" ajv "^6.12.5"
ajv-keywords "^3.5.2" ajv-keywords "^3.5.2"
screenfull@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba"
integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==
select-hose@^2.0.0: select-hose@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@ -9773,6 +9778,13 @@ vue-eslint-parser@^7.0.0, vue-eslint-parser@^7.10.0:
lodash "^4.17.21" lodash "^4.17.21"
semver "^6.3.0" semver "^6.3.0"
vue-fullscreen@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/vue-fullscreen/-/vue-fullscreen-3.1.1.tgz#385097e0b43f4604ded99adb0ceb225dea3e0326"
integrity sha512-I59sIO0O22116gwVPo1qM2cNf5binEC5vTswIm8qJsqgatGfFquIJANpoVggSUS2EgUF0Xg4tHliT1ITcwQw8Q==
dependencies:
screenfull "^5.1.0"
vue-hot-reload-api@^2.3.0: vue-hot-reload-api@^2.3.0:
version "2.3.4" version "2.3.4"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"