feat: remove geolocation logic from components

This commit is contained in:
log101 2024-07-21 20:11:44 +03:00
parent 100d329d47
commit 622c436a9f
4 changed files with 51 additions and 135 deletions

View File

@ -24,22 +24,11 @@ const GoToTargetControl = L.Control.extend({
}); });
const GeolocationControl = L.Control.extend({ const GeolocationControl = L.Control.extend({
options: {
title: "Konum İzni Ver",
},
_watchingLocation: false,
_containerEl: null as HTMLButtonElement | null, _containerEl: null as HTMLButtonElement | null,
_currentLocationMarker: null as L.Marker | null, _currentLocationMarker: null as L.Marker | null,
setCurrentLocationMarker: function (marker?: L.Marker) { setCurrentLocationMarker: function (marker?: L.Marker) {
if (marker) this._currentLocationMarker = marker; if (marker) this._currentLocationMarker = marker;
}, },
initialize: function (options: any) {
L.Util.setOptions(this, options);
},
startWatching: function () {
this._watchingLocation = true;
if (this._containerEl) this._containerEl.textContent = "Konumuma Git";
},
onAdd: function (map: L.Map) { onAdd: function (map: L.Map) {
const locationButton = document.createElement("button"); const locationButton = document.createElement("button");
@ -47,17 +36,15 @@ const GeolocationControl = L.Control.extend({
locationButton.id = "ask-permission-control-button"; locationButton.id = "ask-permission-control-button";
locationButton.textContent = this.options.title; locationButton.textContent = "Konumuma Git";
locationButton.classList.add("custom-map-control-button"); locationButton.classList.add("custom-map-control-button");
locationButton.type = "button"; locationButton.type = "button";
L.DomEvent.on(locationButton, "click", () => { L.DomEvent.on(locationButton, "click", () => {
if (this._watchingLocation && this._currentLocationMarker) { if (this._currentLocationMarker) {
map.setView(this._currentLocationMarker.getLatLng(), 12); map.setView(this._currentLocationMarker.getLatLng(), 12);
} else {
this.startWatching();
} }
}); });

View File

@ -40,19 +40,14 @@ function errorCallback(err: GeolocationPositionError) {
} }
function calculateDistance( function calculateDistance(
currentPosition: GeolocationPosition, currentPosition: L.LatLngTuple,
targetPosition: L.LatLngTuple targetPosition: L.LatLngTuple
) { ) {
const pos = {
lat: currentPosition.coords.latitude,
lng: currentPosition.coords.longitude,
};
// Get target position in latitudes and longitudes // Get target position in latitudes and longitudes
const targetLatLng = L.latLng(targetPosition); const targetLatLng = L.latLng(targetPosition);
// Get current position in latitudes and longitudes // Get current position in latitudes and longitudes
const currentLatLng = L.latLng(pos); const currentLatLng = L.latLng(currentPosition);
// Calculate the distance between target and current position in meters // Calculate the distance between target and current position in meters
const betweenMeters = currentLatLng.distanceTo(targetLatLng); const betweenMeters = currentLatLng.distanceTo(targetLatLng);

View File

@ -1,5 +1,11 @@
// Lit // Lit
import { html, LitElement, unsafeCSS, type CSSResultGroup } from "lit"; import {
html,
LitElement,
unsafeCSS,
type CSSResultGroup,
type PropertyValues,
} from "lit";
import { customElement, property, query, state } from "lit/decorators.js"; import { customElement, property, query, state } from "lit/decorators.js";
// Leaflet // Leaflet
@ -12,7 +18,6 @@ import { GeolocationControl, GoToTargetControl } from "./LeafletMap/controls";
import leafletStyles from "leaflet/dist/leaflet.css?inline"; import leafletStyles from "leaflet/dist/leaflet.css?inline";
import globalStyles from "@/styles/globals.css?inline"; import globalStyles from "@/styles/globals.css?inline";
import mapStyles from "@/styles/locked-page.css?inline"; import mapStyles from "@/styles/locked-page.css?inline";
import { onLocationError, onLocationSuccess } from "./LeafletMap/geolocation";
@customElement("leaflet-map") @customElement("leaflet-map")
export class LeafletMap extends LitElement { export class LeafletMap extends LitElement {
@ -34,27 +39,19 @@ export class LeafletMap extends LitElement {
_askPermissionButton!: HTMLButtonElement; _askPermissionButton!: HTMLButtonElement;
// Properties and states // Properties and states
@property({ type: Object }) targetLocation?: LatLngTuple; @property({ type: Object, noAccessor: true }) targetLocation?: LatLngTuple;
@property({ type: Object })
currentPosition?: LatLngTuple;
@state() @state()
protected _map?: L.Map; protected _map?: L.Map;
@state() @state()
protected _geolocationPermissionStatus: PermissionState = "prompt";
@state()
protected _currentLocationMarker?: L.Marker; protected _currentLocationMarker?: L.Marker;
@state()
protected _watchingLocation = false;
firstUpdated(): void { firstUpdated(): void {
if (!this._mapElement || !this.targetLocation) return; if (!this._mapElement || !this.targetLocation) return;
this._map = new Map(this._mapElement).setView(this.targetLocation, 13); this._map = new Map(this._mapElement).setView(this.targetLocation, 13);
this._map.on("locationerror", onLocationError);
this._map.on("locationfound", (ev) =>
onLocationSuccess(ev, this._map!, this._currentLocationMarker)
);
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 19, maxZoom: 19,
attribution: attribution:
@ -81,38 +78,21 @@ export class LeafletMap extends LitElement {
targetLocationControl.setTargetLocation(this.targetLocation); targetLocationControl.setTargetLocation(this.targetLocation);
targetLocationControl.addTo(this._map); targetLocationControl.addTo(this._map);
// Check geolocation permission, if user has given permission before const currentLocationControl = new GeolocationControl({
// start watching user location position: "bottomleft",
navigator.permissions });
.query({ name: "geolocation" })
.then((permissionStatus) => {
switch (permissionStatus.state) {
case "granted":
this._geolocationPermissionStatus = "granted";
const locateUserControl = new GeolocationControl({
// @ts-expect-error
title: "Konumuma Git",
position: "bottomleft",
});
locateUserControl.setCurrentLocationMarker(
this._currentLocationMarker
);
locateUserControl.addTo(this._map!);
break;
case "denied":
this._geolocationPermissionStatus = "denied";
break;
case "prompt":
const askPermissionControl = new GeolocationControl({
position: "bottomleft",
});
askPermissionControl.addTo(this._map!); currentLocationControl.setCurrentLocationMarker(
break; this._currentLocationMarker
default: );
break; currentLocationControl.addTo(this._map);
} }
});
protected update(changedProperties: PropertyValues): void {
super.update(changedProperties);
if (changedProperties.get("currentPosition")) {
this._currentLocationMarker?.setLatLng(this.currentPosition!);
}
} }
render() { render() {

View File

@ -18,7 +18,7 @@ import {
} from "./LockedContent/templates"; } from "./LockedContent/templates";
// Geolocation utils // Geolocation utils
import { calculateDistance, errorCallback } from "./LockedContent/geolocation"; import { calculateDistance } from "./LockedContent/geolocation";
import { incrementUnlockCounter } from "./LockedContent/serverUtils"; import { incrementUnlockCounter } from "./LockedContent/serverUtils";
// LockedContent is a custom element watching user location and blurring // LockedContent is a custom element watching user location and blurring
@ -44,35 +44,16 @@ export class LockedContent extends LitElement {
@property({ noAccessor: true }) readonly imageURL?: string; @property({ noAccessor: true }) readonly imageURL?: string;
@property({ type: Object, noAccessor: true }) @property({ type: Object, noAccessor: true })
readonly targetPosition?: LatLngTuple; readonly targetPosition?: LatLngTuple;
@property({ type: Object })
currentPosition?: LatLngTuple;
// Reactive states, template is rendered according to this states // Reactive states, template is rendered according to this states
@state() @state()
protected _geolocationPermissionStatus: PermissionState = "prompt";
@state()
protected _unlocked = false; protected _unlocked = false;
@state() @state()
protected _arrived = false; protected _arrived = false;
@state() @state()
protected _distanceText?: string; protected _distanceText?: string;
@state()
protected _watchId?: number;
// This callback will be fired when geolocation info is available
successCallback(position: GeolocationPosition) {
// Set hasGeolocationPermission state true to change the template
this._geolocationPermissionStatus = "granted";
// Target position must be set
if (!this.targetPosition) return;
// Calculate the distance between target and current position in meters
const distance = calculateDistance(position, this.targetPosition);
// Update the text based on the distance
this._updateDistanceText(distance);
this._checkArrived(distance);
}
private _updateDistanceText(distance: number) { private _updateDistanceText(distance: number) {
// Update the proximity text according to the distance remaining // Update the proximity text according to the distance remaining
@ -86,10 +67,6 @@ export class LockedContent extends LitElement {
private _checkArrived(distance: number) { private _checkArrived(distance: number) {
// If target is close less then 100 meters user has arrived to target location // If target is close less then 100 meters user has arrived to target location
if (distance < 100) { if (distance < 100) {
if (this._watchId) {
// Stop watching location
navigator.geolocation.clearWatch(this._watchId);
}
// Update state to reveal the image // Update state to reveal the image
this._arrived = true; this._arrived = true;
} }
@ -98,11 +75,26 @@ export class LockedContent extends LitElement {
// This template is shown when user hasn't give geolocation permission yet // This template is shown when user hasn't give geolocation permission yet
// When user click the button user is asked for geolocation permission // When user click the button user is asked for geolocation permission
private _permissionButtonTemplate = () => private _permissionButtonTemplate = () =>
permissionButtonTemplate(this._startWatchingLocation); permissionButtonTemplate(() => null);
// This template is shown when user has given permission but has not arrived yet // This template is shown when user has given permission but has not arrived yet
private _lockedButtonTemplate = () => private _lockedButtonTemplate = () => {
lockedButtonTemplate(this._distanceText); // Target position must be set
if (!this.targetPosition || !this.currentPosition) return;
// Calculate the distance between target and current position in meters
const distance = calculateDistance(
this.currentPosition,
this.targetPosition
);
// Update the text based on the distance
this._updateDistanceText(distance);
this._checkArrived(distance);
return lockedButtonTemplate(this._distanceText);
};
// This template is shown when user has arrived to the target location // This template is shown when user has arrived to the target location
// When user click the button counter at the bottom of the page is incremented // When user click the button counter at the bottom of the page is incremented
@ -113,46 +105,8 @@ export class LockedContent extends LitElement {
this._unlocked = true; this._unlocked = true;
}); });
// Start watching user location, if user has not given permission yet
// this will ask the user for permission and update the watch id
private _startWatchingLocation() {
// User is already being watched no need to
// watch position
if (this._watchId) return;
const id = navigator.geolocation.watchPosition(
this.successCallback.bind(this),
(err) => {
if (err.code == GeolocationPositionError.PERMISSION_DENIED) {
this._geolocationPermissionStatus = "denied";
}
errorCallback(err);
},
this.geolocationOptions
);
this._watchId = id;
}
connectedCallback(): void { connectedCallback(): void {
super.connectedCallback(); super.connectedCallback();
// Check geolocation permission, if user has given permission before
// start watching user location
navigator.permissions
.query({ name: "geolocation" })
.then((permissionStatus) => {
switch (permissionStatus.state) {
case "granted":
this._startWatchingLocation();
break;
case "denied":
this._geolocationPermissionStatus = "denied";
case "prompt":
default:
break;
}
});
} }
render() { render() {
@ -165,9 +119,9 @@ export class LockedContent extends LitElement {
// 4 - User did not give geolocation permission // 4 - User did not give geolocation permission
if (this._arrived) { if (this._arrived) {
buttonTemplate = this._unlockedButtonTemplate; buttonTemplate = this._unlockedButtonTemplate;
} else if (this._geolocationPermissionStatus == "granted") { } else if (this.currentPosition) {
buttonTemplate = this._lockedButtonTemplate; buttonTemplate = this._lockedButtonTemplate;
} else if (this._geolocationPermissionStatus == "prompt") { } else if (!this.currentPosition) {
buttonTemplate = this._permissionButtonTemplate; buttonTemplate = this._permissionButtonTemplate;
} else { } else {
buttonTemplate = permissionDeniedButtonTemplate; buttonTemplate = permissionDeniedButtonTemplate;