From 3ce365e439ae9295adda79b1fb6912f00e0ec7be Mon Sep 17 00:00:00 2001 From: Davide Scaini Date: Fri, 24 Apr 2026 21:55:56 +0200 Subject: [PATCH] feat: tap map thumbnail to open full-screen interactive map --- mobile/app/activity/[id].tsx | 92 ++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/mobile/app/activity/[id].tsx b/mobile/app/activity/[id].tsx index 8c94340..10b0a8b 100644 --- a/mobile/app/activity/[id].tsx +++ b/mobile/app/activity/[id].tsx @@ -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 ( @@ -131,35 +133,58 @@ function RouteMap({ geojson, loading }: { geojson: object | null; loading: boole if (!geojson) return null; const bounds = geoJsonBounds(geojson); + const routeSource = ( + + + + ); + const camera = bounds ? ( + + ) : null; return ( - - - {bounds && ( - - )} - - - - - + <> + {/* Thumbnail — tap to expand */} + setFullscreen(true)}> + + {camera} + {routeSource} + + + ⤢ tap to explore + + + + {/* Full-screen interactive map */} + setFullscreen(false)}> + + + {camera} + {routeSource} + + setFullscreen(false)}> + + + + + ); } @@ -278,9 +303,14 @@ const styles = StyleSheet.create({ sport: { color: '#71717a', fontSize: 12, fontWeight: '600', letterSpacing: 0.8, paddingHorizontal: 16, marginBottom: 4 }, title: { color: '#f4f4f5', fontSize: 22, fontWeight: '700', paddingHorizontal: 16, marginBottom: 4 }, date: { color: '#71717a', fontSize: 13, paddingHorizontal: 16, marginBottom: 16 }, - 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 }, + 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 },