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

View File

@ -40,19 +40,14 @@ function errorCallback(err: GeolocationPositionError) {
}
function calculateDistance(
currentPosition: GeolocationPosition,
currentPosition: L.LatLngTuple,
targetPosition: L.LatLngTuple
) {
const pos = {
lat: currentPosition.coords.latitude,
lng: currentPosition.coords.longitude,
};
// Get target position in latitudes and longitudes
const targetLatLng = L.latLng(targetPosition);
// 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
const betweenMeters = currentLatLng.distanceTo(targetLatLng);

View File

@ -1,5 +1,11 @@
// 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";
// Leaflet
@ -12,7 +18,6 @@ import { GeolocationControl, GoToTargetControl } from "./LeafletMap/controls";
import leafletStyles from "leaflet/dist/leaflet.css?inline";
import globalStyles from "@/styles/globals.css?inline";
import mapStyles from "@/styles/locked-page.css?inline";
import { onLocationError, onLocationSuccess } from "./LeafletMap/geolocation";
@customElement("leaflet-map")
export class LeafletMap extends LitElement {
@ -34,27 +39,19 @@ export class LeafletMap extends LitElement {
_askPermissionButton!: HTMLButtonElement;
// Properties and states
@property({ type: Object }) targetLocation?: LatLngTuple;
@property({ type: Object, noAccessor: true }) targetLocation?: LatLngTuple;
@property({ type: Object })
currentPosition?: LatLngTuple;
@state()
protected _map?: L.Map;
@state()
protected _geolocationPermissionStatus: PermissionState = "prompt";
@state()
protected _currentLocationMarker?: L.Marker;
@state()
protected _watchingLocation = false;
firstUpdated(): void {
if (!this._mapElement || !this.targetLocation) return;
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", {
maxZoom: 19,
attribution:
@ -81,38 +78,21 @@ export class LeafletMap extends LitElement {
targetLocationControl.setTargetLocation(this.targetLocation);
targetLocationControl.addTo(this._map);
// 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._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({
const currentLocationControl = new GeolocationControl({
position: "bottomleft",
});
askPermissionControl.addTo(this._map!);
break;
default:
break;
currentLocationControl.setCurrentLocationMarker(
this._currentLocationMarker
);
currentLocationControl.addTo(this._map);
}
protected update(changedProperties: PropertyValues): void {
super.update(changedProperties);
if (changedProperties.get("currentPosition")) {
this._currentLocationMarker?.setLatLng(this.currentPosition!);
}
});
}
render() {

View File

@ -18,7 +18,7 @@ import {
} from "./LockedContent/templates";
// Geolocation utils
import { calculateDistance, errorCallback } from "./LockedContent/geolocation";
import { calculateDistance } from "./LockedContent/geolocation";
import { incrementUnlockCounter } from "./LockedContent/serverUtils";
// 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({ type: Object, noAccessor: true })
readonly targetPosition?: LatLngTuple;
@property({ type: Object })
currentPosition?: LatLngTuple;
// Reactive states, template is rendered according to this states
@state()
protected _geolocationPermissionStatus: PermissionState = "prompt";
@state()
protected _unlocked = false;
@state()
protected _arrived = false;
@state()
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) {
// Update the proximity text according to the distance remaining
@ -86,10 +67,6 @@ export class LockedContent extends LitElement {
private _checkArrived(distance: number) {
// If target is close less then 100 meters user has arrived to target location
if (distance < 100) {
if (this._watchId) {
// Stop watching location
navigator.geolocation.clearWatch(this._watchId);
}
// Update state to reveal the image
this._arrived = true;
}
@ -98,11 +75,26 @@ export class LockedContent extends LitElement {
// This template is shown when user hasn't give geolocation permission yet
// When user click the button user is asked for geolocation permission
private _permissionButtonTemplate = () =>
permissionButtonTemplate(this._startWatchingLocation);
permissionButtonTemplate(() => null);
// This template is shown when user has given permission but has not arrived yet
private _lockedButtonTemplate = () =>
lockedButtonTemplate(this._distanceText);
private _lockedButtonTemplate = () => {
// 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
// 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;
});
// 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 {
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() {
@ -165,9 +119,9 @@ export class LockedContent extends LitElement {
// 4 - User did not give geolocation permission
if (this._arrived) {
buttonTemplate = this._unlockedButtonTemplate;
} else if (this._geolocationPermissionStatus == "granted") {
} else if (this.currentPosition) {
buttonTemplate = this._lockedButtonTemplate;
} else if (this._geolocationPermissionStatus == "prompt") {
} else if (!this.currentPosition) {
buttonTemplate = this._permissionButtonTemplate;
} else {
buttonTemplate = permissionDeniedButtonTemplate;