|
@@ -1,15 +1,13 @@
|
|
|
import {
|
|
|
Platform,
|
|
|
TouchableOpacity,
|
|
|
- View,
|
|
|
Image,
|
|
|
- Animated as Animation,
|
|
|
Linking,
|
|
|
TextInput,
|
|
|
- Dimensions
|
|
|
+ Dimensions,
|
|
|
+ StatusBar
|
|
|
} from 'react-native';
|
|
|
import React, { FC, useEffect, useRef, useState } from 'react';
|
|
|
-import MapView, { UrlTile, Marker } from 'react-native-maps';
|
|
|
import * as Location from 'expo-location';
|
|
|
import Animated, {
|
|
|
Easing,
|
|
@@ -34,8 +32,101 @@ import SearchModal from '../../MapScreen/UniversalSearch';
|
|
|
import { useGetUniversalSearch } from '@api/search';
|
|
|
import { storage, StoreType } from 'src/storage';
|
|
|
import { NAVIGATION_PAGES } from 'src/types';
|
|
|
+import { SafeAreaView } from 'react-native-safe-area-context';
|
|
|
+import MapLibreGL, { CameraRef, MapViewRef } from '@maplibre/maplibre-react-native';
|
|
|
+import {
|
|
|
+ usePostGetVisitedCountriesIdsQuery,
|
|
|
+ usePostGetVisitedDareIdsQuery,
|
|
|
+ usePostGetVisitedRegionsIdsQuery
|
|
|
+} from '@api/maps';
|
|
|
+import moment from 'moment';
|
|
|
+
|
|
|
+MapLibreGL.setAccessToken(null);
|
|
|
+
|
|
|
+const generateFilter = (ids: number[]) => {
|
|
|
+ return ids.length ? ['any', ...ids.map((id) => ['==', 'id', id])] : ['==', 'id', -1];
|
|
|
+};
|
|
|
|
|
|
-const AnimatedMarker = Animation.createAnimatedComponent(Marker);
|
|
|
+let regions_visited = {
|
|
|
+ id: 'regions_visited',
|
|
|
+ type: 'fill',
|
|
|
+ source: 'regions',
|
|
|
+ 'source-layer': 'regions',
|
|
|
+ style: {
|
|
|
+ fillColor: 'rgba(255, 126, 0, 1)',
|
|
|
+ fillOpacity: 0.5,
|
|
|
+ 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.5,
|
|
|
+ fillOutlineColor: 'rgba(14, 80, 109, 1)'
|
|
|
+ },
|
|
|
+ filter: generateFilter([]),
|
|
|
+ maxzoom: 12
|
|
|
+};
|
|
|
+
|
|
|
+let dare_visited = {
|
|
|
+ id: '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)'
|
|
|
+ },
|
|
|
+ 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
|
|
|
+};
|
|
|
|
|
|
type Props = {
|
|
|
navigation: NavigationProp<any>;
|
|
@@ -47,12 +138,17 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
|
|
|
const userId = route.params?.userId;
|
|
|
const data = route.params?.data;
|
|
|
|
|
|
- const tilesBaseURL = `${FASTEST_MAP_HOST}/tiles_osm`;
|
|
|
- const gridUrl = `${FASTEST_MAP_HOST}/tiles_nm/grid`;
|
|
|
- const visitedDefaultTiles = `${FASTEST_MAP_HOST}/tiles_nm/user_visited/${userId}`;
|
|
|
+ const [regionsVisitedFilter, setRegionsVisitedFilter] = useState(generateFilter([]));
|
|
|
+ const [countriesVisitedFilter, setCountriesVisitedFilter] = useState(generateFilter([]));
|
|
|
+ const [dareVisitedFilter, setDareVisitedFilter] = useState(generateFilter([]));
|
|
|
+
|
|
|
+ const [regionsFilter, setRegionsFilter] = useState<any>({
|
|
|
+ visitedLabel: 'by',
|
|
|
+ year: moment().year()
|
|
|
+ });
|
|
|
|
|
|
- const mapRef = useRef<MapView>(null);
|
|
|
- const strokeWidthAnim = useRef(new Animation.Value(2)).current;
|
|
|
+ const mapRef = useRef<MapViewRef>(null);
|
|
|
+ const cameraRef = useRef<CameraRef>(null);
|
|
|
const [isFilterVisible, setIsFilterVisible] = useState(false);
|
|
|
const [tilesType, setTilesType] = useState({ label: 'NM regions', value: 0 });
|
|
|
const tilesTypes = [
|
|
@@ -60,8 +156,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
|
|
|
{ label: 'UN countries', value: 1 },
|
|
|
{ label: 'DARE places', value: 2 }
|
|
|
];
|
|
|
- const [type, setType] = useState(0);
|
|
|
- const [visitedTiles, setVisitedTiles] = useState(visitedDefaultTiles);
|
|
|
+ const [type, setType] = useState('regions');
|
|
|
const [location, setLocation] = useState<Location.LocationObjectCoords | null>(null);
|
|
|
const [askLocationVisible, setAskLocationVisible] = useState<boolean>(false);
|
|
|
const [openSettingsVisible, setOpenSettingsVisible] = useState<boolean>(false);
|
|
@@ -74,22 +169,43 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
|
|
|
const [searchInput, setSearchInput] = useState('');
|
|
|
const { data: searchData } = useGetUniversalSearch(search, search.length > 0);
|
|
|
|
|
|
+ const { data: visitedRegionIds } = usePostGetVisitedRegionsIdsQuery(
|
|
|
+ token,
|
|
|
+ regionsFilter.visitedLabel,
|
|
|
+ regionsFilter.year,
|
|
|
+ +userId,
|
|
|
+ type === 'regions' && !!userId
|
|
|
+ );
|
|
|
+ const { data: visitedCountryIds } = usePostGetVisitedCountriesIdsQuery(
|
|
|
+ token,
|
|
|
+ regionsFilter.visitedLabel,
|
|
|
+ regionsFilter.year,
|
|
|
+ +userId,
|
|
|
+ type === 'countries' && !!userId
|
|
|
+ );
|
|
|
+ const { data: visitedDareIds } = usePostGetVisitedDareIdsQuery(
|
|
|
+ token,
|
|
|
+ +userId,
|
|
|
+ type === 'dare' && !!userId
|
|
|
+ );
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (visitedRegionIds) {
|
|
|
+ setRegionsVisitedFilter(generateFilter(visitedRegionIds.ids));
|
|
|
+ }
|
|
|
+ }, [visitedRegionIds]);
|
|
|
+
|
|
|
useEffect(() => {
|
|
|
- Animation.loop(
|
|
|
- Animation.sequence([
|
|
|
- Animation.timing(strokeWidthAnim, {
|
|
|
- toValue: 3,
|
|
|
- duration: 700,
|
|
|
- useNativeDriver: false
|
|
|
- }),
|
|
|
- Animation.timing(strokeWidthAnim, {
|
|
|
- toValue: 2,
|
|
|
- duration: 700,
|
|
|
- useNativeDriver: false
|
|
|
- })
|
|
|
- ])
|
|
|
- ).start();
|
|
|
- }, [strokeWidthAnim]);
|
|
|
+ if (visitedCountryIds) {
|
|
|
+ setCountriesVisitedFilter(generateFilter(visitedCountryIds.ids));
|
|
|
+ }
|
|
|
+ }, [visitedCountryIds]);
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (visitedDareIds) {
|
|
|
+ setDareVisitedFilter(generateFilter(visitedDareIds.ids));
|
|
|
+ }
|
|
|
+ }, [visitedDareIds]);
|
|
|
|
|
|
const handleGetLocation = async () => {
|
|
|
let { status, canAskAgain } = await Location.getForegroundPermissionsAsync();
|
|
@@ -109,15 +225,12 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
|
|
|
});
|
|
|
setLocation(currentLocation.coords);
|
|
|
|
|
|
- mapRef.current?.animateToRegion(
|
|
|
- {
|
|
|
- latitude: currentLocation.coords.latitude,
|
|
|
- longitude: currentLocation.coords.longitude,
|
|
|
- latitudeDelta: 5,
|
|
|
- longitudeDelta: 5
|
|
|
- },
|
|
|
- 800
|
|
|
- );
|
|
|
+ if (currentLocation.coords) {
|
|
|
+ cameraRef.current?.flyTo(
|
|
|
+ [currentLocation.coords.longitude, currentLocation.coords.latitude],
|
|
|
+ 1000
|
|
|
+ );
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
const handleAcceptPermission = async () => {
|
|
@@ -154,19 +267,6 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
|
|
|
setSearchVisible(true);
|
|
|
};
|
|
|
|
|
|
- const renderMapTiles = (url: string, zIndex: number, opacity = 1) => (
|
|
|
- <UrlTile
|
|
|
- key={url}
|
|
|
- urlTemplate={`${url}/{z}/{x}/{y}`}
|
|
|
- maximumZ={15}
|
|
|
- maximumNativeZ={13}
|
|
|
- shouldReplaceMapContent
|
|
|
- minimumZ={0}
|
|
|
- opacity={opacity}
|
|
|
- zIndex={zIndex}
|
|
|
- />
|
|
|
- );
|
|
|
-
|
|
|
const handleGoBack = () => {
|
|
|
navigation.goBack();
|
|
|
};
|
|
@@ -199,32 +299,88 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
|
|
|
};
|
|
|
|
|
|
return (
|
|
|
- <View style={styles.container}>
|
|
|
- <MapView
|
|
|
- initialRegion={{
|
|
|
- latitude: 0,
|
|
|
- longitude: 0,
|
|
|
- latitudeDelta: 180,
|
|
|
- longitudeDelta: 180
|
|
|
- }}
|
|
|
+ <SafeAreaView style={{ height: '100%' }}>
|
|
|
+ <StatusBar translucent backgroundColor="transparent" />
|
|
|
+
|
|
|
+ <MapLibreGL.MapView
|
|
|
ref={mapRef}
|
|
|
- showsMyLocationButton={false}
|
|
|
- showsCompass={false}
|
|
|
- zoomControlEnabled={false}
|
|
|
style={styles.map}
|
|
|
- mapType={Platform.OS == 'android' ? 'none' : 'standard'}
|
|
|
- maxZoomLevel={15}
|
|
|
- minZoomLevel={0}
|
|
|
+ styleJSON={API_HOST + '/omt/app.json'}
|
|
|
+ rotateEnabled={false}
|
|
|
+ attributionEnabled={false}
|
|
|
>
|
|
|
- {renderMapTiles(tilesBaseURL, 1)}
|
|
|
- {type === 0 && renderMapTiles(gridUrl, 2)}
|
|
|
- {userId && renderMapTiles(visitedTiles, 2, 0.5)}
|
|
|
+ {type === 'regions' && (
|
|
|
+ <>
|
|
|
+ <MapLibreGL.FillLayer
|
|
|
+ id={regions.id}
|
|
|
+ sourceID={regions.source}
|
|
|
+ sourceLayerID={regions['source-layer']}
|
|
|
+ filter={regions.filter as any}
|
|
|
+ style={regions.style}
|
|
|
+ maxZoomLevel={regions.maxzoom}
|
|
|
+ belowLayerID={regions_visited.id}
|
|
|
+ />
|
|
|
+ <MapLibreGL.FillLayer
|
|
|
+ id={regions_visited.id}
|
|
|
+ sourceID={regions_visited.source}
|
|
|
+ sourceLayerID={regions_visited['source-layer']}
|
|
|
+ filter={regionsVisitedFilter as any}
|
|
|
+ style={regions_visited.style}
|
|
|
+ maxZoomLevel={regions_visited.maxzoom}
|
|
|
+ />
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+ {type === 'countries' && (
|
|
|
+ <>
|
|
|
+ <MapLibreGL.FillLayer
|
|
|
+ id={countries.id}
|
|
|
+ sourceID={countries.source}
|
|
|
+ sourceLayerID={countries['source-layer']}
|
|
|
+ filter={countries.filter as any}
|
|
|
+ style={countries.style}
|
|
|
+ maxZoomLevel={countries.maxzoom}
|
|
|
+ belowLayerID={countries_visited.id}
|
|
|
+ />
|
|
|
+ <MapLibreGL.FillLayer
|
|
|
+ id={countries_visited.id}
|
|
|
+ sourceID={countries_visited.source}
|
|
|
+ sourceLayerID={countries_visited['source-layer']}
|
|
|
+ filter={countriesVisitedFilter as any}
|
|
|
+ style={countries_visited.style}
|
|
|
+ maxZoomLevel={countries_visited.maxzoom}
|
|
|
+ />
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+ {type === 'dare' && (
|
|
|
+ <>
|
|
|
+ <MapLibreGL.FillLayer
|
|
|
+ id={dare.id}
|
|
|
+ sourceID={dare.source}
|
|
|
+ sourceLayerID={dare['source-layer']}
|
|
|
+ filter={dare.filter as any}
|
|
|
+ style={dare.style}
|
|
|
+ maxZoomLevel={dare.maxzoom}
|
|
|
+ belowLayerID={dare_visited.id}
|
|
|
+ />
|
|
|
+ <MapLibreGL.FillLayer
|
|
|
+ id={dare_visited.id}
|
|
|
+ sourceID={dare_visited.source}
|
|
|
+ sourceLayerID={dare_visited['source-layer']}
|
|
|
+ filter={dareVisitedFilter as any}
|
|
|
+ style={dare_visited.style}
|
|
|
+ maxZoomLevel={dare_visited.maxzoom}
|
|
|
+ />
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+
|
|
|
+ <MapLibreGL.Camera ref={cameraRef} />
|
|
|
{location && (
|
|
|
- <AnimatedMarker coordinate={location} anchor={{ x: 0.5, y: 0.5 }}>
|
|
|
- <Animation.View style={[styles.location, { borderWidth: strokeWidthAnim }]} />
|
|
|
- </AnimatedMarker>
|
|
|
+ <MapLibreGL.UserLocation
|
|
|
+ animated={true}
|
|
|
+ showsUserHeadingIndicator={true}
|
|
|
+ ></MapLibreGL.UserLocation>
|
|
|
)}
|
|
|
- </MapView>
|
|
|
+ </MapLibreGL.MapView>
|
|
|
|
|
|
{!isExpanded ? (
|
|
|
<TouchableOpacity
|
|
@@ -298,10 +454,9 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
|
|
|
tilesTypes={tilesTypes}
|
|
|
tilesType={tilesType}
|
|
|
setTilesType={setTilesType}
|
|
|
- type={type}
|
|
|
setType={setType}
|
|
|
userId={userId}
|
|
|
- setVisitedTiles={setVisitedTiles}
|
|
|
+ setRegionsFilter={setRegionsFilter}
|
|
|
isPublicView={true}
|
|
|
isLogged={true}
|
|
|
/>
|
|
@@ -328,7 +483,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
|
|
|
setIndex={setIndex}
|
|
|
token={token}
|
|
|
/>
|
|
|
- </View>
|
|
|
+ </SafeAreaView>
|
|
|
);
|
|
|
};
|
|
|
|