Преглед на файлове

top of the tops fix + dropdown filter

Viktoriia преди 1 месец
родител
ревизия
e1e0bf29b8

+ 29 - 7
src/screens/InAppScreens/MapScreen/FilterModal/index.tsx

@@ -240,13 +240,35 @@ const FilterModal = ({
 
   useEffect(() => {
     if (seriesList?.data) {
-      setSeries([
-        ...seriesList.data.map((item) => ({
-          label: item.name,
-          value: item.id,
-          icon: item.icon
-        }))
-      ]);
+      setSeries(
+        seriesList.data.reduce(
+          (acc, item) => {
+            if (item.id < 0) {
+              const alreadyExists = acc.some((i) => i.value === -1);
+              if (alreadyExists) {
+                return acc;
+              }
+
+              acc.push({
+                label: 'Top of the tops',
+                value: -1,
+                icon: item.icon
+              });
+
+              return acc;
+            }
+
+            acc.push({
+              label: item.name,
+              value: item.id,
+              icon: item.icon
+            });
+
+            return acc;
+          },
+          [] as { label: string; value: number; icon: string }[]
+        )
+      );
       !savedFilterSettings && setSelectedSeries(seriesList.data.map((item) => item.id));
     }
   }, [seriesList]);

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

@@ -746,17 +746,38 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
         setSeriesVisitedFilter([
           'all',
           ['any', ...seriesVisited.map((id) => ['==', 'id', id])],
-          ['any', ...seriesFilter.groups.map((groupId: number) => ['==', 'series_id', groupId])]
+          [
+            'any',
+            ...seriesFilter.groups.map((groupId: number) => {
+              if (groupId === -1) {
+                return ['==', 'must', 1];
+              }
+              return ['==', 'series_id', groupId];
+            })
+          ]
         ]);
         setSeriesNotVisitedFilter([
           'all',
           ['all', ...seriesVisited.map((id) => ['!=', 'id', id])],
-          ['any', ...seriesFilter.groups.map((groupId: number) => ['==', 'series_id', groupId])]
+          [
+            'any',
+            ...seriesFilter.groups.map((groupId: number) => {
+              if (groupId === -1) {
+                return ['==', 'must', 1];
+              }
+              return ['==', 'series_id', groupId];
+            })
+          ]
         ]);
       } else {
         setSeriesNotVisitedFilter([
           'any',
-          ...seriesFilter.groups.map((groupId: number) => ['==', 'series_id', groupId])
+          ...seriesFilter.groups.map((groupId: number) => {
+            if (groupId === -1) {
+              return ['==', 'must', 1];
+            }
+            return ['==', 'series_id', groupId];
+          })
         ]);
       }
     } else {
@@ -1811,7 +1832,21 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
                         iconOpacity: 1,
                         iconHaloColor: '#ffffff',
                         iconHaloWidth: 1,
-                        iconHaloBlur: 0.5
+                        iconHaloBlur: 0.5,
+                        textAnchor: 'top',
+                        textField: '{name}',
+                        textFont: ['Noto Sans Regular'],
+                        textMaxWidth: 9,
+                        textOffset: [0, 1.3],
+                        textPadding: 2,
+                        textSize: 12,
+                        textOptional: true,
+                        textIgnorePlacement: false,
+                        textAllowOverlap: false,
+                        textColor: '#666',
+                        textHaloColor: '#ffffff',
+                        textHaloWidth: 1,
+                        textHaloBlur: 0.5
                       }}
                     />
                   );
@@ -1844,7 +1879,21 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
                         iconOpacity: 1,
                         iconHaloColor: '#ffffff',
                         iconHaloWidth: 1,
-                        iconHaloBlur: 0.5
+                        iconHaloBlur: 0.5,
+                        textAnchor: 'top',
+                        textField: '{name}',
+                        textFont: ['Noto Sans Regular'],
+                        textMaxWidth: 9,
+                        textOffset: [0, 1.3],
+                        textPadding: 2,
+                        textSize: 12,
+                        textOptional: true,
+                        textIgnorePlacement: false,
+                        textAllowOverlap: false,
+                        textColor: '#666',
+                        textHaloColor: '#ffffff',
+                        textHaloWidth: 1,
+                        textHaloBlur: 0.5
                       }}
                     />
                   );

+ 190 - 22
src/screens/InAppScreens/ProfileScreen/RegionsRenderer/index.tsx

@@ -1,13 +1,24 @@
-import React, { useCallback, useEffect, useState } from 'react';
-import { View, Text, Image, TouchableOpacity, ScrollView, Dimensions, Switch } from 'react-native';
+import React, { useCallback, useEffect, useRef, useState } from 'react';
+import {
+  View,
+  Text,
+  Image,
+  TouchableOpacity,
+  ScrollView,
+  Dimensions,
+  Switch,
+  Pressable,
+  Animated
+} from 'react-native';
 import { Colors } from 'src/theme';
 import { styles } from './styles';
 import { Loading } from 'src/components';
 
 import CheckSvg from 'assets/icons/mark.svg';
 import CloseSVG from 'assets/icons/close.svg';
