feat: tap map thumbnail to open full-screen interactive map

This commit is contained in:
Davide Scaini
2026-04-24 21:55:56 +02:00
parent a306682b52
commit 3ce365e439
+50 -20
View File
@@ -1,7 +1,7 @@
import { Camera, GeoJSONSource, Layer, Map } from '@maplibre/maplibre-react-native';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { useEffect, useState } from 'react';
import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
import { ActivityIndicator, Modal, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
import Svg, { Defs, LinearGradient, Path, Stop } from 'react-native-svg';
import { useActivity, useSetting } from '@/db/queries';
@@ -121,6 +121,8 @@ export default function ActivityScreen() {
// ── Map ───────────────────────────────────────────────────────────────────────
function RouteMap({ geojson, loading }: { geojson: object | null; loading: boolean }) {
const [fullscreen, setFullscreen] = useState(false);
if (loading) {
return (
<View style={styles.mapPlaceholder}>
@@ -131,25 +133,7 @@ function RouteMap({ geojson, loading }: { geojson: object | null; loading: boole
if (!geojson) return null;
const bounds = geoJsonBounds(geojson);
return (
<View style={styles.mapContainer}>
<Map
style={styles.map}
mapStyle={MAP_STYLE}
dragPan={false}
touchZoom={false}
touchPitch={false}
touchRotate={false}
>
{bounds && (
<Camera
initialViewState={{
bounds,
padding: { top: 24, bottom: 24, left: 24, right: 24 },
}}
/>
)}
const routeSource = (
<GeoJSONSource id="route" data={geojson as GeoJSON.FeatureCollection}>
<Layer
type="line"
@@ -158,8 +142,49 @@ function RouteMap({ geojson, loading }: { geojson: object | null; loading: boole
layout={{ 'line-cap': 'round', 'line-join': 'round' }}
/>
</GeoJSONSource>
);
const camera = bounds ? (
<Camera
initialViewState={{
bounds,
padding: { top: 24, bottom: 24, left: 24, right: 24 },
}}
/>
) : null;
return (
<>
{/* Thumbnail — tap to expand */}
<Pressable style={styles.mapContainer} onPress={() => setFullscreen(true)}>
<Map
style={styles.map}
mapStyle={MAP_STYLE}
dragPan={false}
touchZoom={false}
touchPitch={false}
touchRotate={false}
>
{camera}
{routeSource}
</Map>
<View style={styles.mapExpandHint}>
<Text style={styles.mapExpandText}> tap to explore</Text>
</View>
</Pressable>
{/* Full-screen interactive map */}
<Modal visible={fullscreen} animationType="slide" onRequestClose={() => setFullscreen(false)}>
<View style={styles.fullscreenMap}>
<Map style={styles.map} mapStyle={MAP_STYLE}>
{camera}
{routeSource}
</Map>
<Pressable style={styles.closeButton} onPress={() => setFullscreen(false)}>
<Text style={styles.closeText}></Text>
</Pressable>
</View>
</Modal>
</>
);
}
@@ -281,6 +306,11 @@ const styles = StyleSheet.create({
mapContainer: { height: 220, marginBottom: 16, borderTopWidth: 1, borderBottomWidth: 1, borderColor: '#27272a' },
map: { flex: 1 },
mapPlaceholder: { height: 220, backgroundColor: '#18181b', alignItems: 'center', justifyContent: 'center', borderTopWidth: 1, borderBottomWidth: 1, borderColor: '#27272a', marginBottom: 16 },
mapExpandHint: { position: 'absolute', bottom: 8, right: 8, backgroundColor: 'rgba(0,0,0,0.55)', borderRadius: 6, paddingHorizontal: 8, paddingVertical: 4 },
mapExpandText: { color: '#a1a1aa', fontSize: 11 },
fullscreenMap: { flex: 1, backgroundColor: '#09090b' },
closeButton: { position: 'absolute', top: 56, right: 16, backgroundColor: 'rgba(0,0,0,0.6)', borderRadius: 20, width: 36, height: 36, alignItems: 'center', justifyContent: 'center' },
closeText: { color: '#fff', fontSize: 16 },
chartContainer: { marginHorizontal: 16, marginBottom: 16, backgroundColor: '#18181b', borderRadius: 10, borderWidth: 1, borderColor: '#27272a', padding: 12, alignItems: 'flex-start' },
chartPlaceholder: { height: 120, backgroundColor: '#18181b', alignItems: 'center', justifyContent: 'center', borderRadius: 10, borderWidth: 1, borderColor: '#27272a', marginHorizontal: 16, marginBottom: 16 },
chartLabel: { color: '#3f3f46', fontSize: 10, marginBottom: 2 },