import React, { useState, useEffect, useRef } from 'react'; import { Dimensions, StyleSheet, View, SafeAreaView } from 'react-native'; import MapView, { PROVIDER_GOOGLE, Marker, Region, Callout } from 'react-native-maps'; import Geocoder from 'react-native-geocoding'; import { useLanguage, useConfig, useUtils } from 'ordering-components/native'; import { GoogleMapsParams } from '../../types'; import Alert from '../../providers/AlertProvider'; import { OIconButton, OIcon, OText, OButton } from '../shared'; import { FloatingButton } from '../FloatingButton'; import Icon from 'react-native-vector-icons/FontAwesome5'; import { useTheme } from 'styled-components/native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useLocation } from '../../hooks/useLocation'; import { showLocation } from 'react-native-map-link'; export const GoogleMap = (props: GoogleMapsParams) => { const { location, handleChangeAddressMap, maxLimitLocation, readOnly, handleViewActionOrder, markerTitle, showAcceptOrReject, driverLocation, saveLocation, handleOpenMapView, setSaveLocation, handleToggleMap, locations, isSetInputs, navigation, } = props; const { hasLocation, initialPosition, followUserLocation, getCurrentLocation, userLocation, stopFollowUserLocation, } = useLocation(); const { top } = useSafeAreaInsets(); const theme = useTheme(); const [, t] = useLanguage(); const [configState] = useConfig(); const [{ optimizeImage }] = useUtils(); const { width, height } = Dimensions.get('window'); const ASPECT_RATIO = width / height; const [isMapReady, setIsMapReady] = useState(false); const [markerPosition, setMarkerPosition] = useState({ latitude: locations ? typeof locations[locations.length - 1].lat === 'string' ? parseFloat(locations[locations.length - 1].lat) || 0 : locations[locations.length - 1].lat || 0 : typeof location?.lat === 'string' ? parseFloat(location?.lat) || 0 : location?.lat || 0, longitude: locations ? typeof locations[locations.length - 1].lng === 'string' ? parseFloat(locations[locations.length - 1].lng) || 0 : locations[locations.length - 1].lng || 0 : typeof location?.lng === 'string' ? parseFloat(location?.lng) || 0 : location?.lng || 0, }); const [region, setRegion] = useState({ latitude: typeof location?.lat === 'string' ? parseFloat(location?.lat) || 0 : location?.lat || 0, longitude: typeof location?.lng === 'string' ? parseFloat(location?.lng) || 0 : location?.lng || 0, latitudeDelta: 0.001, longitudeDelta: 0.001 * ASPECT_RATIO, }); let mapRef = useRef(null); const googleMapsApiKey = configState?.configs?.google_maps_api_key?.value; const center = { lat: location?.lat, lng: location?.lng }; const [alertState, setAlertState] = useState<{ open: boolean; content: Array; key?: string | null; }>({ open: false, content: [], key: null }); const mapErrors: any = { ERROR_NOT_FOUND_ADDRESS: "Sorry, we couldn't find an address", ERROR_MAX_LIMIT_LOCATION_TO: 'Sorry, You can only set the position to', }; let MARKERS = locations && locations.map((location: { lat: number; lng: number; level: number }) => { return location.level === 4 && driverLocation?.lat && driverLocation?.lng ? { latitude: typeof driverLocation?.lat === 'string' ? parseFloat(driverLocation?.lat) || 0 : driverLocation?.lat, longitude: typeof driverLocation?.lng === 'string' ? parseFloat(driverLocation?.lng) || 0 : driverLocation?.lng, } : { latitude: typeof location?.lat === 'string' ? parseFloat(location?.lat) || 0 : location?.lat, longitude: typeof location?.lng === 'string' ? parseFloat(location?.lng) || 0 : location?.lng }; }); const handleArrowBack: any = () => { handleOpenMapView && handleOpenMapView(); }; const geocodePosition = (pos: { latitude: number; longitude: number }) => { Geocoder.from({ latitude: pos.latitude, longitude: pos.longitude, }) .then(({ results }) => { let zipcode = null; if (results && results.length > 0) { for (const component of results[0].address_components) { const addressType = component.types[0]; if (addressType === 'postal_code') { zipcode = component.short_name; break; } } let data = null; const details = { geometry: { location: { lat: pos?.latitude, lng: pos?.longitude } }, }; if (isSetInputs) { data = { address: results[0]?.formatted_address, location: results[0]?.geometry?.location, zipcode, place_id: results[0]?.place_id, }; } handleChangeAddressMap && handleChangeAddressMap(data, details); setSaveLocation && setSaveLocation(false); handleToggleMap && handleToggleMap(); } else { setMapErrors && setMapErrors('ERROR_NOT_FOUND_ADDRESS'); } }) .catch(err => { setMapErrors && setMapErrors(err.message); }); }; const validateResult = (curPos: { latitude: number; longitude: number }) => { const loc1 = center; const loc2 = curPos; const distance = calculateDistance(loc1, loc2); if (!maxLimitLocation) { geocodePosition(curPos); setMarkerPosition(curPos); setRegion({ ...region, longitude: curPos.longitude, latitude: curPos.latitude, }); return; } if (distance <= maxLimitLocation) { setMarkerPosition(curPos); setRegion({ ...region, longitude: curPos.longitude, latitude: curPos.latitude, }); } else { setMapErrors && setMapErrors('ERROR_MAX_LIMIT_LOCATION'); setMarkerPosition({ latitude: center.lat, longitude: center.lng }); } }; const calculateDistance = ( pointA: { lat: number; lng: number }, pointB: { latitude: number; longitude: number }, ) => { const lat1 = pointA.lat; const lon1 = pointA.lng; const lat2 = pointB?.latitude; const lon2 = pointB?.longitude; const R = 6371e3; const φ1 = lat1 * (Math.PI / 180); const φ2 = lat2 * (Math.PI / 180); const Δφ = (lat2 - lat1) * (Math.PI / 180); const Δλ = (lon2 - lon1) * (Math.PI / 180); const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) + Math.cos(φ1) * Math.cos(φ2) * (Math.sin(Δλ / 2) * Math.sin(Δλ / 2)); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); const distance = R * c; return distance; }; const handleChangeRegion = (coordinates: Region) => { validateResult(coordinates); }; const closeAlert = () => { setAlertState({ open: false, content: [], }); }; const setMapErrors = (errKey: string) => { setAlertState({ open: true, content: !(errKey === 'ERROR_MAX_LIMIT_LOCATION_TO') ? [t(errKey, mapErrors[errKey])] : [ `${t(errKey, mapErrors[errKey])} ${maxLimitLocation} ${t( 'METTERS', 'meters', )}`, ], key: errKey, }); }; const fitAllMarkers = () => { mapRef.current.fitToCoordinates(MARKERS, { edgePadding: { top: 80, right: 80, bottom: 80, left: 80 }, animated: true, }); }; useEffect(() => { Geocoder.init(googleMapsApiKey); }, []); useEffect(() => { if (saveLocation) { geocodePosition(markerPosition); } }, [saveLocation]); useEffect(() => { if (mapRef.current && locations && isMapReady) { fitAllMarkers(); } }, [isMapReady]); useEffect(() => { if (!locations) return MARKERS = locations.map((location: { lat: number; lng: number; level: number }) => { return location.level === 4 && driverLocation?.lat ? { latitude: driverLocation?.lat, longitude: driverLocation?.lng, } : { latitude: location.lat, longitude: location.lng }; }) fitAllMarkers(); }, [driverLocation]) const styles = StyleSheet.create({ map: { flex: 1, ...StyleSheet.absoluteFillObject, }, image: { borderRadius: 50, }, view: { width: 25, position: 'absolute', top: 6, left: 6, bottom: 0, right: 0, }, buttonBack: { borderRadius: 7.6, borderWidth: 0, paddingLeft: 0, }, buttonContainer: { position: 'absolute', top: 0, left: 15, marginTop: top, }, }); return ( handleChangeRegion(coordinates) : () => { } } zoomTapEnabled zoomEnabled zoomControlEnabled cacheEnabled moveOnMarkerPress ref={mapRef} onMapReady={() => setIsMapReady(true)}> {locations ? ( <> {MARKERS && MARKERS.map( ( location: { latitude: number; longitude: number }, i: number, ) => ( { mapRef.current?.animateCamera({ center: { latitude: location?.latitude, longitude: location?.longitude }, }) }} > { showLocation({ latitude: location?.latitude, longitude: location?.longitude, sourceLatitude: userLocation?.latitude, sourceLongitude: userLocation.longitude, naverCallerName: 'com.businessapp', dialogTitle: t('SHOW_IN_OTHER_MAPS', 'Show in other maps'), dialogMessage: t('WHAT_APP_WOULD_YOU_USE', 'What app would you like to use?'), cancelText: t('CANCEL', 'Cancel'), }) }} > {locations[i]?.title} {locations[i]?.address && ( <> {locations[i]?.address.addressName} {locations[i]?.address.zipcode} )} ), )} ) : ( )} {showAcceptOrReject && ( handleViewActionOrder && handleViewActionOrder('accept') } firstButtonClick={() => handleViewActionOrder && handleViewActionOrder('accept') } secondButton={false} firstColorCustom={theme.colors.red} secondColorCustom={theme.colors.green} widthButton={'100%'} isPadding /> )} handleArrowBack()} /> ); };