diff --git a/langs/en.json b/langs/en.json index 7d75d0a7bd..f25b61951f 100644 --- a/langs/en.json +++ b/langs/en.json @@ -344,6 +344,8 @@ }, "useSearch": "Use the search above to see presets", "useSearchForMore": "Use the search function to search within {total} more values…", + "waitingForGeopermission": "Waiting for your permission to use the geolocation...", + "waitingForLocation": "Searching your current location...", "weekdays": { "abbreviations": { "friday": "Fri", diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index b6011afb63..f38fd858e6 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -858,6 +858,10 @@ video { margin-right: 3rem; } +.mb-4 { + margin-bottom: 1rem; +} + .mr-2 { margin-right: 0.5rem; } @@ -886,10 +890,6 @@ video { margin-right: 0.25rem; } -.mb-4 { - margin-bottom: 1rem; -} - .ml-1 { margin-left: 0.25rem; } @@ -2662,6 +2662,46 @@ a.link-underline { opacity: 1; } +@media (prefers-reduced-motion: no-preference) { + @-webkit-keyframes spin { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + @keyframes spin { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + + .motion-safe\:animate-spin { + -webkit-animation: spin 1s linear infinite; + animation: spin 1s linear infinite; + } +} + +@media (prefers-reduced-motion: reduce) { + @-webkit-keyframes spin { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + @keyframes spin { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + + .motion-reduce\:animate-spin { + -webkit-animation: spin 1s linear infinite; + animation: spin 1s linear infinite; + } +} + @media (max-width: 480px) { .max-\[480px\]\:w-full { width: 100%; diff --git a/src/Logic/State/GeoLocationState.ts b/src/Logic/State/GeoLocationState.ts index e88b6bfa77..ff7f3ac449 100644 --- a/src/Logic/State/GeoLocationState.ts +++ b/src/Logic/State/GeoLocationState.ts @@ -1,13 +1,13 @@ -import { UIEventSource } from "../UIEventSource" -import { LocalStorageSource } from "../Web/LocalStorageSource" -import { QueryParameters } from "../Web/QueryParameters" +import { UIEventSource } from "../UIEventSource"; +import { LocalStorageSource } from "../Web/LocalStorageSource"; +import { QueryParameters } from "../Web/QueryParameters"; export type GeolocationPermissionState = "prompt" | "requested" | "granted" | "denied" export interface GeoLocationPointProperties extends GeolocationCoordinates { - id: "gps" - "user:location": "yes" - date: string + id: "gps"; + "user:location": "yes"; + date: string; } /** @@ -23,22 +23,22 @@ export class GeoLocationState { */ public readonly permission: UIEventSource = new UIEventSource( "prompt" - ) + ); /** * Important to determine e.g. if we move automatically on fix or not */ - public readonly requestMoment: UIEventSource = new UIEventSource(undefined) + public readonly requestMoment: UIEventSource = new UIEventSource(undefined); /** * If true: the map will center (and re-center) to this location */ - public readonly allowMoving: UIEventSource = new UIEventSource(true) + public readonly allowMoving: UIEventSource = new UIEventSource(true); /** * The latest GeoLocationCoordinates, as given by the WebAPI */ public readonly currentGPSLocation: UIEventSource = - new UIEventSource(undefined) + new UIEventSource(undefined); /** * A small flag on localstorage. If the user previously granted the geolocation, it will be set. @@ -50,69 +50,50 @@ export class GeoLocationState { */ private readonly _previousLocationGrant: UIEventSource<"true" | "false"> = ( LocalStorageSource.Get("geolocation-permissions") - ) + ); /** * Used to detect a permission retraction */ - private readonly _grantedThisSession: UIEventSource = new UIEventSource(false) + private readonly _grantedThisSession: UIEventSource = new UIEventSource(false); + constructor() { - const self = this + const self = this; this.permission.addCallbackAndRunD(async (state) => { + console.trace("GEOPERMISSION", state) if (state === "granted") { - self._previousLocationGrant.setData("true") - self._grantedThisSession.setData(true) + self._previousLocationGrant.setData("true"); + self._grantedThisSession.setData(true); } if (state === "prompt" && self._grantedThisSession.data) { // This is _really_ weird: we had a grant earlier, but it's 'prompt' now? // This means that the rights have been revoked again! - // self.permission.setData("denied") - self._previousLocationGrant.setData("false") - self.permission.setData("denied") - self.currentGPSLocation.setData(undefined) - console.warn("Detected a downgrade in permissions!") + self._previousLocationGrant.setData("false"); + self.permission.setData("denied"); + self.currentGPSLocation.setData(undefined); + console.warn("Detected a downgrade in permissions!"); } if (state === "denied") { - self._previousLocationGrant.setData("false") + self._previousLocationGrant.setData("false"); } - }) - console.log("Previous location grant:", this._previousLocationGrant.data) + }); + console.log("Previous location grant:", this._previousLocationGrant.data); if (this._previousLocationGrant.data === "true") { // A previous visit successfully granted permission. Chance is high that we are allowed to use it again! // We set the flag to false again. If the user only wanted to share their location once, we are not gonna keep bothering them - this._previousLocationGrant.setData("false") - console.log("Requesting access to GPS as this was previously granted") + this._previousLocationGrant.setData("false"); + console.log("Requesting access to GPS as this was previously granted"); const latLonGivenViaUrl = - QueryParameters.wasInitialized("lat") || QueryParameters.wasInitialized("lon") + QueryParameters.wasInitialized("lat") || QueryParameters.wasInitialized("lon"); if (!latLonGivenViaUrl) { - this.requestMoment.setData(new Date()) + this.requestMoment.setData(new Date()); } - this.requestPermission() + this.requestPermission(); } } - /** - * Installs the listener for updates - * @private - */ - private async startWatching() { - const self = this - navigator.geolocation.watchPosition( - function (position) { - self.currentGPSLocation.setData(position.coords) - self._previousLocationGrant.setData("true") - }, - function () { - console.warn("Could not get location with navigator.geolocation") - }, - { - enableHighAccuracy: true, - } - ) - } - /** * Requests the user to allow access to their position. * When granted, will be written to the 'geolocationState'. @@ -121,33 +102,57 @@ export class GeoLocationState { public requestPermission() { if (typeof navigator === "undefined") { // Not compatible with this browser - this.permission.setData("denied") - return + this.permission.setData("denied"); + return; } if (this.permission.data !== "prompt" && this.permission.data !== "requested") { // If the user denies the first prompt, revokes the deny and then tries again, we have to run the flow as well // Hence that we continue the flow if it is "requested" - return + return; } - this.permission.setData("requested") + this.permission.setData("requested"); try { navigator?.permissions ?.query({ name: "geolocation" }) .then((status) => { - console.log("Status update: received geolocation permission is ", status.state) - this.permission.setData(status.state) - const self = this - status.onchange = function () { + const self = this; + if(status.state === "granted" || status.state === "denied"){ self.permission.setData(status.state) + return } + status.addEventListener("change", (e) => { + self.permission.setData(status.state); + + }); + // The code above might have reset it to 'prompt', but we _did_ request permission! this.permission.setData("requested") // We _must_ call 'startWatching', as that is the actual trigger for the popup... - self.startWatching() + self.startWatching(); }) - .catch((e) => console.error("Could not get geopermission", e)) + .catch((e) => console.error("Could not get geopermission", e)); } catch (e) { - console.error("Could not get permission:", e) + console.error("Could not get permission:", e); } } + + /** + * Installs the listener for updates + * @private + */ + private async startWatching() { + const self = this; + navigator.geolocation.watchPosition( + function(position) { + self.currentGPSLocation.setData(position.coords); + self._previousLocationGrant.setData("true"); + }, + function() { + console.warn("Could not get location with navigator.geolocation"); + }, + { + enableHighAccuracy: true + } + ); + } } diff --git a/src/UI/BigComponents/ThemeIntroPanel.svelte b/src/UI/BigComponents/ThemeIntroPanel.svelte index 8806d21fee..2e43de9b08 100644 --- a/src/UI/BigComponents/ThemeIntroPanel.svelte +++ b/src/UI/BigComponents/ThemeIntroPanel.svelte @@ -1,40 +1,44 @@ @@ -58,12 +62,24 @@
- p === "denied")}> + {#if $currentGPSLocation !== undefined || $geopermission === "prompt"} - + + {:else if $geopermission === "requested"} + + {:else if $geopermission !== "denied"} + + {/if}