refactor: add utility classes for dom access
This commit is contained in:
parent
193b8604b6
commit
820cdc903b
46
src/components/LockedContent/domUtils.ts
Normal file
46
src/components/LockedContent/domUtils.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import { incrementUnlockCounter } from "./serverUtils";
|
||||||
|
|
||||||
|
function updateText(elemId: string, text: string) {
|
||||||
|
const elem = document.getElementById(elemId);
|
||||||
|
if (elem) elem.innerText = text;
|
||||||
|
else console.error("Element could not be found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleClass(elemId: string, className: string) {
|
||||||
|
const elem = document.getElementById(elemId);
|
||||||
|
if (elem) elem.classList.toggle(className);
|
||||||
|
else console.error("Element could not be found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeClasses(elemId: string, ...inputs: string[]) {
|
||||||
|
const elem = document.getElementById(elemId);
|
||||||
|
if (elem) elem.classList.remove(...inputs);
|
||||||
|
else console.error("Element could not be found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
function addClasses(elemId: string, ...inputs: string[]) {
|
||||||
|
const elem = document.getElementById(elemId);
|
||||||
|
if (elem) elem.classList.add(...inputs);
|
||||||
|
else console.error("Element could not be found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeElement(elemId: string) {
|
||||||
|
const elem = document.getElementById(elemId);
|
||||||
|
if (elem) elem.remove();
|
||||||
|
else console.error("Element could not be found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
function revealContent() {
|
||||||
|
incrementUnlockCounter(document.URL.slice(-12));
|
||||||
|
removeClasses("content", "blur-2xl");
|
||||||
|
removeElement("unlock-button-container");
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
addClasses,
|
||||||
|
removeClasses,
|
||||||
|
removeElement,
|
||||||
|
toggleClass,
|
||||||
|
updateText,
|
||||||
|
revealContent,
|
||||||
|
};
|
|
@ -1,5 +1,68 @@
|
||||||
import Toastify from "toastify-js";
|
import Toastify from "toastify-js";
|
||||||
import L from "leaflet";
|
import L, { type LatLngTuple } from "leaflet";
|
||||||
|
import {
|
||||||
|
addClasses,
|
||||||
|
removeClasses,
|
||||||
|
removeElement,
|
||||||
|
revealContent,
|
||||||
|
toggleClass,
|
||||||
|
updateText,
|
||||||
|
} from "./domUtils";
|
||||||
|
import { mapLocationSuccessCallback } from "@/scripts/initMap";
|
||||||
|
|
||||||
|
// Update the elements according to distance remaining
|
||||||
|
function locationSuccessCallback(
|
||||||
|
position: GeolocationPosition,
|
||||||
|
targetPosition: LatLngTuple
|
||||||
|
) {
|
||||||
|
const newPosition = position.coords;
|
||||||
|
|
||||||
|
// Calculate the distance remaining
|
||||||
|
const distance = calculateDistance(
|
||||||
|
[newPosition.latitude, newPosition.longitude],
|
||||||
|
targetPosition
|
||||||
|
);
|
||||||
|
|
||||||
|
// If user has arrived to destination
|
||||||
|
if (distance < 100) {
|
||||||
|
// Change the description texts
|
||||||
|
updateText("button-text", "İçeriği Göster");
|
||||||
|
updateText("locked-content-description", "İçeriği görmek için butona bas!");
|
||||||
|
|
||||||
|
// Swap the icon
|
||||||
|
toggleClass("lock-icon", "hidden");
|
||||||
|
toggleClass("unlock-icon", "hidden");
|
||||||
|
|
||||||
|
// Tansform the unlock button
|
||||||
|
removeClasses("unlock-content-button", "bg-primary", "hover:bg-primary/90");
|
||||||
|
addClasses(
|
||||||
|
"unlock-content-button",
|
||||||
|
"bg-indigo-600",
|
||||||
|
"hover:bg-indigo-700",
|
||||||
|
"hover:animate-none",
|
||||||
|
"border-2",
|
||||||
|
"border-indigo-800"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Wait for transition to finish then add animation
|
||||||
|
setTimeout(() => {
|
||||||
|
removeClasses("unlock-content-button", "duration-1000");
|
||||||
|
addClasses("unlock-content-button", "animate-pulse");
|
||||||
|
}, 800);
|
||||||
|
|
||||||
|
// Reveal image when the unlock button is clicked
|
||||||
|
const unlockButton = document.getElementById("unlock-content-button");
|
||||||
|
unlockButton?.addEventListener("click", revealContent);
|
||||||
|
} else {
|
||||||
|
const distanceText = generateDistanceText(distance);
|
||||||
|
updateText("locked-content-description", `Kalan mesafe: ${distanceText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeElement("location-permission-button");
|
||||||
|
|
||||||
|
// Update leaflet controls
|
||||||
|
mapLocationSuccessCallback(position);
|
||||||
|
}
|
||||||
|
|
||||||
// This callback will be fired on geolocation error
|
// This callback will be fired on geolocation error
|
||||||
function errorCallback(err: GeolocationPositionError) {
|
function errorCallback(err: GeolocationPositionError) {
|
||||||
|
@ -55,4 +118,19 @@ function calculateDistance(
|
||||||
return betweenMeters;
|
return betweenMeters;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { errorCallback, calculateDistance };
|
// Generates a human readable destination text according to
|
||||||
|
// distance remaining
|
||||||
|
function generateDistanceText(distance: number) {
|
||||||
|
if (distance > 1000) {
|
||||||
|
return `${(distance / 1000).toFixed()} KM`;
|
||||||
|
} else if (distance > 100) {
|
||||||
|
return `${distance.toFixed(0)} M`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
errorCallback,
|
||||||
|
locationSuccessCallback,
|
||||||
|
calculateDistance,
|
||||||
|
generateDistanceText,
|
||||||
|
};
|
||||||
|
|
|
@ -33,7 +33,7 @@ L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||||
|
|
||||||
let currentLocationMarker: L.Marker;
|
let currentLocationMarker: L.Marker;
|
||||||
|
|
||||||
export function onLocationSuccess(position: GeolocationPosition) {
|
export function mapLocationSuccessCallback(position: GeolocationPosition) {
|
||||||
const currentPos = {
|
const currentPos = {
|
||||||
lat: position.coords.latitude,
|
lat: position.coords.latitude,
|
||||||
lng: position.coords.longitude,
|
lng: position.coords.longitude,
|
||||||
|
|
|
@ -1,132 +1,58 @@
|
||||||
import {
|
import {
|
||||||
calculateDistance,
|
|
||||||
errorCallback,
|
errorCallback,
|
||||||
|
locationSuccessCallback,
|
||||||
} from "@/components/LockedContent/geolocation";
|
} from "@/components/LockedContent/geolocation";
|
||||||
import { incrementUnlockCounter } from "@/components/LockedContent/serverUtils";
|
import type { LatLngTuple } from "leaflet";
|
||||||
import { onLocationSuccess } from "@/scripts/initMap";
|
|
||||||
|
// Geolocation watch id to avoid duplicate watchPosition calls
|
||||||
let watchId: number;
|
let watchId: number;
|
||||||
|
|
||||||
const targetLocation = document.getElementById("locked-content-container")
|
// DOM ELEMENTS
|
||||||
?.dataset.targetPosition;
|
|
||||||
|
|
||||||
// This element display the distance between the user and the
|
|
||||||
// target if geolocation permission is granted
|
|
||||||
const descriptionElement = document.getElementById(
|
|
||||||
"locked-content-description"
|
|
||||||
);
|
|
||||||
const locationPermissionButton = document.getElementById(
|
const locationPermissionButton = document.getElementById(
|
||||||
"location-permission-button"
|
"location-permission-button"
|
||||||
);
|
);
|
||||||
|
|
||||||
const unlockButton = document.getElementById("unlock-content-button");
|
const unlockButton = document.getElementById("unlock-content-button");
|
||||||
const unlockIcon = document.getElementById("unlock-icon");
|
|
||||||
const lockIcon = document.getElementById("lock-icon");
|
|
||||||
const buttonText = document.getElementById("button-text");
|
|
||||||
const description = document.getElementById("locked-content-description");
|
|
||||||
|
|
||||||
function updateText(elemId: string, text: string) {
|
|
||||||
const elem = document.getElementById(elemId);
|
|
||||||
if (elem) elem.innerText = text;
|
|
||||||
else console.error("Element could not be found!");
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleClass(elemId: string, className: string) {
|
|
||||||
const elem = document.getElementById(elemId);
|
|
||||||
if (elem) elem.classList.toggle(className);
|
|
||||||
else console.error("Element could not be found!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (locationPermissionButton)
|
|
||||||
locationPermissionButton.addEventListener("click", startWatchingLocation);
|
|
||||||
|
|
||||||
// Generates a human readable destination text according to
|
|
||||||
// distance remaining
|
|
||||||
function generateDistanceText(distance: number) {
|
|
||||||
if (distance > 1000) {
|
|
||||||
return `${(distance / 1000).toFixed()} KM`;
|
|
||||||
} else if (distance > 100) {
|
|
||||||
return `${distance.toFixed(0)} M`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the elements according to distance remaining
|
|
||||||
function updateCurrentLocation(position: GeolocationPosition) {
|
|
||||||
const newPosition = position.coords;
|
|
||||||
|
|
||||||
if (!targetLocation) return;
|
|
||||||
// Calculate the distance remaining
|
|
||||||
const distance = calculateDistance(
|
|
||||||
[newPosition.latitude, newPosition.longitude],
|
|
||||||
JSON.parse(targetLocation)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (distance < 100) {
|
|
||||||
// If user has arrived to destination
|
|
||||||
// Transform locked button to reveal button
|
|
||||||
updateText("button-text", "İçeriği Göster");
|
|
||||||
toggleClass("lock-icon", "hidden");
|
|
||||||
toggleClass("unlock-icon", "hidden");
|
|
||||||
updateText("locked-content-description", "İçeriği görmek için butona bas!");
|
|
||||||
unlockButton?.classList.remove("bg-primary", "hover:bg-primary/90");
|
|
||||||
unlockButton?.classList.add(
|
|
||||||
"bg-indigo-600",
|
|
||||||
"hover:bg-indigo-700",
|
|
||||||
"hover:animate-none",
|
|
||||||
"border-2",
|
|
||||||
"border-indigo-800"
|
|
||||||
);
|
|
||||||
setTimeout(() => {
|
|
||||||
unlockButton?.classList.remove("duration-1000");
|
|
||||||
unlockButton?.classList.add("animate-pulse");
|
|
||||||
}, 800);
|
|
||||||
|
|
||||||
unlockButton?.addEventListener("click", () => {
|
|
||||||
const image = document.getElementById("content");
|
|
||||||
const unlockButtonContainer = document.getElementById(
|
|
||||||
"unlock-button-container"
|
|
||||||
);
|
|
||||||
incrementUnlockCounter(document.URL.slice(-12));
|
|
||||||
if (image) image.classList.remove("blur-2xl");
|
|
||||||
if (unlockButtonContainer) unlockButtonContainer.remove();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const distanceText = generateDistanceText(distance);
|
|
||||||
|
|
||||||
if (descriptionElement)
|
|
||||||
descriptionElement.innerText = `Kalan mesafe: ${distanceText}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (locationPermissionButton) {
|
|
||||||
locationPermissionButton.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update leaflet controls
|
|
||||||
onLocationSuccess(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
function startWatchingLocation() {
|
|
||||||
if (!watchId) {
|
|
||||||
watchId = window.navigator.geolocation.watchPosition(
|
|
||||||
updateCurrentLocation,
|
|
||||||
errorCallback
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const lockedContentContainer = document.getElementById(
|
const lockedContentContainer = document.getElementById(
|
||||||
"locked-content-container"
|
"locked-content-container"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (lockedContentContainer)
|
// These elements MUST be defined
|
||||||
lockedContentContainer.addEventListener(
|
// Throw error if they are not found
|
||||||
"askpermission",
|
if (!locationPermissionButton || !lockedContentContainer || !unlockButton) {
|
||||||
startWatchingLocation
|
throw new Error("Element not found!");
|
||||||
);
|
}
|
||||||
|
|
||||||
|
// EVENT LISTENERS
|
||||||
|
locationPermissionButton.addEventListener("click", startWatchingLocation);
|
||||||
|
|
||||||
|
lockedContentContainer.addEventListener("askpermission", startWatchingLocation);
|
||||||
|
|
||||||
|
const targetPositionString = lockedContentContainer.dataset.targetPosition;
|
||||||
|
|
||||||
|
// TARGET_POSITION is required to calculate distance
|
||||||
|
if (!targetPositionString) throw new Error("Target position is not provided!");
|
||||||
|
|
||||||
|
const TARGET_POSITION = JSON.parse(targetPositionString) as LatLngTuple;
|
||||||
|
|
||||||
|
// Call Geolocation API to start watching user location
|
||||||
|
function startWatchingLocation() {
|
||||||
|
if (!watchId) {
|
||||||
|
watchId = window.navigator.geolocation.watchPosition(
|
||||||
|
position => locationSuccessCallback(position, TARGET_POSITION),
|
||||||
|
errorCallback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the web page is loaded, check if user has given geolocation
|
||||||
|
// permission before
|
||||||
navigator.permissions.query({ name: "geolocation" }).then(permissionStatus => {
|
navigator.permissions.query({ name: "geolocation" }).then(permissionStatus => {
|
||||||
switch (permissionStatus.state) {
|
switch (permissionStatus.state) {
|
||||||
case "granted":
|
case "granted":
|
||||||
watchId = window.navigator.geolocation.watchPosition(
|
watchId = window.navigator.geolocation.watchPosition(
|
||||||
updateCurrentLocation,
|
position => locationSuccessCallback(position, TARGET_POSITION),
|
||||||
errorCallback
|
errorCallback
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -136,5 +62,3 @@ navigator.permissions.query({ name: "geolocation" }).then(permissionStatus => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export { startWatchingLocation };
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
},
|
},
|
||||||
|
"noUnusedLocals": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"useDefineForClassFields": false,
|
"useDefineForClassFields": false,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user