Untitled
unknown
plain_text
a year ago
10 kB
6
Indexable
"use client";
//IMPORT LIKE THIS REACT => React.useState | React.useEffect
import * as React from "react";
import { GoogleMap, MarkerF, InfoWindow } from "@react-google-maps/api";
import { mapStyles } from "@/lib/mapStyles";
import { useSearchContext } from "@/context/SearchContext";
import { useSearchParams } from "next/navigation";
import useMarkerStore from "@/store/markers-store";
import { useMap } from "@/context/MapContext";
import BeeSpinner from "../BeeSpinner";
import { Button } from "@/components/ui/button";
import ModernInfoWindow from "../ModernInfoWindow";
interface Marker {
id: string;
name: string;
address: string;
lat?: number;
lng?: number;
referral_code: string;
}
interface GoogleMapViewProps {
errorMessage?: string | null;
}
function GoogleMapView({ errorMessage }: GoogleMapViewProps) {
const { isLoaded, loadError } = useMap();
const { center, setCenter } = useSearchContext();
const { users } = useMarkerStore();
// SEPARATE HOOKS FROM STATE
const [userLocation, setUserLocation] =
useState<google.maps.LatLngLiteral | null>(null);
const [markers, setMarkers] = useState<Marker[]>([]);
const [selectedMarker, setSelectedMarker] = useState<Marker | null>(null);
const searchParams = useSearchParams();
const lat = searchParams.get("lat");
const lng = searchParams.get("lng");
// perche' usare useMemo? puoi dichiarare direttamente l'oggetto fuori dalla funzione come costante. Non Cambierà mai, e puoi tranquillamente usarlo come costante
const mapOptions = useMemo(
() => ({
styles: mapStyles,
language: "it",
region: "IT",
clickableIcons: false,
zoomControl: true,
streetViewControl: false,
fullscreenControl: false,
mapTypeControl: false,
gestureHandling: "greedy",
}),
[]
);
useEffect(() => {
if (lat && lng) {
setCenter({ lat: parseFloat(lat), lng: parseFloat(lng) });
} else if (navigator.geolocation) {
// THIS FAIL SILENTLY!!! RETURN ERROR TO AT LEAST U!
navigator.geolocation.getCurrentPosition(
(position) => {
const location = {
lat: position.coords.latitude,
lng: position.coords.longitude,
};
setCenter(location);
setUserLocation(location);
},
(error) => {
console.error("Error getting user's location:", error);
}
);
}
}, [lat, lng, setCenter]);
useEffect(() => {
if (isLoaded && users.length > 0) {
const geocoder = new google.maps.Geocoder();
const markersWithCoordinates = users.map(
(user, index) =>
// I FEEL LIKE THIS HAS SOME LOGIC ISSUES, IF THE ADDRESS IS NOT FOUND, IT WILL BE NULL, AND THE FILTER WILL REMOVE IT
new Promise<Marker>((resolve) => {
geocoder.geocode(
{ address: user.fullAddress },
(results, status) => {
if (status === "OK" && results && results[0]) {
resolve({
id: `${user.referral_code}_${user.address}_${user.cap}_${index}`,
name: user.company_name,
address: user.fullAddress,
lat: results[0].geometry.location.lat(),
lng: results[0].geometry.location.lng(),
referral_code: user.referral_code,
});
} else {
console.error(
"Geocode was not successful for the following reason:",
status
);
resolve({
id: `${user.referral_code}_${user.address}_${user.cap}_${index}`,
name: user.company_name,
address: user.fullAddress,
referral_code: user.referral_code,
});
}
}
);
})
);
Promise.all(markersWithCoordinates).then((updatedMarkers) => {
setMarkers(updatedMarkers.filter((marker) => marker.lat && marker.lng));
});
} else {
setMarkers([]);
}
}, [isLoaded, users]);
const handleMarkerClick = useCallback((marker: Marker) => {
setSelectedMarker(marker);
}, []);
// ABSTRACT THIS FUNCTION OUTSIDE THE COMPONENT IN A UTILS! NO NEED TO RECREATE IT EVERY RENDER
const calculateDistance = useCallback(
(lat1: number, lon1: number, lat2: number, lon2: number) => {
const R = 6371;
const dLat = deg2rad(lat2 - lat1);
const dLon = deg2rad(lon2 - lon1);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2rad(lat1)) *
Math.cos(deg2rad(lat2)) *
Math.sin(dLon / 2) *
Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const d = R * c;
return d;
},
[]
);
// SAME AS ABOVE, MORE ADD A ERROR OF SOME KM CAUSE THIS IS HAVERSINE FORMULA, AND IT DOESNT ACCOUNT ROUTES, JUST STRAIGHT LINE
const deg2rad = (deg: number) => {
return deg * (Math.PI / 180);
};
useEffect(() => {
// THIS IS NOT READABLE,MORE: U CAN USE CN TO HANDLE THIS, AND USE A CLASSNAME TO STYLE THE INFO WINDOW
if (isLoaded) {
const removeInfoWindowPadding = () => {
const infoWindows = document.querySelectorAll(".gm-style-iw-c");
infoWindows.forEach((iw) => {
(iw as HTMLElement).style.padding = "0";
(iw as HTMLElement).style.maxWidth = "none";
(iw as HTMLElement).style.maxHeight = "none";
(iw as HTMLElement).style.overflow = "visible";
(iw as HTMLElement).style.background = "none";
(iw as HTMLElement).style.boxShadow = "none";
});
const infoWindowContents = document.querySelectorAll(".gm-style-iw-d");
infoWindowContents.forEach((iwc) => {
(iwc as HTMLElement).style.padding = "0";
(iwc as HTMLElement).style.maxWidth = "none";
(iwc as HTMLElement).style.maxHeight = "none";
(iwc as HTMLElement).style.overflow = "visible";
});
const infoWindowBackground = document.querySelector(".gm-style-iw-t");
if (infoWindowBackground) {
(infoWindowBackground as HTMLElement).style.background = "none";
}
// Remove the default close button
const closeButtons = document.querySelectorAll(".gm-ui-hover-effect");
closeButtons.forEach((button) => {
(button as HTMLElement).style.display = "none";
});
// Remove the arrow
const arrows = document.querySelectorAll(".gm-style-iw-tc");
arrows.forEach((arrow) => {
(arrow as HTMLElement).style.display = "none";
});
};
removeInfoWindowPadding();
const observer = new MutationObserver(removeInfoWindowPadding);
observer.observe(document.body, { childList: true, subtree: true });
return () => observer.disconnect();
}
}, [isLoaded, selectedMarker]);
if (loadError)
return <div>Errore nel caricamento di Maps. Ricarica la pagina.</div>;
if (!isLoaded)
return (
<div className="flex items-center justify-center h-[calc(100dvh-80px)] w-full">
<BeeSpinner />
</div>
);
let infoWindowOptions = {};
if (isLoaded) {
infoWindowOptions = {
pixelOffset: new google.maps.Size(0, -30),
maxWidth: 256,
disableAutoPan: false,
};
}
// THOSE ARE CONSTANT AND SHOULD BE OUTSIDE THE COMPONENT OR EVEN ASTRACTED
const defaultIcon = {
url: "/assets/icons/Mapmarkerviola.svg",
scaledSize: new google.maps.Size(50, 50),
};
const selectedIcon = {
url: "/assets/icons/Mapmarkergiallo.svg",
scaledSize: new google.maps.Size(55, 55),
};
return (
<div className="flex flex-col h-full w-full relative">
<GoogleMap
mapContainerClassName="flex-1 lg:rounded-[1rem] lg:h-full rounded-none h-[calc(100vh-4rem)]"
center={center}
zoom={14}
options={mapOptions}
>
{userLocation && (
<MarkerF position={userLocation} title="Your Location" />
)}
{markers.map(
(marker) =>
marker.lat &&
marker.lng && (
<MarkerF
key={marker.id}
position={{ lat: marker.lat, lng: marker.lng }}
title={`${marker.name} - ${marker.address}`}
icon={
selectedMarker?.id === marker.id ? selectedIcon : defaultIcon
}
onClick={() => handleMarkerClick(marker)}
/>
)
)}
{selectedMarker && selectedMarker.lat && selectedMarker.lng && (
<InfoWindow
position={{ lat: selectedMarker.lat, lng: selectedMarker.lng }}
onCloseClick={() => setSelectedMarker(null)}
options={infoWindowOptions}
>
<div className="w-64 h-80 overflow-hidden rounded-lg">
<ModernInfoWindow
name={selectedMarker.name}
address={selectedMarker.address}
onClose={() => setSelectedMarker(null)}
distance={
userLocation
? calculateDistance(
userLocation.lat,
userLocation.lng,
selectedMarker.lat,
selectedMarker.lng
)
: null
}
referralCode={selectedMarker.referral_code}
/>
</div>
</InfoWindow>
)}
</GoogleMap
// USE A SEPARATE COMPONENT
{errorMessage && (
<div className="absolute inset-0 z-[10] flex items-center justify-center bg-black/75 backdrop-blur-sm lg:rounded-[1rem]">
<div className="bg-white p-6 rounded-lg shadow-lg max-w-md text-center">
<p className="text-red-600 font-semibold text-xl mb-2">
Ops! Qualcosa è andato storto.
</p>
<p className="mb-4">
Errore nel caricamento dei marker. Ricarica la pagina.
</p>
<Button
variant="nodropButton"
onClick={() => window.location.reload()}
className="text-white px-6 py-2 rounded-[10px] transition-colors"
>
Ricarica la pagina
</Button>
</div>
</div>
)}
</div>
);
}
export default GoogleMapView;Editor is loading...
Leave a Comment