|
@@ -0,0 +1,563 @@
|
|
|
|
+import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
|
+import { View, StyleSheet, TouchableOpacity, Text, ScrollView, Alert } from 'react-native';
|
|
|
|
+import { SafeAreaView } from 'react-native-safe-area-context';
|
|
|
|
+import { Modal, FlatList as List, Header } from 'src/components';
|
|
|
|
+import * as turf from '@turf/turf';
|
|
|
|
+import * as MapLibreRN from '@maplibre/maplibre-react-native';
|
|
|
|
+import NetInfo from '@react-native-community/netinfo';
|
|
|
|
+
|
|
|
|
+import { getFontSize } from 'src/utils';
|
|
|
|
+import { useGetRegionsForTripsQuery } from '@api/trips';
|
|
|
|
+import { useGetListRegionsQuery } from '@api/regions';
|
|
|
|
+import { Colors } from 'src/theme';
|
|
|
|
+
|
|
|
|
+import SearchSvg from 'assets/icons/search.svg';
|
|
|
|
+import SaveSvg from 'assets/icons/travels-screens/save.svg';
|
|
|
|
+import { VECTOR_MAP_HOST } from 'src/constants';
|
|
|
|
+import { storage, StoreType } from 'src/storage';
|
|
|
|
+import { usePostGetMapDataForRegionMutation } from '@api/maps';
|
|
|
|
+import { formatBytes } from '../formatters';
|
|
|
|
+import { ActivityIndicator } from 'react-native-paper';
|
|
|
|
+import { offlineMapManager } from '../OfflineMapManager';
|
|
|
|
+
|
|
|
|
+const generateFilter = (ids: number[]) => {
|
|
|
|
+ return ids?.length ? ['any', ...ids.map((id) => ['==', 'id', id])] : ['==', 'id', -1];
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+let nm_regions = {
|
|
|
|
+ id: 'regions',
|
|
|
|
+ type: 'fill',
|
|
|
|
+ source: 'regions',
|
|
|
|
+ 'source-layer': 'regions',
|
|
|
|
+ style: {
|
|
|
|
+ fillColor: 'rgba(15, 63, 79, 0)'
|
|
|
|
+ },
|
|
|
|
+ filter: ['all'],
|
|
|
|
+ maxzoom: 16
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+let selected_region = {
|
|
|
|
+ id: 'selected_region',
|
|
|
|
+ type: 'fill',
|
|
|
|
+ source: 'regions',
|
|
|
|
+ 'source-layer': 'regions',
|
|
|
|
+ style: {
|
|
|
|
+ fillColor: 'rgba(237, 147, 52, 0.7)'
|
|
|
|
+ },
|
|
|
|
+ maxzoom: 12
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const OFFLINE_MAPS_KEY = 'offline_maps';
|
|
|
|
+
|
|
|
|
+export const SelectRegionScreen = ({ navigation }: { navigation: any }) => {
|
|
|
|
+ const token = storage.get('token', StoreType.STRING) as string;
|
|
|
|
+ const { data } = useGetRegionsForTripsQuery(true);
|
|
|
|
+ const { data: regionsList } = useGetListRegionsQuery(true);
|
|
|
|
+
|
|
|
|
+ const [regions, setRegions] = useState<any[] | null>(null);
|
|
|
|
+ const [isModalVisible, setIsModalVisible] = useState(false);
|
|
|
|
+ const [selectedRegions, setSelectedRegions] = useState<any[]>([]);
|
|
|
|
+ const [regionsToSave, setRegionsToSave] = useState<any[]>([]);
|
|
|
|
+ const [regionData, setRegionData] = useState<any | null>(null);
|
|
|
|
+ const [isConnectedToWifi, setIsConnectedToWifi] = useState(true);
|
|
|
|
+
|
|
|
|
+ const { mutateAsync: getMapDataForRegion } = usePostGetMapDataForRegionMutation();
|
|
|
|
+ const [loadingSize, setLoadingSize] = useState(false);
|
|
|
|
+ const [estimatedSize, setEstimatedSize] = useState(0);
|
|
|
|
+
|
|
|
|
+ const [regionPopupVisible, setRegionPopupVisible] = useState(false);
|
|
|
|
+ const mapRef = useRef<MapLibreRN.MapViewRef>(null);
|
|
|
|
+ const cameraRef = useRef<MapLibreRN.CameraRef>(null);
|
|
|
|
+
|
|
|
|
+ const [filterSelectedRegions, setFilterSelectedRegions] = useState<any[]>(generateFilter([]));
|
|
|
|
+
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ if (data && data.regions) {
|
|
|
|
+ setRegions(data.regions);
|
|
|
|
+ }
|
|
|
|
+ }, [data]);
|
|
|
|
+
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ const ids = selectedRegions.map((region) => region.id);
|
|
|
|
+ setFilterSelectedRegions(generateFilter(ids));
|
|
|
|
+ }, [selectedRegions]);
|
|
|
|
+
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ const checkConnectionType = async () => {
|
|
|
|
+ try {
|
|
|
|
+ const connectionInfo = await NetInfo.fetch();
|
|
|
|
+ setIsConnectedToWifi(connectionInfo.type === 'wifi');
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('Error checking network connection:', error);
|
|
|
|
+ setIsConnectedToWifi(true);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ checkConnectionType();
|
|
|
|
+ }, []);
|
|
|
|
+
|
|
|
|
+ const addRegionFromSearch = async (searchRegion: any) => {
|
|
|
|
+ setLoadingSize(true);
|
|
|
|
+ const regionIndex = selectedRegions.findIndex((region) => region.id === searchRegion.id);
|
|
|
|
+ const regionFromApi = regions?.find((region) => region.id === searchRegion.id);
|
|
|
|
+
|
|
|
|
+ if (regionIndex < 0 && regionFromApi) {
|
|
|
|
+ const newRegion = {
|
|
|
|
+ id: searchRegion.id,
|
|
|
|
+ name: searchRegion.name
|
|
|
|
+ };
|
|
|
|
+ setSelectedRegions([...selectedRegions, newRegion] as any);
|
|
|
|
+
|
|
|
|
+ await getMapDataForRegion(
|
|
|
|
+ { token, region_id: searchRegion.id },
|
|
|
|
+ {
|
|
|
|
+ onSuccess: (res) => {
|
|
|
|
+ console.log('Map data for region:', res);
|
|
|
|
+ if (res.size) {
|
|
|
|
+ setRegionsToSave((prevRegions) => [...prevRegions, { ...res, id: searchRegion.id }]);
|
|
|
|
+ setEstimatedSize((prevSize) => prevSize + res.size);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ setLoadingSize(false);
|
|
|
|
+ },
|
|
|
|
+ onError: (error) => {
|
|
|
|
+ console.error('Error fetching map data for region:', error);
|
|
|
|
+ setLoadingSize(false);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ setRegionPopupVisible(true);
|
|
|
|
+
|
|
|
|
+ if (regionsList) {
|
|
|
|
+ const region = regionsList.data.find((region) => region.id === searchRegion.id);
|
|
|
|
+ if (region) {
|
|
|
|
+ const bounds = turf.bbox(region.bbox);
|
|
|
|
+ cameraRef.current?.fitBounds(
|
|
|
|
+ [bounds[2], bounds[3]],
|
|
|
|
+ [bounds[0], bounds[1]],
|
|
|
|
+ [50, 50, 50, 50],
|
|
|
|
+ 600
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ setLoadingSize(false);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleSavePress = () => {
|
|
|
|
+ console.log('Selected regions to save:', regionsToSave);
|
|
|
|
+ if (!isConnectedToWifi) {
|
|
|
|
+ Alert.alert(
|
|
|
|
+ 'Mobile Data Connection',
|
|
|
|
+ 'You are not connected to WiFi. Downloading map tiles may use a significant amount of data. Do you want to continue?',
|
|
|
|
+ [
|
|
|
|
+ { text: 'Cancel', style: 'cancel' },
|
|
|
|
+ { text: 'Download', onPress: () => startDownload() }
|
|
|
|
+ ]
|
|
|
|
+ );
|
|
|
|
+ } else {
|
|
|
|
+ startDownload();
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const startDownload = async () => {
|
|
|
|
+ try {
|
|
|
|
+ const results = {
|
|
|
|
+ success: [],
|
|
|
|
+ failed: []
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ for (const region of regionsToSave) {
|
|
|
|
+ try {
|
|
|
|
+ const [west, south, east, north] = region.bbox;
|
|
|
|
+
|
|
|
|
+ const packId = `region_${region.id}_${Date.now()}`;
|
|
|
|
+
|
|
|
|
+ const offlinePackOptions = {
|
|
|
|
+ name: packId,
|
|
|
|
+ styleURL: `${VECTOR_MAP_HOST}/nomadmania-maps.json`,
|
|
|
|
+ bounds: [
|
|
|
|
+ [west, south],
|
|
|
|
+ [east, north]
|
|
|
|
+ ],
|
|
|
|
+ minZoom: 10,
|
|
|
|
+ maxZoom: 14
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const bounds = {
|
|
|
|
+ north,
|
|
|
|
+ south,
|
|
|
|
+ east,
|
|
|
|
+ west
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const savedMapsString = storage.get(OFFLINE_MAPS_KEY, StoreType.STRING) as string;
|
|
|
|
+ const savedMaps = savedMapsString ? JSON.parse(savedMapsString) : [];
|
|
|
|
+
|
|
|
|
+ const mapData = {
|
|
|
|
+ id: packId,
|
|
|
|
+ regionId: region.id,
|
|
|
|
+ name: region.name || `Region ${region.id}`,
|
|
|
|
+ bounds: JSON.stringify(bounds),
|
|
|
|
+ size: region.size || 0,
|
|
|
|
+ updatedAt: Date.now(),
|
|
|
|
+ status: 'downloading',
|
|
|
|
+ progress: 0,
|
|
|
|
+ styleURL: `${VECTOR_MAP_HOST}/nomadmania-maps.json`,
|
|
|
|
+ minZoom: 10,
|
|
|
|
+ maxZoom: 14
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ savedMaps.unshift(mapData);
|
|
|
|
+
|
|
|
|
+ storage.set(OFFLINE_MAPS_KEY, JSON.stringify(savedMaps));
|
|
|
|
+
|
|
|
|
+ offlineMapManager.createPack(
|
|
|
|
+ offlinePackOptions,
|
|
|
|
+ (progress: any) => {
|
|
|
|
+ const mapsString = storage.get(OFFLINE_MAPS_KEY, StoreType.STRING) as string;
|
|
|
|
+ const maps = mapsString ? JSON.parse(mapsString) : [];
|
|
|
|
+ const mapIndex = maps.findIndex((map: any) => map.id === packId);
|
|
|
|
+
|
|
|
|
+ if (mapIndex >= 0) {
|
|
|
|
+ maps[mapIndex].progress = progress.percentage;
|
|
|
|
+
|
|
|
|
+ if (progress.percentage === 100) {
|
|
|
|
+ maps[mapIndex].status = 'valid';
|
|
|
|
+ maps[mapIndex].size = progress.completedSize || maps[mapIndex].size;
|
|
|
|
+ maps[mapIndex].updatedAt = Date.now();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ storage.set(OFFLINE_MAPS_KEY, JSON.stringify(maps));
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ (error: any) => {
|
|
|
|
+ console.error(`Download error for region ${region.id}:`, error);
|
|
|
|
+
|
|
|
|
+ const mapsString = storage.get(OFFLINE_MAPS_KEY, StoreType.STRING) as string;
|
|
|
|
+ const maps = mapsString ? JSON.parse(mapsString) : [];
|
|
|
|
+ const mapIndex = maps.findIndex((map: any) => map.id === packId);
|
|
|
|
+
|
|
|
|
+ if (mapIndex >= 0) {
|
|
|
|
+ maps[mapIndex].status = 'invalid';
|
|
|
|
+ maps[mapIndex].error = error.message || 'Unknown error';
|
|
|
|
+ storage.set(OFFLINE_MAPS_KEY, JSON.stringify(maps));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ );
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error(`Error processing region ${region.id}:`, error);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ navigation.goBack();
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('Error starting download:', error);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleSetRegionData = (regionId: number) => {
|
|
|
|
+ const foundRegion = regions?.find((region) => region.id === regionId);
|
|
|
|
+
|
|
|
|
+ if (foundRegion) {
|
|
|
|
+ setRegionData(foundRegion);
|
|
|
|
+ // setRegionsToSave((prevRegions) => [...prevRegions, foundRegion]);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleMapPress = useCallback(
|
|
|
|
+ async (event: any) => {
|
|
|
|
+ if (!mapRef.current) return;
|
|
|
|
+ setLoadingSize(true);
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ const { screenPointX, screenPointY } = event.properties;
|
|
|
|
+
|
|
|
|
+ const { features } = await mapRef.current.queryRenderedFeaturesAtPoint(
|
|
|
|
+ [screenPointX, screenPointY],
|
|
|
|
+ undefined,
|
|
|
|
+ ['regions']
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ if (features?.length) {
|
|
|
|
+ const selectedRegion = features[0];
|
|
|
|
+
|
|
|
|
+ if (selectedRegion.properties) {
|
|
|
|
+ const id = selectedRegion.properties.id;
|
|
|
|
+
|
|
|
|
+ const regionIndex = selectedRegions.findIndex((region) => region.id === id);
|
|
|
|
+
|
|
|
|
+ if (regionIndex >= 0) {
|
|
|
|
+ const regionToRemove = regionsToSave.find((region) => region.id == id);
|
|
|
|
+
|
|
|
|
+ const newSelectedRegions = [...selectedRegions];
|
|
|
|
+ newSelectedRegions.splice(regionIndex, 1);
|
|
|
|
+ setSelectedRegions(newSelectedRegions);
|
|
|
|
+ console.log('regionsToSave:', regionsToSave);
|
|
|
|
+
|
|
|
|
+ setEstimatedSize((prevSize) => {
|
|
|
|
+ console.log('Region to remove:', regionToRemove, id);
|
|
|
|
+ console.log('Region to remove2:', regionsToSave);
|
|
|
|
+
|
|
|
|
+ return regionToRemove ? prevSize - regionToRemove.size : prevSize;
|
|
|
|
+ });
|
|
|
|
+ setRegionsToSave(regionsToSave.filter((region) => region.id !== id));
|
|
|
|
+ setRegionPopupVisible(false);
|
|
|
|
+ setLoadingSize(false);
|
|
|
|
+ return;
|
|
|
|
+ } else {
|
|
|
|
+ setSelectedRegions([...selectedRegions, selectedRegion.properties] as any);
|
|
|
|
+
|
|
|
|
+ await getMapDataForRegion(
|
|
|
|
+ { token, region_id: id },
|
|
|
|
+ {
|
|
|
|
+ onSuccess: (res) => {
|
|
|
|
+ console.log('Map data for region:', res);
|
|
|
|
+ if (res.size) {
|
|
|
|
+ setRegionsToSave((prevRegions) => [...prevRegions, { ...res, id }]);
|
|
|
|
+ setEstimatedSize((prevSize) => prevSize + res.size);
|
|
|
|
+
|
|
|
|
+ setLoadingSize(false);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ onError: (error) => {
|
|
|
|
+ console.error('Error fetching map data for region:', error);
|
|
|
|
+ setLoadingSize(false);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ handleSetRegionData(id);
|
|
|
|
+ setRegionPopupVisible(true);
|
|
|
|
+
|
|
|
|
+ if (regionsList) {
|
|
|
|
+ const region = regionsList.data.find((region) => region.id === id);
|
|
|
|
+ if (region) {
|
|
|
|
+ const bounds = turf.bbox(region.bbox);
|
|
|
|
+ cameraRef.current?.fitBounds(
|
|
|
|
+ [bounds[2], bounds[3]],
|
|
|
|
+ [bounds[0], bounds[1]],
|
|
|
|
+ [50, 50, 50, 50],
|
|
|
|
+ 600
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('Failed to get coordinates on AddRegionsScreen', error);
|
|
|
|
+ setLoadingSize(false);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ [selectedRegions, regions, regionsToSave]
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ <SafeAreaView style={{ height: '100%' }} edges={['top']}>
|
|
|
|
+ <View style={styles.wrapper}>
|
|
|
|
+ <Header label={'Add Regions'} />
|
|
|
|
+ <View style={styles.searchContainer}>
|
|
|
|
+ <TouchableOpacity style={[styles.regionSelector]} onPress={() => setIsModalVisible(true)}>
|
|
|
|
+ <SearchSvg fill={Colors.LIGHT_GRAY} />
|
|
|
|
+ <Text style={styles.regionText}>Search</Text>
|
|
|
|
+ </TouchableOpacity>
|
|
|
|
+ <TouchableOpacity
|
|
|
|
+ style={[
|
|
|
|
+ styles.saveBtn,
|
|
|
|
+ selectedRegions.length ? styles.saveBtnActive : styles.saveBtnDisabled
|
|
|
|
+ ]}
|
|
|
|
+ onPress={handleSavePress}
|
|
|
|
+ disabled={!selectedRegions.length}
|
|
|
|
+ >
|
|
|
|
+ <SaveSvg fill={selectedRegions.length ? Colors.WHITE : Colors.LIGHT_GRAY} />
|
|
|
|
+ </TouchableOpacity>
|
|
|
|
+ </View>
|
|
|
|
+ </View>
|
|
|
|
+
|
|
|
|
+ <View style={styles.container}>
|
|
|
|
+ <MapLibreRN.MapView
|
|
|
|
+ ref={mapRef}
|
|
|
|
+ style={styles.map}
|
|
|
|
+ mapStyle={VECTOR_MAP_HOST + '/nomadmania-maps.json'}
|
|
|
|
+ rotateEnabled={false}
|
|
|
|
+ attributionEnabled={false}
|
|
|
|
+ onPress={handleMapPress}
|
|
|
|
+ >
|
|
|
|
+ <MapLibreRN.Camera ref={cameraRef} />
|
|
|
|
+
|
|
|
|
+ <MapLibreRN.LineLayer
|
|
|
|
+ id="nm-regions-line-layer"
|
|
|
|
+ sourceID={nm_regions.source}
|
|
|
|
+ sourceLayerID={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 }
|
|
|
|
+ }}
|
|
|
|
+ belowLayerID="waterway-name"
|
|
|
|
+ />
|
|
|
|
+ <MapLibreRN.FillLayer
|
|
|
|
+ id={nm_regions.id}
|
|
|
|
+ sourceID={nm_regions.source}
|
|
|
|
+ sourceLayerID={nm_regions['source-layer']}
|
|
|
|
+ filter={nm_regions.filter as any}
|
|
|
|
+ style={nm_regions.style}
|
|
|
|
+ maxZoomLevel={nm_regions.maxzoom}
|
|
|
|
+ belowLayerID="nm-regions-line-layer"
|
|
|
|
+ />
|
|
|
|
+
|
|
|
|
+ {selectedRegions && selectedRegions.length > 0 ? (
|
|
|
|
+ <MapLibreRN.FillLayer
|
|
|
|
+ id={selected_region.id}
|
|
|
|
+ sourceID={nm_regions.source}
|
|
|
|
+ sourceLayerID={nm_regions['source-layer']}
|
|
|
|
+ filter={filterSelectedRegions as any}
|
|
|
|
+ style={selected_region.style}
|
|
|
|
+ maxZoomLevel={selected_region.maxzoom}
|
|
|
|
+ belowLayerID="nm-regions-line-layer"
|
|
|
|
+ />
|
|
|
|
+ ) : null}
|
|
|
|
+ </MapLibreRN.MapView>
|
|
|
|
+ </View>
|
|
|
|
+
|
|
|
|
+ <View style={styles.infoContainer}>
|
|
|
|
+ {regionPopupVisible && regionData && (
|
|
|
|
+ <View style={styles.popupWrapper}>
|
|
|
|
+ <View style={styles.popupContainer}>
|
|
|
|
+ <Text style={styles.popupText}>{regionData.name ?? regionData.region_name}</Text>
|
|
|
|
+ </View>
|
|
|
|
+ </View>
|
|
|
|
+ )}
|
|
|
|
+ <View style={styles.infoRow}>
|
|
|
|
+ <Text style={styles.sizeText}>Estimated size: </Text>
|
|
|
|
+ {loadingSize ? (
|
|
|
|
+ <ActivityIndicator size={15} color={Colors.DARK_BLUE} />
|
|
|
|
+ ) : (
|
|
|
|
+ <Text style={[styles.sizeText, { fontWeight: '600' }]}>
|
|
|
|
+ {formatBytes(estimatedSize)}
|
|
|
|
+ </Text>
|
|
|
|
+ )}
|
|
|
|
+ </View>
|
|
|
|
+
|
|
|
|
+ <Text style={[styles.sizeText, { marginBottom: 8 }]}>Regions:</Text>
|
|
|
|
+ <ScrollView>
|
|
|
|
+ {regionsToSave.map((region, index) => (
|
|
|
|
+ <View style={{ marginBottom: 8 }} key={`${region.id}`}>
|
|
|
|
+ <Text style={styles.tilesText}>{region.name}</Text>
|
|
|
|
+ </View>
|
|
|
|
+ ))}
|
|
|
|
+ </ScrollView>
|
|
|
|
+ </View>
|
|
|
|
+
|
|
|
|
+ <Modal
|
|
|
|
+ onRequestClose={() => setIsModalVisible(false)}
|
|
|
|
+ headerTitle={'Select Regions'}
|
|
|
|
+ visible={isModalVisible}
|
|
|
|
+ >
|
|
|
|
+ <List
|
|
|
|
+ itemObject={(object) => {
|
|
|
|
+ setIsModalVisible(false);
|
|
|
|
+ setRegionData(object);
|
|
|
|
+ addRegionFromSearch(object);
|
|
|
|
+ }}
|
|
|
|
+ />
|
|
|
|
+ </Modal>
|
|
|
|
+ </SafeAreaView>
|
|
|
|
+ );
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const styles = StyleSheet.create({
|
|
|
|
+ container: {
|
|
|
|
+ flex: 1
|
|
|
|
+ },
|
|
|
|
+ wrapper: {
|
|
|
|
+ marginLeft: '5%',
|
|
|
|
+ marginRight: '5%',
|
|
|
|
+ alignItems: 'center'
|
|
|
|
+ },
|
|
|
|
+ map: {
|
|
|
|
+ ...StyleSheet.absoluteFillObject
|
|
|
|
+ },
|
|
|
|
+ searchContainer: {
|
|
|
|
+ gap: 16,
|
|
|
|
+ flexDirection: 'row',
|
|
|
|
+ alignItems: 'center',
|
|
|
|
+ marginBottom: 8
|
|
|
|
+ },
|
|
|
|
+ regionSelector: {
|
|
|
|
+ flexDirection: 'row',
|
|
|
|
+ alignItems: 'center',
|
|
|
|
+ paddingHorizontal: 16,
|
|
|
|
+ borderRadius: 4,
|
|
|
|
+ height: 36,
|
|
|
|
+ backgroundColor: Colors.FILL_LIGHT,
|
|
|
|
+ justifyContent: 'flex-start',
|
|
|
|
+ gap: 10,
|
|
|
|
+ flex: 1
|
|
|
|
+ },
|
|
|
|
+ regionText: {
|
|
|
|
+ fontSize: 15,
|
|
|
|
+ fontWeight: '500',
|
|
|
|
+ color: Colors.LIGHT_GRAY
|
|
|
|
+ },
|
|
|
|
+ saveBtn: {
|
|
|
|
+ borderRadius: 20,
|
|
|
|
+ paddingVertical: 10,
|
|
|
|
+ paddingHorizontal: 16,
|
|
|
|
+ borderWidth: 1
|
|
|
|
+ },
|
|
|
|
+ saveBtnActive: {
|
|
|
|
+ borderColor: Colors.ORANGE,
|
|
|
|
+ backgroundColor: Colors.ORANGE
|
|
|
|
+ },
|
|
|
|
+ saveBtnDisabled: {
|
|
|
|
+ borderColor: Colors.LIGHT_GRAY
|
|
|
|
+ },
|
|
|
|
+ popupWrapper: {
|
|
|
|
+ marginHorizontal: 24,
|
|
|
|
+ position: 'absolute',
|
|
|
|
+ top: -48,
|
|
|
|
+ left: 0,
|
|
|
|
+ right: 0
|
|
|
|
+ },
|
|
|
|
+ popupContainer: {
|
|
|
|
+ alignItems: 'center',
|
|
|
|
+ justifyContent: 'center',
|
|
|
|
+ alignSelf: 'center',
|
|
|
|
+ paddingVertical: 8,
|
|
|
|
+ paddingHorizontal: 16,
|
|
|
|
+ backgroundColor: 'rgba(33, 37, 41, 0.8)',
|
|
|
|
+ borderRadius: 100
|
|
|
|
+ },
|
|
|
|
+ popupText: {
|
|
|
|
+ color: Colors.WHITE,
|
|
|
|
+ fontSize: getFontSize(12),
|
|
|
|
+ fontWeight: '600'
|
|
|
|
+ },
|
|
|
|
+ infoContainer: {
|
|
|
|
+ marginHorizontal: '5%',
|
|
|
|
+ maxHeight: 150,
|
|
|
|
+ position: 'relative'
|
|
|
|
+ },
|
|
|
|
+ infoRow: {
|
|
|
|
+ flexDirection: 'row',
|
|
|
|
+ gap: 4,
|
|
|
|
+ marginBottom: 8,
|
|
|
|
+ marginTop: 8
|
|
|
|
+ },
|
|
|
|
+ sizeText: {
|
|
|
|
+ fontSize: 13,
|
|
|
|
+ color: Colors.DARK_BLUE,
|
|
|
|
+ fontFamily: 'montserrat-700'
|
|
|
|
+ },
|
|
|
|
+ tilesText: {
|
|
|
|
+ fontSize: getFontSize(12),
|
|
|
|
+ color: Colors.DARK_BLUE,
|
|
|
|
+ fontWeight: '500'
|
|
|
|
+ }
|
|
|
|
+});
|