|
@@ -1,12 +1,246 @@
|
|
|
-import React from 'react';
|
|
|
-import { View, Text } from 'react-native';
|
|
|
+import {
|
|
|
+ View,
|
|
|
+ Platform,
|
|
|
+ TouchableOpacity,
|
|
|
+ Text
|
|
|
+} from 'react-native';
|
|
|
+import React, { useEffect, useState, useRef, useMemo } from 'react';
|
|
|
+import MapView, { UrlTile, Geojson } from 'react-native-maps';
|
|
|
+import * as turf from '@turf/turf';
|
|
|
+import * as FileSystem from 'expo-file-system';
|
|
|
+
|
|
|
+import MenuIcon from '../../../../assets/icons/menu.svg';
|
|
|
+import SearchIcon from '../../../../assets/icons/search.svg';
|
|
|
+import RadarIcon from '../../../../assets/icons/radar.svg';
|
|
|
+import LocationIcon from '../../../../assets/icons/location.svg';
|
|
|
+import CloseSvg from '../../../../assets/icons/close.svg';
|
|
|
+
|
|
|
+import regions from '../../../../assets/geojson/nm2022.json';
|
|
|
+import dareRegions from '../../../../assets/geojson/mqp.json';
|
|
|
+
|
|
|
+import NetInfo from "@react-native-community/netinfo";
|
|
|
+import { getFirstDatabase, getSecondDatabase } from '../../../db';
|
|
|
+import RegionPopup from '../../../components/RegionPopup';
|
|
|
+
|
|
|
+import { BottomTabNavigationProp } from '@react-navigation/bottom-tabs';
|
|
|
+import { styles } from './style';
|
|
|
+import { findRegionInDataset, calculateMapRegion } from '../../../utils/mapHelpers';
|
|
|
+import { getData } from '../../../modules/map/regionData';
|
|
|
+
|
|
|
+const tilesBaseURL = 'https://maps.nomadmania.com/tiles_osm';
|
|
|
+const localTileDir = `${FileSystem.cacheDirectory}tiles`;
|
|
|
+
|
|
|
+const gridUrl = 'https://maps.nomadmania.com/tiles_nm/grid';
|
|
|
+const localGridDir = `${FileSystem.cacheDirectory}tiles/grid`;
|
|
|
+
|
|
|
+const visitedTiles = 'https://maps.nomadmania.com/tiles_nm/user_visited/51363';
|
|
|
+const localVisitedDir = `${FileSystem.cacheDirectory}tiles/user_visited`;
|
|
|
+
|
|
|
+const dareTiles = 'https://maps.nomadmania.com/tiles_nm/regions_mqp';
|
|
|
+const localDareDir = `${FileSystem.cacheDirectory}tiles/regions_mqp`;
|
|
|
+
|
|
|
+interface Region {
|
|
|
+ id: number;
|
|
|
+ name: string;
|
|
|
+ region_photos: string;
|
|
|
+ visitors_count: number;
|
|
|
+}
|
|
|
+
|
|
|
+interface MapScreenProps {
|
|
|
+ navigation: BottomTabNavigationProp<any>;
|
|
|
+}
|
|
|
+
|
|
|
+const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
|
|
|
+ const mapRef = useRef<MapView>(null);
|
|
|
+
|
|
|
+ const [isConnected, setIsConnected] = useState<boolean | null>(true);
|
|
|
+ const [selectedRegion, setSelectedRegion] = useState(null);
|
|
|
+ const [popupVisible, setPopupVisible] = useState<boolean | null>(false);
|
|
|
+ const [regionData, setRegionData] = useState<Region | null>(null);
|
|
|
+ const [userAvatars, setUserAvatars] = useState<string[]>([]);
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ navigation.setOptions({
|
|
|
+ tabBarStyle: {
|
|
|
+ display: popupVisible ? 'none' : 'flex',
|
|
|
+ position: 'absolute',
|
|
|
+ ...Platform.select({
|
|
|
+ android: {
|
|
|
+ height: 58,
|
|
|
+ },
|
|
|
+ }),
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }, [popupVisible, navigation]);
|
|
|
+
|
|
|
+ const handleRegionData = (regionData: Region, avatars: string[]) => {
|
|
|
+ setRegionData(regionData);
|
|
|
+ setUserAvatars(avatars);
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleMapPress = async (event: { nativeEvent: { coordinate: { latitude: any; longitude: any; }; }; }) => {
|
|
|
+ const { latitude, longitude } = event.nativeEvent.coordinate;
|
|
|
+ const point = turf.point([longitude, latitude]);
|
|
|
+ setUserAvatars([]);
|
|
|
+
|
|
|
+ let db = getSecondDatabase();
|
|
|
+ let tableName = 'places';
|
|
|
+
|
|
|
+ let foundRegion = findRegionInDataset(dareRegions, point);
|
|
|
+
|
|
|
+ if (!foundRegion) {
|
|
|
+ foundRegion = findRegionInDataset(regions, point);
|
|
|
+ db = getFirstDatabase();
|
|
|
+ tableName = 'regions';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (foundRegion) {
|
|
|
+ const id = foundRegion.properties?.id;
|
|
|
+
|
|
|
+ setSelectedRegion({
|
|
|
+ type: 'FeatureCollection',
|
|
|
+ features: [{
|
|
|
+ geometry: foundRegion.geometry,
|
|
|
+ properties: {
|
|
|
+ ...foundRegion.properties,
|
|
|
+ fill: "rgba(57, 115, 172, 0.2)",
|
|
|
+ stroke: "#3973AC",
|
|
|
+ },
|
|
|
+ type: 'Feature',
|
|
|
+ }]
|
|
|
+ });
|
|
|
+
|
|
|
+ await getData(db, id, tableName, handleRegionData)
|
|
|
+ .then(() => {
|
|
|
+ setPopupVisible(true);
|
|
|
+ })
|
|
|
+ .catch(error => {
|
|
|
+ console.error("Error fetching data", error);
|
|
|
+ });
|
|
|
+
|
|
|
+ const bounds = turf.bbox(foundRegion);
|
|
|
+ const region = calculateMapRegion(bounds);
|
|
|
+
|
|
|
+ mapRef.current?.animateToRegion(region, 1000);
|
|
|
+ } else {
|
|
|
+ handleClosePopup();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ const unsubscribe = NetInfo.addEventListener(state => {
|
|
|
+ setIsConnected(state.isConnected);
|
|
|
+ });
|
|
|
+
|
|
|
+ return () => unsubscribe();
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ const renderMapTiles = (
|
|
|
+ url: string,
|
|
|
+ cacheDir: string,
|
|
|
+ zIndex: number,
|
|
|
+ opacity = 1
|
|
|
+ ) => (
|
|
|
+ <UrlTile
|
|
|
+ urlTemplate={`${url}/{z}/{x}/{y}`}
|
|
|
+ maximumZ={15}
|
|
|
+ maximumNativeZ={13}
|
|
|
+ tileCachePath={`${cacheDir}`}
|
|
|
+ shouldReplaceMapContent
|
|
|
+ minimumZ={0}
|
|
|
+ offlineMode={!isConnected}
|
|
|
+ opacity={opacity}
|
|
|
+ zIndex={zIndex}
|
|
|
+ />
|
|
|
+ );
|
|
|
+
|
|
|
+ function renderGeoJSON() {
|
|
|
+ if (!selectedRegion) return null;
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Geojson
|
|
|
+ geojson={selectedRegion}
|
|
|
+ fillColor="rgba(57, 115, 172, 0.2)"
|
|
|
+ strokeColor="#3973ac"
|
|
|
+ strokeWidth={Platform.OS == 'android' ? 3 : 2}
|
|
|
+ zIndex={3}
|
|
|
+ />
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleClosePopup = () => {
|
|
|
+ setPopupVisible(false);
|
|
|
+ setSelectedRegion(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ const renderedGeoJSON = useMemo(() => renderGeoJSON(), [selectedRegion]);
|
|
|
|
|
|
-const MapScreen = () => {
|
|
|
return (
|
|
|
- <View>
|
|
|
- <Text>Map Screen</Text>
|
|
|
+ <View style={styles.container}>
|
|
|
+ <MapView
|
|
|
+ ref={mapRef}
|
|
|
+ showsMyLocationButton={false}
|
|
|
+ showsCompass={false}
|
|
|
+ zoomControlEnabled={false}
|
|
|
+ onPress={handleMapPress}
|
|
|
+ style={styles.map}
|
|
|
+ mapType={Platform.OS == 'android' ? 'none' : 'standard'}
|
|
|
+ offlineMode={!isConnected}
|
|
|
+ maxZoomLevel={15}
|
|
|
+ minZoomLevel={0}
|
|
|
+ >
|
|
|
+ {renderedGeoJSON}
|
|
|
+ {renderMapTiles(tilesBaseURL, localTileDir, 1)}
|
|
|
+ {renderMapTiles(gridUrl, localGridDir, 2)}
|
|
|
+ {renderMapTiles(visitedTiles, localVisitedDir, 2, 0.5)}
|
|
|
+ {renderMapTiles(dareTiles, localDareDir, 2, 0.5)}
|
|
|
+ </MapView>
|
|
|
+
|
|
|
+ {popupVisible && regionData ? (
|
|
|
+ <>
|
|
|
+ <TouchableOpacity
|
|
|
+ style={[ styles.cornerButton, styles.topLeftButton, styles.closeLeftButton ]}
|
|
|
+ onPress={handleClosePopup}
|
|
|
+ >
|
|
|
+ <CloseSvg
|
|
|
+ fill="white"
|
|
|
+ width={13}
|
|
|
+ height={13}
|
|
|
+ />
|
|
|
+ <Text style={styles.textClose}>Close</Text>
|
|
|
+ </TouchableOpacity>
|
|
|
+
|
|
|
+ <TouchableOpacity style={[ styles.cornerButton, styles.topRightButton, styles.bottomButton ]}>
|
|
|
+ <LocationIcon />
|
|
|
+ </TouchableOpacity>
|
|
|
+
|
|
|
+ <RegionPopup
|
|
|
+ region={regionData}
|
|
|
+ userAvatars={userAvatars}
|
|
|
+ onMarkVisited={() => console.log('Mark as visited')}
|
|
|
+ />
|
|
|
+ </>
|
|
|
+ ) : (
|
|
|
+ <>
|
|
|
+ <TouchableOpacity style={[styles.cornerButton, styles.topLeftButton]}>
|
|
|
+ <MenuIcon />
|
|
|
+ </TouchableOpacity>
|
|
|
+
|
|
|
+ <TouchableOpacity style={[styles.cornerButton, styles.topRightButton]}>
|
|
|
+ <SearchIcon />
|
|
|
+ </TouchableOpacity>
|
|
|
+
|
|
|
+ <TouchableOpacity style={[styles.cornerButton, styles.bottomButton, styles.bottomLeftButton]}>
|
|
|
+ <RadarIcon />
|
|
|
+ </TouchableOpacity>
|
|
|
+
|
|
|
+ <TouchableOpacity style={[styles.cornerButton, styles.bottomButton, styles.bottomRightButton]}>
|
|
|
+ <LocationIcon />
|
|
|
+ </TouchableOpacity>
|
|
|
+ </>
|
|
|
+ )}
|
|
|
</View>
|
|
|
);
|
|
|
-};
|
|
|
+}
|
|
|
|
|
|
export default MapScreen;
|