diff --git a/assets/images/compass.svg b/assets/images/compass.svg new file mode 100644 index 000000000000..984c9bc53fec --- /dev/null +++ b/assets/images/compass.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 2baa3f70769e..45700c69b9d2 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -5207,6 +5207,8 @@ const CONST = { WAYPOINT: {width: 40, height: 40}, }, + MAP_VIEW_COMPASS_SIZE: {width: 44, height: 44}, + QUICK_REACTIONS: [ { name: '+1', @@ -8967,6 +8969,9 @@ const CONST = { SHARE_DETAIL: { DISMISS_KEYBOARD_BUTTON: 'ShareDetail-DismissKeyboardButton', }, + MAP_VIEW: { + COMPASS: 'compass', + }, MONEY_REQUEST: { AMOUNT_NEXT_BUTTON: 'MoneyRequest-AmountNextButton', AMOUNT_PAY_BUTTON: 'MoneyRequest-AmountPayButton', diff --git a/src/components/ConfirmedRoute.tsx b/src/components/ConfirmedRoute.tsx index 2316c66ec1a4..f200d192dd63 100644 --- a/src/components/ConfirmedRoute.tsx +++ b/src/components/ConfirmedRoute.tsx @@ -33,9 +33,12 @@ type ConfirmedRouteProps = { /** Whether the map is interactive or not */ interactive?: boolean; + + /** Whether it should display the compass on the map */ + shouldDisplayCompass?: boolean; }; -function ConfirmedRoute({transaction, isSmallerIcon, shouldHaveBorderRadius = true, requireRouteToDisplayMap = false, interactive}: ConfirmedRouteProps) { +function ConfirmedRoute({transaction, isSmallerIcon, shouldHaveBorderRadius = true, requireRouteToDisplayMap = false, interactive, shouldDisplayCompass = true}: ConfirmedRouteProps) { const {isOffline} = useNetwork(); const {route0: route} = transaction?.routes ?? {}; const waypoints = transaction?.comment?.waypoints ?? {}; @@ -92,6 +95,7 @@ function ConfirmedRoute({transaction, isSmallerIcon, shouldHaveBorderRadius = tr styleURL={CONST.MAPBOX.STYLE_URL} requireRouteToDisplayMap={requireRouteToDisplayMap} shouldDisplayCurrentLocation={false} + shouldDisplayCompass={shouldDisplayCompass} /> ) : ( (null); const [isIdle, setIsIdle] = useState(false); const initialLocation = useMemo(() => initialState && {longitude: initialState.location[0], latitude: initialState.location[1]}, [initialState]); @@ -194,6 +197,25 @@ function MapView({ onMapReady(); } }; + + const mapHeading = useSharedValue(0); + + const onCameraChanged = (e: MapState) => { + mapHeading.set(e.properties.heading ?? 0); + }; + + // Rotate the compass needle opposite to the map's bearing so it keeps pointing to true north. + const compassAnimatedStyle = useAnimatedStyle(() => ({ + transform: [{rotate: `${-mapHeading.get()}deg`}], + })); + + const resetMapToNorth = () => { + cameraRef.current?.setCamera({ + heading: 0, + animationDuration: CONST.MAPBOX.ANIMATION_DURATION_ON_CENTER_ME, + }); + }; + const centerMap = useCallback(() => { const waypointCoordinates = waypoints?.map((waypoint) => waypoint.coordinate) ?? []; if (waypointCoordinates.length > 1 || (directionCoordinates ?? []).length > 1) { @@ -262,14 +284,14 @@ function MapView({ style={{flex: 1}} styleURL={styleURL} onMapIdle={setMapIdle} + onCameraChanged={onCameraChanged} onTouchStart={() => setUserInteractedWithMap(true)} pitchEnabled={pitchEnabled} attributionPosition={{...styles.r2, ...styles.b2}} scaleBarEnabled={false} // We use scaleBarPosition with top: -32 to hide the scale bar on iOS because scaleBarEnabled={false} not work on iOS scaleBarPosition={{...styles.tn8, left: 0}} - compassEnabled - compassPosition={{...styles.l2, ...styles.t5}} + compassEnabled={false} logoPosition={{...styles.l2, ...styles.b2}} {...responder.panHandlers} > @@ -338,6 +360,24 @@ function MapView({ )} + {interactive && shouldDisplayCompass && ( + + + + + + + + )} {interactive && (