Viktoriia 1 年間 前
コミット
32002dc5ec

+ 104 - 0
src/screens/InAppScreens/MapScreen/FilterModal/index.tsx

@@ -0,0 +1,104 @@
+import React from 'react';
+import { View, Text, TouchableOpacity } from 'react-native';
+import Modal from 'react-native-modal';
+import CloseSvg from 'assets/icons/close.svg';
+import FilterIcon from 'assets/icons/filter.svg';
+import { Colors } from 'src/theme';
+import { ModalStyles } from '../../TravellersScreen/Components/styles';
+import { Dropdown } from 'react-native-searchable-dropdown-kj';
+import { Button } from 'src/components';
+import { ButtonVariants } from 'src/types/components';
+
+const FilterModal = ({
+  isFilterVisible,
+  setIsFilterVisible,
+  tilesTypes,
+  tilesType,
+  setTilesType,
+  type,
+  setType
+}: {
+  isFilterVisible: boolean;
+  setIsFilterVisible: (isVisible: boolean) => void;
+  tilesTypes: any[];
+  tilesType: any;
+  setTilesType: (item: any) => void;
+  type: number;
+  setType: (type: any) => void;
+}) => {
+  return (
+    <Modal isVisible={isFilterVisible}>
+      <View style={{ backgroundColor: 'white', borderRadius: 15 }}>
+        <View style={{ marginHorizontal: '5%', marginTop: '5%', marginBottom: '10%' }}>
+          <View style={{ alignSelf: 'flex-end' }}>
+            <TouchableOpacity
+              onPress={() => {
+                type === 0
+                  ? setTilesType({ label: 'NM regions', value: 0 })
+                  : setTilesType({ label: 'UN countries', value: 1 });
+                setIsFilterVisible(!isFilterVisible);
+              }}
+            >
+              <CloseSvg fill={Colors.LIGHT_GRAY} />
+            </TouchableOpacity>
+          </View>
+          <View style={{ display: 'flex', alignItems: 'center' }}>
+            <Text style={{ color: Colors.DARK_BLUE, fontSize: 20, fontWeight: '700' }}>
+              Map filter
+            </Text>
+            <View style={[ModalStyles.ageAndRankingWrapper, { marginTop: 24 }]}>
+              <Dropdown
+                style={[ModalStyles.dropdown, { width: '100%' }]}
+                placeholderStyle={ModalStyles.placeholderStyle}
+                selectedTextStyle={ModalStyles.selectedTextStyle}
+                data={tilesTypes}
+                labelField="label"
+                valueField="value"
+                value={tilesType?.label}
+                placeholder={tilesType?.label}
+                onChange={(item) => {
+                  setTilesType(item);
+                }}
+              />
+            </View>
+            <View style={[ModalStyles.buttonsWrapper, { marginTop: 24 }]}>
+              <Button
+                variant={ButtonVariants.OPACITY}
+                containerStyles={{
+                  borderColor: Colors.DARK_BLUE,
+                  backgroundColor: 'white',
+                  width: '45%'
+                }}
+                textStyles={{
+                  color: Colors.DARK_BLUE
+                }}
+                onPress={() => {
+                  setTilesType({ label: 'NM regions', value: 0 });
+                }}
+                children={'Clear'}
+              />
+              <Button
+                variant={ButtonVariants.FILL}
+                containerStyles={{
+                  borderColor: Colors.DARK_BLUE,
+                  backgroundColor: Colors.DARK_BLUE,
+                  width: '45%'
+                }}
+                textStyles={{
+                  color: Colors.WHITE
+                }}
+                onPress={() => {
+                  setType(tilesType.value);
+                  setIsFilterVisible(false);
+                }}
+                children={'Filter'}
+              />
+            </View>
+          </View>
+        </View>
+      </View>
+    </Modal>
+  );
+};
+
+export default FilterModal;

+ 2 - 2
src/screens/InAppScreens/MapScreen/UniversalSearch/index.tsx

