292 lines
7.2 KiB
JavaScript
292 lines
7.2 KiB
JavaScript
// public/js/activityModal.js
|
||
|
||
let activityMap = null;
|
||
|
||
function closeActivityModal() {
|
||
const modal = document.getElementById("activityModal");
|
||
const container = document.getElementById("activityModalContent");
|
||
|
||
if (activityMap) {
|
||
activityMap.remove();
|
||
activityMap = null;
|
||
}
|
||
|
||
if (container) {
|
||
container.innerHTML = "";
|
||
}
|
||
|
||
if (modal) {
|
||
modal.classList.remove("open");
|
||
modal.setAttribute("aria-hidden", "true");
|
||
}
|
||
|
||
window.currentActivity = null;
|
||
|
||
if (typeof window.closeInfoPanel === "function") {
|
||
window.closeInfoPanel();
|
||
}
|
||
|
||
document.body.style.overflow = "";
|
||
}
|
||
|
||
window.openActivityModal = async function(item, list = [], index = 0) {
|
||
window.closeBottomSheet?.();
|
||
|
||
const modal = document.getElementById("activityModal");
|
||
const container = document.getElementById("activityModalContent");
|
||
|
||
if (!modal || !container) {
|
||
console.error("[activity modal] Mancano activityModal o activityModalContent");
|
||
return;
|
||
}
|
||
|
||
if (!item) return;
|
||
|
||
let points = [];
|
||
|
||
try {
|
||
if (typeof window.getActivityRoutePoints === "function") {
|
||
points = await window.getActivityRoutePoints(item);
|
||
} else if (typeof getActivityRoutePoints === "function") {
|
||
points = await getActivityRoutePoints(item);
|
||
}
|
||
} catch (error) {
|
||
console.error("[activity modal] Errore traccia:", error);
|
||
}
|
||
|
||
if (activityMap) {
|
||
activityMap.remove();
|
||
activityMap = null;
|
||
}
|
||
|
||
window.currentActivity = item;
|
||
window.activityModalList = Array.isArray(list) ? list : [];
|
||
window.activityModalIndex = Number.isInteger(index) ? index : 0;
|
||
|
||
const title =
|
||
item.display_name ||
|
||
item.route_name ||
|
||
item.title ||
|
||
item.file_name ||
|
||
item.name ||
|
||
"Attività";
|
||
|
||
const distance = Number(item.distance_meters);
|
||
const duration = Number(item.duration_millis);
|
||
const elevation = Number(item.elevation_gain_meters);
|
||
|
||
container.innerHTML = `
|
||
<div class="activity-modal-wrapper">
|
||
<div id="activityMap"></div>
|
||
|
||
<button
|
||
id="activityInfoBtn"
|
||
class="modal-info-btn activity-info-btn"
|
||
type="button"
|
||
aria-label="Informazioni attività"
|
||
>
|
||
ℹ️
|
||
</button>
|
||
|
||
<div class="activity-modal-info">
|
||
<div class="activity-modal-title">
|
||
${escapeActivityHtml(title)}
|
||
</div>
|
||
|
||
${
|
||
item.sport
|
||
? `<div class="activity-modal-meta">
|
||
${escapeActivityHtml(item.sport)}
|
||
</div>`
|
||
: ""
|
||
}
|
||
|
||
<div class="activity-modal-meta">
|
||
${
|
||
Number.isFinite(distance)
|
||
? `${(distance / 1000).toFixed(1)} km`
|
||
: "Distanza non disponibile"
|
||
}
|
||
·
|
||
${formatActivityModalDuration(duration)}
|
||
</div>
|
||
|
||
${
|
||
Number.isFinite(elevation) && elevation > 0
|
||
? `<div class="activity-modal-meta">
|
||
⛰ ${Math.round(elevation)} m D+
|
||
</div>`
|
||
: ""
|
||
}
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
modal.classList.add("open");
|
||
modal.setAttribute("aria-hidden", "false");
|
||
document.body.style.overflow = "hidden";
|
||
|
||
const activityInfoBtn = document.getElementById("activityInfoBtn");
|
||
|
||
if (activityInfoBtn) {
|
||
activityInfoBtn.onclick = event => {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
event.stopImmediatePropagation();
|
||
|
||
window.currentActivity = item;
|
||
|
||
if (typeof window.toggleInfoPanel === "function") {
|
||
window.toggleInfoPanel(item);
|
||
} else if (typeof window.openInfoPanel === "function") {
|
||
window.openInfoPanel(item);
|
||
} else {
|
||
console.error("[activity info] Nessuna funzione info panel disponibile");
|
||
}
|
||
};
|
||
}
|
||
|
||
requestAnimationFrame(() => {
|
||
requestAnimationFrame(() => {
|
||
const mapElement = document.getElementById("activityMap");
|
||
|
||
if (!mapElement || typeof L === "undefined") {
|
||
console.error("[activity modal] Leaflet o contenitore non disponibile");
|
||
return;
|
||
}
|
||
|
||
activityMap = L.map(mapElement, {
|
||
zoomControl: true,
|
||
attributionControl: true
|
||
});
|
||
|
||
L.tileLayer(
|
||
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||
{
|
||
subdomains: "abc",
|
||
maxZoom: 19,
|
||
attribution: "© OpenStreetMap"
|
||
}
|
||
).addTo(activityMap);
|
||
|
||
const latlngs = Array.isArray(points)
|
||
? points
|
||
.map(point => [
|
||
Number(point?.lat),
|
||
Number(point?.lng)
|
||
])
|
||
.filter(
|
||
point =>
|
||
Number.isFinite(point[0]) &&
|
||
Number.isFinite(point[1])
|
||
)
|
||
: [];
|
||
|
||
if (latlngs.length >= 2) {
|
||
const route = L.polyline(latlngs, {
|
||
color: "#2b8cff",
|
||
weight: 5,
|
||
opacity: 1,
|
||
lineCap: "round",
|
||
lineJoin: "round"
|
||
}).addTo(activityMap);
|
||
|
||
L.circleMarker(latlngs[0], {
|
||
radius: 6,
|
||
color: "#087f23",
|
||
fillColor: "#18b83f",
|
||
fillOpacity: 1
|
||
})
|
||
.bindTooltip("Partenza")
|
||
.addTo(activityMap);
|
||
|
||
L.circleMarker(latlngs[latlngs.length - 1], {
|
||
radius: 6,
|
||
color: "#b00020",
|
||
fillColor: "#e32636",
|
||
fillOpacity: 1
|
||
})
|
||
.bindTooltip("Arrivo")
|
||
.addTo(activityMap);
|
||
|
||
activityMap.fitBounds(route.getBounds(), {
|
||
paddingTopLeft: [30, 70],
|
||
paddingBottomRight: [30, 120],
|
||
maxZoom: 16
|
||
});
|
||
} else {
|
||
const latitude = Number(item.center_latitude);
|
||
const longitude = Number(item.center_longitude);
|
||
|
||
const hasCenter =
|
||
Number.isFinite(latitude) &&
|
||
Number.isFinite(longitude);
|
||
|
||
activityMap.setView(
|
||
hasCenter ? [latitude, longitude] : [42.5, 12.5],
|
||
hasCenter ? 12 : 6
|
||
);
|
||
}
|
||
|
||
activityMap.invalidateSize(true);
|
||
|
||
setTimeout(() => {
|
||
activityMap?.invalidateSize(true);
|
||
}, 150);
|
||
|
||
setTimeout(() => {
|
||
activityMap?.invalidateSize(true);
|
||
}, 400);
|
||
});
|
||
});
|
||
};
|
||
|
||
function formatActivityModalDuration(milliseconds) {
|
||
if (!Number.isFinite(milliseconds)) {
|
||
return "Durata non disponibile";
|
||
}
|
||
|
||
const totalSeconds = Math.round(milliseconds / 1000);
|
||
const hours = Math.floor(totalSeconds / 3600);
|
||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||
|
||
return hours > 0
|
||
? `${hours}h ${minutes}m`
|
||
: `${minutes}m`;
|
||
}
|
||
|
||
function escapeActivityHtml(value) {
|
||
return String(value ?? "")
|
||
.replaceAll("&", "&")
|
||
.replaceAll("<", "<")
|
||
.replaceAll(">", ">")
|
||
.replaceAll('"', """)
|
||
.replaceAll("'", "'");
|
||
}
|
||
|
||
window.closeActivityModal = closeActivityModal;
|
||
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const modal = document.getElementById("activityModal");
|
||
const closeButton = document.getElementById("activityModalClose");
|
||
|
||
closeButton?.addEventListener("click", event => {
|
||
event.stopPropagation();
|
||
closeActivityModal();
|
||
});
|
||
|
||
modal?.addEventListener("click", event => {
|
||
if (event.target === modal) {
|
||
closeActivityModal();
|
||
}
|
||
});
|
||
|
||
document.addEventListener("keydown", event => {
|
||
if (
|
||
event.key === "Escape" &&
|
||
modal?.classList.contains("open")
|
||
) {
|
||
closeActivityModal();
|
||
}
|
||
});
|
||
});
|