From 127532472dc04af9aa3a10b566da17bed458ec94 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Sat, 2 Sep 2023 01:16:06 +0500 Subject: [PATCH 1/2] feat: correct panning of the maps --- src/components/MapView/MapView.tsx | 99 +++++++++++++++++++ src/components/MapView/MapView.web.tsx | 96 ++++++++++++++++++ src/components/MapView/responder/index.ios.ts | 5 + src/components/MapView/responder/index.ts | 8 ++ 4 files changed, 208 insertions(+) create mode 100644 src/components/MapView/MapView.tsx create mode 100644 src/components/MapView/MapView.web.tsx create mode 100644 src/components/MapView/responder/index.ios.ts create mode 100644 src/components/MapView/responder/index.ts diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx new file mode 100644 index 000000000000..2d24d598adea --- /dev/null +++ b/src/components/MapView/MapView.tsx @@ -0,0 +1,99 @@ +import {View} from 'react-native'; +import {useFocusEffect} from '@react-navigation/native'; +import Mapbox, {MapState, MarkerView, setAccessToken} from '@rnmapbox/maps'; +import {forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; + +import responder from './responder'; +import utils from './utils'; +import Direction from './Direction'; +import CONST from '../../CONST'; + +import {MapViewProps, MapViewHandle} from './MapViewTypes'; + +const MapView = forwardRef(({accessToken, style, mapPadding, styleURL, pitchEnabled, initialState, waypoints, directionCoordinates}, ref) => { + const cameraRef = useRef(null); + const [isIdle, setIsIdle] = useState(false); + + useImperativeHandle( + ref, + () => ({ + flyTo: (location: [number, number], zoomLevel: number = CONST.MAPBOX.DEFAULT_ZOOM, animationDuration?: number) => + cameraRef.current?.setCamera({zoomLevel, centerCoordinate: location, animationDuration}), + fitBounds: (northEast: [number, number], southWest: [number, number], paddingConfig?: number | number[] | undefined, animationDuration?: number | undefined) => + cameraRef.current?.fitBounds(northEast, southWest, paddingConfig, animationDuration), + }), + [], + ); + + // When the page loses focus, we temporarily set the "idled" state to false. + // When the page regains focus, the onIdled method of the map will set the actual "idled" state, + // which in turn triggers the callback. + useFocusEffect( + useCallback(() => { + if (waypoints?.length && isIdle) { + if (waypoints.length === 1) { + cameraRef.current?.setCamera({ + zoomLevel: 15, + animationDuration: 1500, + centerCoordinate: waypoints[0].coordinate, + }); + } else { + const {southWest, northEast} = utils.getBounds(waypoints.map((waypoint) => waypoint.coordinate)); + cameraRef.current?.fitBounds(northEast, southWest, mapPadding, 1000); + } + } + return () => { + setIsIdle(false); + }; + }, [mapPadding, waypoints, isIdle]), + ); + + useEffect(() => { + setAccessToken(accessToken); + }, [accessToken]); + + const setMapIdle = (e: MapState) => { + if (e.gestures.isGestureActive) return; + setIsIdle(true); + }; + + return ( + + + + + {waypoints?.map(({coordinate, markerComponent, id}) => { + const MarkerComponent = markerComponent; + return ( + + + + ); + })} + + {directionCoordinates && } + + + ); +}); + +export default memo(MapView); diff --git a/src/components/MapView/MapView.web.tsx b/src/components/MapView/MapView.web.tsx new file mode 100644 index 000000000000..4d25f74270af --- /dev/null +++ b/src/components/MapView/MapView.web.tsx @@ -0,0 +1,96 @@ +// Explanation: Different Mapbox libraries are required for web and native mobile platforms. +// This is why we have separate components for web and native to handle the specific implementations. +// For the web version, we use the Mapbox Web library called react-map-gl, while for the native mobile version, +// we utilize a different Mapbox library @rnmapbox/maps tailored for mobile development. + +import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useState} from 'react'; +import {View} from 'react-native'; +import Map, {MapRef, Marker} from 'react-map-gl'; + +import responder from './responder'; +import utils from './utils'; + +import CONST from '../../CONST'; +import Direction from './Direction'; +import {MapViewHandle, MapViewProps} from './MapViewTypes'; + +import 'mapbox-gl/dist/mapbox-gl.css'; + +const MapView = forwardRef( + ({style, styleURL, waypoints, mapPadding, accessToken, directionCoordinates, initialState = {location: CONST.MAPBOX.DEFAULT_COORDINATE, zoom: CONST.MAPBOX.DEFAULT_ZOOM}}, ref) => { + const [mapRef, setMapRef] = useState(null); + const setRef = useCallback((newRef: MapRef | null) => setMapRef(newRef), []); + + useEffect(() => { + if (!waypoints || waypoints.length === 0) { + return; + } + + if (!mapRef) { + return; + } + + if (waypoints.length === 1) { + mapRef.flyTo({ + center: waypoints[0].coordinate, + zoom: 15, + }); + return; + } + + const map = mapRef.getMap(); + + const {northEast, southWest} = utils.getBounds(waypoints.map((waypoint) => waypoint.coordinate)); + map.fitBounds([northEast, southWest], {padding: mapPadding}); + }, [waypoints, mapRef, mapPadding]); + + useImperativeHandle( + ref, + () => ({ + flyTo: (location: [number, number], zoomLevel: number = CONST.MAPBOX.DEFAULT_ZOOM, animationDuration?: number) => + mapRef?.flyTo({ + center: location, + zoom: zoomLevel, + duration: animationDuration, + }), + fitBounds: (northEast: [number, number], southWest: [number, number]) => mapRef?.fitBounds([northEast, southWest]), + }), + [mapRef], + ); + + return ( + + + {waypoints?.map(({coordinate, markerComponent, id}) => { + const MarkerComponent = markerComponent; + return ( + + + + ); + })} + {directionCoordinates && } + + + ); + }, +); + +export default MapView; diff --git a/src/components/MapView/responder/index.ios.ts b/src/components/MapView/responder/index.ios.ts new file mode 100644 index 000000000000..234e6c76bcc7 --- /dev/null +++ b/src/components/MapView/responder/index.ios.ts @@ -0,0 +1,5 @@ +const responder = { + panHandlers: {}, +}; + +export default responder; \ No newline at end of file diff --git a/src/components/MapView/responder/index.ts b/src/components/MapView/responder/index.ts new file mode 100644 index 000000000000..ec7ef5341acd --- /dev/null +++ b/src/components/MapView/responder/index.ts @@ -0,0 +1,8 @@ +import {PanResponder} from 'react-native'; + +const responder = PanResponder.create({ + onStartShouldSetPanResponder: () => true, + onPanResponderTerminationRequest: () => false, +}); + +export default responder; \ No newline at end of file From 96d7fb6a5bafdfcbdd9142e432377d44f2ee10f5 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Sat, 2 Sep 2023 01:19:57 +0500 Subject: [PATCH 2/2] fix lint errors --- src/components/MapView/MapView.tsx | 4 +--- src/components/MapView/MapView.web.tsx | 2 +- src/components/MapView/responder/index.ios.ts | 2 +- src/components/MapView/responder/index.ts | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index 2d24d598adea..aebd63edf559 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -58,9 +58,7 @@ const MapView = forwardRef(({accessToken, style, ma }; return ( - + ( ); return ( - false, }); -export default responder; \ No newline at end of file +export default responder;