Viktoriia 1 тиждень тому
батько
коміт
97ca23a13b

+ 4 - 2
eas.json

@@ -31,7 +31,8 @@
       "env": {
         "ENV": "development",
         "SENTRY_AUTH_TOKEN": "$SENTRY_AUTH_TOKEN"
-      }
+      },
+      "node": "22.22.2"
     },
     "testflight": {
       "channel": "development",
@@ -48,7 +49,8 @@
       "env": {
         "ENV": "production",
         "SENTRY_AUTH_TOKEN": "$SENTRY_AUTH_TOKEN"
-      }
+      },
+      "node": "22.22.2"
     },
     "production-apk": {
       "channel": "production",

+ 1 - 1
package.json

@@ -23,7 +23,7 @@
   },
   "dependencies": {
     "@expo/vector-icons": "^15.0.2",
-    "@maplibre/maplibre-react-native": "^10.2.1",
+    "@maplibre/maplibre-react-native": "^11.3.6",
     "@nozbe/watermelondb": "^0.28.0",
     "@react-native-clipboard/clipboard": "^1.16.3",
     "@react-native-community/datetimepicker": "8.4.4",

+ 16 - 12
src/database/tilesService/index.ts

@@ -10,6 +10,8 @@ const STYLE_URL = `${VECTOR_MAP_HOST}/nomadmania-maps2025.json`;
 const PACK_NAME = 'vector-map-pack';
 
 function getPackName(p: any): string | undefined {
+  if (p?.metadata?.name) return p.metadata.name;
+  
   if (p?.name) return p.name;
 
   const metaStr = p?.pack?.metadata;
@@ -22,16 +24,18 @@ function getPackName(p: any): string | undefined {
   }
 }
 
+async function getPackByName(name: string) {
+  const packs = await MapLibreRN.OfflineManager.getPacks();
+  return packs.find((p) => getPackName(p) === name);
+}
+
 async function setupOfflineRegion(): Promise<void> {
   try {
-    const bounds: [GeoJSON.Position, GeoJSON.Position] = [
-      [-180, -85],
-      [180, 85]
-    ];
+    const bounds: [number, number, number, number] = [-180, -85, 180, 85];
     const minZoom = 0;
     const maxZoom = 6;
 
-    const pack = await MapLibreRN.OfflineManager.getPack(PACK_NAME);
+    const pack = await getPackByName(PACK_NAME);
 
     if (pack) {
       try {
@@ -41,9 +45,9 @@ async function setupOfflineRegion(): Promise<void> {
         }
       } catch {
         await MapLibreRN.OfflineManager.createPack(
-          { name: PACK_NAME, bounds, minZoom, maxZoom, styleURL: STYLE_URL },
+          { metadata: { name: PACK_NAME }, bounds, minZoom, maxZoom, mapStyle: STYLE_URL },
           () => {},
-          (error) => {
+          (packInfo, error) => {
             if (error) console.error('Error creating offline pack:', error);
           }
         );
@@ -51,14 +55,14 @@ async function setupOfflineRegion(): Promise<void> {
     } else {
       await MapLibreRN.OfflineManager.createPack(
         {
-          name: PACK_NAME,
+          metadata: { name: PACK_NAME },
           bounds,
           minZoom,
           maxZoom,
-          styleURL: STYLE_URL
+          mapStyle: STYLE_URL
         },
         () => {},
-        (error) => {
+        (packInfo, error) => {
           if (error) {
             console.error('Error creating offline pack:', error);
           }
@@ -88,9 +92,9 @@ export async function updateMapsCache() {
       if (!name) continue;
       try {
         if (name === PACK_NAME) {
-          await MapLibreRN.OfflineManager.deletePack(name);
+          await MapLibreRN.OfflineManager.deletePack(pack.id);
         } else {
-          await MapLibreRN.OfflineManager.invalidatePack(name);
+          await MapLibreRN.OfflineManager.invalidatePack(pack.id);
           updateMapMetadata(name, { status: 'invalid' });
         }
       } catch {}

+ 17 - 11
src/screens/InAppScreens/MapScreen/MarkerItem/index.tsx

@@ -1,5 +1,5 @@
-import { useEffect, useRef } from 'react';
-import { View, Image, Text, TouchableOpacity, Platform, Share } from 'react-native';
+import React, { useEffect, useRef } from 'react';
+import { View, Image, Text, TouchableOpacity, Platform, Share, Pressable } from 'react-native';
 
 import { styles } from './styles';
 import { Colors } from 'src/theme';
@@ -16,13 +16,15 @@ const MarkerItem = ({
   toggleSeries,
   token,
   isPremium,
-  setPremiumModalVisible
+  setPremiumModalVisible,
+  calloutPressRef
 }: {
   marker: any;
   toggleSeries: (item: any) => void;
   token: string;
   isPremium: boolean;
   setPremiumModalVisible: (premium: boolean) => void;
+  calloutPressRef?: React.MutableRefObject<boolean>;
 }) => {
   const navigation = useNavigation();
 
@@ -44,11 +46,12 @@ const MarkerItem = ({
   return (
     <>
       {Platform.OS === 'ios' ? (
-        <MapLibreRN.PointAnnotation
+        <MapLibreRN.Marker
           id="selected_marker_callout"
-          coordinate={marker.coordinates}
-          anchor={{ x: 0.5, y: 1 }}
+          lngLat={marker.coordinates}
+          anchor="bottom"
         >
+          <Pressable onPressIn={() => { if (calloutPressRef) calloutPressRef.current = true; }}>
           <View style={styles.customView}>
             <View style={styles.calloutContainer}>
               <View style={styles.calloutImageContainer}>
@@ -145,14 +148,16 @@ const MarkerItem = ({
               )}
             </View>
           </View>
-        </MapLibreRN.PointAnnotation>
+          </Pressable>
+        </MapLibreRN.Marker>
       ) : (
-        <MapLibreRN.MarkerView
+        <MapLibreRN.Marker
           key={`${marker.id}-${marker.visited}`}
           id="selected_marker_callout"
-          coordinate={marker.coordinates}
-          anchor={{ x: 0.5, y: 0.9 }}
+          lngLat={marker.coordinates}
+          anchor="bottom"
         >
+          <Pressable onPressIn={() => { if (calloutPressRef) calloutPressRef.current = true; }}>
           <View style={styles.customView}>
             <View style={styles.calloutContainer}>
               <View style={styles.calloutImageContainer}>
@@ -249,7 +254,8 @@ const MarkerItem = ({
               )}
             </View>
           </View>
-        </MapLibreRN.MarkerView>
+          </Pressable>
+        </MapLibreRN.Marker>
       )}
     </>
   );

+ 0 - 1
src/screens/InAppScreens/MapScreen/MarkerItem/styles.tsx

@@ -61,7 +61,6 @@ export const styles = StyleSheet.create({
     borderColor: Colors.FILL_LIGHT
   },
   calloutTextContainer: {
-    flex: 1,
     gap: 4,
     alignItems: 'center',
     marginVertical: 10

+ 10 - 17
src/screens/InAppScreens/MapScreen/UserItem/index.tsx

@@ -10,7 +10,7 @@ import moment from 'moment';
 import { styles } from '../MarkerItem/styles';
 
 const UserItem = ({ marker }: { marker: any }) => {
-  const calloutUserRef = useRef<MapLibreRN.PointAnnotationRef>(null);
+  const calloutUserRef = useRef<MapLibreRN.MarkerRef>(null);
   const navigation = useNavigation();
   const [refreshKey, setRefreshKey] = useState(0);
 
@@ -18,7 +18,6 @@ const UserItem = ({ marker }: { marker: any }) => {
     useCallback(() => {
       if (Platform.OS === 'android') {
         const timer = setTimeout(() => {
-          calloutUserRef.current?.refresh();
           setRefreshKey((prev) => prev + 1);
         }, 100);
 
@@ -27,12 +26,6 @@ const UserItem = ({ marker }: { marker: any }) => {
     }, [])
   );
 
-  useEffect(() => {
-    if (Platform.OS === 'android') {
-      calloutUserRef.current?.refresh();
-    }
-  }, [marker]);
-
   const formatDateToLocalTime = (utcDate: string) => {
     const date = moment.utc(utcDate).local();
     const now = moment();
@@ -51,10 +44,10 @@ const UserItem = ({ marker }: { marker: any }) => {
   return (
     <>
       {Platform.OS === 'ios' ? (
-        <MapLibreRN.PointAnnotation
+        <MapLibreRN.Marker
           id={marker.id.toString()}
-          coordinate={marker.coordinates}
-          anchor={{ x: 0.5, y: 0 }}
+          lngLat={marker.coordinates}
+          anchor="top"
         >
           <View style={styles.customView}>
             <View style={styles.calloutContainer}>
@@ -112,14 +105,14 @@ const UserItem = ({ marker }: { marker: any }) => {
               </TouchableOpacity>
             </View>
           </View>
-        </MapLibreRN.PointAnnotation>
+        </MapLibreRN.Marker>
       ) : (
-        <MapLibreRN.PointAnnotation
+        <MapLibreRN.Marker
           id={marker.id.toString()}
           key={refreshKey}
-          coordinate={marker.coordinates}
-          anchor={{ x: 0.5, y: 1.1 }}
-          onSelected={() =>
+          lngLat={marker.coordinates}
+          anchor="bottom"
+          onPress={() =>
             navigation.navigate(
               ...([NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW, { userId: marker.id }] as never)
             )
@@ -173,7 +166,7 @@ const UserItem = ({ marker }: { marker: any }) => {
               </TouchableOpacity>
             </View>
           </View>
-        </MapLibreRN.PointAnnotation>
+        </MapLibreRN.Marker>
       )}
     </>
   );

Різницю між файлами не показано, бо вона завелика
+ 286 - 531
src/screens/InAppScreens/MapScreen/index.tsx


+ 13 - 11
src/screens/InAppScreens/MessagesScreen/Components/MessageLocation.tsx

@@ -18,7 +18,7 @@ const MessageLocation = ({
   onLongPress: (currentMessage: any, props: any) => void;
 }) => {
   const navigation = useNavigation();
-  const mapRef = useRef<MapLibreRN.MapViewRef>(null);
+  const mapRef = useRef<MapLibreRN.MapRef>(null);
   const cameraRef = useRef<MapLibreRN.CameraRef>(null);
 
   return (
@@ -29,21 +29,23 @@ const MessageLocation = ({
       }
       onLongPress={() => onLongPress(props.currentMessage, props)}
     >
-      <MapLibreRN.MapView
+      <MapLibreRN.Map
         ref={mapRef}
         style={styles.map}
         mapStyle={VECTOR_MAP_HOST + '/nomadmania-maps2025.json'}
-        rotateEnabled={false}
-        attributionEnabled={false}
-        scrollEnabled={false}
-        zoomEnabled={false}
-        pitchEnabled={false}
+        touchRotate={false}
+        attribution={false}
+        dragPan={false}
+        touchZoom={false}
+        doubleTapZoom={false}
+        doubleTapHoldZoom={false}
+        touchPitch={false}
       >
         <MapLibreRN.Camera
           ref={cameraRef}
-          defaultSettings={{ centerCoordinate: [lng, lat], zoomLevel: 10 }}
+          initialViewState={{ center: [lng, lat], zoom: 10 }}
         />
-        <MapLibreRN.MarkerView coordinate={[lng, lat]}>
+        <MapLibreRN.Marker lngLat={[lng, lat]}>
           <View
             style={{
               width: 20,
@@ -54,8 +56,8 @@ const MessageLocation = ({
               borderColor: Colors.WHITE
             }}
           />
-        </MapLibreRN.MarkerView>
-      </MapLibreRN.MapView>
+        </MapLibreRN.Marker>
+      </MapLibreRN.Map>
     </TouchableOpacity>
   );
 };

+ 18 - 10
src/screens/InAppScreens/MessagesScreen/Components/RouteB.tsx

@@ -46,7 +46,11 @@ const RouteB = () => {
       if (Platform.OS === 'android') {
         setRenderCamera(true);
         requestAnimationFrame(() => {
-          cameraRef.current?.setCamera(config);
+          cameraRef.current?.flyTo({
+            center: config.centerCoordinate,
+            zoom: config.zoomLevel,
+            duration: config.animationDuration
+          });
         });
 
         animationTimeoutRef.current = setTimeout(
@@ -57,7 +61,11 @@ const RouteB = () => {
           (config.animationDuration || 1000) + 200
         );
       } else {
-        cameraRef.current?.setCamera(config);
+        cameraRef.current?.flyTo({
+          center: config.centerCoordinate,
+          zoom: config.zoomLevel,
+          duration: config.animationDuration
+        });
         animationTimeoutRef.current = setTimeout(
           () => {
             isAnimatingRef.current = false;
@@ -154,25 +162,25 @@ const RouteB = () => {
           setMapDimensions({ x, y, width, height });
         }}
       >
-        <MapLibreRN.MapView
+        <MapLibreRN.Map
           style={{ flex: 1 }}
           mapStyle={VECTOR_MAP_HOST + '/nomadmania-maps2025.json'}
-          compassEnabled={false}
-          rotateEnabled={false}
-          attributionEnabled={false}
+          compass={false}
+          touchRotate={false}
+          attribution={false}
           onRegionDidChange={handleRegionChange}
         >
           {(Platform.OS === 'ios' || renderCamera) && <MapLibreRN.Camera ref={cameraRef} />}
 
           {currentLocation && (
-            <MapLibreRN.PointAnnotation
+            <MapLibreRN.Marker
               id="currentLocation"
-              coordinate={[currentLocation.longitude, currentLocation.latitude]}
+              lngLat={[currentLocation.longitude, currentLocation.latitude]}
             >
               <View style={styles.currentLocationMarker} />
-            </MapLibreRN.PointAnnotation>
+            </MapLibreRN.Marker>
           )}
-        </MapLibreRN.MapView>
+        </MapLibreRN.Map>
       </View>
 
       <View

+ 33 - 13
src/screens/InAppScreens/MessagesScreen/FullMapScreen/index.tsx

@@ -30,7 +30,7 @@ const FullMapScreen = ({ route }: { route: any }) => {
   const insets = useSafeAreaInsets();
 
   const navigation = useNavigation();
-  const mapRef = useRef<MapLibreRN.MapViewRef>(null);
+  const mapRef = useRef<MapLibreRN.MapRef>(null);
   const cameraRef = useRef<MapLibreRN.CameraRef>(null);
   const [renderCamera, setRenderCamera] = useState(Platform.OS === 'ios');
   const animationTimeoutRef = useRef<NodeJS.Timeout | null>(null);
@@ -59,7 +59,14 @@ const FullMapScreen = ({ route }: { route: any }) => {
       if (Platform.OS === 'android') {
         setRenderCamera(true);
         requestAnimationFrame(() => {
-          cameraRef.current?.setCamera(config);
+          const options = {
+            center: config.centerCoordinate,
+            zoom: config.zoomLevel,
+            duration: config.animationDuration
+          };
+          if (config.animationMode === 'jumpTo') cameraRef.current?.jumpTo(options);
+          else if (config.animationMode === 'easeTo') cameraRef.current?.easeTo(options);
+          else cameraRef.current?.flyTo(options);
         });
 
         animationTimeoutRef.current = setTimeout(
@@ -70,7 +77,14 @@ const FullMapScreen = ({ route }: { route: any }) => {
           (config.animationDuration || 1000) + 200
         );
       } else {
-        cameraRef.current?.setCamera(config);
+        const options = {
+          center: config.centerCoordinate,
+          zoom: config.zoomLevel,
+          duration: config.animationDuration
+        };
+        if (config.animationMode === 'jumpTo') cameraRef.current?.jumpTo(options);
+        else if (config.animationMode === 'easeTo') cameraRef.current?.easeTo(options);
+        else cameraRef.current?.flyTo(options);
 
         animationTimeoutRef.current = setTimeout(
           () => {
@@ -92,7 +106,10 @@ const FullMapScreen = ({ route }: { route: any }) => {
         setRenderCamera(true);
 
         requestAnimationFrame(() => {
-          cameraRef.current?.flyTo(coordinates, duration);
+          cameraRef.current?.flyTo({
+            center: coordinates as [number, number],
+            duration: duration
+          });
         });
 
         animationTimeoutRef.current = setTimeout(() => {
@@ -100,7 +117,10 @@ const FullMapScreen = ({ route }: { route: any }) => {
           setRenderCamera(false);
         }, duration + 200);
       } else {
-        cameraRef.current?.flyTo(coordinates, duration);
+        cameraRef.current?.flyTo({
+          center: coordinates as [number, number],
+          duration: duration
+        });
 
         animationTimeoutRef.current = setTimeout(() => {
           isAnimatingRef.current = false;
@@ -215,12 +235,12 @@ const FullMapScreen = ({ route }: { route: any }) => {
     <SafeAreaView style={{ height: '100%' }}>
       <StatusBar translucent backgroundColor="transparent" />
 
-      <MapLibreRN.MapView
+      <MapLibreRN.Map
         ref={mapRef}
         style={styles.map}
         mapStyle={VECTOR_MAP_HOST + '/nomadmania-maps2025.json'}
-        rotateEnabled={false}
-        attributionEnabled={false}
+        touchRotate={false}
+        attribution={false}
         onRegionDidChange={() => {
           hideTimer.current = setTimeout(() => {
             setIsZooming(false);
@@ -232,17 +252,17 @@ const FullMapScreen = ({ route }: { route: any }) => {
         {(Platform.OS === 'ios' || renderCamera) && (
           <MapLibreRN.Camera
             ref={cameraRef}
-            defaultSettings={{ centerCoordinate: [lng, lat], zoomLevel: 12 }}
+            initialViewState={{ center: [lng, lat], zoom: 12 }}
           />
         )}
-        <MapLibreRN.MarkerView coordinate={[lng, lat]}>
+        <MapLibreRN.Marker lngLat={[lng, lat]}>
           <View style={styles.marker} />
-        </MapLibreRN.MarkerView>
+        </MapLibreRN.Marker>
 
         {location && (
           <MapLibreRN.UserLocation
             animated={true}
-            showsUserHeadingIndicator={true}
+            heading={true}
             onPress={async () => {
               const currentZoom = await mapRef.current?.getZoom();
               const newZoom = (currentZoom || 0) + 2;
@@ -256,7 +276,7 @@ const FullMapScreen = ({ route }: { route: any }) => {
             }}
           ></MapLibreRN.UserLocation>
         )}
-      </MapLibreRN.MapView>
+      </MapLibreRN.Map>
       <TouchableOpacity
         onPress={() => {
           navigation.goBack();

+ 168 - 138
src/screens/InAppScreens/ProfileScreen/UsersMap/index.tsx

@@ -73,7 +73,7 @@ import { area } from '@turf/turf';
 const defaultUserAvatar = require('assets/icon-user-share-location-solid.png');
 
 const generateFilter = (ids: number[]) => {
-  return ids.length ? ['any', ...ids.map((id) => ['==', 'id', id])] : ['==', 'id', -1];
+  return ids.length ? ['any', ...ids.map((id) => ['==', ['get', 'id'], id])] : ['==', ['get', 'id'], -1];
 };
 
 let regions_visited = {
@@ -81,10 +81,10 @@ let regions_visited = {
   type: 'fill',
   source: 'regions',
   'source-layer': 'regions',
-  style: {
-    fillColor: 'rgba(255, 126, 0, 1)',
-    fillOpacity: 0.5,
-    fillOutlineColor: 'rgba(14, 80, 109, 0)'
+  paint: {
+    'fill-color': 'rgba(255, 126, 0, 1)',
+    'fill-opacity': 0.5,
+    'fill-outline-color': 'rgba(14, 80, 109, 0)'
   },
   filter: generateFilter([]),
   maxzoom: 12
@@ -95,10 +95,10 @@ let countries_visited = {
   type: 'fill',
   source: 'countries',
   'source-layer': 'countries',
-  style: {
-    fillColor: 'rgba(255, 126, 0, 1)',
-    fillOpacity: 0.5,
-    fillOutlineColor: 'rgba(14, 80, 109, 0)'
+  paint: {
+    'fill-color': 'rgba(255, 126, 0, 1)',
+    'fill-opacity': 0.5,
+    'fill-outline-color': 'rgba(14, 80, 109, 0)'
   },
   filter: generateFilter([]),
   maxzoom: 12
@@ -109,10 +109,10 @@ let dare_visited = {
   type: 'fill',
   source: 'dare',
   'source-layer': 'dare',
-  style: {
-    fillColor: 'rgba(255, 126, 0, 1)',
-    fillOpacity: 0.5,
-    fillOutlineColor: 'rgba(255, 126, 0, 1)'
+  paint: {
+    'fill-color': 'rgba(255, 126, 0, 1)',
+    'fill-opacity': 0.5,
+    'fill-outline-color': 'rgba(255, 126, 0, 1)'
   },
   filter: generateFilter([]),
   maxzoom: 12
@@ -123,9 +123,9 @@ let regions = {
   type: 'fill',
   source: 'regions',
   'source-layer': 'regions',
-  style: {
-    fillColor: 'rgba(15, 63, 79, 0)',
-    fillOutlineColor: 'rgba(14, 80, 109, 0)'
+  paint: {
+    'fill-color': 'rgba(15, 63, 79, 0)',
+    'fill-outline-color': 'rgba(14, 80, 109, 0)'
   },
   filter: ['all'],
   maxzoom: 16
@@ -136,9 +136,9 @@ let countries = {
   type: 'fill',
   source: 'countries',
   'source-layer': 'countries',
-  style: {
-    fillColor: 'rgba(15, 63, 79, 0)',
-    fillOutlineColor: 'rgba(14, 80, 109, 0)'
+  paint: {
+    'fill-color': 'rgba(15, 63, 79, 0)',
+    'fill-outline-color': 'rgba(14, 80, 109, 0)'
   },
   filter: ['all'],
   maxzoom: 16
@@ -149,9 +149,9 @@ let dare = {
   type: 'fill',
   source: 'dare',
   'source-layer': 'dare',
-  style: {
-    fillColor: 'rgba(14, 80, 109, 0.6)',
-    fillOutlineColor: 'rgba(14, 80, 109, 1)'
+  paint: {
+    'fill-color': 'rgba(14, 80, 109, 0.6)',
+    'fill-outline-color': 'rgba(14, 80, 109, 1)'
   },
   filter: ['all'],
   maxzoom: 16
@@ -162,8 +162,8 @@ let selected_region = {
   type: 'fill',
   source: 'regions',
   'source-layer': 'regions',
-  style: {
-    fillColor: 'rgba(57, 115, 172, 0.3)'
+  paint: {
+    'fill-color': 'rgba(57, 115, 172, 0.3)'
   },
   maxzoom: 12
 };
@@ -173,11 +173,11 @@ let selected_region_outline = {
   type: 'line',
   source: 'regions',
   'source-layer': 'regions',
-  style: {
-    lineColor: '#ED9334',
-    lineTranslate: [0, 0],
-    lineTranslateAnchor: 'map',
-    lineWidth: ['interpolate', ['linear'], ['zoom'], 0, 2, 4, 3, 5, 4, 12, 5]
+  paint: {
+    'line-color': '#ED9334',
+    'line-translate': [0, 0],
+    'line-translate-anchor': 'map',
+    'line-width': ['interpolate', ['linear'], ['zoom'], 0, 2, 4, 3, 5, 4, 12, 5]
   },
   maxzoom: 12
 };
@@ -202,7 +202,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
     year: moment().year()
   });
 
-  const mapRef = useRef<MapLibreRN.MapViewRef>(null);
+  const mapRef = useRef<MapLibreRN.MapRef>(null);
   const cameraRef = useRef<MapLibreRN.CameraRef>(null);
   const animationTimeoutRef = useRef<NodeJS.Timeout | null>(null);
   const isAnimatingRef = useRef(false);
@@ -295,11 +295,10 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
       }
 
       if (coordinates) {
-        cameraRef.current?.setCamera({
-          centerCoordinate: coordinates,
-          zoomLevel: 10,
-          animationDuration: 1000,
-          animationMode: 'flyTo'
+        cameraRef.current?.flyTo({
+          center: coordinates as [number, number],
+          zoom: 10,
+          duration: 1000
         });
 
         setSelectedMarker({
@@ -379,14 +378,14 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
       if (Platform.OS === 'android') {
         setRenderCamera(true);
         requestAnimationFrame(() => {
-          cameraRef.current?.flyTo(coordinates, duration);
+          cameraRef.current?.flyTo({ center: coordinates as [number, number], duration });
         });
         animationTimeoutRef.current = setTimeout(() => {
           isAnimatingRef.current = false;
           setRenderCamera(false);
         }, duration + 200);
       } else {
-        cameraRef.current?.flyTo(coordinates, duration);
+        cameraRef.current?.flyTo({ center: coordinates as [number, number], duration });
         animationTimeoutRef.current = setTimeout(() => {
           isAnimatingRef.current = false;
         }, duration + 100);
@@ -397,10 +396,27 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
       if (animationTimeoutRef.current) {
         clearTimeout(animationTimeoutRef.current);
       }
+
+      const v11Config = {
+        center: config.centerCoordinate as [number, number],
+        zoom: config.zoomLevel,
+        duration: config.animationDuration
+      };
+
+      const executeCamera = () => {
+        if (config.animationMode === 'flyTo') {
+          cameraRef.current?.flyTo(v11Config);
+        } else if (config.animationMode === 'easeTo') {
+          cameraRef.current?.easeTo(v11Config);
+        } else {
+          cameraRef.current?.jumpTo(v11Config);
+        }
+      };
+
       if (Platform.OS === 'android') {
         setRenderCamera(true);
         requestAnimationFrame(() => {
-          cameraRef.current?.setCamera(config);
+          executeCamera();
         });
         animationTimeoutRef.current = setTimeout(
           () => {
@@ -410,7 +426,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
           (config.animationDuration ?? 1000) + 200
         );
       } else {
-        cameraRef.current?.setCamera(config);
+        executeCamera();
         animationTimeoutRef.current = setTimeout(
           () => {
             isAnimatingRef.current = false;
@@ -431,7 +447,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
         setRenderCamera(true);
 
         requestAnimationFrame(() => {
-          cameraRef.current?.fitBounds(ne, sw, padding, duration);
+          cameraRef.current?.fitBounds([sw[0], sw[1], ne[0], ne[1]], { padding: { top: padding[0], right: padding[1], bottom: padding[2], left: padding[3] }, duration });
         });
 
         animationTimeoutRef.current = setTimeout(() => {
@@ -439,7 +455,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
           setRenderCamera(false);
         }, duration + 200);
       } else {
-        cameraRef.current?.fitBounds(ne, sw, padding, duration);
+        cameraRef.current?.fitBounds([sw[0], sw[1], ne[0], ne[1]], { padding: { top: padding[0], right: padding[1], bottom: padding[2], left: padding[3] }, duration });
 
         animationTimeoutRef.current = setTimeout(() => {
           isAnimatingRef.current = false;
@@ -485,7 +501,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
     if (visitedRegionIds) {
       setRegionsVisitedFilter(generateFilter(visitedRegionIds.ids));
     } else {
-      setRegionsVisitedFilter(['==', 'id', -1]);
+      setRegionsVisitedFilter(['==', ['get', 'id'], -1]);
     }
   }, [visitedRegionIds]);
 
@@ -493,7 +509,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
     if (visitedCountryIds) {
       setCountriesVisitedFilter(generateFilter(visitedCountryIds.ids));
     } else {
-      setCountriesVisitedFilter(['==', 'id', -1]);
+      setCountriesVisitedFilter(['==', ['get', 'id'], -1]);
     }
   }, [visitedCountryIds]);
 
@@ -501,7 +517,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
     if (visitedDareIds) {
       setDareVisitedFilter(generateFilter(visitedDareIds.ids));
     } else {
-      setDareVisitedFilter(['==', 'id', -1]);
+      setDareVisitedFilter(['==', ['get', 'id'], -1]);
     }
   }, [visitedDareIds]);
 
@@ -744,13 +760,12 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
     if (selectedMarker) setSelectedMarker(null);
     if (type === 'blank') return;
     try {
-      const { screenPointX, screenPointY } = event.properties;
-      const [lng, lat] = event.geometry?.coordinates;
+      const { point: screenPoint } = event.nativeEvent;
+      const [lng, lat] = event.nativeEvent.lngLat;
 
-      const { features } = await mapRef.current.queryRenderedFeaturesAtPoint(
-        [screenPointX, screenPointY],
-        undefined,
-        ['regions', 'countries', 'dare']
+      const features = await mapRef.current.queryRenderedFeatures(
+        screenPoint,
+        { filter: undefined, layers: ['regions', 'countries', 'dare'] }
       );
 
       if (features?.length) {
@@ -890,12 +905,12 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
     <SafeAreaView style={{ height: '100%' }}>
       <StatusBar translucent backgroundColor="transparent" />
 
-      <MapLibreRN.MapView
+      <MapLibreRN.Map
         ref={mapRef}
         style={styles.map}
         mapStyle={VECTOR_MAP_HOST + '/nomadmania-maps2025.json'}
-        rotateEnabled={false}
-        attributionEnabled={false}
+        touchRotate={false}
+        attribution={false}
         onPress={onMapPress}
         onRegionDidChange={() => {
           hideTimer.current = setTimeout(() => {
@@ -905,146 +920,161 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
         onRegionWillChange={_.debounce(handleMapChange, 200)}
       >
         <>
-          <MapLibreRN.LineLayer
+          <MapLibreRN.Layer
+            type="line"
             id="nm-regions-line-layer"
-            sourceID={regions.source}
-            sourceLayerID={regions['source-layer']}
+            source={regions.source}
+            source-layer={regions['source-layer']}
             filter={regions.filter as any}
-            maxZoomLevel={regions.maxzoom}
-            style={{
-              lineColor: 'rgba(14, 80, 109, 1)',
-              lineWidth: ['interpolate', ['linear'], ['zoom'], 0, 0.2, 4, 1, 5, 1.5, 12, 3],
-              lineWidthTransition: { duration: 300, delay: 0 },
+            maxzoom={regions.maxzoom}
+            paint={{
+              'line-color': 'rgba(14, 80, 109, 1)',
+              'line-width': ['interpolate', ['linear'], ['zoom'], 0, 0.2, 4, 1, 5, 1.5, 12, 3],
+              'line-width-transition': { duration: 300, delay: 0 }
+            }}
+            layout={{
               visibility: type === 'regions' ? 'visible' : 'none'
             }}
-            belowLayerID="waterway-name"
+            beforeId="waterway-name"
           />
-          <MapLibreRN.FillLayer
+          <MapLibreRN.Layer
+            type="fill"
             id={regions.id}
-            sourceID={regions.source}
-            sourceLayerID={regions['source-layer']}
+            source={regions.source}
+            source-layer={regions['source-layer']}
             filter={regions.filter as any}
-            style={{
-              ...regions.style,
+            paint={regions.paint}
+            layout={{
               visibility: type === 'regions' ? 'visible' : 'none'
             }}
-            maxZoomLevel={regions.maxzoom}
-            belowLayerID={regions_visited.id}
+            maxzoom={regions.maxzoom}
+            beforeId={regions_visited.id}
           />
-          <MapLibreRN.FillLayer
+          <MapLibreRN.Layer
+            type="fill"
             id={regions_visited.id}
-            sourceID={regions_visited.source}
-            sourceLayerID={regions_visited['source-layer']}
+            source={regions_visited.source}
+            source-layer={regions_visited['source-layer']}
             filter={regionsVisitedFilter as any}
-            style={{
-              ...regions_visited.style,
+            paint={regions_visited.paint}
+            layout={{
               visibility: type === 'regions' ? 'visible' : 'none'
             }}
-            maxZoomLevel={regions_visited.maxzoom}
-            belowLayerID="waterway-name"
+            maxzoom={regions_visited.maxzoom}
+            beforeId="waterway-name"
           />
         </>
 
         <>
-          <MapLibreRN.LineLayer
+          <MapLibreRN.Layer
+            type="line"
             id="countries-line-layer"
-            sourceID={countries.source}
-            sourceLayerID={countries['source-layer']}
+            source={countries.source}
+            source-layer={countries['source-layer']}
             filter={countries.filter as any}
-            maxZoomLevel={countries.maxzoom}
-            style={{
-              lineColor: 'rgba(14, 80, 109, 1)',
-              lineWidth: ['interpolate', ['linear'], ['zoom'], 0, 0.2, 4, 1, 5, 1.5, 12, 3],
-              lineWidthTransition: { duration: 300, delay: 0 },
+            maxzoom={countries.maxzoom}
+            paint={{
+              'line-color': 'rgba(14, 80, 109, 1)',
+              'line-width': ['interpolate', ['linear'], ['zoom'], 0, 0.2, 4, 1, 5, 1.5, 12, 3],
+              'line-width-transition': { duration: 300, delay: 0 }
+            }}
+            layout={{
               visibility: type === 'countries' ? 'visible' : 'none'
             }}
-            belowLayerID="waterway-name"
+            beforeId="waterway-name"
           />
-          <MapLibreRN.FillLayer
+          <MapLibreRN.Layer
+            type="fill"
             id={countries.id}
-            sourceID={countries.source}
-            sourceLayerID={countries['source-layer']}
+            source={countries.source}
+            source-layer={countries['source-layer']}
             filter={countries.filter as any}
-            style={{
-              ...countries.style,
+            paint={countries.paint}
+            layout={{
               visibility: type === 'countries' ? 'visible' : 'none'
             }}
-            maxZoomLevel={countries.maxzoom}
-            belowLayerID={countries_visited.id}
+            maxzoom={countries.maxzoom}
+            beforeId={countries_visited.id}
           />
-          <MapLibreRN.FillLayer
+          <MapLibreRN.Layer
+            type="fill"
             id={countries_visited.id}
-            sourceID={countries_visited.source}
-            sourceLayerID={countries_visited['source-layer']}
+            source={countries_visited.source}
+            source-layer={countries_visited['source-layer']}
             filter={countriesVisitedFilter as any}
-            style={{
-              ...countries_visited.style,
+            paint={countries_visited.paint}
+            layout={{
               visibility: type === 'countries' ? 'visible' : 'none'
             }}
-            maxZoomLevel={countries_visited.maxzoom}
-            belowLayerID="waterway-name"
+            maxzoom={countries_visited.maxzoom}
+            beforeId="waterway-name"
           />
         </>
 
         <>
-          <MapLibreRN.FillLayer
+          <MapLibreRN.Layer
+            type="fill"
             id={dare.id}
-            sourceID={dare.source}
-            sourceLayerID={dare['source-layer']}
+            source={dare.source}
+            source-layer={dare['source-layer']}
             filter={dare.filter as any}
-            style={{
-              ...dare.style,
+            paint={dare.paint}
+            layout={{
               visibility: type === 'dare' ? 'visible' : 'none'
             }}
-            maxZoomLevel={dare.maxzoom}
-            belowLayerID={dare_visited.id}
+            maxzoom={dare.maxzoom}
+            beforeId={dare_visited.id}
           />
-          <MapLibreRN.FillLayer
+          <MapLibreRN.Layer
+            type="fill"
             id={dare_visited.id}
-            sourceID={dare_visited.source}
-            sourceLayerID={dare_visited['source-layer']}
+            source={dare_visited.source}
+            source-layer={dare_visited['source-layer']}
             filter={dareVisitedFilter as any}
-            style={{
-              ...dare_visited.style,
+            paint={dare_visited.paint}
+            layout={{
               visibility: type === 'dare' ? 'visible' : 'none'
             }}
-            maxZoomLevel={dare_visited.maxzoom}
-            belowLayerID="waterway-name"
+            maxzoom={dare_visited.maxzoom}
+            beforeId="waterway-name"
           />
         </>
 
         {selectedRegion && type && (
           <>
-            <MapLibreRN.FillLayer
+            <MapLibreRN.Layer
+              type="fill"
               id={selected_region.id}
-              sourceID={type}
-              sourceLayerID={type}
-              filter={['==', 'id', selectedRegion]}
-              style={selected_region.style}
-              maxZoomLevel={selected_region.maxzoom}
-              belowLayerID="waterway-name"
+              source={type}
+              source-layer={type}
+              filter={['==', ['get', 'id'], selectedRegion]}
+              paint={selected_region.paint}
+              maxzoom={selected_region.maxzoom}
+              beforeId="waterway-name"
             />
-            <MapLibreRN.LineLayer
+            <MapLibreRN.Layer
+              type="line"
               id={selected_region_outline.id}
-              sourceID={type}
-              sourceLayerID={type}
-              filter={['==', 'id', selectedRegion]}
-              style={selected_region_outline.style as any}
-              maxZoomLevel={selected_region_outline.maxzoom}
-              belowLayerID="waterway-name"
+              source={type}
+              source-layer={type}
+              filter={['==', ['get', 'id'], selectedRegion]}
+              paint={selected_region_outline.paint as any}
+              maxzoom={selected_region_outline.maxzoom}
+              beforeId="waterway-name"
             />
           </>
         )}
 
         {data.location_sharing && data.location_last_seen_location && data.own_profile !== 1 && (
-          <MapLibreRN.ShapeSource id="user_location" shape={locationFeature}>
-            <MapLibreRN.SymbolLayer
+          <MapLibreRN.GeoJSONSource id="user_location" data={locationFeature}>
+            <MapLibreRN.Layer
+              type="symbol"
               id="user_symbol"
               filter={['!', ['has', 'point_count']]}
-              aboveLayerID={Platform.OS === 'android' ? 'place-continent' : undefined}
-              style={{
-                iconImage: defaultUserAvatar,
-                iconSize: [
+              afterId={Platform.OS === 'android' ? 'place-continent' : undefined}
+              layout={{
+                'icon-image': defaultUserAvatar,
+                'icon-size': [
                   'interpolate',
                   ['linear'],
                   ['zoom'],
@@ -1059,17 +1089,16 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
                   20,
                   0.42
                 ],
-                iconAllowOverlap: true
+                'icon-allow-overlap': true
               }}
             />
-          </MapLibreRN.ShapeSource>
+          </MapLibreRN.GeoJSONSource>
         )}
 
         {(Platform.OS === 'ios' || renderCamera) && <MapLibreRN.Camera ref={cameraRef} />}
         {location && (
           <MapLibreRN.UserLocation
-            showsUserHeadingIndicator={true}
-            visible={true}
+            heading={true}
           ></MapLibreRN.UserLocation>
         )}
           {selectedMarker && (
@@ -1082,7 +1111,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
               token={token}
             />
           )}
-      </MapLibreRN.MapView>
+      </MapLibreRN.Map>
 
       {center ? (
         <ScaleBar zoom={zoom} latitude={center[1]} isVisible={isZooming} bottom={80} />
@@ -1299,3 +1328,4 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
 };
 
 export default UsersMapScreen;
+

+ 58 - 43
src/screens/InAppScreens/TravelsScreen/AddRegionsNewScreen/index.tsx

@@ -21,7 +21,7 @@ import SearchSvg from '../../../../../assets/icons/search.svg';
 import LocationIcon from 'assets/icons/location.svg';
 
 const generateFilter = (ids: number[]) => {
-  return ids?.length ? ['any', ...ids.map((id) => ['==', 'id', id])] : ['==', 'id', -1];
+  return ids?.length ? ['any', ...ids.map((id) => ['==', ['get', 'id'], id])] : ['==', ['get', 'id'], -1];
 };
 
 let nm_regions = {
@@ -29,8 +29,9 @@ let nm_regions = {
   type: 'fill',
   source: 'regions',
   'source-layer': 'regions',
-  style: {
-    fillColor: 'rgba(15, 63, 79, 0)'
+  paint: {
+    'fill-color': 'rgba(15, 63, 79, 0)'
+
   },
   filter: ['all'],
   maxzoom: 16
@@ -41,8 +42,9 @@ let selected_region = {
   type: 'fill',
   source: 'regions',
   'source-layer': 'regions',
-  style: {
-    fillColor: 'rgba(237, 147, 52, 0.7)'
+  paint: {
+    'fill-color': 'rgba(237, 147, 52, 0.7)'
+
   },
   maxzoom: 12
 };
@@ -62,7 +64,7 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
   const [regionsToSave, setRegionsToSave] = useState<RegionAddData[]>([]);
   const [regionData, setRegionData] = useState<RegionAddData | null>(null);
   const [regionPopupVisible, setRegionPopupVisible] = useState(false);
-  const mapRef = useRef<MapLibreRN.MapViewRef>(null);
+  const mapRef = useRef<MapLibreRN.MapRef>(null);
   const cameraRef = useRef<MapLibreRN.CameraRef>(null);
   const [renderCamera, setRenderCamera] = useState(Platform.OS === 'ios');
   const isAnimatingRef = useRef(false);
@@ -89,14 +91,14 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
       if (Platform.OS === 'android') {
         setRenderCamera(true);
         requestAnimationFrame(() => {
-          cameraRef.current?.flyTo(coordinates, duration);
+          cameraRef.current?.flyTo({ center: coordinates as [number, number], duration });
         });
         animationTimeoutRef.current = setTimeout(() => {
           isAnimatingRef.current = false;
           setRenderCamera(false);
         }, duration + 200);
       } else {
-        cameraRef.current?.flyTo(coordinates, duration);
+        cameraRef.current?.flyTo({ center: coordinates as [number, number], duration });
         animationTimeoutRef.current = setTimeout(() => {
           isAnimatingRef.current = false;
         }, duration + 100);
@@ -112,7 +114,13 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
       if (Platform.OS === 'android') {
         setRenderCamera(true);
         requestAnimationFrame(() => {
-          cameraRef.current?.setCamera(config);
+          if (config.animationMode === 'flyTo') {
+            cameraRef.current?.flyTo({ center: config.centerCoordinate as [number, number], zoom: config.zoomLevel, duration: config.animationDuration });
+          } else if (config.animationMode === 'easeTo') {
+            cameraRef.current?.easeTo({ center: config.centerCoordinate as [number, number], zoom: config.zoomLevel, duration: config.animationDuration });
+          } else {
+            cameraRef.current?.jumpTo({ center: config.centerCoordinate as [number, number], zoom: config.zoomLevel });
+          }
         });
         animationTimeoutRef.current = setTimeout(
           () => {
@@ -122,7 +130,13 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
           (config.animationDuration ?? 1000) + 200
         );
       } else {
-        cameraRef.current?.setCamera(config);
+        if (config.animationMode === 'flyTo') {
+          cameraRef.current?.flyTo({ center: config.centerCoordinate as [number, number], zoom: config.zoomLevel, duration: config.animationDuration });
+        } else if (config.animationMode === 'easeTo') {
+          cameraRef.current?.easeTo({ center: config.centerCoordinate as [number, number], zoom: config.zoomLevel, duration: config.animationDuration });
+        } else {
+          cameraRef.current?.jumpTo({ center: config.centerCoordinate as [number, number], zoom: config.zoomLevel });
+        }
         animationTimeoutRef.current = setTimeout(
           () => {
             isAnimatingRef.current = false;
@@ -143,7 +157,7 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
         setRenderCamera(true);
 
         requestAnimationFrame(() => {
-          cameraRef.current?.fitBounds(ne, sw, padding, duration);
+          cameraRef.current?.fitBounds([sw[0], sw[1], ne[0], ne[1]], { padding: { top: padding[0], right: padding[1], bottom: padding[2], left: padding[3] }, duration });
         });
 
         animationTimeoutRef.current = setTimeout(() => {
@@ -151,7 +165,7 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
           setRenderCamera(false);
         }, duration + 200);
       } else {
-        cameraRef.current?.fitBounds(ne, sw, padding, duration);
+        cameraRef.current?.fitBounds([sw[0], sw[1], ne[0], ne[1]], { padding: { top: padding[0], right: padding[1], bottom: padding[2], left: padding[3] }, duration });
 
         animationTimeoutRef.current = setTimeout(() => {
           isAnimatingRef.current = false;
@@ -253,12 +267,11 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
       if (!mapRef.current) return;
 
       try {
-        const { screenPointX, screenPointY } = event.properties;
+        const { point } = event.nativeEvent;
 
-        const { features } = await mapRef.current.queryRenderedFeaturesAtPoint(
-          [screenPointX, screenPointY],
-          undefined,
-          ['regions']
+        const features = await mapRef.current.queryRenderedFeatures(
+          point,
+          { layers: ['regions'] }
         );
 
         if (features?.length) {
@@ -389,55 +402,55 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
       </View>
 
       <View style={styles.container}>
-        <MapLibreRN.MapView
+        <MapLibreRN.Map
           ref={mapRef}
           style={styles.map}
           mapStyle={VECTOR_MAP_HOST + '/nomadmania-maps2025.json'}
-          rotateEnabled={false}
-          attributionEnabled={false}
+          touchRotate={false}
+          attribution={false}
           onPress={handleMapPress}
         >
           {(Platform.OS === 'ios' || renderCamera) && <MapLibreRN.Camera ref={cameraRef} />}
 
-          <MapLibreRN.LineLayer
+          <MapLibreRN.Layer type="line"
             id="nm-regions-line-layer"
-            sourceID={nm_regions.source}
-            sourceLayerID={nm_regions['source-layer']}
+            source={nm_regions.source}
+            source-layer={nm_regions['source-layer']}
             filter={nm_regions.filter as any}
-            maxZoomLevel={nm_regions.maxzoom}
-            style={{
-              lineColor: 'rgba(14, 80, 109, 1)',
-              lineWidth: ['interpolate', ['linear'], ['zoom'], 0, 0.2, 4, 1, 5, 1.5, 12, 3],
-              lineWidthTransition: { duration: 300, delay: 0 }
+            maxzoom={nm_regions.maxzoom}
+            paint={{
+              'line-color': 'rgba(14, 80, 109, 1)',
+              'line-width': ['interpolate', ['linear'], ['zoom'], 0, 0.2, 4, 1, 5, 1.5, 12, 3],
+              'line-width-transition': { duration: 300, delay: 0 }
             }}
-            belowLayerID="waterway-name"
+            beforeId="waterway-name"
           />
-          <MapLibreRN.FillLayer
+          <MapLibreRN.Layer type="fill"
             id={nm_regions.id}
-            sourceID={nm_regions.source}
-            sourceLayerID={nm_regions['source-layer']}
+            source={nm_regions.source}
+            source-layer={nm_regions['source-layer']}
             filter={nm_regions.filter as any}
-            style={nm_regions.style}
-            maxZoomLevel={nm_regions.maxzoom}
-            belowLayerID="nm-regions-line-layer"
+            paint={nm_regions.paint}
+            maxzoom={nm_regions.maxzoom}
+            beforeId="nm-regions-line-layer"
           />
 
           {selectedRegions && selectedRegions.length > 0 ? (
-            <MapLibreRN.FillLayer
+            <MapLibreRN.Layer type="fill"
               id={selected_region.id}
-              sourceID={nm_regions.source}
-              sourceLayerID={nm_regions['source-layer']}
+              source={nm_regions.source}
+              source-layer={nm_regions['source-layer']}
               filter={filterSelectedRegions as any}
-              style={selected_region.style}
-              maxZoomLevel={selected_region.maxzoom}
-              belowLayerID="nm-regions-line-layer"
+              paint={selected_region.paint}
+              maxzoom={selected_region.maxzoom}
+              beforeId="nm-regions-line-layer"
             />
           ) : null}
 
           {location && (
             <MapLibreRN.UserLocation
               animated={true}
-              showsUserHeadingIndicator={true}
+              heading={true}
               onPress={async () => {
                 const currentZoom = await mapRef.current?.getZoom();
                 const newZoom = (currentZoom || 0) + 2;
@@ -451,7 +464,7 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
               }}
             ></MapLibreRN.UserLocation>
           )}
-        </MapLibreRN.MapView>
+        </MapLibreRN.Map>
 
         <TouchableOpacity
           onPress={handleGetLocation}
@@ -521,3 +534,5 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
 };
 
 export default AddRegionsScreen;
+
+

+ 58 - 43
src/screens/InAppScreens/TravelsScreen/AddRegionsScreen/index.tsx

@@ -22,7 +22,7 @@ import SaveSvg from '../../../../../assets/icons/travels-screens/save.svg';
 import LocationIcon from 'assets/icons/location.svg';
 
 const generateFilter = (ids: number[]) => {
-  return ids?.length ? ['any', ...ids.map((id) => ['==', 'id', id])] : ['==', 'id', -1];
+  return ids?.length ? ['any', ...ids.map((id) => ['==', ['get', 'id'], id])] : ['==', ['get', 'id'], -1];
 };
 
 let nm_regions = {
@@ -30,8 +30,9 @@ let nm_regions = {
   type: 'fill',
   source: 'regions',
   'source-layer': 'regions',
-  style: {
-    fillColor: 'rgba(15, 63, 79, 0)'
+  paint: {
+    'fill-color': 'rgba(15, 63, 79, 0)'
+
   },
   filter: ['all'],
   maxzoom: 16
@@ -42,8 +43,9 @@ let selected_region = {
   type: 'fill',
   source: 'regions',
   'source-layer': 'regions',
-  style: {
-    fillColor: 'rgba(237, 147, 52, 0.7)'
+  paint: {
+    'fill-color': 'rgba(237, 147, 52, 0.7)'
+
   },
   maxzoom: 12
 };
@@ -61,7 +63,7 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
   const [regionsToSave, setRegionsToSave] = useState<RegionAddData[]>([]);
   const [regionData, setRegionData] = useState<RegionAddData | null>(null);
   const [regionPopupVisible, setRegionPopupVisible] = useState(false);
-  const mapRef = useRef<MapLibreRN.MapViewRef>(null);
+  const mapRef = useRef<MapLibreRN.MapRef>(null);
   const cameraRef = useRef<MapLibreRN.CameraRef>(null);
   const [renderCamera, setRenderCamera] = useState(Platform.OS === 'ios');
   const isAnimatingRef = useRef(false);
@@ -83,14 +85,14 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
       if (Platform.OS === 'android') {
         setRenderCamera(true);
         requestAnimationFrame(() => {
-          cameraRef.current?.flyTo(coordinates, duration);
+          cameraRef.current?.flyTo({ center: coordinates as [number, number], duration });
         });
         animationTimeoutRef.current = setTimeout(() => {
           isAnimatingRef.current = false;
           setRenderCamera(false);
         }, duration + 200);
       } else {
-        cameraRef.current?.flyTo(coordinates, duration);
+        cameraRef.current?.flyTo({ center: coordinates as [number, number], duration });
         animationTimeoutRef.current = setTimeout(() => {
           isAnimatingRef.current = false;
         }, duration + 100);
@@ -106,7 +108,13 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
       if (Platform.OS === 'android') {
         setRenderCamera(true);
         requestAnimationFrame(() => {
-          cameraRef.current?.setCamera(config);
+          if (config.animationMode === 'flyTo') {
+            cameraRef.current?.flyTo({ center: config.centerCoordinate as [number, number], zoom: config.zoomLevel, duration: config.animationDuration });
+          } else if (config.animationMode === 'easeTo') {
+            cameraRef.current?.easeTo({ center: config.centerCoordinate as [number, number], zoom: config.zoomLevel, duration: config.animationDuration });
+          } else {
+            cameraRef.current?.jumpTo({ center: config.centerCoordinate as [number, number], zoom: config.zoomLevel });
+          }
         });
         animationTimeoutRef.current = setTimeout(
           () => {
@@ -116,7 +124,13 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
           (config.animationDuration ?? 1000) + 200
         );
       } else {
-        cameraRef.current?.setCamera(config);
+        if (config.animationMode === 'flyTo') {
+          cameraRef.current?.flyTo({ center: config.centerCoordinate as [number, number], zoom: config.zoomLevel, duration: config.animationDuration });
+        } else if (config.animationMode === 'easeTo') {
+          cameraRef.current?.easeTo({ center: config.centerCoordinate as [number, number], zoom: config.zoomLevel, duration: config.animationDuration });
+        } else {
+          cameraRef.current?.jumpTo({ center: config.centerCoordinate as [number, number], zoom: config.zoomLevel });
+        }
         animationTimeoutRef.current = setTimeout(
           () => {
             isAnimatingRef.current = false;
@@ -137,7 +151,7 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
         setRenderCamera(true);
 
         requestAnimationFrame(() => {
-          cameraRef.current?.fitBounds(ne, sw, padding, duration);
+          cameraRef.current?.fitBounds([sw[0], sw[1], ne[0], ne[1]], { padding: { top: padding[0], right: padding[1], bottom: padding[2], left: padding[3] }, duration });
         });
 
         animationTimeoutRef.current = setTimeout(() => {
@@ -145,7 +159,7 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
           setRenderCamera(false);
         }, duration + 200);
       } else {
-        cameraRef.current?.fitBounds(ne, sw, padding, duration);
+        cameraRef.current?.fitBounds([sw[0], sw[1], ne[0], ne[1]], { padding: { top: padding[0], right: padding[1], bottom: padding[2], left: padding[3] }, duration });
 
         animationTimeoutRef.current = setTimeout(() => {
           isAnimatingRef.current = false;
@@ -248,12 +262,11 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
       if (!mapRef.current) return;
 
       try {
-        const { screenPointX, screenPointY } = event.properties;
+        const { point } = event.nativeEvent;
 
-        const { features } = await mapRef.current.queryRenderedFeaturesAtPoint(
-          [screenPointX, screenPointY],
-          undefined,
-          ['regions']
+        const features = await mapRef.current.queryRenderedFeatures(
+          point,
+          { layers: ['regions'] }
         );
 
         if (features?.length) {
@@ -378,55 +391,55 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
       </View>
 
       <View style={styles.container}>
-        <MapLibreRN.MapView
+        <MapLibreRN.Map
           ref={mapRef}
           style={styles.map}
           mapStyle={VECTOR_MAP_HOST + '/nomadmania-maps2025.json'}
-          rotateEnabled={false}
-          attributionEnabled={false}
+          touchRotate={false}
+          attribution={false}
           onPress={handleMapPress}
         >
           {(Platform.OS === 'ios' || renderCamera) && <MapLibreRN.Camera ref={cameraRef} />}
 
-          <MapLibreRN.LineLayer
+          <MapLibreRN.Layer type="line"
             id="nm-regions-line-layer"
-            sourceID={nm_regions.source}
-            sourceLayerID={nm_regions['source-layer']}
+            source={nm_regions.source}
+            source-layer={nm_regions['source-layer']}
             filter={nm_regions.filter as any}
-            maxZoomLevel={nm_regions.maxzoom}
-            style={{
-              lineColor: 'rgba(14, 80, 109, 1)',
-              lineWidth: ['interpolate', ['linear'], ['zoom'], 0, 0.2, 4, 1, 5, 1.5, 12, 3],
-              lineWidthTransition: { duration: 300, delay: 0 }
+            maxzoom={nm_regions.maxzoom}
+            paint={{
+              'line-color': 'rgba(14, 80, 109, 1)',
+              'line-width': ['interpolate', ['linear'], ['zoom'], 0, 0.2, 4, 1, 5, 1.5, 12, 3],
+              'line-width-transition': { duration: 300, delay: 0 }
             }}
-            belowLayerID="waterway-name"
+            beforeId="waterway-name"
           />
-          <MapLibreRN.FillLayer
+          <MapLibreRN.Layer type="fill"
             id={nm_regions.id}
-            sourceID={nm_regions.source}
-            sourceLayerID={nm_regions['source-layer']}
+            source={nm_regions.source}
+            source-layer={nm_regions['source-layer']}
             filter={nm_regions.filter as any}
-            style={nm_regions.style}
-            maxZoomLevel={nm_regions.maxzoom}
-            belowLayerID="nm-regions-line-layer"
+            paint={nm_regions.paint}
+            maxzoom={nm_regions.maxzoom}
+            beforeId="nm-regions-line-layer"
           />
 
           {selectedRegions && selectedRegions.length > 0 ? (
-            <MapLibreRN.FillLayer
+            <MapLibreRN.Layer type="fill"
               id={selected_region.id}
-              sourceID={nm_regions.source}
-              sourceLayerID={nm_regions['source-layer']}
+              source={nm_regions.source}
+              source-layer={nm_regions['source-layer']}
               filter={filterSelectedRegions as any}
-              style={selected_region.style}
-              maxZoomLevel={selected_region.maxzoom}
-              belowLayerID="nm-regions-line-layer"
+              paint={selected_region.paint}
+              maxzoom={selected_region.maxzoom}
+              beforeId="nm-regions-line-layer"
             />
           ) : null}
 
           {location && (
             <MapLibreRN.UserLocation
               animated={true}
-              showsUserHeadingIndicator={true}
+              heading={true}
               onPress={async () => {
                 const currentZoom = await mapRef.current?.getZoom();
                 const newZoom = (currentZoom || 0) + 2;
@@ -440,7 +453,7 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
               }}
             ></MapLibreRN.UserLocation>
           )}
-        </MapLibreRN.MapView>
+        </MapLibreRN.Map>
 
         <TouchableOpacity
           onPress={handleGetLocation}
@@ -509,3 +522,5 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
 };
 
 export default AddRegionsScreen;
+
+

+ 68 - 54
src/screens/InAppScreens/TravelsScreen/EarthScreen/index.tsx

@@ -13,8 +13,9 @@ import * as MapLibreRN from '@maplibre/maplibre-react-native';
 
 let kye_fill = {
   id: 'kye_fill',
-  style: {
-    fillColor: 'rgba(21, 99, 123, 0.4)'
+  paint: {
+    'fill-color': 'rgba(21, 99, 123, 0.4)'
+
   },
 
   maxzoom: 12
@@ -22,19 +23,18 @@ let kye_fill = {
 
 let kye_fill_visited = {
   id: 'kye_fill_visited',
-  style: {
-    fillColor: 'rgba(132, 138, 68, 0.6)'
+  paint: {
+    'fill-color': 'rgba(132, 138, 68, 0.6)'
+
   },
   maxzoom: 12
 };
 
 let kye_line = {
   id: 'kye_line',
-  filter: ['all'],
-  style: {
-    lineColor: 'rgba(14, 80, 109, 1)',
-    lineWidth: ['interpolate', ['linear'], ['zoom'], 0, 0.8, 4, 1, 5, 1.5, 12, 3],
-    lineWidthTransition: { duration: 300, delay: 0 }
+  paint: {
+    'line-color': 'rgba(14, 80, 109, 1)',
+    'line-width': ['interpolate', ['linear'], ['zoom'], 0, 0.8, 4, 1, 5, 1.5, 12, 3]
   },
   maxzoom: 16
 };
@@ -48,23 +48,26 @@ const EarthScreen = () => {
   const [quadrantsAll, setQuadrantsAll] = useState<number>(1);
 
   const generateFilter = (ids: number[]) => {
-    if (!ids || !ids.length) return ['==', 'id', -1];
-
-    return [
-      'any',
-      ...ids.map((id) => {
-        if (id > 612) {
-          return ['>=', 'id', 613];
-        } else {
-          return ['==', 'id', id];
-        }
-      })
-    ];
+    if (!ids || !ids.length) return ['==', ['get', 'id'], -1];
+
+    const conditions = ids.map((id) => {
+      if (id > 612) {
+        return ['>=', ['get', 'id'], 613];
+      } else {
+        return ['==', ['get', 'id'], id];
+      }
+    });
+
+    if (conditions.length === 1) return conditions[0];
+    return ['any', ...conditions];
   };
 
   const [filter, setFilter] = useState<any[]>(generateFilter([]));
 
-  const mapRef = useRef<MapLibreRN.MapViewRef>(null);
+  const mapRef = useRef<MapLibreRN.MapRef>(null);
+
+  const [isMapReady, setIsMapReady] = useState(false);
+  const [geoData, setGeoData] = useState<any>({ type: 'FeatureCollection', features: [] });
 
   useEffect(() => {
     if (!data || !data.regions) return;
@@ -77,6 +80,14 @@ const EarthScreen = () => {
     showScore();
   }, [visited]);
 
+  useEffect(() => {
+    if (isMapReady) {
+      setTimeout(() => {
+        setGeoData(kye);
+      }, 500);
+    }
+  }, [isMapReady]);
+
   const markQuadrant = async (qid: number) => {
     if (!token) return;
 
@@ -152,12 +163,11 @@ const EarthScreen = () => {
     if (!mapRef.current) return;
 
     try {
-      const { screenPointX, screenPointY } = event.properties;
+      const { point } = event.nativeEvent;
 
-      const { features } = await mapRef.current.queryRenderedFeaturesAtPoint(
-        [screenPointX, screenPointY],
-        undefined,
-        ['kye_fill']
+      const features = await mapRef.current.queryRenderedFeatures(
+        point,
+        { layers: ['kye_fill'] }
       );
 
       if (features?.length) {
@@ -190,41 +200,45 @@ const EarthScreen = () => {
           { paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : 0 }
         ]}
       >
-        <MapLibreRN.MapView
+        <MapLibreRN.Map
           ref={mapRef}
           style={styles.map}
           mapStyle={VECTOR_MAP_HOST + '/nomadmania-maps2025.json'}
-          rotateEnabled={false}
-          attributionEnabled={false}
+          touchRotate={false}
+          attribution={false}
           onPress={onMapPress}
+          onDidFinishLoadingStyle={() => setIsMapReady(true)}
         >
-          <MapLibreRN.ShapeSource id="kye" shape={kye as any}>
-            <MapLibreRN.LineLayer
-              id={kye_line.id}
-              filter={kye_line.filter as any}
-              maxZoomLevel={kye_line.maxzoom}
-              style={kye_line.style as any}
-              belowLayerID="waterway-name"
-            />
-            <MapLibreRN.FillLayer
-              id={kye_fill.id}
-              filter={['==', 'tt', '1']}
-              style={kye_fill.style}
-              maxZoomLevel={kye_fill.maxzoom}
-              belowLayerID={kye_fill_visited.id}
-            />
-            <MapLibreRN.FillLayer
-              id={kye_fill_visited.id}
-              filter={filter as any}
-              style={kye_fill_visited.style}
-              maxZoomLevel={kye_fill_visited.maxzoom}
-              belowLayerID={kye_line.id}
-            />
-          </MapLibreRN.ShapeSource>
-        </MapLibreRN.MapView>
+          {isMapReady && (
+            <MapLibreRN.GeoJSONSource id="kye" data={geoData}>
+              <MapLibreRN.Layer type="line"
+                id={kye_line.id}
+                maxzoom={kye_line.maxzoom}
+                paint={kye_line.paint as any}
+                beforeId="waterway-name"
+              />
+              <MapLibreRN.Layer type="fill"
+                id={kye_fill.id}
+                paint={kye_fill.paint}
+                maxzoom={kye_fill.maxzoom}
+                filter={['==', ['get', 'tt'], '1']}
+                beforeId={kye_fill_visited.id}
+              />
+              <MapLibreRN.Layer type="fill"
+                id={kye_fill_visited.id}
+                filter={filter as any}
+                paint={kye_fill_visited.paint}
+                maxzoom={kye_fill_visited.maxzoom}
+                beforeId={kye_line.id}
+              />
+            </MapLibreRN.GeoJSONSource>
+          )}
+        </MapLibreRN.Map>
       </View>
     </SafeAreaView>
   );
 };
 
 export default EarthScreen;
+
+

+ 132 - 26
src/screens/OfflineMapsScreen/OfflineMapManager.ts

@@ -69,11 +69,40 @@ const setupAppStateListener = () => {
 
 setupAppStateListener();
 
-const init = () => {
-  MapLibreGL.OfflineManager.setProgressEventThrottle(800);
+const migrateExistingPacks = async () => {
+  try {
+    const packs = await MapLibreGL.OfflineManager.getPacks();
+    const mapsString = storage.getString(OFFLINE_MAPS_KEY);
+    if (!mapsString) return;
 
-  resumeAllPendingDownloads();
-  cleanupCompletedMaps();
+    const maps = JSON.parse(mapsString);
+    let modified = false;
+
+    for (const pack of packs) {
+      const packName = pack.metadata?.name;
+      if (packName) {
+        const mapIndex = maps.findIndex((m) => m.id === packName);
+        if (mapIndex >= 0 && maps[mapIndex].packId !== pack.id) {
+          maps[mapIndex].packId = pack.id;
+          modified = true;
+        }
+      }
+    }
+
+    if (modified) {
+      storage.set(OFFLINE_MAPS_KEY, JSON.stringify(maps));
+    }
+  } catch (error) {
+    console.error('Error migrating packs:', error);
+  }
+};
+
+const init = () => {
+  migrateExistingPacks().then(() => {
+    MapLibreGL.OfflineManager.setProgressEventThrottle(800);
+    resumeAllPendingDownloads();
+    cleanupCompletedMaps();
+  });
 };
 
 const cleanupCompletedMaps = async () => {
@@ -108,14 +137,38 @@ const cleanupCompletedMaps = async () => {
   }
 };
 
-const subscribeToPackProgress = (packName, progressCallback, errorCallback) => {
+const getPackByName = async (name) => {
+  try {
+    const mapsString = storage.getString(OFFLINE_MAPS_KEY);
+    if (mapsString) {
+      const maps = JSON.parse(mapsString);
+      const mapData = maps.find((m) => m.id === name);
+      if (mapData && mapData.packId) {
+        try {
+          const pack = await MapLibreGL.OfflineManager.getPack(mapData.packId);
+          if (pack) return pack;
+        } catch (e) {}
+      }
+    }
+    const packs = await MapLibreGL.OfflineManager.getPacks();
+    return packs.find((p) => p.metadata?.name === name);
+  } catch (error) {
+    console.error(`Error getting offline pack ${name}:`, error);
+    return null;
+  }
+};
+
+const subscribeToPackProgress = async (packName, progressCallback, errorCallback) => {
   if (progressCallback) {
     progressCallbacks[packName] = progressCallback;
   }
 
-  MapLibreGL.OfflineManager.subscribe(
-    packName,
-    (pack, status) => {
+  const pack = await getPackByName(packName);
+  if (!pack) return;
+
+  MapLibreGL.OfflineManager.addListener(
+    pack.id,
+    (packInfo, status) => {
       if (status.completedResourceCount === status.requiredResourceCount) {
         markPackAsCompleted(packName);
       }
@@ -140,7 +193,7 @@ const subscribeToPackProgress = (packName, progressCallback, errorCallback) => {
         });
       }
     },
-    (error) => {
+    (packInfo, error) => {
       console.error(`Error in progress listener for ${packName}:`, error);
 
       if (errorCallback) {
@@ -152,7 +205,9 @@ const subscribeToPackProgress = (packName, progressCallback, errorCallback) => {
 
 const createPack = async (options, progressCallback, errorCallback) => {
   try {
-    subscribeToPackProgress(options.name, progressCallback, errorCallback);
+    if (progressCallback) {
+      progressCallbacks[options.name] = progressCallback;
+    }
 
     if (!pendingDownloads.includes(options.name)) {
       pendingDownloads.push(options.name);
@@ -163,19 +218,67 @@ const createPack = async (options, progressCallback, errorCallback) => {
     const maps = mapsString ? JSON.parse(mapsString) : [];
     const mapIndex = maps.findIndex((map) => map.id === options.name);
 
+    let packOptions;
+    const [[west, south], [east, north]] = options.bounds;
+
     if (mapIndex >= 0) {
       maps[mapIndex].styleURL = options.styleURL;
       maps[mapIndex].minZoom = options.minZoom;
       maps[mapIndex].maxZoom = options.maxZoom;
 
-      const [[west, south], [east, north]] = options.bounds;
       const bounds = { north, south, east, west };
       maps[mapIndex].bounds = JSON.stringify(bounds);
 
       storage.set(OFFLINE_MAPS_KEY, JSON.stringify(maps));
     }
 
-    return await MapLibreGL.OfflineManager.createPack(options);
+    packOptions = {
+      metadata: { name: options.name },
+      mapStyle: options.styleURL,
+      minZoom: options.minZoom,
+      maxZoom: options.maxZoom,
+      bounds: [west, south, east, north] as [number, number, number, number]
+    };
+
+    const pack = await MapLibreGL.OfflineManager.createPack(
+      packOptions,
+      (packInfo, status) => {
+        if (status.completedResourceCount === status.requiredResourceCount) {
+          markPackAsCompleted(options.name);
+        }
+
+        const percentage =
+          status.requiredResourceCount > 0
+            ? (status.completedResourceCount / status.requiredResourceCount) * 100
+            : 0;
+
+        updateMapMetadata(options.name, {
+          progress: percentage,
+          size: status.completedResourceSize || 0
+        });
+
+        if (progressCallbacks[options.name]) {
+          progressCallbacks[options.name]({
+            name: options.name,
+            percentage,
+            completedSize: status.completedResourceSize,
+            completedResourceCount: status.completedResourceCount,
+            requiredResourceCount: status.requiredResourceCount
+          });
+        }
+      },
+      (packInfo, error) => {
+        console.error(`Error in progress listener for ${options.name}:`, error);
+
+        if (errorCallback) {
+          errorCallback(error);
+        }
+      }
+    );
+
+    updateMapMetadata(options.name, { packId: pack.id });
+
+    return pack;
   } catch (error) {
     console.error('Error creating offline pack:', error);
 
@@ -198,24 +301,19 @@ const getPacks = async () => {
 };
 
 const getPack = async (name) => {
-  try {
-    return await MapLibreGL.OfflineManager.getPack(name);
-  } catch (error) {
-    console.error(`Error getting offline pack ${name}:`, error);
-    return null;
-  }
+  return await getPackByName(name);
 };
 
 const deletePack = async (name) => {
   try {
-    const pack = await getPack(name);
+    const pack = await getPackByName(name);
     if (pack) {
-      await MapLibreGL.OfflineManager.unsubscribe(name);
+      await MapLibreGL.OfflineManager.removeListener(pack.id);
 
       delete progressCallbacks[name];
 
       // Delete the pack
-      await MapLibreGL.OfflineManager.deletePack(name);
+      await MapLibreGL.OfflineManager.deletePack(pack.id);
     }
 
     pendingDownloads = pendingDownloads.filter((downloadName) => downloadName !== name);
@@ -228,7 +326,10 @@ const deletePack = async (name) => {
 
 const invalidatePack = async (name) => {
   try {
-    await MapLibreGL.OfflineManager.deletePack(name);
+    const pack = await getPackByName(name);
+    if (pack) {
+      await MapLibreGL.OfflineManager.invalidatePack(pack.id);
+    }
 
     updateMapMetadata(name, { status: 'invalid', progress: 100 });
   } catch (error) {
@@ -239,7 +340,7 @@ const invalidatePack = async (name) => {
 
 const resumePackDownload = async (name: string, progressCallback?: any, errorCallback?: any) => {
   try {
-    let pack = await getPack(name);
+    let pack = await getPackByName(name);
 
     if (progressCallback) {
       progressCallbacks[name] = progressCallback;
@@ -262,7 +363,7 @@ const resumePackDownload = async (name: string, progressCallback?: any, errorCal
       }
 
       if (pack) {
-        subscribeToPackProgress(name, progressCallbacks[name], errorCallback);
+        await subscribeToPackProgress(name, progressCallbacks[name], errorCallback);
 
         try {
           await pack.resume();
@@ -364,12 +465,17 @@ export const updateMapMetadata = (id, updates) => {
 
 const cancelPackDownload = async (name) => {
   try {
-    await MapLibreGL.OfflineManager.unsubscribe(name);
+    const pack = await getPackByName(name);
+    if (pack) {
+      await MapLibreGL.OfflineManager.removeListener(pack.id);
+    }
 
     delete progressCallbacks[name];
 
     try {
-      await MapLibreGL.OfflineManager.deletePack(name);
+      if (pack) {
+        await MapLibreGL.OfflineManager.deletePack(pack.id);
+      }
     } catch (e) {}
 
     pendingDownloads = pendingDownloads.filter((downloadName) => downloadName !== name);

+ 8 - 6
src/screens/OfflineMapsScreen/SelectOwnMapScreen/index.tsx

@@ -50,6 +50,7 @@ const TILE_SIZE_MULTIPLIER_BY_ZOOM = {
 
 export default function SelectOwnMapScreen({ navigation, route }) {
   const map = useRef(null);
+  const camera = useRef(null);
   const [mapLoaded, setMapLoaded] = useState(false);
   const [currentZoom, setCurrentZoom] = useState(1);
   const [selectorBounds, setSelectorBounds] = useState({
@@ -138,7 +139,7 @@ export default function SelectOwnMapScreen({ navigation, route }) {
           west: bounds.west
         });
 
-        map.current.fitBounds([bounds.west, bounds.south], [bounds.east, bounds.north], 50, 500);
+        camera.current?.fitBounds([bounds.west, bounds.south, bounds.east, bounds.north], { padding: { top: 50, right: 50, bottom: 50, left: 50 }, duration: 500 });
 
         calculateEstimatedSizeFromSelector(bounds);
       } catch (error) {
@@ -405,7 +406,7 @@ export default function SelectOwnMapScreen({ navigation, route }) {
         </View>
 
         <View style={styles.mapContainer}>
-          <MapLibreGL.MapView
+          <MapLibreGL.Map
             ref={map}
             style={styles.map}
             mapStyle={`${VECTOR_MAP_HOST}/nomadmania-maps2025.json`}
@@ -413,9 +414,10 @@ export default function SelectOwnMapScreen({ navigation, route }) {
             onRegionDidChange={(feature) => onRegionDidChange(feature)}
           >
             <MapLibreGL.Camera
-              defaultSettings={{
-                centerCoordinate: [0, 0],
-                zoomLevel: 1
+              ref={camera}
+              initialViewState={{
+                center: [0, 0],
+                zoom: 1
               }}
             />
 
@@ -434,7 +436,7 @@ export default function SelectOwnMapScreen({ navigation, route }) {
                 </View>
               </View>
             )}
-          </MapLibreGL.MapView>
+          </MapLibreGL.Map>
         </View>
 
         <View style={styles.infoContainer}>

+ 60 - 48
src/screens/OfflineMapsScreen/SelectRegionsScreen/index.tsx

@@ -32,7 +32,8 @@ import { offlineMapManager } from '../OfflineMapManager';
 import LocationIcon from 'assets/icons/location.svg';
 
 const generateFilter = (ids: number[]) => {
-  return ids?.length ? ['any', ...ids.map((id) => ['==', 'id', id])] : ['==', 'id', -1];
+  if (!ids || ids.length === 0) return ['==', ['get', 'id'], -1];
+  return ['in', ['get', 'id'], ['literal', ids]];
 };
 
 let nm_regions = {
@@ -40,8 +41,9 @@ let nm_regions = {
   type: 'fill',
   source: 'regions',
   'source-layer': 'regions',
-  style: {
-    fillColor: 'rgba(15, 63, 79, 0)'
+  paint: {
+    'fill-color': 'rgba(15, 63, 79, 0)'
+
   },
   filter: ['all'],
   maxzoom: 16
@@ -52,8 +54,9 @@ let selected_region = {
   type: 'fill',
   source: 'regions',
   'source-layer': 'regions',
-  style: {
-    fillColor: 'rgba(237, 147, 52, 0.7)'
+  paint: {
+    'fill-color': 'rgba(237, 147, 52, 0.7)'
+
   },
   maxzoom: 12
 };
@@ -77,14 +80,14 @@ export const SelectRegionScreen = ({ navigation }: { navigation: any }) => {
   const [estimatedSize, setEstimatedSize] = useState(0);
 
   const [regionPopupVisible, setRegionPopupVisible] = useState(false);
-  const mapRef = useRef<MapLibreRN.MapViewRef>(null);
+  const mapRef = useRef<MapLibreRN.MapRef>(null);
   const cameraRef = useRef<MapLibreRN.CameraRef>(null);
 
   const [filterSelectedRegions, setFilterSelectedRegions] = useState<any[]>(generateFilter([]));
   const [modalState, setModalState] = useState({
     visible: false,
     type: 'confirm',
-    action: () => {},
+    action: () => { },
     message: '',
     title: ''
   });
@@ -100,7 +103,7 @@ export const SelectRegionScreen = ({ navigation }: { navigation: any }) => {
   const animationTimeoutRef = useRef<NodeJS.Timeout | null>(null);
 
   const cameraController = {
-    flyTo: useCallback((coordinates: number[], duration: number = 1000) => {
+    flyTo: useCallback((coordinates: [number, number], duration: number = 1000) => {
       isAnimatingRef.current = true;
       if (animationTimeoutRef.current) {
         clearTimeout(animationTimeoutRef.current);
@@ -109,14 +112,14 @@ export const SelectRegionScreen = ({ navigation }: { navigation: any }) => {
       if (Platform.OS === 'android') {
         setRenderCamera(true);
         requestAnimationFrame(() => {
-          cameraRef.current?.flyTo(coordinates, duration);
+          cameraRef.current?.flyTo({ center: coordinates, duration });
         });
         animationTimeoutRef.current = setTimeout(() => {
           isAnimatingRef.current = false;
           setRenderCamera(false);
         }, duration + 200);
       } else {
-        cameraRef.current?.flyTo(coordinates, duration);
+        cameraRef.current?.flyTo({ center: coordinates, duration });
         animationTimeoutRef.current = setTimeout(() => {
           isAnimatingRef.current = false;
         }, duration + 100);
@@ -132,7 +135,7 @@ export const SelectRegionScreen = ({ navigation }: { navigation: any }) => {
       if (Platform.OS === 'android') {
         setRenderCamera(true);
         requestAnimationFrame(() => {
-          cameraRef.current?.setCamera(config);
+          cameraRef.current?.setStop(config);
         });
         animationTimeoutRef.current = setTimeout(
           () => {
@@ -142,7 +145,7 @@ export const SelectRegionScreen = ({ navigation }: { navigation: any }) => {
           (config.animationDuration ?? 1000) + 200
         );
       } else {
-        cameraRef.current?.setCamera(config);
+        cameraRef.current?.setStop(config);
         animationTimeoutRef.current = setTimeout(
           () => {
             isAnimatingRef.current = false;
@@ -159,11 +162,17 @@ export const SelectRegionScreen = ({ navigation }: { navigation: any }) => {
         clearTimeout(animationTimeoutRef.current);
       }
 
+      const bounds = [sw[0], sw[1], ne[0], ne[1]] as [number, number, number, number];
+      const options = {
+        padding: { top: padding[0], right: padding[1], bottom: padding[2], left: padding[3] },
+        duration: duration
+      };
+
       if (Platform.OS === 'android') {
         setRenderCamera(true);
 
         requestAnimationFrame(() => {
-          cameraRef.current?.fitBounds(ne, sw, padding, duration);
+          cameraRef.current?.fitBounds(bounds, options);
         });
 
         animationTimeoutRef.current = setTimeout(() => {
@@ -171,7 +180,7 @@ export const SelectRegionScreen = ({ navigation }: { navigation: any }) => {
           setRenderCamera(false);
         }, duration + 200);
       } else {
-        cameraRef.current?.fitBounds(ne, sw, padding, duration);
+        cameraRef.current?.fitBounds(bounds, options);
 
         animationTimeoutRef.current = setTimeout(() => {
           isAnimatingRef.current = false;
@@ -221,7 +230,7 @@ export const SelectRegionScreen = ({ navigation }: { navigation: any }) => {
     setModalState({
       visible: false,
       type: 'confirm',
-      action: () => {},
+      action: () => { },
       message: '',
       title: ''
     });
@@ -413,12 +422,11 @@ export const SelectRegionScreen = ({ navigation }: { navigation: any }) => {
       setLoadingSize(true);
 
       try {
-        const { screenPointX, screenPointY } = event.properties;
+        const { point } = event.nativeEvent;
 
-        const { features } = await mapRef.current.queryRenderedFeaturesAtPoint(
-          [screenPointX, screenPointY],
-          undefined,
-          ['regions']
+        const features = await mapRef.current.queryRenderedFeatures(
+          point,
+          { layers: ['regions'] }
         );
 
         if (features?.length) {
@@ -559,69 +567,72 @@ export const SelectRegionScreen = ({ navigation }: { navigation: any }) => {
       </View>
 
       <View style={styles.container}>
-        <MapLibreRN.MapView
+        <MapLibreRN.Map
           ref={mapRef}
           style={styles.map}
           mapStyle={VECTOR_MAP_HOST + '/nomadmania-maps2025.json'}
-          rotateEnabled={false}
-          attributionEnabled={false}
+          touchRotate={false}
+          attribution={false}
           onPress={handleMapPress}
         >
           {(Platform.OS === 'ios' || renderCamera) && <MapLibreRN.Camera ref={cameraRef} />}
 
-          <MapLibreRN.LineLayer
+          <MapLibreRN.Layer
+            type="line"
             id="nm-regions-line-layer"
-            sourceID={nm_regions.source}
-            sourceLayerID={nm_regions['source-layer']}
+            source={nm_regions.source}
+            source-layer={nm_regions['source-layer']}
             filter={nm_regions.filter as any}
-            maxZoomLevel={nm_regions.maxzoom}
-            style={{
-              lineColor: 'rgba(14, 80, 109, 1)',
-              lineWidth: ['interpolate', ['linear'], ['zoom'], 0, 0.2, 4, 1, 5, 1.5, 12, 3],
-              lineWidthTransition: { duration: 300, delay: 0 }
+            maxzoom={nm_regions.maxzoom}
+            paint={{
+              'line-color': 'rgba(14, 80, 109, 1)',
+              'line-width': ['interpolate', ['linear'], ['zoom'], 0, 0.2, 4, 1, 5, 1.5, 12, 3],
+              'line-width-transition': { duration: 300, delay: 0 }
             }}
-            belowLayerID="waterway-name"
+            beforeId="waterway-name"
           />
-          <MapLibreRN.FillLayer
+          <MapLibreRN.Layer
+            type="fill"
             id={nm_regions.id}
-            sourceID={nm_regions.source}
-            sourceLayerID={nm_regions['source-layer']}
+            source={nm_regions.source}
+            source-layer={nm_regions['source-layer']}
             filter={nm_regions.filter as any}
-            style={nm_regions.style}
-            maxZoomLevel={nm_regions.maxzoom}
-            belowLayerID="nm-regions-line-layer"
+            paint={nm_regions.paint}
+            maxzoom={nm_regions.maxzoom}
+            beforeId="nm-regions-line-layer"
           />
 
           {selectedRegions && selectedRegions.length > 0 ? (
-            <MapLibreRN.FillLayer
+            <MapLibreRN.Layer
+              type="fill"
               id={selected_region.id}
-              sourceID={nm_regions.source}
-              sourceLayerID={nm_regions['source-layer']}
+              source={nm_regions.source}
+              source-layer={nm_regions['source-layer']}
               filter={filterSelectedRegions as any}
-              style={selected_region.style}
-              maxZoomLevel={selected_region.maxzoom}
-              belowLayerID="nm-regions-line-layer"
+              paint={selected_region.paint}
+              maxzoom={selected_region.maxzoom}
+              beforeId="nm-regions-line-layer"
             />
           ) : null}
 
           {location && (
             <MapLibreRN.UserLocation
               animated={true}
-              showsUserHeadingIndicator={true}
+              heading={true}
               onPress={async () => {
                 const currentZoom = await mapRef.current?.getZoom();
                 const newZoom = (currentZoom || 0) + 2;
 
                 cameraController.setCamera({
-                  centerCoordinate: [location.longitude, location.latitude],
-                  zoomLevel: newZoom,
+                  center: [location.longitude, location.latitude],
+                  zoom: newZoom,
                   animationDuration: 500,
                   animationMode: 'flyTo'
                 });
               }}
             ></MapLibreRN.UserLocation>
           )}
-        </MapLibreRN.MapView>
+        </MapLibreRN.Map>
 
         <TouchableOpacity
           onPress={handleGetLocation}
@@ -837,3 +848,4 @@ const styles = StyleSheet.create({
     fontWeight: '500'
   }
 });
+

+ 2 - 2
src/screens/OfflineMapsScreen/index.tsx

@@ -86,7 +86,7 @@ export default function OfflineMapsScreen({ navigation }: { navigation: any }) {
       const savedMaps = savedMapsString ? JSON.parse(savedMapsString) : [];
 
       const availablePacks = await offlineMapManager.getPacks();
-      const availablePackIds = availablePacks.map((pack) => pack.name);
+      const availablePackIds = availablePacks.map((pack) => (pack.metadata?.name as string) || pack.id);
 
       const updatedMaps = savedMaps.map((map: any) => {
         if (map.status === 'downloading' && map.progress < 100) {
@@ -185,7 +185,7 @@ export default function OfflineMapsScreen({ navigation }: { navigation: any }) {
     try {
       const packs = await offlineMapManager.getPacks();
       for (const pack of packs) {
-        await offlineMapManager.invalidatePack(pack.name);
+        await offlineMapManager.invalidatePack((pack.metadata?.name as string) || pack.id);
       }
 
       loadMaps();

Деякі файли не було показано, через те що забагато файлів було змінено