refactor: add utility classes for dom access

This commit is contained in:
log101 2024-07-26 21:23:56 +03:00
parent 193b8604b6
commit 820cdc903b
5 changed files with 164 additions and 115 deletions

View 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,
};

View File

@ -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,
};

View File

@ -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,

View File

@ -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 };

View File

@ -7,6 +7,7 @@
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["./src/*"]
}, },
"noUnusedLocals": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"useDefineForClassFields": false, "useDefineForClassFields": false,
} }