@@ -32,8 +32,8 @@ const SearchModal = ({
   const navigation = useNavigation();
   const [routes] = useState([
     { key: 'users', title: 'Nomads' },
-    { key: 'regions', title: 'Regions' },
-    { key: 'dare', title: 'Places' }
+    { key: 'regions', title: 'NM regions' },
+    { key: 'dare', title: 'DARE places' }
   ]);
   const [shouldOpenModal, setShouldOpenModal] = useState<{ id: number; type: string } | null>(null);
   const [warningVisible, setWarningVisible] = useState(false);

+ 32 - 5
src/screens/InAppScreens/MapScreen/index.tsx

@@ -14,18 +14,20 @@ import * as turf from '@turf/turf';
 import * as FileSystem from 'expo-file-system';
 import * as Location from 'expo-location';
 import { storage, StoreType } from '../../../storage';
+import Modal from 'react-native-modal';
 
 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 FilterIcon from 'assets/icons/filter.svg';
 
 import regions from '../../../../assets/geojson/nm2022.json';
 import jsonData, { fetchJsonData } from '../../../database/geojsonService';
 
 import { getFirstDatabase, getSecondDatabase, refreshDatabases } from '../../../db';
-import { LocationPopup, WarningModal, EditNmModal } from '../../../components';
+import { LocationPopup, WarningModal, EditNmModal, Button } from '../../../components';
 
 import { styles } from './style';
 import {
@@ -55,6 +57,10 @@ import { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reani
 import { Colors } from 'src/theme';
 import { useGetUniversalSearch } from '@api/search';
 import SearchModal from './UniversalSearch';
+import { Dropdown } from 'react-native-searchable-dropdown-kj';
+import { ButtonVariants } from 'src/types/components';
+import { ModalStyles } from '../TravellersScreen/Components/styles';
+import FilterModal from './FilterModal';
 
 const localTileDir = `${FileSystem.cacheDirectory}tiles/background`;
 const localGridDir = `${FileSystem.cacheDirectory}tiles/grid`;
@@ -80,6 +86,7 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
   const { mutate: updateNM } = usePostSetNmRegionMutation();
   const { mutate: updateDARE } = usePostSetDareRegionMutation();
   const visitedTiles = `${FASTEST_MAP_HOST}/tiles_nm/user_visited/${userId}`;
+  const visitedUNTiles = `${FASTEST_MAP_HOST}/tiles_nm/user_visited_un/${userId}`;
   const mapRef = useRef<MapView>(null);
 
   const [isConnected, setIsConnected] = useState<boolean | null>(true);
@@ -109,6 +116,13 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
   const [search, setSearch] = useState('');
   const [searchInput, setSearchInput] = useState('');
   const { data: searchData } = useGetUniversalSearch(search);
+  const [isFilterVisible, setIsFilterVisible] = useState(false);
+  const [tilesType, setTilesType] = useState({ label: 'NM regions', value: 0 });
+  const tilesTypes = [
+    { label: 'NM regions', value: 0 },
+    { label: 'UN countries', value: 1 }
+  ];
+  const [type, setType] = useState(0);
 
   useEffect(() => {
     if (!dareData) {
@@ -707,7 +721,8 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
         {renderedGeoJSON}
         {renderMapTiles(tilesBaseURL, localTileDir, 1)}
         {renderMapTiles(gridUrl, localGridDir, 2)}
-        {userId && renderMapTiles(visitedTiles, localVisitedDir, 2, 0.5)}
+        {userId &&
+          renderMapTiles(type === 1 ? visitedUNTiles : visitedTiles, localVisitedDir, 2, 0.5)}
         {renderMapTiles(dareTiles, localDareDir, 2, 0.5)}
         {location && (
           <AnimatedMarker coordinate={location} anchor={{ x: 0.5, y: 0.5 }}>
@@ -790,6 +805,7 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
                   placeholderTextColor={Colors.LIGHT_GRAY}
                   value={searchInput}
                   onChangeText={(text) => setSearchInput(text)}
+                  onSubmitEditing={handleSearch}
                 />
                 <TouchableOpacity onPress={handleSearch} style={styles.iconButton}>
                   <SearchIcon fill={'#0F3F4F'} />
@@ -802,11 +818,12 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
             )}
           </Animated.View>
 
-          {/* <TouchableOpacity
+          <TouchableOpacity
             style={[styles.cornerButton, styles.bottomButton, styles.bottomLeftButton]}
+            onPress={() => setIsFilterVisible(true)}
           >
-            <RadarIcon />
-          </TouchableOpacity> */}
+            <FilterIcon />
+          </TouchableOpacity>
 
           <TouchableOpacity
             onPress={handleGetLocation}
@@ -837,6 +854,16 @@ const MapScreen: React.FC<MapScreenProps> = ({ navigation }) => {
         setIndex={setIndex}
         token={token}
       />
+      <FilterModal
+        isFilterVisible={isFilterVisible}
+        setIsFilterVisible={setIsFilterVisible}
+        tilesTypes={tilesTypes}
+        tilesType={tilesType}
+        setTilesType={setTilesType}
+        type={type}
+        setType={setType}
+        
+      />
     </View>
   );
 };

+ 6 - 6
src/screens/InAppScreens/ProfileScreen/RegionsRenderer/index.tsx

@@ -302,27 +302,27 @@ const RegionsRenderer = ({
                       <View style={styles.slow}>
                         <Text style={styles.alignCenter}>
                           <Text
-                            style={[styles.megaregionTitle, { fontSize: isSmallWidth ? 10 : 12 }]}
+                            style={[styles.megaregionTitle, { fontSize: 12 }]}
                           >
-                            Slow11
+                            11+
                           </Text>
                         </Text>
                       </View>
                       <View style={styles.slow}>
                         <Text style={styles.alignCenter}>
                           <Text
-                            style={[styles.megaregionTitle, { fontSize: isSmallWidth ? 10 : 12 }]}
+                            style={[styles.megaregionTitle, { fontSize: 12 }]}
                           >
-                            Slow31
+                            31+
                           </Text>
                         </Text>
                       </View>
                       <View style={styles.slow}>
                         <Text style={styles.alignCenter}>
                           <Text
-                            style={[styles.megaregionTitle, { fontSize: isSmallWidth ? 10 : 12 }]}
+                            style={[styles.megaregionTitle, { fontSize: 12 }]}
                           >
-                            Slow101
+                            101+
                           </Text>
                         </Text>
                       </View>

+ 19 - 3
src/screens/InAppScreens/ProfileScreen/UsersMap/index.tsx

@@ -10,6 +10,7 @@ import { Colors } from 'src/theme';
 
 import CloseSvg from 'assets/icons/close.svg';
 import FilterIcon from 'assets/icons/filter.svg';
+import FilterModal from '../../MapScreen/FilterModal';
 
 type Props = {
   navigation: NavigationProp<any>;
@@ -26,7 +27,13 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
   const visitedUNTiles = `${FASTEST_MAP_HOST}/tiles_nm/user_visited_un/${userId}`;
 
   const mapRef = useRef<MapView>(null);
-  const [unVisible, setUnVisible] = useState(false);
+  const [isFilterVisible, setIsFilterVisible] = useState(false);
+  const [tilesType, setTilesType] = useState({ label: 'NM regions', value: 0 });
+  const tilesTypes = [
+    { label: 'NM regions', value: 0 },
+    { label: 'UN countries', value: 1 }
+  ];
+  const [type, setType] = useState(0);
 
   useEffect(() => {
     navigation.getParent()?.setOptions({
@@ -89,7 +96,7 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
       >
         {renderMapTiles(tilesBaseURL, 1)}
         {renderMapTiles(gridUrl, 2)}
-        {userId && renderMapTiles(unVisible ? visitedUNTiles : visitedTiles, 2, 0.5)}
+        {userId && renderMapTiles(type === 1 ? visitedUNTiles : visitedTiles, 2, 0.5)}
       </MapView>
 
       <TouchableOpacity
@@ -113,10 +120,19 @@ const UsersMapScreen: FC<Props> = ({ navigation, route }) => {
       </View>
       <TouchableOpacity
         style={[styles.cornerButton, styles.bottomButton, styles.bottomLeftButton]}
-        onPress={() => setUnVisible(!unVisible)}
+        onPress={() => setIsFilterVisible(true)}
       >
         <FilterIcon />
       </TouchableOpacity>
+      <FilterModal
+        isFilterVisible={isFilterVisible}
+        setIsFilterVisible={setIsFilterVisible}
+        tilesTypes={tilesTypes}
+        tilesType={tilesType}
+        setTilesType={setTilesType}
+        type={type}
+        setType={setType}
+      />
     </View>
   );
 };

+ 116 - 28
src/screens/InAppScreens/TravellersScreen/Components/Profile.tsx

@@ -20,6 +20,7 @@ import TBTIcon from '../../../../../assets/icons/tbt.svg';
 
 import { NAVIGATION_PAGES } from '../../../../types';
 import { useConnection } from 'src/contexts/ConnectionContext';
+import Tooltip from 'react-native-walkthrough-tooltip';
 
 type Props = {
   avatar: string;
@@ -64,6 +65,7 @@ export const Profile: FC<Props> = ({
   const netInfo = useConnection();
   const token = storage.get('token', StoreType.STRING);
   const [modalType, setModalType] = useState<string | null>(null);
+  const [toolTipVisible, setToolTipVisible] = useState<number | string | null>(null);
 
   const handlePress = () => {
     if (!netInfo?.isInternetReachable) {
@@ -99,32 +101,101 @@ export const Profile: FC<Props> = ({
     );
 
     return (
-      <View style={adaptiveStyle(TBTStyles.badgeRoot, {})}>
-        <View style={adaptiveStyle(TBTStyles.badgeWrapper, {})}>
-          {badge_tbt && tbt_rank ? (
-            <Rank color={colors[tbt_rank - 1]} />
-          ) : (
-            <View style={adaptiveStyle(ProfileStyles.badge, {})} />
-          )}
-          {tbt_rank && tbt_rank >= 1 ? (
-            <Text style={adaptiveStyle([ScoreStyles.scoreNameText], {})}>TBT # {tbt_rank}</Text>
-          ) : (
-            <View style={{ height: 11 }} />
-          )}
-        </View>
-      </View>
+      <Tooltip
+        isVisible={toolTipVisible === 'TBT'}
+        content={<Text style={{ color: Colors.DARK_BLUE }}>{handleGetTooltip('TBT')}</Text>}
+        contentStyle={{ backgroundColor: Colors.WHITE }}
+        placement="top"
+        onClose={() => setToolTipVisible(null)}
+        backgroundColor="transparent"
+        allowChildInteraction={false}
+        parentWrapperStyle={adaptiveStyle(TBTStyles.badgeRoot, {})}
+      >
+        <TouchableOpacity
+          style={{ flex: 1 }}
+          disabled={!tbt_rank || tbt_rank < 1}
+          onPress={() => setToolTipVisible('TBT')}
+        >
+          <View style={adaptiveStyle(TBTStyles.badgeWrapper, {})}>
+            {badge_tbt && tbt_rank ? (
+              <Rank color={colors[tbt_rank - 1]} />
+            ) : (
+              <View style={adaptiveStyle(ProfileStyles.badge, {})} />
+            )}
+            {tbt_rank && tbt_rank >= 1 ? (
+              <Text style={adaptiveStyle([ScoreStyles.scoreNameText], {})}>TBT # {tbt_rank}</Text>
+            ) : (
+              <View style={{ height: 11 }} />
+            )}
+          </View>
+        </TouchableOpacity>
+      </Tooltip>
     );
   };
 
-  const EmptyScore: FC<{ scoreName: string }> = ({ scoreName }) => {
+  const EmptyScore: FC<{ scoreName: string; index: number }> = ({ scoreName, index }) => {
     return (
-      <View style={adaptiveStyle(ScoreStyles.scoreWrapper, {})}>
-        <Text style={adaptiveStyle([ScoreStyles.scoreHeaderText], {})}>-</Text>
-        <Text style={adaptiveStyle([ScoreStyles.scoreNameText], {})}>{scoreName}</Text>
-      </View>
+      <Tooltip
+        isVisible={toolTipVisible === index}
+        content={<Text style={{ color: Colors.DARK_BLUE }}>{handleGetTooltip(scoreName)}</Text>}
+        contentStyle={{ backgroundColor: Colors.WHITE }}
+        placement="top"
+        onClose={() => setToolTipVisible(null)}
+        backgroundColor="transparent"
+        allowChildInteraction={false}
+        parentWrapperStyle={adaptiveStyle(ScoreStyles.scoreWrapper, {})}
+      >
+        <TouchableOpacity onPress={() => setToolTipVisible(index)}>
+          <Text style={adaptiveStyle([ScoreStyles.scoreHeaderText], {})}>-</Text>
+          <Text style={adaptiveStyle([ScoreStyles.scoreNameText], {})}>{scoreName}</Text>
+        </TouchableOpacity>
+      </Tooltip>
     );
   };
 
+  const handleGetTooltip = (score: string) => {
+    switch (score) {
+      case 'NM':
+        return 'NM list of regions';
+      case 'DARE':
+        return 'DARE list of places';
+      case 'UN':
+        return 'UN countries';
+      case 'UN+':
+        return 'UN countries and territories';
+      case 'TCC':
+        return (
+          <Text>
+            <Text style={{ fontStyle: 'italic' }}>Travelers’ Century Club</Text> list
+          </Text>
+        );
+      case 'DEEP':
+        return (
+          <Text>
+            <Text style={{ fontWeight: '700' }}>D</Text>efinite{' '}
+            <Text style={{ fontWeight: '700' }}>E</Text>xhaustive{'\n'}
+            <Text style={{ fontWeight: '700' }}>E</Text>xploring{' '}
+            <Text style={{ fontWeight: '700' }}>P</Text>
+            roportion{'\n'}NM regions to UN countries ratio{'\n'}
+            <Text style={{ fontSize: 11 }}>
+              For travellers with minimum 30 UN countries visited.
+            </Text>
+          </Text>
+        );
+      case 'YES':
+        return 'Years Elapsed Since list';
+      case 'SLOW':
+        return 'SLOW travel list';
+      case 'WHS':
+        return 'World Heritage Sites';
+      case 'KYE':
+        return 'Know Your Earth geographic based list';
+      case 'TBT':
+        return 'The Biggest Travellers based on all rankings';
+    }
+    return score;
+  };
+
   return (
     <View style={ProfileStyles.wrapper}>
       <View
@@ -206,17 +277,34 @@ export const Profile: FC<Props> = ({
           <View style={adaptiveStyle(ScoreStyles.rankingScoresWrapper, {})}>
             {score.map((number, index) => {
               if (scoreNames[index] === 'SLOW' && number >= 4500)
-                return <EmptyScore key={index} scoreName={scoreNames[index]} />;
+                return <EmptyScore key={index} scoreName={scoreNames[index]} index={index} />;
               if (scoreNames[index] === 'YES' && number >= 10000)
-                return <EmptyScore key={index} scoreName={scoreNames[index]} />;
-              if (!number) return <EmptyScore key={index} scoreName={scoreNames[index]} />;
+                return <EmptyScore key={index} scoreName={scoreNames[index]} index={index} />;
+              if (!number)
+                return <EmptyScore key={index} scoreName={scoreNames[index]} index={index} />;
               return (
-                <View key={index} style={adaptiveStyle(ScoreStyles.scoreWrapper, {})}>
-                  <Text style={adaptiveStyle([ScoreStyles.scoreHeaderText], {})}>{number}</Text>
-                  <Text style={adaptiveStyle([ScoreStyles.scoreNameText], {})}>
-                    {scoreNames[index]}
-                  </Text>
-                </View>
+                <Tooltip
+                  isVisible={toolTipVisible === index}
+                  content={
+                    <Text style={{ color: Colors.DARK_BLUE }}>
+                      {handleGetTooltip(scoreNames[index])}
+                    </Text>
+                  }
+                  contentStyle={{ backgroundColor: Colors.WHITE }}
+                  placement="top"
+                  onClose={() => setToolTipVisible(null)}
+                  backgroundColor="transparent"
+                  allowChildInteraction={false}
+                  key={index}
+                  parentWrapperStyle={adaptiveStyle(ScoreStyles.scoreWrapper, {})}
+                >
+                  <TouchableOpacity onPress={() => setToolTipVisible(index)}>
+                    <Text style={adaptiveStyle([ScoreStyles.scoreHeaderText], {})}>{number}</Text>
+                    <Text style={adaptiveStyle([ScoreStyles.scoreNameText], {})}>
+                      {scoreNames[index]}
+                    </Text>
+                  </TouchableOpacity>
+                </Tooltip>
               );
             })}
           </View>

+ 3 - 3
src/screens/InAppScreens/TravelsScreen/Components/CountryItem/index.tsx

@@ -43,15 +43,15 @@ const CountryItem = React.memo(
           <View style={styles.durationContainer}>
             <View style={styles.durationItem}>
               {renderDurationIcon(item.slow11)}
-              <Text style={styles.visitDuration}>11 days</Text>
+              <Text style={styles.visitDuration}>11+ days</Text>
             </View>
             <View style={styles.durationItem}>
               {renderDurationIcon(item.slow31)}
-              <Text style={styles.visitDuration}>31 days</Text>
+              <Text style={styles.visitDuration}>31+ days</Text>
             </View>
             <View style={styles.durationItem}>
               {renderDurationIcon(item.slow101)}
-              <Text style={styles.visitDuration}>101 days</Text>
+              <Text style={styles.visitDuration}>101+ days</Text>
             </View>
           </View>
 

+ 1 - 1
src/screens/InAppScreens/TravelsScreen/Components/CountryItem/styles.tsx

@@ -35,7 +35,7 @@ export const styles = StyleSheet.create({
     justifyContent: 'center',
     gap: 8,
     flexWrap: 'wrap',
-    flex: 1
+    flex: Dimensions.get('window').width < 385 ? 1 : 0
   },
   durationItem: {
     alignItems: 'center',

+ 3 - 3
src/screens/InAppScreens/TravelsScreen/Components/EditSlowModal/index.tsx

@@ -57,7 +57,7 @@ const EditModal = ({
               color={'#0F3F4F'}
               disabled={isEnabled31 || isEnabled101}
             />
-            <Text style={styles.optionText}>11 days</Text>
+            <Text style={styles.optionText}>11+ days</Text>
           </TouchableOpacity>
           <TouchableOpacity
             disabled={!isEnabled11 || isEnabled101}
@@ -76,7 +76,7 @@ const EditModal = ({
               color={'#0F3F4F'}
               disabled={!isEnabled11 || isEnabled101}
             />
-            <Text style={styles.optionText}>31 days</Text>
+            <Text style={styles.optionText}>31+ days</Text>
           </TouchableOpacity>
           <TouchableOpacity
             disabled={!isEnabled11 || !isEnabled31}
@@ -89,7 +89,7 @@ const EditModal = ({
               color={'#0F3F4F'}
               disabled={!isEnabled11 || !isEnabled31}
             />
-            <Text style={styles.optionText}>101 days</Text>
+            <Text style={styles.optionText}>101+ days</Text>
           </TouchableOpacity>
         </View>
         <Button