+import ArrowIcon from 'assets/icons/mark-to-up.svg';
 import { API_HOST } from 'src/constants';
-import { FlashList } from '@shopify/flash-list';
+import { FlashList, FlashListRef } from '@shopify/flash-list';
 import { useNavigation } from '@react-navigation/native';
 import { NAVIGATION_PAGES } from 'src/types';
 
@@ -49,15 +60,35 @@ const RegionsRenderer = ({
     minimumViewTime: 1000
   };
   const isSmallWidth = Dimensions.get('window').width < 383;
-  const [showAll, setShowAll] = useState(true);
+  const [showAll, setShowAll] = useState<'all' | 'visited' | 'not_visited'>('all');
   const [filteredRegions, setFilteredRegions] = useState<any[]>([]);
   const [filteredMegaregions, setFilteredMegaregions] = useState<any[]>([]);
   const disabled = type === 'whs' || type === 'tcc' ? true : false;
 
+  const [dropdownOpen, setDropdownOpen] = useState(false);
+
+  const animation = useRef(new Animated.Value(0)).current;
+
+  useEffect(() => {
+    Animated.timing(animation, {
+      toValue: dropdownOpen ? 1 : 0,
+      duration: 100,
+      useNativeDriver: true
+    }).start();
+  }, [dropdownOpen]);
+
+  const showAllLabels: Record<'all' | 'visited' | 'not_visited', string> = {
+    all: 'All',
+    visited: 'Visited',
+    not_visited: 'Not Visited'
+  };
+
+  const flashListRef = useRef<FlashListRef<any> | null>(null);
+
   useEffect(() => {
     if (!regions?.data) return;
 
-    if (showAll) {
+    if (showAll === 'all') {
       switch (type) {
         case 'nm':
         case 'mqp':
@@ -74,7 +105,7 @@ const RegionsRenderer = ({
           setFilteredRegions(regions.data);
           break;
       }
-    } else {
+    } else if (showAll === 'visited') {
       switch (type) {
         case 'nm':
         case 'mqp':
@@ -102,6 +133,38 @@ const RegionsRenderer = ({
           setFilteredRegions(regions.data.filter((item: any) => item.visited));
           break;
       }
+    } else {
+      switch (type) {
+        case 'nm':
+        case 'mqp':
+          const filteredMegaregions = regions.data?.megaregions
+            ?.map((item: Mega) => ({
+              ...item,
+              regions: item.regions.filter((region) => !item.visits.includes(region.id)),
+              total: item.regions.length
+            }))
+            ?.filter((item: Mega) => item.regions.length > 0);
+
+          setFilteredMegaregions(filteredMegaregions);
+          break;
+        case 'un':
+        case 'unp':
+        case 'tcc':
+          setFilteredRegions(
+            regions.data[0].filter((item: Region) => !regions.data[1].includes(item.name))
+          );
+          break;
+        case 'slow':
+          setFilteredRegions(regions.data[0].filter((item: any) => item.visited === 0));
+          break;
+        case 'whs':
+          setFilteredRegions(regions.data.filter((item: any) => !item.visited));
+          break;
+      }
+    }
+
+    if (flashListRef && flashListRef.current) {
+      flashListRef.current?.scrollToOffset({ offset: 0, animated: false });
     }
   }, [showAll, regions]);
 
@@ -337,17 +400,122 @@ const RegionsRenderer = ({
         onRequestClose={() => setIsModalVisible(false)}
         rightElement={
           rightElement ? (
-            <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
-              <Text style={{ color: Colors.DARK_BLUE, fontSize: 12, fontWeight: '600' }}>
-                Show/Hide All
-              </Text>
-              <Switch
-                trackColor={{ false: Colors.LIGHT_GRAY, true: Colors.DARK_BLUE }}
-                thumbColor={Colors.WHITE}
-                onValueChange={() => setShowAll(!showAll)}
-                value={showAll}
-                style={{ transform: 'scale(0.8)' }}
-              />
+            <View style={{ alignItems: 'flex-end' }}>
+              <Pressable
+                onPress={() => setDropdownOpen((prev) => !prev)}
+                style={{
+                  paddingHorizontal: 10,
+                  paddingVertical: 6,
+                  borderRadius: 6,
+                  borderWidth: 1,
+                  borderColor: Colors.BORDER_LIGHT,
+                  flexDirection: 'row',
+                  alignItems: 'center',
+                  justifyContent: 'space-between',
+                  minWidth: 110
+                }}
+              >
+                <Text
+                  style={{
+                    fontSize: 12,
+                    fontWeight: '600',
+                    color: Colors.DARK_BLUE,
+                    marginRight: 6
+                  }}
+                >
+                  {showAllLabels[showAll]}
+                </Text>
+
+                <Animated.View
+                  style={{
+                    transform: [
+                      {
+                        rotate: animation.interpolate({
+                          inputRange: [0, 1],
+                          outputRange: ['0deg', '180deg']
+                        })
+                      }
+                    ]
+                  }}
+                >
+                  <ArrowIcon height={12} width={12} stroke={Colors.BORDER_LIGHT} />
+                </Animated.View>
+              </Pressable>
+
+              {dropdownOpen && (
+                <Pressable
+                  onPress={() => setDropdownOpen(false)}
+                  style={{
+                    position: 'absolute',
+                    top: -1000,
+                    left: -1000,
+                    right: -1000,
+                    bottom: -1000,
+                    zIndex: 1
+                  }}
+                />
+              )}
+
+              {dropdownOpen && (
+                <Animated.View
+                  pointerEvents="auto"
+                  style={{
+                    position: 'absolute',
+                    top: 36,
+                    right: 0,
+                    backgroundColor: Colors.WHITE,
+                    borderRadius: 8,
+                    paddingVertical: 6,
+                    minWidth: 150,
+                    borderWidth: 1,
+                    borderColor: Colors.BORDER_LIGHT,
+                    opacity: animation,
+                    zIndex: 1000,
+                    elevation: 10,
+                    transform: [
+                      {
+                        translateY: animation.interpolate({
+                          inputRange: [0, 1],
+                          outputRange: [-6, 0]
+                        })
+                      }
+                    ]
+                  }}
+                >
+                  {(Object.keys(showAllLabels) as Array<'all' | 'visited' | 'not_visited'>).map(
+                    (key) => (
+                      <Pressable
+                        key={key}
+                        onPress={() => {
+                          setShowAll(key);
+                          setDropdownOpen(false);
+                        }}
+                        style={{
+                          paddingVertical: 10,
+                          paddingHorizontal: 14,
+                          flexDirection: 'row',
+                          alignItems: 'center',
+                          justifyContent: 'space-between'
+                        }}
+                      >
+                        <Text
+                          style={{
+                            fontSize: 13,
+                            color: Colors.DARK_BLUE,
+                            fontWeight: '600'
+                          }}
+                        >
+                          {showAllLabels[key]}
+                        </Text>
+
+                        {showAll === key && (
+                          <CheckSvg fill={Colors.DARK_BLUE} height={12} width={12} />
+                        )}
+                      </Pressable>
+                    )
+                  )}
+                </Animated.View>
+              )}
             </View>
           ) : null
         }
@@ -361,8 +529,8 @@ const RegionsRenderer = ({
           <>
             {renderHeader(type === 'nm' ? 'NM' : 'DARE', true)}
             <FlashList
+              ref={flashListRef}
               viewabilityConfig={flashlistConfig}
-              estimatedItemSize={4000}
               data={filteredMegaregions}
               renderItem={renderMegaregion}
               keyExtractor={(megaregion) => megaregion?.id?.toString()}
@@ -379,8 +547,8 @@ const RegionsRenderer = ({
           <>
             {renderHeader(type === 'un' ? 'UN' : type === 'unp' ? 'UN+' : 'TCC', true)}
             <FlashList
+              ref={flashListRef}
               viewabilityConfig={flashlistConfig}
-              estimatedItemSize={50}
               data={filteredRegions}
               renderItem={(region) => renderRegion(region.item)}
               keyExtractor={(region: Region) => region.name}
@@ -406,8 +574,8 @@ const RegionsRenderer = ({
           <>
             {renderHeader('SLOW', true)}
             <FlashList
+              ref={flashListRef}
               viewabilityConfig={flashlistConfig}
-              estimatedItemSize={50}
               data={filteredRegions}
               renderItem={(region) => renderRegion(region.item)}
               keyExtractor={(region: Region) => region.country_id?.toString()}
@@ -457,8 +625,8 @@ const RegionsRenderer = ({
           <>
             {renderHeader('YES', false)}
             <FlashList
+              ref={flashListRef}
               viewabilityConfig={flashlistConfig}
-              estimatedItemSize={50}
               data={regions.data[0]}
               renderItem={(region) => renderRegion(region.item)}
               keyExtractor={(region: Region) => region.id?.toString()}
@@ -499,8 +667,8 @@ const RegionsRenderer = ({
           <>
             {renderHeader('WHS', true)}
             <FlashList
+              ref={flashListRef}
               viewabilityConfig={flashlistConfig}
-              estimatedItemSize={50}
               data={filteredRegions}
               renderItem={(region) => renderRegion(region.item)}
               keyExtractor={(region: Region) => region.name}