Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4934,7 +4934,12 @@ const CONST = {
PINK: 'Pink',
},

MAP_MARKER_SIZE: 20,
MAP_MARKER_SIZES: {
CURRENT_LOCATION: {width: 48, height: 48},
START_WAYPOINT: {width: 48, height: 48},
STOP_WAYPOINT: {width: 48, height: 53},
WAYPOINT: {width: 40, height: 40},
},

QUICK_REACTIONS: [
{
Expand Down Expand Up @@ -9888,6 +9893,7 @@ const CONST = {
USER_LOCATION: 'user-location',
ROUTE_SOURCE: 'route-source',
ROUTE_FILL: 'route-fill',
ROUTE_BORDER: 'route-border',
},

PARTNER_ID: {
Expand Down
29 changes: 8 additions & 21 deletions src/components/ConfirmedRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import React, {useEffect} from 'react';
import type {ReactNode} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useMapMarkers from '@hooks/useMapMarkers';
import type {MapMarkerType} from '@hooks/useMapMarkers/types';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import getArrayDepth from '@libs/getArrayDepth';
import {getWaypointIndex} from '@libs/TransactionUtils';
import {init as initMapboxToken, stop as stopMapboxToken} from '@userActions/MapboxToken';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Transaction} from '@src/types/onyx';
import type IconAsset from '@src/types/utils/IconAsset';
import DistanceMapView from './DistanceMapView';
import ImageSVG from './ImageSVG';
import type {WayPoint} from './MapView/MapViewTypes';
import PendingMapView from './MapView/PendingMapView';

Expand All @@ -42,10 +40,9 @@ function ConfirmedRoute({transaction, isSmallerIcon, shouldHaveBorderRadius = tr
const {route0: route} = transaction?.routes ?? {};
const waypoints = transaction?.comment?.waypoints ?? {};
const coordinates = route?.geometry?.coordinates ?? [];
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const expensifyIcons = useMemoizedLazyExpensifyIcons(['DotIndicator', 'DotIndicatorUnfilled', 'Location']);
const getMapMarkerIconComponent = useMapMarkers();

const [mapboxAccessToken] = useOnyx(ONYXKEYS.MAPBOX_ACCESS_TOKEN);

Expand All @@ -54,15 +51,6 @@ function ConfirmedRoute({transaction, isSmallerIcon, shouldHaveBorderRadius = tr
return stopMapboxToken;
}, []);

const getMarkerComponent = (icon: IconAsset): ReactNode => (
<ImageSVG
src={icon}
width={CONST.MAP_MARKER_SIZE}
height={CONST.MAP_MARKER_SIZE}
fill={theme.icon}
/>
);

const lastWaypointIndex = Object.keys(waypoints).length - 1;
const waypointMarkers: WayPoint[] = [];
for (const [key, waypoint] of Object.entries(waypoints)) {
Expand All @@ -71,19 +59,17 @@ function ConfirmedRoute({transaction, isSmallerIcon, shouldHaveBorderRadius = tr
}

const index = getWaypointIndex(key);
let MarkerComponent: IconAsset;
let markerType: MapMarkerType = 'WAYPOINT';
if (index === 0) {
MarkerComponent = expensifyIcons.DotIndicatorUnfilled;
markerType = 'START_WAYPOINT';
} else if (index === lastWaypointIndex) {
MarkerComponent = expensifyIcons.Location;
} else {
MarkerComponent = expensifyIcons.DotIndicator;
markerType = 'STOP_WAYPOINT';
}

waypointMarkers.push({
id: `${waypoint.lng},${waypoint.lat},${index}`,
coordinate: [waypoint.lng, waypoint.lat] as const,
markerComponent: (): ReactNode => getMarkerComponent(MarkerComponent),
markerComponent: (): ReactNode => getMapMarkerIconComponent(markerType),
});
}

Expand All @@ -105,6 +91,7 @@ function ConfirmedRoute({transaction, isSmallerIcon, shouldHaveBorderRadius = tr
waypoints={waypointMarkers}
styleURL={CONST.MAPBOX.STYLE_URL}
requireRouteToDisplayMap={requireRouteToDisplayMap}
shouldDisplayCurrentLocation={false}
/>
) : (
<PendingMapView
Expand Down
28 changes: 8 additions & 20 deletions src/components/DistanceRequest/DistanceRequestFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import type {StyleProp, ViewStyle} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import Button from '@components/Button';
import DistanceMapView from '@components/DistanceMapView';
import ImageSVG from '@components/ImageSVG';
import type {WayPoint} from '@components/MapView/MapViewTypes';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useMapMarkers from '@hooks/useMapMarkers';
import type {MapMarkerType} from '@hooks/useMapMarkers/types';
import useOnyx from '@hooks/useOnyx';
import usePolicy from '@hooks/usePolicy';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import DistanceRequestUtils from '@libs/DistanceRequestUtils';
import {getDistanceInMeters, getWaypointIndex, isCustomUnitRateIDForP2P} from '@libs/TransactionUtils';
Expand All @@ -20,7 +20,6 @@ import ONYXKEYS from '@src/ONYXKEYS';
import type {Policy} from '@src/types/onyx';
import type {WaypointCollection} from '@src/types/onyx/Transaction';
import type Transaction from '@src/types/onyx/Transaction';
import type IconAsset from '@src/types/utils/IconAsset';

const MAX_WAYPOINTS = 25;

Expand All @@ -42,10 +41,10 @@ type DistanceRequestFooterProps = {
};

function DistanceRequestFooter({waypoints, transaction, navigateToWaypointEditPage, policy, mapContainerStyle}: DistanceRequestFooterProps) {
const theme = useTheme();
const styles = useThemeStyles();
const {translate} = useLocalize();
const expensifyIcons = useMemoizedLazyExpensifyIcons(['DotIndicator', 'DotIndicatorUnfilled', 'Location', 'Plus']);
const expensifyIcons = useMemoizedLazyExpensifyIcons(['Plus']);
const getMapMarkerIconComponent = useMapMarkers();
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID);
const activePolicy = usePolicy(activePolicyID);
Expand All @@ -60,35 +59,24 @@ function DistanceRequestFooter({waypoints, transaction, navigateToWaypointEditPa
const mileageRate = isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(policyCurrency, transaction) : defaultMileageRate;
const {unit} = mileageRate ?? {};

const getMarkerComponent = (icon: IconAsset): ReactNode => (
<ImageSVG
src={icon}
width={CONST.MAP_MARKER_SIZE}
height={CONST.MAP_MARKER_SIZE}
fill={theme.icon}
/>
);

const waypointMarkers: WayPoint[] = [];
for (const [key, waypoint] of Object.entries(waypoints ?? {})) {
if (!waypoint?.lat || !waypoint?.lng) {
continue;
}

const index = getWaypointIndex(key);
let MarkerComponent: IconAsset;
let markerType: MapMarkerType = 'WAYPOINT';
if (index === 0) {
MarkerComponent = expensifyIcons.DotIndicatorUnfilled;
markerType = 'START_WAYPOINT';
} else if (index === lastWaypointIndex) {
MarkerComponent = expensifyIcons.Location;
} else {
MarkerComponent = expensifyIcons.DotIndicator;
markerType = 'STOP_WAYPOINT';
}

waypointMarkers.push({
id: `${waypoint.lng},${waypoint.lat},${index}`,
coordinate: [waypoint.lng, waypoint.lat] as const,
markerComponent: (): ReactNode => getMarkerComponent(MarkerComponent),
markerComponent: (): ReactNode => getMapMarkerIconComponent(markerType),
});
}

Expand Down
10 changes: 10 additions & 0 deletions src/components/Icon/chunks/expensify-icons.chunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,12 @@ import Workflows from '@assets/images/workflows.svg';
import Workspace from '@assets/images/workspace-default-avatar.svg';
import Clear from '@assets/images/x-circle.svg';
import Zoom from '@assets/images/zoom.svg';
// These icons are React components because they have
// drop shadow that is NOT handled by babel-plugin-transform-react-native-svg
import MapCurrentLocation from '@components/MapView/Icons/MapCurrentLocation';
import MapStartWaypoint from '@components/MapView/Icons/MapStartWaypoint';
import MapStopWaypoint from '@components/MapView/Icons/MapStopWaypoint';
import MapWaypoint from '@components/MapView/Icons/MapWaypoint';

const Expensicons = {
ReceiptBody,
Expand Down Expand Up @@ -393,6 +399,10 @@ const Expensicons = {
Mail,
MakeAdmin,
Map,
MapCurrentLocation,
MapStartWaypoint,
MapStopWaypoint,
MapWaypoint,
Menu,
Meter,
Megaphone,
Expand Down
10 changes: 10 additions & 0 deletions src/components/MapView/Direction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ function Direction({coordinates, belowLayerID}: DirectionProps) {
id={`${CONST.MAP_VIEW_LAYERS.ROUTE_FILL}-segment-${index}`}
style={styles.mapDirection}
/>
<Mapbox.LineLayer
belowLayerID={`${CONST.MAP_VIEW_LAYERS.ROUTE_FILL}-segment-${index}`}
id={`${CONST.MAP_VIEW_LAYERS.ROUTE_BORDER}-segment-${index}`}
style={styles.mapDirectionBorder}
/>
</Mapbox.ShapeSource>
))}
</>
Expand Down Expand Up @@ -63,6 +68,11 @@ function Direction({coordinates, belowLayerID}: DirectionProps) {
id={CONST.MAP_VIEW_LAYERS.ROUTE_FILL}
style={styles.mapDirection}
/>
<Mapbox.LineLayer
belowLayerID={CONST.MAP_VIEW_LAYERS.ROUTE_FILL}
id={CONST.MAP_VIEW_LAYERS.ROUTE_BORDER}
style={styles.mapDirectionBorder}
/>
</Mapbox.ShapeSource>
);
}
Expand Down
67 changes: 42 additions & 25 deletions src/components/MapView/Direction.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ function Direction({coordinates}: DirectionProps) {
const styles = useThemeStyles();
const layerLayoutStyle: Record<string, string> = styles.mapDirectionLayer.layout;
const layerPointStyle: Record<string, string | number> = styles.mapDirectionLayer.paint;
const layerBorderLayoutStyle: Record<string, string> = styles.mapDirectionLayerBorder.layout;
const layerBorderPointStyle: Record<string, string | number> = styles.mapDirectionLayerBorder.paint;

if (!utils.isSingleSegmentRoute(coordinates)) {
const validSegments = coordinates.filter((segment) => segment.length >= 2);
Expand All @@ -23,31 +25,39 @@ function Direction({coordinates}: DirectionProps) {

return (
<View>
{validSegments.map((segmentCoordinates, index) => (
<Source
// Using index as key is safe because we are not reordering the routes
// eslint-disable-next-line react/no-array-index-key
key={`${CONST.MAP_VIEW_LAYERS.ROUTE_SOURCE}-segment-${index}`}
id={`${CONST.MAP_VIEW_LAYERS.ROUTE_SOURCE}-segment-${index}`}
type="geojson"
data={{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: segmentCoordinates,
},
}}
>
<Layer
id={`${CONST.MAP_VIEW_LAYERS.ROUTE_FILL}-segment-${index}`}
type="line"
source={`${CONST.MAP_VIEW_LAYERS.ROUTE_SOURCE}-segment-${index}`}
paint={layerPointStyle}
layout={layerLayoutStyle}
/>
</Source>
))}
{validSegments.map((segmentCoordinates, index) => {
const sourceId = `${CONST.MAP_VIEW_LAYERS.ROUTE_SOURCE}-segment-${index}`;
return (
<Source
key={sourceId}
id={sourceId}
type="geojson"
data={{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: segmentCoordinates,
},
}}
>
<Layer
id={`${CONST.MAP_VIEW_LAYERS.ROUTE_BORDER}-segment-${index}`}
type="line"
source={sourceId}
paint={layerBorderPointStyle}
layout={layerBorderLayoutStyle}
/>
<Layer
id={`${CONST.MAP_VIEW_LAYERS.ROUTE_FILL}-segment-${index}`}
type="line"
source={sourceId}
paint={layerPointStyle}
layout={layerLayoutStyle}
/>
</Source>
);
})}
</View>
);
}
Expand All @@ -71,6 +81,13 @@ function Direction({coordinates}: DirectionProps) {
},
}}
>
<Layer
id={CONST.MAP_VIEW_LAYERS.ROUTE_BORDER}
type="line"
source={CONST.MAP_VIEW_LAYERS.ROUTE_SOURCE}
paint={layerBorderPointStyle}
layout={layerBorderLayoutStyle}
/>
<Layer
id={CONST.MAP_VIEW_LAYERS.ROUTE_FILL}
type="line"
Expand Down
36 changes: 36 additions & 0 deletions src/components/MapView/Icons/MapCurrentLocation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, {useId} from 'react';
import {G, Path, Svg} from 'react-native-svg';
import type {SvgProps} from 'react-native-svg';
import MapMarkerShadowFilter from './MapMarkerShadowFilter';

function MapCurrentLocation({width = 48, height = 48}: SvgProps) {
const filterId = useId();
return (
<Svg
width={width}
height={height}
viewBox="0 0 48 48"
fill="none"
>
<MapMarkerShadowFilter
id={filterId}
width="48"
height="48"
/>
<G filter={`url(#${filterId})`}>
<Path
fill="#0185ff"
d="M36 20c0 6.627-5.373 12-12 12s-12-5.373-12-12S17.373 8 24 8s12 5.373 12 12"
Comment thread
GCyganek marked this conversation as resolved.
/>
<Path
fill="#fcfbf9"
fillRule="evenodd"
clipRule="evenodd"
d="M24 8c6.627 0 12 5.373 12 12s-5.373 12-12 12-12-5.373-12-12S17.373 8 24 8m0 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18"
/>
</G>
</Svg>
);
}

export default MapCurrentLocation;
33 changes: 33 additions & 0 deletions src/components/MapView/Icons/MapMarkerShadowFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import {Defs, FeDropShadow, Filter} from 'react-native-svg';

type MapMarkerShadowFilterProps = {
id: string;
width: string;
height: string;
};

function MapMarkerShadowFilter({id, width, height}: MapMarkerShadowFilterProps) {
return (
<Defs>
<Filter
id={id}
x="0"
y="0"
width={width}
height={height}
filterUnits="userSpaceOnUse"
>
<FeDropShadow
dx={0}
dy={4}
stdDeviation={6}
floodColor="#021204"
floodOpacity={0.06}
/>
</Filter>
</Defs>
);
}

export default MapMarkerShadowFilter;
Loading
Loading