From 5df3fe99f6f46627f5fa7357ef60494e4f78b698 Mon Sep 17 00:00:00 2001 From: Candid Dauth Date: Mon, 20 May 2024 12:34:45 +0200 Subject: [PATCH] Keep screen on while locate control is active --- docs/src/users/locate/README.md | 4 +- .../leaflet-map/leaflet-map-components.ts | 20 ++++++ frontend/src/lib/utils/wake-lock.ts | 67 +++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 frontend/src/lib/utils/wake-lock.ts diff --git a/docs/src/users/locate/README.md b/docs/src/users/locate/README.md index 43666aa8..9d080abf 100644 --- a/docs/src/users/locate/README.md +++ b/docs/src/users/locate/README.md @@ -14,7 +14,9 @@ The cross-hair icon will be shown in one of these colours: * **Orange:** Your location is shown on the map and the map will follow you as your location changes. * **Blue:** Your location is shown on the map, but the map does not follow you. -Clicking the cross-hair icon will enable the orange mode. As soon as you move the map by hand, it will switch to blue mode. To go back to orange mode, first go back to black mode by clicking the icon once and then go to orange mode by clicking it again. +Clicking the cross-hair icon will enable the orange mode. As soon as you move the map by hand, it will switch to blue mode. To go back to orange mode, click the icon again. + +While your location is visible on the map, FacilMap will try to prevent your screen from turning off, which would be disruptive during live navigation. It will depend on your browser and on your operating system whether this works and whether your browser will ask you for permission for this first. Your location will be indicated as a blue circle with a blue dot at the center. Your location may be anywhere within the circle, it is not necessarily at its centre. If the circle is very small, it means that your browser could determine your location very accurately. If the circle is very big, it means that your browser had trouble to make an accurate guess about your location, and you may be anywhere within the circle. diff --git a/frontend/src/lib/components/leaflet-map/leaflet-map-components.ts b/frontend/src/lib/components/leaflet-map/leaflet-map-components.ts index c7251a52..dd165708 100644 --- a/frontend/src/lib/components/leaflet-map/leaflet-map-components.ts +++ b/frontend/src/lib/components/leaflet-map/leaflet-map-components.ts @@ -19,6 +19,7 @@ import { type Optional } from "facilmap-utils"; import { getI18n, i18nResourceChangeCounter } from "../../utils/i18n"; import { AttributionControl } from "./attribution"; import { isNarrowBreakpoint } from "../../utils/bootstrap"; +import { useWakeLock } from "../../utils/wake-lock"; type MapContextWithoutComponents = Optional; @@ -239,8 +240,27 @@ function useLocateControl(map: Ref, context: FacilMapContext): Ref { + active.value = true; + }; + const handleDeactivate = () => { + active.value = false; + }; + map.on("locateactivate", handleActivate); + map.on("locatedeactivate", handleDeactivate); + + const wakeLockScope = effectScope(); + wakeLockScope.run(() => { + useWakeLock(active); + }); + onScopeDispose(() => { + handleDeactivate(); locateControl.remove(); + map.off("locateactivate", handleActivate); + map.off("locatedeactivate", handleDeactivate); + wakeLockScope.stop(); }); } } diff --git a/frontend/src/lib/utils/wake-lock.ts b/frontend/src/lib/utils/wake-lock.ts new file mode 100644 index 00000000..689b3e8e --- /dev/null +++ b/frontend/src/lib/utils/wake-lock.ts @@ -0,0 +1,67 @@ +import { onScopeDispose, ref, shallowRef, watch, type Ref } from "vue"; +import { useDomEventListener } from "./utils"; + +/** + * Uses the Screen Wake Lock API (https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API) to keep + * the screen turned on while the provided "active" ref is true. + */ +export function useWakeLock(active: Ref): void { + if (!("wakeLock" in navigator)) { + return; + } + + const wakeLockPending = ref(false); + const wakeLock = shallowRef(); + + const requestWakeLock = async () => { + if (!wakeLockPending.value && !wakeLock.value) { + wakeLockPending.value = true; + try { + console.log("wake lock activate"); + wakeLock.value = await navigator.wakeLock.request("screen"); + wakeLock.value.addEventListener("release", () => { + wakeLock.value = undefined; + }); + } catch (err: any) { + console.warn("Error requesting wake lock", err); + } finally { + wakeLockPending.value = false; + } + + if (!active.value) { + // Wake lock was disabled in the meantime + releaseWakeLock(); + } + } + }; + + const releaseWakeLock = () => { + if (wakeLock.value) { + console.log("wake lock deactivate"); + // Will call "release" event handler which sets wakeLock.value to undefined + wakeLock.value.release().catch((err) => { + console.warn("Error releasing wake lock", err); + }); + } + }; + + watch(() => active.value, () => { + if (active.value) { + void requestWakeLock(); + } else { + releaseWakeLock(); + } + }, { immediate: true }); + + // Enable wake lock again if it was disabled by the browser because of moving out of the browser. + // See https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API#reacquiring_a_wake_lock + useDomEventListener(document, "visibilitychange", () => { + if (active.value && document.visibilityState === "visible") { + void requestWakeLock(); + } + }); + + onScopeDispose(() => { + releaseWakeLock(); + }); +} \ No newline at end of file