diff --git a/src/components/LockedContent/geolocation.ts b/src/components/LockedContent/geolocation.ts
new file mode 100644
index 0000000..9fc6ec7
--- /dev/null
+++ b/src/components/LockedContent/geolocation.ts
@@ -0,0 +1,63 @@
+import Toastify from "toastify-js";
+import L from "leaflet";
+
+// This callback will be fired on geolocation error
+function errorCallback(err: GeolocationPositionError) {
+ let errorMessage;
+ // Show toast accoring to the error state
+ switch (err.code) {
+ case GeolocationPositionError.PERMISSION_DENIED:
+ errorMessage =
+ "Konum izni alınamadı, lütfen tarayıcınızın ve cihazınızın gizlilik ayarlarını kontrol edin.";
+ break;
+ case GeolocationPositionError.POSITION_UNAVAILABLE:
+ errorMessage =
+ "Konumunuz tespit edilemedi, lütfen biraz sonra tekrar deneyiniz.";
+ break;
+ case GeolocationPositionError.TIMEOUT:
+ errorMessage =
+ "Konum isteği zaman aşımına uğradı, lütfen sayfayı yenileyip tekrar deneyiniz.";
+ break;
+ default:
+ errorMessage =
+ "Konum izni alınamadı, lütfen tarayıcınızın ve cihazınızın gizlilik ayarlarını kontrol edin.";
+ break;
+ }
+
+ Toastify({
+ text: errorMessage,
+ duration: 3000,
+ gravity: "top",
+ position: "center",
+ stopOnFocus: true,
+ style: {
+ background: "black",
+ borderRadius: "6px",
+ margin: "16px",
+ },
+ onClick: function () {},
+ }).showToast();
+}
+
+function calculateDistance(
+ currentPosition: GeolocationPosition,
+ 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);
+
+ // Calculate the distance between target and current position in meters
+ const betweenMeters = currentLatLng.distanceTo(targetLatLng);
+
+ return betweenMeters;
+}
+
+export { errorCallback, calculateDistance };
diff --git a/src/components/LockedContent/middleware.ts b/src/components/LockedContent/middleware.ts
new file mode 100644
index 0000000..2c00837
--- /dev/null
+++ b/src/components/LockedContent/middleware.ts
@@ -0,0 +1,11 @@
+// This counter is shown at the bottom of the page and incremented
+// each time "show content" button is clicked
+function incrementUnlockCounter(id: string | undefined) {
+ if (id) {
+ fetch(`http://localhost:3000/api/location/increment/${id}`, {
+ method: "PATCH",
+ });
+ }
+}
+
+export { incrementUnlockCounter };
diff --git a/src/components/LockedContent/svg.ts b/src/components/LockedContent/svg.ts
new file mode 100644
index 0000000..f3ab932
--- /dev/null
+++ b/src/components/LockedContent/svg.ts
@@ -0,0 +1,31 @@
+import { html } from "lit";
+
+// Locked lock icon
+const lockSVG = html``;
+
+// Unlocked lock icon
+const unlockSVG = html`
+
+`;
+
+export { lockSVG, unlockSVG };
diff --git a/src/components/LockedContent/templates.ts b/src/components/LockedContent/templates.ts
index a824f10..a81ff21 100644
--- a/src/components/LockedContent/templates.ts
+++ b/src/components/LockedContent/templates.ts
@@ -1,4 +1,5 @@
import { html, type TemplateResult } from "lit";
+import { lockSVG, unlockSVG } from "./svg";
// This template is shown when user hasn't give geolocation permission yet
// When user click the button user is asked for geolocation permission
@@ -29,13 +30,13 @@ function permissionButtonTemplate(onClickHandler: () => void) {
}
// This template is shown when user has given permission but has not arrived yet
-function lockedButtonTemplate(icon: TemplateResult<1>, proximityText: string) {
+function lockedButtonTemplate(proximityText: string | undefined) {
return html`
@@ -51,17 +52,14 @@ function lockedButtonTemplate(icon: TemplateResult<1>, proximityText: string) {
// 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
// and image is revealed
-function unlockedButtonTemplate(
- icon: TemplateResult<1>,
- onClickHandler: () => void
-) {
+function unlockedButtonTemplate(onClickHandler: () => void) {
return html`
diff --git a/src/components/locked-content-lit.ts b/src/components/locked-content-lit.ts
index 5a85317..3564daf 100644
--- a/src/components/locked-content-lit.ts
+++ b/src/components/locked-content-lit.ts
@@ -1,12 +1,27 @@
+// Lit imports
import { LitElement, html, nothing, unsafeCSS, type CSSResultGroup } from "lit";
import { customElement, property, state } from "lit/decorators.js";
-import L, { type LatLngTuple } from "leaflet";
-import Toastify from "toastify-js";
+// Leaflet
+import { type LatLngTuple } from "leaflet";
+// Styles
import globalStyles from "@/styles/globals.css?inline";
import lockedContentStyles from "../styles/locked-content.css?inline";
+// Templates
+import {
+ lockedButtonTemplate,
+ permissionButtonTemplate,
+ unlockedButtonTemplate,
+} from "./LockedContent/templates";
+
+// Geolocation utils
+import { calculateDistance, errorCallback } from "./LockedContent/geolocation";
+import { incrementUnlockCounter } from "./LockedContent/middleware";
+
+// LockedContent is a custom element watching user location and blurring
+// given image until user has arrived a certain position
@customElement("locked-content-lit")
export class LockedContent extends LitElement {
// Constants
@@ -37,212 +52,82 @@ export class LockedContent extends LitElement {
@state()
protected _arrived = false;
@state()
- protected _targetProximityText?: string;
+ protected _distanceText?: string;
@state()
protected _watchId?: number;
- // Locked lock icon
- lockSVG = html``;
-
- // Unlocked lock icon
- unlockSVG = html`
-
- `;
-
// This callback will be fired when geolocation info is available
successCallback(position: GeolocationPosition) {
// Set hasGeolocationPermission state true to change the template
if (!this._hasGeolocationPermission) this._hasGeolocationPermission = true;
- const pos = {
- lat: position.coords.latitude,
- lng: position.coords.longitude,
- };
-
- // targetPosition attribute must be set for geolocation feature to work
+ // Target position must be set
if (!this.targetPosition) return;
- // Get target position in latitudes and longitudes
- const targetLatLng = L.latLng(this.targetPosition);
-
- // Get current position in latitudes and longitudes
- const currentLatLng = L.latLng(pos);
-
// Calculate the distance between target and current position in meters
- const betweenMeters = currentLatLng.distanceTo(targetLatLng);
+ 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
- if (betweenMeters > 1000) {
- this._targetProximityText = `${(betweenMeters / 1000).toFixed()} KM`;
- } else if (betweenMeters > 100) {
- this._targetProximityText = `${betweenMeters.toFixed(0)} M`;
- } else {
- // If target is close less then 100 meters user has arrived to target location
- if (this._watchId) {
- // Stop watching location
- navigator.geolocation.clearWatch(this._watchId);
- // Update state to reveal the image
- this._arrived = true;
- }
+ if (distance > 1000) {
+ this._distanceText = `${(distance / 1000).toFixed()} KM`;
+ } else if (distance > 100) {
+ this._distanceText = `${distance.toFixed(0)} M`;
}
}
- // This callback will be fired on geolocation error
- errorCallback(err: GeolocationPositionError) {
- let errorMessage;
- // Show toast accoring to the error state
- switch (err.code) {
- case GeolocationPositionError.PERMISSION_DENIED:
- errorMessage =
- "Konum izni alınamadı, lütfen tarayıcınızın ve cihazınızın gizlilik ayarlarını kontrol edin.";
- break;
- case GeolocationPositionError.POSITION_UNAVAILABLE:
- errorMessage =
- "Konumunuz tespit edilemedi, lütfen biraz sonra tekrar deneyiniz.";
- break;
- case GeolocationPositionError.TIMEOUT:
- errorMessage =
- "Konum isteği zaman aşımına uğradı, lütfen sayfayı yenileyip tekrar deneyiniz.";
- break;
- default:
- errorMessage =
- "Konum izni alınamadı, lütfen tarayıcınızın ve cihazınızın gizlilik ayarlarını kontrol edin.";
- break;
+ 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;
}
-
- Toastify({
- text: errorMessage,
- duration: 3000,
- gravity: "top",
- position: "center",
- stopOnFocus: true,
- style: {
- background: "black",
- borderRadius: "6px",
- margin: "16px",
- },
- onClick: function () {},
- }).showToast();
}
// This template is shown when user hasn't give geolocation permission yet
// When user click the button user is asked for geolocation permission
- permissionButtonTemplate() {
- return html`
-
-
-
-
-
- Ne kadar yaklaştığını görmek için aşağıdaki butona bas.
-
-
-
-
-
- `;
- }
+ private _permissionButtonTemplate = () =>
+ permissionButtonTemplate(this._startWatchingLocation);
// This template is shown when user has given permission but has not arrived yet
- lockedButtonTemplate() {
- return html`
-
-
-
-
- İçeriği görmek için konuma gitmelisin! Kalan mesafe:
- ${this._targetProximityText}
-
-
-
-
`;
- }
+ private _lockedButtonTemplate = () =>
+ 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
// and image is revealed
- unlockedButtonTemplate() {
- return html`
-
-
-
-
-
İçeriği görmek için butona bas!
-
-
-
`;
- }
+ private _unlockedButtonTemplate = () =>
+ unlockedButtonTemplate(() => {
+ incrementUnlockCounter(this.imageId);
+ 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),
- this.errorCallback.bind(this),
+ errorCallback,
this.geolocationOptions
);
this._watchId = id;
}
- // This counter is shown at the bottom of the page and incremented
- // each time "show content" button is clicked
- private async _incrementUnlockCounter(id: string | undefined) {
- if (id) {
- fetch(`http://localhost:3000/api/location/increment/${id}`, {
- method: "PATCH",
- });
- }
- }
-
connectedCallback(): void {
super.connectedCallback();
@@ -253,13 +138,11 @@ export class LockedContent extends LitElement {
.then((permissionStatus) => {
switch (permissionStatus.state) {
case "granted":
- this._hasGeolocationPermission = true;
this._startWatchingLocation();
break;
case "denied":
case "prompt":
default:
- this._hasGeolocationPermission = false;
break;
}
});
@@ -274,11 +157,11 @@ export class LockedContent extends LitElement {
// 3 - Arrived to target position
// 4 - User did not give geolocation permission
if (this._arrived) {
- buttonTemplate = this.unlockedButtonTemplate.bind(this);
+ buttonTemplate = this._unlockedButtonTemplate;
} else if (this._hasGeolocationPermission) {
- buttonTemplate = this.lockedButtonTemplate.bind(this);
+ buttonTemplate = this._lockedButtonTemplate;
} else {
- buttonTemplate = this.permissionButtonTemplate.bind(this);
+ buttonTemplate = this._permissionButtonTemplate;
}
return html`
@@ -289,7 +172,7 @@ export class LockedContent extends LitElement {
${this._unlocked ? nothing : buttonTemplate()}