feat: themed user location dot on map

Replace default MapLibre puck with a custom CircleLayer child:
accent-coloured fill, 2.5px white stroke, map-pitch-aligned.
Dot colour updates live when the palette changes in Settings.
This commit is contained in:
Davide Scaini
2026-06-03 10:24:58 +02:00
parent ec6a6facd1
commit 358f3f12c1
+12 -2
View File
@@ -4,7 +4,7 @@ import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { activateKeepAwakeAsync, deactivateKeepAwake } from 'expo-keep-awake'; import { activateKeepAwakeAsync, deactivateKeepAwake } from 'expo-keep-awake';
import { Map, Camera, GeoJSONSource, Layer, UserLocation } from '@maplibre/maplibre-react-native'; import { Map, Camera, GeoJSONSource, Layer, UserLocation } from '@maplibre/maplibre-react-native';
import type { LineLayerStyle } from '@maplibre/maplibre-react-native'; import type { LineLayerStyle, CircleLayerStyle } from '@maplibre/maplibre-react-native';
import { useRecordingStore } from '../store/recording'; import { useRecordingStore } from '../store/recording';
import { startGpsRecording, stopGpsRecording, requestLocationPermissions, requestForegroundLocation } from '../services/gps'; import { startGpsRecording, stopGpsRecording, requestLocationPermissions, requestForegroundLocation } from '../services/gps';
import { RootStackParamList } from '../types'; import { RootStackParamList } from '../types';
@@ -43,6 +43,14 @@ export function RecordingScreen() {
lineCap: 'round', lineCap: 'round',
}), [accent]); }), [accent]);
const userDotStyle = useMemo<CircleLayerStyle>(() => ({
circleRadius: 8,
circleColor: accent,
circleStrokeWidth: 2.5,
circleStrokeColor: '#ffffff',
circlePitchAlignment: 'map',
}), [accent]);
const trackGeoJSON = useMemo<GeoJSON.Feature<GeoJSON.LineString>>(() => ({ const trackGeoJSON = useMemo<GeoJSON.Feature<GeoJSON.LineString>>(() => ({
type: 'Feature', type: 'Feature',
geometry: { type: 'LineString', coordinates: trackPoints.map((p) => [p.lon, p.lat]) }, geometry: { type: 'LineString', coordinates: trackPoints.map((p) => [p.lon, p.lat]) },
@@ -101,7 +109,9 @@ export function RecordingScreen() {
trackUserLocation={status === 'recording' ? 'course' : 'default'} trackUserLocation={status === 'recording' ? 'course' : 'default'}
initialViewState={{ zoom: 14 }} initialViewState={{ zoom: 14 }}
/> />
<UserLocation /> <UserLocation>
<Layer id="user-dot" type="circle" style={userDotStyle} />
</UserLocation>
{trackPoints.length >= 2 && ( {trackPoints.length >= 2 && (
<GeoJSONSource id="track" data={trackGeoJSON}> <GeoJSONSource id="track" data={trackGeoJSON}>
<Layer id="track-line" type="line" style={trackLineStyle} /> <Layer id="track-line" type="line" style={trackLineStyle} />