-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: Map new architecture PoC #4785
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import {getCurrentCoordinatesGlobal} from '@atb/GeolocationContext'; | ||
import {FOCUS_ORIGIN} from '@atb/api/geocoder'; | ||
import {StyleSheet} from '@atb/theme'; | ||
import MapboxGL, {LocationPuck} from '@rnmapbox/maps'; | ||
import React, {useRef} from 'react'; | ||
import {View} from 'react-native'; | ||
import {MapCameraConfig, MapViewConfig} from '@atb/components/map/MapConfig'; | ||
Check failure on line 7 in src/components/map_v2/MapV2.tsx GitHub Actions / tsc (atb)
Check failure on line 7 in src/components/map_v2/MapV2.tsx GitHub Actions / tsc (nfk)
Check failure on line 7 in src/components/map_v2/MapV2.tsx GitHub Actions / tsc (fram)
|
||
import {getFeaturesAtClick} from '@atb/components/map/utils.ts'; | ||
Check failure on line 8 in src/components/map_v2/MapV2.tsx GitHub Actions / tsc (atb)
Check failure on line 8 in src/components/map_v2/MapV2.tsx GitHub Actions / tsc (nfk)
Check failure on line 8 in src/components/map_v2/MapV2.tsx GitHub Actions / tsc (fram)
|
||
import {isFeaturePoint} from '@atb/components/map'; | ||
import {usePlugins} from '@atb/components/map_v2/plugins/use-plugins.tsx'; | ||
Check failure on line 10 in src/components/map_v2/MapV2.tsx GitHub Actions / tsc (atb)
Check failure on line 10 in src/components/map_v2/MapV2.tsx GitHub Actions / tsc (nfk)
Check failure on line 10 in src/components/map_v2/MapV2.tsx GitHub Actions / tsc (fram)
|
||
import {useMapRegion} from '@atb/components/map_v2/use-map-region.ts'; | ||
Check failure on line 11 in src/components/map_v2/MapV2.tsx GitHub Actions / tsc (atb)
Check failure on line 11 in src/components/map_v2/MapV2.tsx GitHub Actions / tsc (nfk)
Check failure on line 11 in src/components/map_v2/MapV2.tsx GitHub Actions / tsc (fram)
|
||
|
||
export const MapV2 = () => { | ||
const mapCameraRef = useRef<MapboxGL.Camera>(null); | ||
const mapViewRef = useRef<MapboxGL.MapView>(null); | ||
const styles = useMapStyles(); | ||
|
||
const startingCoordinates = getCurrentCoordinatesGlobal() || FOCUS_ORIGIN; | ||
const {mapRegion, onDidFinishLoadingMap, onMapIdle} = | ||
useMapRegion(mapViewRef); | ||
|
||
const {renderedFeatures, handleClick} = usePlugins(mapRegion); | ||
|
||
return ( | ||
<View style={styles.container}> | ||
<View style={{flex: 1}}> | ||
<MapboxGL.MapView | ||
ref={mapViewRef} | ||
style={{flex: 1}} | ||
pitchEnabled={false} | ||
onDidFinishLoadingMap={onDidFinishLoadingMap} | ||
onMapIdle={onMapIdle} | ||
onPress={(f) => { | ||
if (isFeaturePoint(f)) { | ||
getFeaturesAtClick(f, mapViewRef).then( | ||
(f) => f && handleClick(f as any), | ||
); | ||
} | ||
}} | ||
{...MapViewConfig} | ||
> | ||
<MapboxGL.Camera | ||
ref={mapCameraRef} | ||
zoomLevel={15} | ||
centerCoordinate={[ | ||
startingCoordinates.longitude, | ||
startingCoordinates.latitude, | ||
]} | ||
{...MapCameraConfig} | ||
/> | ||
|
||
{renderedFeatures} | ||
|
||
<LocationPuck puckBearing="heading" puckBearingEnabled={true} /> | ||
</MapboxGL.MapView> | ||
</View> | ||
</View> | ||
); | ||
}; | ||
|
||
const useMapStyles = StyleSheet.createThemeHook(() => ({ | ||
container: {flex: 1}, | ||
})); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export {MapV2} from './MapV2.tsx'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import {isScooter, ScooterSheet, useVehicles} from '@atb/mobility'; | ||
import {Scooters} from '@atb/components/map/components/mobility/Scooters.tsx'; | ||
Check failure on line 2 in src/components/map_v2/plugins/el-scooter-plugin/use-el-scooters-plugin.tsx GitHub Actions / tsc (atb)
Check failure on line 2 in src/components/map_v2/plugins/el-scooter-plugin/use-el-scooters-plugin.tsx GitHub Actions / tsc (nfk)
Check failure on line 2 in src/components/map_v2/plugins/el-scooter-plugin/use-el-scooters-plugin.tsx GitHub Actions / tsc (fram)
Check failure on line 2 in src/components/map_v2/plugins/el-scooter-plugin/use-el-scooters-plugin.tsx GitHub Actions / tsc (troms)
|
||
import React, {useEffect} from 'react'; | ||
import {Feature, Point} from 'geojson'; | ||
import {MapPluginType} from '@atb/components/map_v2/plugins/types.ts'; | ||
Check failure on line 5 in src/components/map_v2/plugins/el-scooter-plugin/use-el-scooters-plugin.tsx GitHub Actions / tsc (atb)
Check failure on line 5 in src/components/map_v2/plugins/el-scooter-plugin/use-el-scooters-plugin.tsx GitHub Actions / tsc (nfk)
Check failure on line 5 in src/components/map_v2/plugins/el-scooter-plugin/use-el-scooters-plugin.tsx GitHub Actions / tsc (fram)
Check failure on line 5 in src/components/map_v2/plugins/el-scooter-plugin/use-el-scooters-plugin.tsx GitHub Actions / tsc (troms)
|
||
import {useBottomSheet} from '@atb/components/bottom-sheet'; | ||
|
||
export const useElScootersPlugin: MapPluginType = ({ | ||
region, | ||
setSharedState, | ||
}) => { | ||
const {open: openBottomSheet, close: closeBottomSheet} = useBottomSheet(); | ||
const vehicles = useVehicles({SCOOTER: {showAll: true, operators: []}}); | ||
const elScooterFeatures = vehicles?.vehicles.scooters; | ||
const updateRegion = vehicles?.updateRegion; | ||
|
||
useEffect(() => { | ||
if (region) updateRegion?.(region); | ||
}, [region, updateRegion]); | ||
|
||
return { | ||
handlePress: (features: Feature<Point>[]) => { | ||
const scooterFeature = features.find(isScooter); | ||
if (scooterFeature) { | ||
openBottomSheet(() => { | ||
return ( | ||
<ScooterSheet | ||
vehicleId={scooterFeature.properties.id} | ||
onClose={() => { | ||
setSharedState({selectedEntityId: undefined}); | ||
closeBottomSheet(); | ||
}} | ||
onReportParkingViolation={() => {}} | ||
/> | ||
); | ||
}, false); | ||
Comment on lines
+25
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there is something very "un-react" about calling functions like this. |
||
setSharedState({selectedEntityId: scooterFeature.properties.id}); | ||
return true; | ||
} | ||
return false; | ||
}, | ||
renderedFeature: elScooterFeatures ? ( | ||
<Scooters scooters={elScooterFeatures} onClusterClick={() => {}} /> | ||
) : undefined, | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import React from 'react'; | ||
import {Feature, FeatureCollection, Point} from 'geojson'; | ||
import {MapPluginType} from '@atb/components/map_v2/plugins/types.ts'; | ||
Check failure on line 3 in src/components/map_v2/plugins/geofencing-zones-plugin/use-geofencing-zones-plugin.tsx GitHub Actions / tsc (atb)
Check failure on line 3 in src/components/map_v2/plugins/geofencing-zones-plugin/use-geofencing-zones-plugin.tsx GitHub Actions / tsc (nfk)
Check failure on line 3 in src/components/map_v2/plugins/geofencing-zones-plugin/use-geofencing-zones-plugin.tsx GitHub Actions / tsc (fram)
Check failure on line 3 in src/components/map_v2/plugins/geofencing-zones-plugin/use-geofencing-zones-plugin.tsx GitHub Actions / tsc (troms)
|
||
import {useVehicleQuery} from '@atb/mobility/queries/use-vehicle-query.tsx'; | ||
import { | ||
hitboxCoveringIconOnly, | ||
PreProcessedGeofencingZones, | ||
usePreProcessedGeofencingZones, | ||
} from '@atb/components/map'; | ||
import MapboxGL from '@rnmapbox/maps'; | ||
import {VehicleExtendedFragment} from '@atb/api/types/generated/fragments/vehicles.ts'; | ||
|
||
export const useGeofencingZonesPlugin: MapPluginType = ({sharedState}) => { | ||
const {data: vehicle} = useVehicleQuery(sharedState.selectedEntityId ?? ''); | ||
|
||
return { | ||
handlePress: (_features: Feature<Point>[]) => { | ||
return true; | ||
}, | ||
renderedFeature: vehicle ? ( | ||
<GeofencingZonesForVehicle vehicle={vehicle} /> | ||
) : undefined, | ||
}; | ||
}; | ||
|
||
type GeofencingZonesForVehicleProps = { | ||
vehicle: VehicleExtendedFragment; | ||
}; | ||
const GeofencingZonesForVehicle = ({ | ||
vehicle, | ||
}: GeofencingZonesForVehicleProps) => { | ||
const preProcessedGeofencingZones = usePreProcessedGeofencingZones(vehicle); | ||
|
||
return ( | ||
<> | ||
{preProcessedGeofencingZones.map((geofencingZone) => ( | ||
<GeofencingZone | ||
geofencingZone={geofencingZone} | ||
key={geofencingZone?.renderKey} | ||
/> | ||
))} | ||
</> | ||
); | ||
}; | ||
|
||
type GeofencingZoneProps = { | ||
geofencingZone: PreProcessedGeofencingZones; | ||
}; | ||
const GeofencingZone = ({geofencingZone}: GeofencingZoneProps) => { | ||
const getGeofencingZoneCustomProps = ['get', 'geofencingZoneCustomProps']; | ||
const bgColor = [ | ||
'get', | ||
'background', | ||
['get', 'color', getGeofencingZoneCustomProps], | ||
]; | ||
const fillOpacity = ['get', 'fillOpacity', getGeofencingZoneCustomProps]; | ||
const lineOpacity = [ | ||
'get', | ||
'background', | ||
|
||
['get', 'strokeOpacity', getGeofencingZoneCustomProps], | ||
]; | ||
return ( | ||
<MapboxGL.ShapeSource | ||
id={'geofencingZonesShapeSource_' + geofencingZone?.renderKey} | ||
shape={geofencingZone.geojson as FeatureCollection} // FeatureCollection from mobility-types_v2 and FeatureCollection from geojson used by MapboxGL don't match perfectly | ||
hitbox={hitboxCoveringIconOnly} // to not be able to hit multiple zones with one click | ||
> | ||
<MapboxGL.FillLayer | ||
id="parkingFill" | ||
style={{ | ||
fillAntialias: true, | ||
fillColor: bgColor, | ||
fillOpacity, | ||
}} | ||
aboveLayerID="water-point-label" | ||
/> | ||
<MapboxGL.LineLayer | ||
id="tariffZonesLine" | ||
style={{ | ||
lineWidth: 3, | ||
lineColor: bgColor, | ||
lineOpacity, | ||
}} | ||
aboveLayerID="water-point-label" | ||
/> | ||
</MapboxGL.ShapeSource> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import {Feature, Point} from 'geojson'; | ||
import {DeparturesDialogSheet} from '../../../map/components/DeparturesDialogSheet'; | ||
import React from 'react'; | ||
import {useBottomSheet} from '@atb/components/bottom-sheet'; | ||
import type {MapPluginType} from '@atb/components/map_v2/plugins/types.ts'; | ||
Check failure on line 5 in src/components/map_v2/plugins/stop-place-plugin/use-stop-place-plugin.tsx GitHub Actions / tsc (atb)
Check failure on line 5 in src/components/map_v2/plugins/stop-place-plugin/use-stop-place-plugin.tsx GitHub Actions / tsc (nfk)
Check failure on line 5 in src/components/map_v2/plugins/stop-place-plugin/use-stop-place-plugin.tsx GitHub Actions / tsc (fram)
Check failure on line 5 in src/components/map_v2/plugins/stop-place-plugin/use-stop-place-plugin.tsx GitHub Actions / tsc (troms)
|
||
import {isStopPlace} from "@atb/components/map/utils.ts"; | ||
Check failure on line 6 in src/components/map_v2/plugins/stop-place-plugin/use-stop-place-plugin.tsx GitHub Actions / tsc (atb)
Check failure on line 6 in src/components/map_v2/plugins/stop-place-plugin/use-stop-place-plugin.tsx GitHub Actions / tsc (nfk)
Check failure on line 6 in src/components/map_v2/plugins/stop-place-plugin/use-stop-place-plugin.tsx GitHub Actions / tsc (fram)
Check failure on line 6 in src/components/map_v2/plugins/stop-place-plugin/use-stop-place-plugin.tsx GitHub Actions / tsc (troms)
|
||
|
||
export const useStopPlacePlugin: MapPluginType = (_) => { | ||
const {open: openBottomSheet, close: closeBottomSheet} = useBottomSheet(); | ||
return { | ||
handlePress: (features: Feature<Point>[]): boolean => { | ||
// if (!enabled) return true; | ||
|
||
const stopPlaceFeature = features.find(isStopPlace); | ||
if (stopPlaceFeature) { | ||
openBottomSheet( | ||
() => ( | ||
<DeparturesDialogSheet | ||
onClose={closeBottomSheet} | ||
distance={200} | ||
stopPlaceFeature={stopPlaceFeature} | ||
navigateToDetails={closeBottomSheet} | ||
navigateToQuay={closeBottomSheet} | ||
navigateToTripSearch={closeBottomSheet} | ||
/> | ||
), | ||
false, | ||
); | ||
return false; | ||
} | ||
|
||
return true; | ||
}, | ||
renderedFeature: undefined, | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import {Feature, Point} from 'geojson'; | ||
import {MapRegion} from '@atb/components/map'; | ||
|
||
export type MapPluginsSharedState = { | ||
selectedEntityId: string | undefined; | ||
}; | ||
|
||
type MapPluginProps = { | ||
region: MapRegion | undefined; | ||
sharedState: MapPluginsSharedState; | ||
setSharedState: (ms: MapPluginsSharedState) => void; | ||
}; | ||
|
||
export type MapPluginType = (props: MapPluginProps) => { | ||
handlePress: (features: Feature<Point>[]) => boolean; | ||
renderedFeature: JSX.Element | undefined; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import {useStopPlacePlugin} from './stop-place-plugin/use-stop-place-plugin'; | ||
import {Feature, Point} from 'geojson'; | ||
import {useElScootersPlugin} from '@atb/components/map_v2/plugins/el-scooter-plugin/use-el-scooters-plugin.tsx'; | ||
Check failure on line 3 in src/components/map_v2/plugins/use-plugins.tsx GitHub Actions / tsc (atb)
Check failure on line 3 in src/components/map_v2/plugins/use-plugins.tsx GitHub Actions / tsc (nfk)
Check failure on line 3 in src/components/map_v2/plugins/use-plugins.tsx GitHub Actions / tsc (fram)
Check failure on line 3 in src/components/map_v2/plugins/use-plugins.tsx GitHub Actions / tsc (troms)
|
||
import {MapRegion} from '@atb/components/map'; | ||
import {MapPluginsSharedState} from '@atb/components/map_v2/plugins/types.ts'; | ||
import {useState} from 'react'; | ||
import {useGeofencingZonesPlugin} from '@atb/components/map_v2/plugins/geofencing-zones-plugin/use-geofencing-zones-plugin.tsx'; | ||
|
||
export const usePlugins = (region?: MapRegion) => { | ||
const [sharedState, setSharedState] = useState<MapPluginsSharedState>({ | ||
selectedEntityId: undefined, | ||
}); | ||
const pluginProps = {region, sharedState, setSharedState}; | ||
const {handlePress: handleStopPlaceClick} = useStopPlacePlugin(pluginProps); | ||
const {handlePress: handleElScooterClick, renderedFeature: elScooterFeature} = | ||
useElScootersPlugin(pluginProps); | ||
const { | ||
handlePress: _handleGeofencingZonesClick, | ||
renderedFeature: geofencingZoneFeature, | ||
} = useGeofencingZonesPlugin(pluginProps); | ||
|
||
const clickHandlers = [handleStopPlaceClick, handleElScooterClick]; | ||
|
||
const handleClick = (features: Feature<Point>[]) => { | ||
for (const handle of clickHandlers) { | ||
const cont = handle(features); | ||
if (!cont) break; | ||
} | ||
return true; | ||
}; | ||
|
||
return { | ||
handleClick, | ||
renderedFeatures: ( | ||
<> | ||
{elScooterFeature} | ||
{geofencingZoneFeature} | ||
</> | ||
), | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import {RefObject, useState} from 'react'; | ||
import {MapRegion} from '@atb/components/map'; | ||
import MapboxGL, {MapState} from '@rnmapbox/maps'; | ||
import isEqual from 'lodash.isequal'; | ||
|
||
export const useMapRegion = (mapViewRef: RefObject<MapboxGL.MapView>) => { | ||
const [mapRegion, setMapRegion] = useState<MapRegion>(); | ||
|
||
const onDidFinishLoadingMap = async () => { | ||
const visibleBounds = await mapViewRef.current?.getVisibleBounds(); | ||
const zoomLevel = await mapViewRef.current?.getZoom(); | ||
const center = await mapViewRef.current?.getCenter(); | ||
|
||
if (!visibleBounds || !zoomLevel || !center) return; | ||
setMapRegion({visibleBounds, zoomLevel, center}); | ||
}; | ||
|
||
/** | ||
* OnMapIdle fires more often than expected, because of that we check if the | ||
* map region is changed before updating its state. | ||
* | ||
* There is a slight performance overhead by deep comparing previous and new | ||
* map regions on each on map idle, but since we have control over the size of | ||
* the objects it should be ok. The risk of firing effects that uses the map | ||
* region too often is greater than the risk introduced by the performance | ||
* overhead. | ||
*/ | ||
const onMapIdle = (state: MapState) => { | ||
const newMapRegion: MapRegion = { | ||
visibleBounds: [state.properties.bounds.ne, state.properties.bounds.sw], | ||
zoomLevel: state.properties.zoom, | ||
center: state.properties.center, | ||
}; | ||
setMapRegion((prevMapRegion) => | ||
isEqual(prevMapRegion, newMapRegion) ? prevMapRegion : newMapRegion, | ||
); | ||
}; | ||
|
||
return {mapRegion, onDidFinishLoadingMap, onMapIdle}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After the tile server + vector source is in place, we can completely remove the updateRegion stuff :)