Reworked watcher events and describing texts

This commit is contained in:
Florian Zumpe
2026-06-16 16:45:59 +02:00
parent 289c6d5a43
commit 4529749baa
17 changed files with 347 additions and 57 deletions
+61 -7
View File
@@ -251,13 +251,57 @@ button,
font-weight: 700;
}
#start-tracking.ui-button {
width: 100%;
min-height: 3rem;
.route-controls {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: .5rem;
margin: 0 0 1rem;
border-color: var(--primary-dark);
}
.route-controls button {
display: grid;
place-items: center;
min-width: 0;
min-height: 3.25rem;
box-sizing: border-box;
padding: .55rem;
border: 1px solid transparent;
border-radius: .55rem;
box-shadow: 0 .12rem .35rem rgba(0, 0, 0, .12);
cursor: pointer;
touch-action: manipulation;
}
.route-controls button:focus-visible {
outline: .2rem solid var(--focus);
outline-offset: .15rem;
}
.route-controls button:disabled {
cursor: default;
filter: grayscale(.65);
opacity: .42;
}
.route-controls img {
width: 2rem;
height: 2rem;
pointer-events: none;
}
#start-route {
background: var(--primary);
color: #fff;
border-color: var(--primary-dark);
}
#pause-route {
background: #9a6500;
border-color: #704a00;
}
#stop-route {
background: #9b2c2c;
border-color: #701f1f;
}
#slideshow {
@@ -371,11 +415,21 @@ button,
.app-page > header .back-button.ui-button:hover,
.app-page > header .back-button.ui-button:focus,
#start-tracking.ui-button:hover,
#start-tracking.ui-button:focus {
#start-route:not(:disabled):hover,
#start-route:not(:disabled):focus {
background: var(--primary-dark);
color: #fff;
}
#pause-route:not(:disabled):hover,
#pause-route:not(:disabled):focus {
background: #704a00;
}
#stop-route:not(:disabled):hover,
#stop-route:not(:disabled):focus {
background: #701f1f;
}
}
@media (max-width: 32rem) {
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" role="img" aria-hidden="true"><path fill="#fff" d="M12 8h9v32h-9zm15 0h9v32h-9z"/></svg>

After

Width:  |  Height:  |  Size: 149 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" role="img" aria-hidden="true"><path fill="#fff" d="M14 8v32l26-16z"/></svg>

After

Width:  |  Height:  |  Size: 136 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" role="img" aria-hidden="true"><path fill="#fff" d="M10 10h28v28H10z"/></svg>

After

Width:  |  Height:  |  Size: 137 B

+11 -1
View File
@@ -79,7 +79,17 @@
<div><dt>POIs</dt><dd id="route-poi-count">–</dd></div>
</dl>
<button id="start-tracking" type="button">GPS-Begleitung starten</button>
<div class="route-controls" role="group" aria-label="Routensteuerung">
<button id="start-route" type="button" aria-label="Route starten" title="Route starten">
<img src="images/icons/play.svg" alt="">
</button>
<button id="pause-route" type="button" aria-label="Route pausieren" title="Route pausieren" disabled>
<img src="images/icons/pause.svg" alt="">
</button>
<button id="stop-route" type="button" aria-label="Route beenden" title="Route beenden" disabled>
<img src="images/icons/stop.svg" alt="">
</button>
</div>
<p id="tracking-status" class="notice" hidden></p>
<h2>Stationen</h2>
+80 -29
View File
@@ -6,7 +6,7 @@
route: null,
position: null,
triggered: new Set(),
tracking: false,
routeState: 'idle',
activePage: null,
routeProgressIndex: null,
deviceHeading: null
@@ -151,27 +151,55 @@
$('#app-shell').addClass('has-navigation');
}
function updateTrackingButton() {
const button = $('#start-tracking');
button.button('option', {
label: state.tracking ? 'GPS-Begleitung stoppen' : 'GPS-Begleitung starten',
icon: state.tracking ? 'ui-icon-stop' : 'ui-icon-pin-s'
});
}
function stopTracking() {
state.tracking = false;
state.routeProgressIndex = null;
state.deviceHeading = null;
function stopRouteSensors() {
ns.Geo.stop();
ns.Orientation.stop();
$('#tracking-status').prop('hidden', true).text('');
state.deviceHeading = null;
}
function updateRouteControls() {
const running = state.routeState === 'running';
const paused = state.routeState === 'paused';
const startLabel = paused ? 'Route fortsetzen' : 'Route starten';
$('#start-route')
.prop('disabled', running)
.attr({ 'aria-label': startLabel, title: startLabel });
$('#pause-route').prop('disabled', !running);
$('#stop-route').prop('disabled', state.routeState === 'idle');
}
function endRoute({ announce = true } = {}) {
const wasActive = state.routeState !== 'idle';
state.routeState = 'idle';
state.routeProgressIndex = null;
state.triggered.clear();
stopRouteSensors();
hideNavigation();
updateTrackingButton();
if (announce && wasActive) {
$('#tracking-status').prop('hidden', false).text('Route beendet.');
} else {
$('#tracking-status').prop('hidden', true).text('');
}
updateRouteControls();
}
function pauseRoute() {
if (state.routeState !== 'running') return;
state.routeState = 'paused';
stopRouteSensors();
$('#tracking-status')
.prop('hidden', false)
.text('Route pausiert. Die Standortverfolgung ist angehalten.');
showNavigation('Route pausiert', $('#navigation-distance').text() || '–');
updateRouteControls();
}
function renderRoute(route) {
if (state.tracking) stopTracking();
if (state.routeState !== 'idle') endRoute({ announce: false });
state.route = route;
state.triggered.clear();
@@ -181,6 +209,8 @@
$('#route-distance').text(ns.Distance.format(route.distanceM));
$('#route-elevation').text(`${Math.round(route.elevationGainM)} m`);
$('#route-poi-count').text(route.pois.length);
$('#tracking-status').prop('hidden', true).text('');
updateRouteControls();
const list = $('#poi-list').empty();
if (!route.pois.length) {
@@ -235,7 +265,7 @@
function updateNavigation(position) {
const points = state.route?.points || [];
if (!state.tracking || !points.length) return;
if (state.routeState !== 'running' || !points.length) return;
const nearestIndex = nearestRoutePointIndex(position);
if (nearestIndex == null) return;
@@ -298,7 +328,7 @@
function evaluatePosition(position) {
state.position = position;
if (!state.route || !state.tracking) return;
if (!state.route || state.routeState !== 'running') return;
updateNavigation(position);
@@ -321,13 +351,22 @@
}
}
async function startTracking() {
state.tracking = true;
state.routeProgressIndex = null;
async function startRoute() {
if (!state.route || state.routeState === 'running') return;
const resumed = state.routeState === 'paused';
if (!resumed) {
state.routeProgressIndex = null;
state.triggered.clear();
}
state.routeState = 'running';
state.deviceHeading = null;
updateTrackingButton();
$('#tracking-status').prop('hidden', false).text('GPS wird gestartet.');
showNavigation();
updateRouteControls();
$('#tracking-status')
.prop('hidden', false)
.text(resumed ? 'Route wird fortgesetzt.' : 'Route wird gestartet. GPS wird aktiviert.');
showNavigation(resumed ? 'Route wird fortgesetzt' : 'Position wird ermittelt');
ns.Geo.start(
evaluatePosition,
@@ -335,7 +374,12 @@
);
const orientationAvailable = await ns.Orientation.start(updateHeading);
if (!orientationAvailable && state.tracking) {
if (state.routeState !== 'running') {
ns.Orientation.stop();
return;
}
if (!orientationAvailable) {
$('#navigation-mode').text('Norden oben · nächster Routenpunkt');
}
}
@@ -372,9 +416,16 @@
else navigate(target, { replace: true });
});
activate('#start-tracking', async function () {
if (state.tracking) stopTracking();
else await startTracking();
activate('#start-route', async function () {
await startRoute();
});
activate('#pause-route', function () {
pauseRoute();
});
activate('#stop-route', function () {
endRoute();
});
window.addEventListener('popstate', event => {
@@ -415,7 +466,7 @@
context.markStep('location', 'done');
initializeRouteSearch();
renderRoutes();
updateTrackingButton();
updateRouteControls();
hideNavigation();
}
};
-1
View File
@@ -44,7 +44,6 @@
const $ = window.jQuery;
$('#bootstrap-retry').button({ icon: 'ui-icon-refresh' });
$('.back-button').button({ icon: 'ui-icon-caret-1-w' });
$('#start-tracking').button({ icon: 'ui-icon-pin-s' });
$('#clear-route-search').button({ icon: 'ui-icon-close', showLabel: false });
$('#slide-prev').button({ icon: 'ui-icon-caret-1-w', showLabel: false });
$('#slide-next').button({ icon: 'ui-icon-caret-1-e', showLabel: false });