import React, { useState, useEffect, useRef, useCallback } from 'react'; import { Dimensions, Platform, SafeAreaView, StyleSheet, View } from 'react-native'; import MapView, { PROVIDER_GOOGLE, Marker } from 'react-native-maps'; import { useLanguage, useConfig, useUtils } from 'ordering-components/native'; import { GoogleMapsParams } from '../../types'; import Alert from '../../providers/AlertProvider'; import { OIconButton, OIcon, OFab, OText, OButton } from '../shared'; import Icon from 'react-native-vector-icons/FontAwesome5'; import { useTheme } from 'styled-components/native'; import { useLocation } from '../../hooks/useLocation'; import { FloatingButton } from '../FloatingButton'; import { Popup } from 'react-native-map-link'; import { transformDistance } from '../../utils'; export const DriverMap = (props: GoogleMapsParams) => { const { location, maxLimitLocation, markerTitle, handleOpenMapView, showAcceptOrReject, saveLocation, setSaveLocation, order, isSetInputs, orderStatus, isBusinessMarker, isToFollow, handleViewActionOrder, driverUpdateLocation, setDriverUpdateLocation, } = props; const theme = useTheme(); const [, t] = useLanguage(); const [configState] = useConfig(); const { width, height } = Dimensions.get('window'); const ASPECT_RATIO = width / height; const mapRef = useRef(null); const following = useRef(true); const autoFit = useRef(true); const googleMapsApiKey = configState?.configs?.google_maps_api_key?.value; const [travelTime, setTravelTime] = useState(0); const [distancesFromTwoPlacesKm, setDistancesFromTwoPlacesKm] = useState(0); const [isMin, setIsMin] = useState(false); const [{ parseDate }] = useUtils(); const [popUp, setPopUp] = useState(false); 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', }; const [alertState, setAlertState] = useState<{ open: boolean; content: Array; key?: string | null; }>({ open: false, content: [], key: null }); const distanceUnit = configState?.configs?.distance_unit?.value const isHideRejectButtons = configState?.configs?.reject_orders_enabled && configState?.configs?.reject_orders_enabled?.value !== '1' const { hasLocation, initialPosition, followUserLocation, getCurrentLocation, userLocation, stopFollowUserLocation, } = useLocation(); const parsedInitialPosition = useCallback(() => { return { latitude: typeof initialPosition.latitude === 'string' ? parseFloat(initialPosition.latitude) || 0 : initialPosition.latitude || 0, longitude: typeof initialPosition.longitude === 'string' ? parseFloat(initialPosition.longitude) || 0 : initialPosition.longitude || 0, } }, [JSON.stringify(initialPosition)]) const parsedUserLocation = useCallback(() => { return { latitude: typeof userLocation?.latitude === 'string' ? parseFloat(userLocation?.latitude) || 0 : userLocation?.latitude || 0, longitude: typeof userLocation?.longitude === 'string' ? parseFloat(userLocation?.longitude) || 0 : userLocation?.longitude || 0 } }, [JSON.stringify(userLocation)]) const parsedDestination = useCallback(() => { return { latitude: typeof location?.lat === 'string' ? parseFloat(location?.lat) || 0 : location?.lat || 0, longitude: typeof location?.lng === 'string' ? parseFloat(location?.lng) || 0 : location?.lng || 0 } }, [JSON.stringify(location)]) useEffect(() => { if (isToFollow) { fitCoordinates(); followUserLocation(); return () => { stopFollowUserLocation(); }; } }, []); 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 closeAlert = () => { setAlertState({ open: false, content: [], }); }; 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; const distanceInKm = distance / 1000; const estimatedTimeTravel = distance / userLocation.speed / 60; const time = estimatedTimeTravel > 60 ? estimatedTimeTravel / 60 : estimatedTimeTravel; setIsMin(estimatedTimeTravel < 60); const timeEstimated = userLocation.speed > 0 ? time : travelTime; setDistancesFromTwoPlacesKm(distanceInKm); setTravelTime(timeEstimated); return distance; }; useEffect(() => { if (driverUpdateLocation.error) { stopFollowUserLocation(); setAlertState({ open: true, content: [ `${driverUpdateLocation.error[0] || driverUpdateLocation.error}. ${t( 'TRY_AGAIN', 'Try Again', )}`, ], }); } }, [driverUpdateLocation.error]); useEffect(() => { if (driverUpdateLocation.error) return; calculateDistance( { lat: parsedUserLocation()?.latitude, lng: parsedUserLocation()?.longitude }, parsedDestination(), ); // geocodePosition(userLocation); if (!following.current) return; fitCoordinates(); const { latitude, longitude } = parsedUserLocation(); mapRef.current?.animateCamera({ center: { latitude, longitude }, }); }, [JSON.stringify(userLocation)]); const handleArrowBack: any = () => { setDriverUpdateLocation?.({ ...driverUpdateLocation, error: null, newLocation: null, }); handleOpenMapView && handleOpenMapView(); }; const centerPosition = async () => { const { latitude, longitude } = await getCurrentLocation(); following.current = true; if (mapRef.current) { mapRef.current.animateToRegion( { latitude: latitude, longitude: longitude, latitudeDelta: 0.001, longitudeDelta: 0.001 * ASPECT_RATIO, }, 2000, ); // mapRef.current?.animateCamera({ // center: { latitude, longitude }, // }); } }; const colors: any = { //BLUE 0: theme.colors.statusOrderBlue, 3: theme.colors.statusOrderBlue, 4: theme.colors.statusOrderBlue, 7: theme.colors.statusOrderBlue, 8: theme.colors.statusOrderBlue, 9: theme.colors.statusOrderBlue, 13: theme.colors.statusOrderBlue, 14: theme.colors.statusOrderBlue, 18: theme.colors.statusOrderBlue, 19: theme.colors.statusOrderBlue, 20: theme.colors.statusOrderBlue, 21: theme.colors.statusOrderBlue, 22: theme.colors.statusOrderBlue, 23: theme.colors.statusOrderBlue, 24: theme.colors.statusOrderBlue, 25: theme.colors.statusOrderBlue, 26: theme.colors.statusOrderBlue, //GREEN 1: theme.colors.statusOrderGreen, 11: theme.colors.statusOrderGreen, 15: theme.colors.statusOrderGreen, //RED 2: theme.colors.statusOrderRed, 5: theme.colors.statusOrderRed, 6: theme.colors.statusOrderRed, 10: theme.colors.statusOrderRed, 12: theme.colors.statusOrderRed, 16: theme.colors.statusOrderRed, 17: theme.colors.statusOrderRed, }; const fitCoordinates = () => { if (mapRef.current) { mapRef.current.fitToCoordinates( [ { latitude: location.lat, longitude: location.lng }, { latitude: parsedInitialPosition()?.latitude, longitude: parsedInitialPosition()?.longitude, }, ], { edgePadding: { top: 80, right: 80, bottom: 80, left: 80 }, animated: true, }, ); } }; useEffect(() => { const interval = setInterval(() => { if (parsedInitialPosition()?.latitude !== 0 && autoFit.current) { if (mapRef.current) { fitCoordinates(); autoFit.current = false; } } }, 1000); return () => clearInterval(interval); }, [parsedInitialPosition()]); const fitCoordinatesZoom = () => { following.current = false; fitCoordinates(); }; 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, }, driverIcon: { height: 25, width: 25, backgroundColor: theme.colors.white, borderRadius: 100, justifyContent: 'center', alignItems: 'center', }, buttonBack: { borderWidth: 0, maxWidth: 40, alignItems: 'flex-start', justifyContent: 'flex-end', }, facContainer: { position: 'absolute', top: 0, zIndex: 9999, width: '100%', backgroundColor: theme.colors.white, paddingHorizontal: 30, paddingTop: 30, }, facOrderStatus: { flexDirection: 'row', borderBottomWidth: 11, minHeight: 70, borderBottomColor: theme.colors.inputChat, paddingVertical: 5, }, facDistance: { flexDirection: 'row', alignItems: 'center', minHeight: 75, }, arrowDistance: { borderWidth: 0, }, buttonContainer: { width: '100%', alignItems: 'center', justifyContent: 'center', paddingVertical: 10, paddingHorizontal: 80, position: 'absolute' }, showButton: { alignSelf: 'center', borderRadius: 10, } }); return ( handleChangeRegion(coordinates) // : () => {} // } zoomEnabled zoomControlEnabled cacheEnabled moveOnMarkerPress onTouchStart={() => (following.current = false)}> {location ? ( <> ) : ( )} handleArrowBack()} /> {order?.delivery_datetime_utc ? parseDate(order?.delivery_datetime_utc) : parseDate(order?.delivery_datetime, { utc: false })} {` - ${t(order?.paymethod?.name?.replace(/\s+/g, '_')?.toUpperCase(), order?.paymethod?.name)}`} {t('INVOICE_ORDER_NO', 'Order No.')} {order?.id} {` ${t('IS', 'is')} `} {`${orderStatus}`} {`${transformDistance(distancesFromTwoPlacesKm, distanceUnit)} ${t(distanceUnit.toUpperCase(), distanceUnit)}`} {`${travelTime.toFixed(2)} - ${isMin ? t('MINNUTES', 'mins') : t('HOURS', 'hours')}`} setPopUp(true)} text={t('SHOW_IN_OTHER_MAPS', 'Show in other maps')} /> setPopUp(false)} onAppPressed={() => setPopUp(false)} onBackButtonPressed={() => setPopUp(false)} modalProps={{ animationIn: 'slideInUp' }} options={{ latitude: parsedDestination()?.latitude, longitude: parsedDestination()?.longitude, sourceLatitude: parsedUserLocation()?.latitude, sourceLongitude: parsedUserLocation()?.longitude, naverCallerName: 'com.deliveryapp', 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'), }} /> {showAcceptOrReject && ( handleViewActionOrder && handleViewActionOrder('accept') } firstButtonClick={() => handleViewActionOrder && handleViewActionOrder('reject') } secondBtnText={t('ACCEPT', 'Accept')} secondButton={true} firstColorCustom={theme.colors.red} secondColorCustom={theme.colors.green} widthButton={isHideRejectButtons ? '100%' : '45%'} isPadding isHideRejectButtons={isHideRejectButtons} /> )} ); };