import { Dimensions, Linking, Platform, Text, TextInput, TouchableOpacity, View, Image, StatusBar } from 'react-native'; import React, { useEffect, useRef, useState, useCallback } from 'react'; import MapLibreGL, { CameraRef, MapViewRef } from '@maplibre/maplibre-react-native'; import { styles } from './style'; import { SafeAreaView } from 'react-native-safe-area-context'; import { Colors } from 'src/theme'; import { storage, StoreType } from 'src/storage'; import { RegionPayload } from '@maplibre/maplibre-react-native/javascript/components/MapView'; import * as turf from '@turf/turf'; import * as Location from 'expo-location'; import SearchIcon from 'assets/icons/search.svg'; import LocationIcon from 'assets/icons/location.svg'; import CloseSvg from 'assets/icons/close.svg'; import FilterIcon from 'assets/icons/filter.svg'; import ProfileIcon from 'assets/icons/bottom-navigation/profile.svg'; import RegionPopup from 'src/components/RegionPopup'; import { useRegion } from 'src/contexts/RegionContext'; import { qualityOptions } from '../TravelsScreen/utils/constants'; import { AvatarWithInitials, EditNmModal, WarningModal } from 'src/components'; import { API_HOST, MAP_HOST, VECTOR_MAP_HOST } from 'src/constants'; import { NAVIGATION_PAGES } from 'src/types'; import Animated, { Easing, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; import { getData } from 'src/modules/map/regionData'; import { getCountriesDatabase, getFirstDatabase, getSecondDatabase, refreshDatabases } from 'src/db'; import { fetchUserData, fetchUserDataDare, useGetListRegionsQuery } from '@api/regions'; import { SQLiteDatabase } from 'expo-sqlite/legacy'; import { useFocusEffect } from '@react-navigation/native'; import { useGetUniversalSearch } from '@api/search'; import { fetchCountryUserData, useGetListCountriesQuery } from '@api/countries'; import SearchModal from './UniversalSearch'; import EditModal from '../TravelsScreen/Components/EditSlowModal'; import CheckSvg from 'assets/icons/mark.svg'; import moment from 'moment'; import { usePostGetVisitedCountriesIdsQuery, usePostGetVisitedDareIdsQuery, usePostGetVisitedRegionsIdsQuery, usePostGetVisitedSeriesIdsQuery } from '@api/maps'; import FilterModal from './FilterModal'; import { useGetListDareQuery } from '@api/myDARE'; import { useGetIconsQuery, usePostSetToggleItem } from '@api/series'; import MarkerItem from './MarkerItem'; import { usePostGetUsersLocationQuery, usePostUpdateLocationMutation } from '@api/location'; MapLibreGL.setAccessToken(null); MapLibreGL.Logger.setLogLevel('error'); const generateFilter = (ids: number[]) => { return ids?.length ? ['any', ...ids.map((id) => ['==', 'id', id])] : ['==', 'id', -1]; }; // to do refactor let regions_visited = { id: 'regions_visited', type: 'fill', source: 'regions', 'source-layer': 'regions', style: { fillColor: 'rgba(255, 126, 0, 1)', fillOpacity: 0.6, fillOutlineColor: 'rgba(14, 80, 109, 1)' }, filter: generateFilter([]), maxzoom: 12 }; let countries_visited = { id: 'countries_visited', type: 'fill', source: 'countries', 'source-layer': 'countries', style: { fillColor: 'rgba(255, 126, 0, 1)', fillOpacity: 0.6, fillOutlineColor: 'rgba(14, 80, 109, 1)' }, filter: generateFilter([]), maxzoom: 10 }; let dare_visited = { id: 'dare_visited', type: 'fill', source: 'dare', 'source-layer': 'dare', style: { fillColor: 'rgba(255, 126, 0, 1)', fillOpacity: 0.6, fillOutlineColor: 'rgba(255, 126, 0, 1)' }, filter: generateFilter([]), maxzoom: 12 }; let regions = { id: 'regions', type: 'fill', source: 'regions', 'source-layer': 'regions', style: { fillColor: 'rgba(15, 63, 79, 0)', fillOutlineColor: 'rgba(14, 80, 109, 1)' }, filter: ['all'], maxzoom: 16 }; let countries = { id: 'countries', type: 'fill', source: 'countries', 'source-layer': 'countries', style: { fillColor: 'rgba(15, 63, 79, 0)', fillOutlineColor: 'rgba(14, 80, 109, 1)' }, filter: ['all'], maxzoom: 16 }; let dare = { id: 'dare', type: 'fill', source: 'dare', 'source-layer': 'dare', style: { fillColor: 'rgba(14, 80, 109, 0.6)', fillOutlineColor: 'rgba(14, 80, 109, 1)' }, filter: ['all'], maxzoom: 16 }; let selected_region = { id: 'selected_region', type: 'fill', source: 'regions', 'source-layer': 'regions', style: { fillColor: 'rgba(57, 115, 172, 0.3)', fillOutlineColor: '#3973ac' }, maxzoom: 12 }; let series_layer = { id: 'series_layer', type: 'symbol', source: 'nomadmania_series', 'source-layer': 'series', layout: { 'icon-image': '{series_id}', 'icon-size': 0.1, 'text-anchor': 'top', 'text-field': '{series_name} - {name}', 'text-font': ['Noto Sans Regular'], 'text-max-width': 9, 'text-offset': [0, 0.6], 'text-padding': 2, 'text-size': 12, visibility: 'visible', 'text-optional': true, 'text-ignore-placement': true, 'text-allow-overlap': true }, paint: { 'text-color': '#666', 'text-halo-blur': 0.5, 'text-halo-color': '#ffffff', 'text-halo-width': 1 }, filter: generateFilter([]) }; let series_visited = { id: 'series_visited', type: 'symbol', source: 'nomadmania_series', 'source-layer': 'series', layout: { 'icon-image': '{series_id}v', 'icon-size': 0.15, 'text-anchor': 'top', 'text-field': '{series_name} - {name}', 'text-font': ['Noto Sans Regular'], 'text-max-width': 9, 'text-offset': [0, 0.6], 'text-padding': 2, 'text-size': 12, visibility: 'visible', 'text-optional': true, 'text-ignore-placement': true, 'text-allow-overlap': true }, paint: { 'text-color': '#666', 'text-halo-blur': 0.5, 'text-halo-color': '#ffffff', 'text-halo-width': 1 }, filter: generateFilter([]) }; const INITIAL_REGION = { latitude: 0, longitude: 0, latitudeDelta: 180, longitudeDelta: 180 }; const MapScreen: any = ({ navigation, route }: { navigation: any; route: any }) => { const userId = storage.get('uid', StoreType.STRING) as string; const token = storage.get('token', StoreType.STRING) as string; const { data: regionsList } = useGetListRegionsQuery(true); const { data: countriesList } = useGetListCountriesQuery(true); const { data: dareList } = useGetListDareQuery(true); const [tilesType, setTilesType] = useState({ label: 'NM regions', value: 0 }); const tilesTypes = [ { label: 'NM regions', value: 0 }, { label: 'UN countries', value: 1 }, { label: 'DARE places', value: 2 } ]; const [type, setType] = useState<'regions' | 'countries' | 'dare'>('regions'); const [seriesFilter, setSeriesFilter] = useState({ visible: true, groups: [], applied: false, status: -1 }); const [regionsFilter, setRegionsFilter] = useState({ visitedLabel: 'by', year: moment().year() }); const [showNomads, setShowNomads] = useState( (storage.get('showNomads', StoreType.BOOLEAN) as boolean) ?? false ); const { mutateAsync: updateLocation } = usePostUpdateLocationMutation(); const { data: visitedRegionIds, refetch: refetchVisitedRegions } = usePostGetVisitedRegionsIdsQuery( token, regionsFilter.visitedLabel, regionsFilter.year, +userId, type === 'regions' && !!userId ); const { data: visitedCountryIds, refetch: refetchVisitedCountries } = usePostGetVisitedCountriesIdsQuery( token, regionsFilter.visitedLabel, regionsFilter.year, +userId, type === 'countries' && !!userId ); const { data: visitedDareIds, refetch: refetchVisitedDare } = usePostGetVisitedDareIdsQuery( token, +userId, type === 'dare' && !!userId ); const { data: visitedSeriesIds } = usePostGetVisitedSeriesIdsQuery(token, !!userId); const { data: seriesIcons } = useGetIconsQuery(true); const userInfo = storage.get('currentUserData', StoreType.STRING) as string; const { mutateAsync: mutateUserData } = fetchUserData(); const { mutateAsync: mutateUserDataDare } = fetchUserDataDare(); const { mutateAsync: mutateCountriesData } = fetchCountryUserData(); const [selectedRegion, setSelectedRegion] = useState(null); const [initialRegion, setInitialRegion] = useState(INITIAL_REGION); const [regionPopupVisible, setRegionPopupVisible] = useState(false); const [regionData, setRegionData] = useState(null); const [location, setLocation] = useState(null); const [userAvatars, setUserAvatars] = useState([]); const [userInfoData, setUserInfoData] = useState(null); const [selectedMarker, setSelectedMarker] = useState(null); const [askLocationVisible, setAskLocationVisible] = useState(false); const [openSettingsVisible, setOpenSettingsVisible] = useState(false); const [isWarningModalVisible, setIsWarningModalVisible] = useState(false); const [isEditSlowModalVisible, setIsEditSlowModalVisible] = useState(false); const [isEditModalVisible, setIsEditModalVisible] = useState(false); const [isFilterVisible, setIsFilterVisible] = useState(false); const [modalState, setModalState] = useState({ selectedFirstYear: 2021, selectedLastYear: 2021, selectedQuality: qualityOptions[2], selectedNoOfVisits: 1, years: [], id: null }); const [isConnected, setIsConnected] = useState(true); const [isExpanded, setIsExpanded] = useState(false); const [search, setSearch] = useState(''); const { data: searchData } = useGetUniversalSearch(search, search.length > 0); const [searchInput, setSearchInput] = useState(''); const [searchVisible, setSearchVisible] = useState(false); const [index, setIndex] = useState(0); const width = useSharedValue(48); const usableWidth = Dimensions.get('window').width - 32; const { handleUpdateNM, handleUpdateDare, handleUpdateSlow, userData, setUserData } = useRegion(); const [db1, setDb1] = useState(null); const [db2, setDb2] = useState(null); const [db3, setDb3] = useState(null); const [regionsVisitedFilter, setRegionsVisitedFilter] = useState(generateFilter([])); const [countriesVisitedFilter, setCountriesVisitedFilter] = useState(generateFilter([])); const [dareVisitedFilter, setDareVisitedFilter] = useState(generateFilter([])); const [seriesVisitedFilter, setSeriesVisitedFilter] = useState(generateFilter([])); const [seriesNotVisitedFilter, setSeriesNotVisitedFilter] = useState(generateFilter([])); const [regionsVisited, setRegionsVisited] = useState([]); const [countriesVisited, setCountriesVisited] = useState([]); const [dareVisited, setDareVisited] = useState([]); const [seriesVisited, setSeriesVisited] = useState([]); const [images, setImages] = useState({}); const { mutateAsync: updateSeriesItem } = usePostSetToggleItem(); const [nomads, setNomads] = useState(null); const { data: usersLocation } = usePostGetUsersLocationQuery( token, !!token && showNomads && Boolean(location) ); useEffect(() => { if (!showNomads) { setNomads(null); } }, [showNomads]); useEffect(() => { if (usersLocation) { console.log('usersLocation', usersLocation); setNomads(usersLocation.geojson); } }, [usersLocation]); useEffect(() => { let loadedImages: any = {}; if (seriesIcons) { seriesIcons.data.forEach(async (icon) => { const id = icon.id; const img = API_HOST + '/static/img/series_new2/' + icon.new_icon_png; const imgVisited = API_HOST + '/static/img/series_new2/' + icon.new_icon_visited_png; if (!img || !imgVisited) return; try { const iconImage = { uri: img }; const visitedIconImage = { uri: imgVisited }; loadedImages[id] = iconImage; loadedImages[`${id}v`] = visitedIconImage; } catch (error) { console.error(`Error loading icon for series_id ${id}:`, error); } }); } if (nomads && nomads.features) { nomads.features.forEach((feature) => { const user_id = `user_${feature.properties?.id}`; const avatarUrl = `${API_HOST}${feature.properties?.avatar}`; if (avatarUrl) { loadedImages[user_id] = { uri: avatarUrl }; if (feature.properties) { feature.properties.icon_key = user_id; } } }); } setImages(loadedImages); }, [nomads, seriesIcons]); useEffect(() => { const loadDatabases = async () => { const firstDb = await getFirstDatabase(); const secondDb = await getSecondDatabase(); const countriesDb = await getCountriesDatabase(); setDb1(firstDb); setDb2(secondDb); setDb3(countriesDb); }; if (!db1 || !db2 || !db3) { loadDatabases(); } }, [db1, db2, db3]); useEffect(() => { const savedFilterSettings = storage.get('filterSettings', StoreType.STRING) as string; if (savedFilterSettings) { const filterSettings = JSON.parse(savedFilterSettings); setTilesType(filterSettings.tilesType); setType(filterSettings.type); setRegionsFilter({ visitedLabel: filterSettings.selectedVisible.value === 1 ? 'in' : 'by', year: filterSettings.selectedYear.value }); setSeriesFilter(filterSettings.seriesFilter); } }, []); useFocusEffect( useCallback(() => { if (token) { refetchVisitedRegions(); refetchVisitedCountries(); refetchVisitedDare(); } }, [navigation]) ); // to do refactor useEffect(() => { if (visitedRegionIds) { setRegionsVisited(visitedRegionIds.ids); } }, [visitedRegionIds]); useEffect(() => { if (visitedCountryIds) { setCountriesVisited(visitedCountryIds.ids); } }, [visitedCountryIds]); useEffect(() => { if (visitedDareIds) { setDareVisited(visitedDareIds.ids); } }, [visitedDareIds]); useEffect(() => { if (visitedSeriesIds && token) { setSeriesVisited(visitedSeriesIds.ids); } }, [visitedSeriesIds]); useEffect(() => { if (regionsVisited && regionsVisited.length) { setRegionsVisitedFilter(generateFilter(regionsVisited)); } }, [regionsVisited]); useEffect(() => { if (countriesVisited && countriesVisited.length) { setCountriesVisitedFilter(generateFilter(countriesVisited)); } }, [countriesVisited]); useEffect(() => { if (dareVisited && dareVisited.length) { setDareVisitedFilter(generateFilter(dareVisited)); } }, [dareVisited]); useEffect(() => { if (!seriesFilter.visible) { setSeriesVisitedFilter(generateFilter([])); setSeriesNotVisitedFilter(generateFilter([])); return; } if (seriesFilter.applied) { if (seriesVisited?.length) { setSeriesVisitedFilter([ 'all', ['any', ...seriesVisited.map((id) => ['==', 'id', id])], ['any', ...seriesFilter.groups.map((groupId: number) => ['==', 'series_id', groupId])] ]); setSeriesNotVisitedFilter([ 'all', ['all', ...seriesVisited.map((id) => ['!=', 'id', id])], ['any', ...seriesFilter.groups.map((groupId: number) => ['==', 'series_id', groupId])] ]); } else { setSeriesNotVisitedFilter([ 'any', ...seriesFilter.groups.map((groupId: number) => ['==', 'series_id', groupId]) ]); } } else { setSeriesVisitedFilter(['any', ...seriesVisited.map((id) => ['==', 'id', id])]); setSeriesNotVisitedFilter(['all', ...seriesVisited.map((id) => ['!=', 'id', id])]); } }, [seriesVisited, seriesFilter]); useEffect(() => { if (route.params?.id && route.params?.type && db1 && db2 && db3) { handleFindRegion(route.params?.id, route.params?.type); } }, [route, db1, db2, db3]); useEffect(() => { if (selectedRegion) { mapRef.current; } }, [selectedRegion]); useEffect(() => { (async () => { let { status } = await Location.getForegroundPermissionsAsync(); if (status !== 'granted') { setShowNomads(false); storage.set('showNomads', false); return; } let currentLocation = await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.Balanced }); setLocation(currentLocation.coords); updateLocation({ token, lat: currentLocation.coords.latitude, lng: currentLocation.coords.longitude }); })(); }, []); useEffect(() => { const currentYear = moment().year(); let yearSelector: { label: string; value: number }[] = [{ label: 'visited', value: 1 }]; for (let i = currentYear; i >= 1951; i--) { yearSelector.push({ label: i.toString(), value: i }); } handleModalStateChange({ years: yearSelector }); }, []); useFocusEffect( useCallback(() => { navigation.getParent()?.setOptions({ tabBarStyle: { display: regionPopupVisible ? 'none' : 'flex', position: 'absolute', ...Platform.select({ android: { height: 58 } }) } }); }, [regionPopupVisible, navigation]) ); const mapRef = useRef(null); const cameraRef = useRef(null); useEffect(() => { if (userInfo) { setUserInfoData(JSON.parse(userInfo)); } }, [userInfo]); const handlePress = () => { if (isExpanded) { setIndex(0); setSearchInput(''); } setIsExpanded((prev) => !prev); width.value = withTiming(isExpanded ? 48 : usableWidth, { duration: 300, easing: Easing.inOut(Easing.ease) }); }; const animatedStyle = useAnimatedStyle(() => { return { width: width.value }; }); const loadInitialRegion = () => { try { const savedInitialRegion = storage.get('initialRegion', StoreType.STRING) as string; if (savedInitialRegion) { const region = JSON.parse(savedInitialRegion); setInitialRegion(region); } } catch (e) { console.error('Failed to load saved initial region:', e); } }; useEffect(() => { loadInitialRegion(); }, []); useEffect(() => { if (initialRegion && !route.params?.id) { setTimeout(() => { cameraRef.current?.setCamera({ centerCoordinate: [initialRegion.longitude, initialRegion.latitude], zoomLevel: Math.log2(360 / initialRegion.latitudeDelta), animationDuration: 500 }); }, 500); } }, [initialRegion]); const onMapPress = async (event: any) => { if (!mapRef.current) return; if (selectedMarker) { closeCallout(); return; } try { const { screenPointX, screenPointY } = event.properties; const { features } = await mapRef.current.queryRenderedFeaturesAtPoint( [screenPointX, screenPointY], undefined, ['regions', 'countries', 'dare'] ); if (features?.length) { const region = features[0]; if (selectedRegion === region.properties?.id) return; let db = type === 'regions' ? db1 : type === 'countries' ? db3 : db2; let tableName = type === 'dare' ? 'places' : type; let foundRegion = region.properties?.id; setSelectedRegion(region.properties?.id); await getData(db, foundRegion, tableName, handleRegionData) .then(() => { setRegionPopupVisible(true); }) .catch((error) => { console.error('Error fetching data', error); refreshDatabases(); }); if (tableName === 'regions') { token ? await mutateUserData( { region_id: +foundRegion, token: String(token) }, { onSuccess: (data) => { setUserData({ type: 'nm', id: +foundRegion, ...data }); } } ) : setUserData({ type: 'nm', id: +foundRegion }); if (regionsList) { const region = regionsList.data.find((region) => region.id === +foundRegion); if (region) { const bounds = turf.bbox(region.bbox); cameraRef.current?.fitBounds( [bounds[2], bounds[3]], [bounds[0], bounds[1]], [10, 10, 50, 10], 1000 ); } } } else if (tableName === 'countries') { token ? await mutateCountriesData( { id: +foundRegion, token }, { onSuccess: (data) => { setUserData({ type: 'countries', id: +foundRegion, ...data.data }); } } ) : setUserData({ type: 'countries', id: +foundRegion }); if (countriesList) { const region = countriesList.data.find((region) => region.id === +foundRegion); if (region) { const bounds = turf.bbox(region.bbox); cameraRef.current?.fitBounds( [bounds[2], bounds[3]], [bounds[0], bounds[1]], [10, 10, 50, 10], 1000 ); } } } else { token ? await mutateUserDataDare( { dare_id: +foundRegion, token: String(token) }, { onSuccess: (data) => { setUserData({ type: 'dare', id: +foundRegion, ...data }); } } ) : setUserData({ type: 'dare', id: +foundRegion }); if (dareList) { const region = dareList.data.find((region) => region.id === +foundRegion); if (region) { const bounds = turf.bbox(region.bbox); cameraRef.current?.fitBounds( [bounds[2], bounds[3]], [bounds[0], bounds[1]], [10, 10, 50, 10], 1000 ); } } } } else { handleClosePopup(); } } catch (error) { console.error('Error onMapPress features:', error); } }; const handleRegionDidChange = async (feature: GeoJSON.Feature) => { if (!feature) return; const { zoomLevel } = feature.properties; const { coordinates } = feature.geometry; if (!zoomLevel || !coordinates) return; const latitudeDelta = 360 / 2 ** zoomLevel; const longitudeDelta = latitudeDelta; const region = { latitude: coordinates[1], longitude: coordinates[0], latitudeDelta, longitudeDelta }; storage.set('initialRegion', JSON.stringify(region)); }; const handleClosePopup = async () => { setSelectedRegion(null); setRegionPopupVisible(false); setRegionData(null); }; const handleGetLocation = async () => { let { status, canAskAgain } = await Location.getForegroundPermissionsAsync(); if (status === 'granted') { getLocation(); } else if (!canAskAgain) { setOpenSettingsVisible(true); } else { setAskLocationVisible(true); } }; const getLocation = async () => { let currentLocation = await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.Balanced }); setLocation(currentLocation.coords); updateLocation({ token, lat: currentLocation.coords.latitude, lng: currentLocation.coords.longitude }); if (currentLocation.coords) { cameraRef.current?.flyTo( [currentLocation.coords.longitude, currentLocation.coords.latitude], 1000 ); } handleClosePopup(); }; const handleAcceptPermission = async () => { setAskLocationVisible(false); let { status, canAskAgain } = await Location.requestForegroundPermissionsAsync(); if (status === 'granted') { getLocation(); } else if (!canAskAgain) { setOpenSettingsVisible(true); } }; const handleOpenEditModal = () => { handleModalStateChange({ selectedFirstYear: userData?.first_visit_year, selectedLastYear: userData?.last_visit_year, selectedQuality: qualityOptions.find((quality) => quality.id === userData?.best_visit_quality) || qualityOptions[2], selectedNoOfVisits: userData?.no_of_visits || 1, id: regionData?.id }); setIsEditModalVisible(true); }; const handleOpenEditSlowModal = () => { setIsEditSlowModalVisible(true); }; const handleSearch = async () => { setSearch(searchInput); setSearchVisible(true); }; const handleCloseModal = () => { setSearchInput(''); setSearchVisible(false); handlePress(); }; const handleRegionData = (regionData: any, avatars: string[]) => { setRegionData(regionData); setUserAvatars(avatars); }; const handleFindRegion = async (id: number, type: 'regions' | 'countries' | 'places') => { setType(type === 'places' ? 'dare' : type); if (!db1 || !db2 || !db3) { return; } const db = type === 'regions' ? db1 : type === 'countries' ? db3 : db2; if (id) { setSelectedRegion(id); await getData(db, id, type, handleRegionData) .then(() => { setRegionPopupVisible(true); }) .catch((error) => { console.error('Error fetching data', error); refreshDatabases(); }); if (type === 'regions') { token ? await mutateUserData( { region_id: id, token: String(token) }, { onSuccess: (data) => { setUserData({ type: 'nm', id, ...data }); } } ) : setUserData({ type: 'nm', id }); 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]], [10, 10, 50, 10], 1000 ); } } } else if (type === 'countries') { token ? await mutateCountriesData( { id, token }, { onSuccess: (data) => { setUserData({ type: 'countries', id, ...data.data }); } } ) : setUserData({ type: 'countries', id }); if (countriesList) { const region = countriesList.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]], [10, 10, 50, 10], 1000 ); } } } else { token ? await mutateUserDataDare( { dare_id: +id, token: String(token) }, { onSuccess: (data) => { setUserData({ type: 'dare', id: +id, ...data }); } } ) : setUserData({ type: 'dare', id: +id }); if (dareList) { const region = dareList.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]], [10, 10, 50, 10], 1000 ); } } } } else { handleClosePopup(); } }; const handleMarkerPress = async (event: any) => { const { features } = event; if (features?.length) { const selectedFeature = features[0]; const { coordinates } = selectedFeature.geometry; const visited = seriesVisited.includes(selectedFeature.properties.id) ? 1 : 0; const icon = images[selectedFeature.properties.series_id]; const { name, description, series_name, series_id, id } = selectedFeature.properties; setSelectedMarker({ coordinates, name, icon, description, series_name, visited, series_id, id }); } }; const closeCallout = () => { setSelectedMarker(null); }; const toggleSeries = useCallback( async (item: any) => { if (!token) { setIsWarningModalVisible(true); return; } const itemData = { token, series_id: item.series_id, item_id: item.id, checked: (item.visited === 0 ? 1 : 0) as 0 | 1, double: 0 as 0 | 1 }; try { updateSeriesItem(itemData); if (item.visited === 1) { setSeriesVisited((current) => current.filter((id) => id !== item.id)); setSelectedMarker((current: any) => ({ ...current, visited: 0 })); } else { setSeriesVisited((current) => [...current, item.id]); setSelectedMarker((current: any) => ({ ...current, visited: 1 })); } } catch (error) { console.error('Failed to update series state', error); } }, [token, updateSeriesItem] ); const handleModalStateChange = (updates: { [key: string]: any }) => { setModalState((prevState) => ({ ...prevState, ...updates })); }; return ( {type === 'regions' && ( <> )} {type === 'countries' && ( <> )} {type === 'dare' && ( <> )} {selectedRegion && type && ( )} {seriesFilter.status !== 1 ? ( ) : ( <> )} {seriesFilter.status !== 0 ? ( ) : ( <> )} {nomads && ( console.log(event.features)} > )} {selectedMarker && ( )} {location && ( {/* to do custom user location */} )} {regionPopupVisible && regionData ? ( <> Close { if (!token) { setIsWarningModalVisible(true); return; } handleUpdateNM(id, first, last, visits, quality); const updatedIds = regionsVisited.includes(id) ? regionsVisited.filter((visitedId) => visitedId !== id) : [...regionsVisited, id]; setRegionsVisited(updatedIds); refetchVisitedCountries(); }} updateDare={(id, visits) => { if (!token) { setIsWarningModalVisible(true); return; } handleUpdateDare(id, visits); const updatedIds = dareVisited.includes(id) ? dareVisited.filter((visitedId) => visitedId !== id) : [...dareVisited, id]; setDareVisited(updatedIds); }} disabled={!token || !isConnected} updateSlow={(id, v, s11, s31, s101) => { if (!token) { setIsWarningModalVisible(true); return; } handleUpdateSlow(id, v, s11, s31, s101); const updatedIds = countriesVisited.includes(id) ? countriesVisited.filter((visitedId) => visitedId !== id) : [...countriesVisited, id]; setCountriesVisited(updatedIds); }} openEditSlowModal={handleOpenEditSlowModal} /> ) : ( <> {!isExpanded ? ( navigation.navigate(NAVIGATION_PAGES.PROFILE_TAB)} > {token ? ( userInfoData?.avatar ? ( ) : ( ) ) : ( )} ) : null} {isExpanded ? ( <> setSearchInput(text)} onSubmitEditing={handleSearch} /> ) : ( )} { setIsFilterVisible(true); closeCallout(); }} > )} setIsWarningModalVisible(false)} /> setIsEditModalVisible(false)} modalState={modalState} updateModalState={handleModalStateChange} updateNM={handleUpdateNM} /> setIsEditSlowModalVisible(false)} item={{ ...userData, country_id: regionData?.id }} updateSlow={(id, v, s11, s31, s101) => handleUpdateSlow(id, v, s11, s31, s101)} /> setAskLocationVisible(false)} action={handleAcceptPermission} message="To use this feature we need your permission to access your location. If you press OK your system will ask you to approve location sharing with NomadMania app." /> setOpenSettingsVisible(false)} action={() => Platform.OS === 'ios' ? Linking.openURL('app-settings:') : Linking.openSettings() } message="NomadMania app needs location permissions to function properly. Open settings?" /> ); }; export default MapScreen;