From 9d82084fa113adc787c5215dd81af4a3dcd410e5 Mon Sep 17 00:00:00 2001 From: Davide Scaini Date: Wed, 3 Jun 2026 09:32:49 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20section=205=20=E2=80=94=20MapLibre=20ma?= =?UTF-8?q?p=20with=20live=20track=20and=20camera=20follow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace SVG TrackView with a real MapLibre map: - OpenFreeMap liberty tiles (no API key) - Camera follows user in course mode while recording - GeoJSONSource + LineLayer renders track polyline updated live - UserLocation dot shows current GPS position - Sensors button overlaid with semi-transparent background --- CLAUDE.md | 12 ++--- src/screens/RecordingScreen.tsx | 94 +++++++++++++-------------------- 2 files changed, 43 insertions(+), 63 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index da873a1..8b9dd9a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -121,11 +121,9 @@ Items below are what remains before v1 is shippable. - [x] `src/services/batteryOptimization.ts` — Android-only, one-time prompt (dismissed flag in AsyncStorage); uses `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` intent to open the system dialog for bincio-rec directly; falls back to `IGNORE_BATTERY_OPTIMIZATION_SETTINGS` (general page) on OEMs that block the direct intent; called from `App.tsx` on mount alongside notification permission request -### 5 — Map (optional upgrade) +### 5 — Map ✅ -The track view from section 1 already shows the GPX polyline scaled to fit the screen. -MapLibre can be added later for a real basemap (streets/terrain), but is not required for v1. - -- [ ] **MapLibre basemap** — replace `TrackView` SVG with `` + a tile source (e.g. OpenFreeMap); call `MapLibreGL.setAccessToken(null)` for raster-free usage -- [ ] **Camera follow** — add `` that follows `trackPoints[last]` during recording -- [ ] **Line layer** — replace SVG polyline with `` + `` +- [x] **MapLibre basemap** — `Map` with OpenFreeMap liberty style (`tiles.openfreemap.org`); no API key required; logo and attribution hidden +- [x] **Camera follow** — `Camera` with `trackUserLocation="course"` while recording; switches off when idle/paused +- [x] **Live track line** — `GeoJSONSource` fed a memoized `LineString` from `trackPoints`; `Layer` with `type="line"` and blue stroke rendered on top of the basemap +- [x] **User location dot** — `UserLocation` component shows current position on map diff --git a/src/screens/RecordingScreen.tsx b/src/screens/RecordingScreen.tsx index 5e99d5a..475503c 100644 --- a/src/screens/RecordingScreen.tsx +++ b/src/screens/RecordingScreen.tsx @@ -1,20 +1,29 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { View, Text, StyleSheet, TouchableOpacity, Alert, LayoutChangeEvent } from 'react-native'; +import React, { useEffect, useRef, useState, useMemo } from 'react'; +import { View, Text, StyleSheet, TouchableOpacity, Alert } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { activateKeepAwakeAsync, deactivateKeepAwake } from 'expo-keep-awake'; -import Svg, { Polyline } from 'react-native-svg'; +import { Map, Camera, GeoJSONSource, Layer, UserLocation } from '@maplibre/maplibre-react-native'; +import type { LineLayerStyle } from '@maplibre/maplibre-react-native'; import { useRecordingStore } from '../store/recording'; import { startGpsRecording, stopGpsRecording, requestLocationPermissions } from '../services/gps'; import { RootStackParamList, TrackPoint } from '../types'; +const MAP_STYLE = 'https://tiles.openfreemap.org/styles/liberty'; + +const trackLineStyle: LineLayerStyle = { + lineColor: '#3b82f6', + lineWidth: 3, + lineJoin: 'round', + lineCap: 'round', +}; + type Nav = NativeStackNavigationProp; export function RecordingScreen() { const nav = useNavigation