"use client" import { useEffect, useCallback, useRef } from "react" import type { IIncidentLogs } from "@/app/_utils/types/crimes" interface RecentIncidentsLayerProps { visible?: boolean map: any incidents?: IIncidentLogs[] } export default function RecentIncidentsLayer({ visible = false, map, incidents = [], }: RecentIncidentsLayerProps) { const isInteractingWithMarker = useRef(false); // Filter incidents from the last 24 hours const recentIncidents = incidents.filter(incident => { if (!incident.timestamp) return false; const incidentDate = new Date(incident.timestamp); const now = new Date(); const timeDiff = now.getTime() - incidentDate.getTime(); // 86400000 = 24 hours in milliseconds return timeDiff <= 86400000; }); const handleIncidentClick = useCallback( (e: any) => { if (!map) return; const features = map.queryRenderedFeatures(e.point, { layers: ["recent-incidents"] }); if (!features || features.length === 0) return; isInteractingWithMarker.current = true; const incident = features[0]; if (!incident.properties) return; e.originalEvent.stopPropagation(); e.preventDefault(); const incidentDetails = { id: incident.properties.id, description: incident.properties.description, status: incident.properties?.status || "Active", longitude: (incident.geometry as any).coordinates[0], latitude: (incident.geometry as any).coordinates[1], timestamp: new Date(incident.properties.timestamp || Date.now()), category: incident.properties.category, }; console.log("Recent incident clicked:", incidentDetails); // Ensure markers stay visible if (map.getLayer("recent-incidents")) { map.setLayoutProperty("recent-incidents", "visibility", "visible"); } // First fly to the incident location map.flyTo({ center: [incidentDetails.longitude, incidentDetails.latitude], zoom: 15, bearing: 0, pitch: 45, duration: 2000, }); // Dispatch the incident_click event to show the popup const customEvent = new CustomEvent("incident_click", { detail: incidentDetails, bubbles: true, }); map.getCanvas().dispatchEvent(customEvent); document.dispatchEvent(customEvent); // Reset the flag after a delay setTimeout(() => { isInteractingWithMarker.current = false; }, 5000); }, [map] ); useEffect(() => { if (!map || !visible) return; console.log(`Setting up recent incidents layer with ${recentIncidents.length} incidents from the last 24 hours`); // Convert incidents to GeoJSON const recentData = { type: "FeatureCollection" as const, features: recentIncidents.map(incident => ({ type: "Feature" as const, geometry: { type: "Point" as const, coordinates: [incident.longitude, incident.latitude], }, properties: { id: incident.id, user_id: incident.user_id, address: incident.address, description: incident.description, timestamp: incident.timestamp ? incident.timestamp.toString() : new Date().toString(), category: incident.category, district: incident.district, severity: incident.severity, status: incident.verified, source: incident.source, }, })), }; const setupLayerAndSource = () => { try { // Check if source exists and update it if (map.getSource("recent-incidents-source")) { (map.getSource("recent-incidents-source") as any).setData(recentData); } else { // If not, add source map.addSource("recent-incidents-source", { type: "geojson", data: recentData, }); } // Find first symbol layer for proper layering const layers = map.getStyle().layers; let firstSymbolId: string | undefined; for (const layer of layers) { if (layer.type === "symbol") { firstSymbolId = layer.id; break; } } // Check if layer exists already if (!map.getLayer("recent-incidents")) { map.addLayer({ id: "recent-incidents", type: "circle", source: "recent-incidents-source", paint: { "circle-color": "#FF5252", // Red color for recent incidents "circle-radius": [ "interpolate", ["linear"], ["zoom"], 7, 4, // Slightly larger at lower zooms for visibility 12, 8, 15, 12, // Larger maximum size ], "circle-stroke-width": 2, "circle-stroke-color": "#FFFFFF", "circle-opacity": 0.8, // Add a pulsing effect "circle-stroke-opacity": [ "interpolate", ["linear"], ["zoom"], 7, 0.5, 15, 0.8 ], }, layout: { visibility: visible ? "visible" : "none", } }, firstSymbolId); // Add a glow effect with a larger circle behind map.addLayer({ id: "recent-incidents-glow", type: "circle", source: "recent-incidents-source", paint: { "circle-color": "#FF5252", "circle-radius": [ "interpolate", ["linear"], ["zoom"], 7, 6, 12, 12, 15, 18, ], "circle-opacity": 0.2, "circle-blur": 1, }, layout: { visibility: visible ? "visible" : "none", } }, "recent-incidents"); // Add mouse events map.on("mouseenter", "recent-incidents", () => { map.getCanvas().style.cursor = "pointer"; }); map.on("mouseleave", "recent-incidents", () => { map.getCanvas().style.cursor = ""; }); } else { // Update existing layer visibility map.setLayoutProperty("recent-incidents", "visibility", visible ? "visible" : "none"); map.setLayoutProperty("recent-incidents-glow", "visibility", visible ? "visible" : "none"); } // Ensure click handler is properly registered map.off("click", "recent-incidents", handleIncidentClick); map.on("click", "recent-incidents", handleIncidentClick); } catch (error) { console.error("Error setting up recent incidents layer:", error); } }; // Check if style is loaded and set up layer accordingly if (map.isStyleLoaded()) { setupLayerAndSource(); } else { map.once("style.load", setupLayerAndSource); // Fallback setTimeout(() => { if (map.isStyleLoaded()) { setupLayerAndSource(); } else { console.warn("Map style still not loaded after timeout"); } }, 1000); } return () => { if (map) { map.off("click", "recent-incidents", handleIncidentClick); } }; }, [map, visible, recentIncidents, handleIncidentClick]); return null; }