Pārlūkot izejas kodu

statistics screen

Viktoriia 1 gadu atpakaļ
vecāks
revīzija
c220808944

+ 4 - 0
src/database/statisticsService/index.ts

@@ -127,6 +127,10 @@ export interface Type3 {
   first_name: string;
   last_name: string;
   flag: string;
+  score?: string;
+  nation?: string;
+  user_count?: number;
+  visited_regions?: number;
 }
 
 export interface Type4 {

+ 1 - 1
src/screens/InAppScreens/TravellersScreen/Components/SeriesItem.tsx

@@ -25,7 +25,7 @@ export const SeriesItem: FC<Props> = (data) => {
         {data.index + 1}
       </Text>
       <View style={ItemStyles.contentWrapper}>
-        <View style={{ width: '85%' }}>
+        <View style={{ width: '85%', gap: 4 }}>
           {flags && (
             <View style={ItemStyles.withFlagWrapper}>
               {flags.map((flag, index) => (

+ 48 - 19
src/screens/InAppScreens/TravellersScreen/Components/StatisticRouter.tsx

@@ -1,4 +1,4 @@
-import React, { FC } from 'react';
+import React, { FC, useEffect } from 'react';
 import { Image } from 'expo-image';
 import { Text, View } from 'react-native';
 
@@ -23,75 +23,103 @@ import { API_HOST } from '../../../../constants';
 import { ItemStyles } from './styles';
 
 type Props = {
-  index: number;
+  blockIndex: number;
   item: ListType;
   ranking: any;
+  idx: number;
 };
 
-export const RenderItem: FC<Props> = ({ item, index, ranking }) => {
+export const RenderItem: FC<Props> = ({ item, blockIndex, ranking, idx }) => {
   if (isType1(item) || isType4(item) || isType5(item)) {
     return (
       <>
         {'megaregion' in item && (
           <View>
-            {item.megaregion !== ranking[index - 1]?.megaregion ? (
+            {item.megaregion !== ranking[idx - 1]?.megaregion ? (
               <Text style={[ItemStyles.nameAndCnt, ItemStyles.headerWrapper]}>
                 {item.megaregion as string}
               </Text>
             ) : null}
           </View>
         )}
-        <StatisticItem cnt={item.cnt} flag={item.flag} index={index} name={item.region_name} />
+        <StatisticItem cnt={item.cnt} flag={item.flag} index={blockIndex} name={item.region_name} />
       </>
     );
   } else if (isType6(item)) {
     return (
       <>
         <View>
-          {item.country !== ranking[index - 1]?.country ? (
-            <Text style={[ItemStyles.nameAndCnt, ItemStyles.headerWrapper]}>{item.country}</Text>
+          {item.country !== ranking[idx - 1]?.country ? (
+            <View style={ItemStyles.headerContainer}>
+              <Image
+                style={ItemStyles.flagSquare}
+                source={{ uri: API_HOST + '/img/flags_new/' + item.country_flag }}
+              />
+              <Text
+                style={[
+                  ItemStyles.nameAndCnt,
+                  { fontSize: getFontSize(14), textAlign: 'center', textTransform: 'uppercase' }
+                ]}
+              >
+                {item.country}
+              </Text>
+            </View>
           ) : null}
         </View>
-        <StatisticItem cnt={item.cnt} flag={item.region_flag} index={index} name={item.region} />
+        <StatisticItem
+          cnt={item.cnt}
+          flag={item.region_flag}
+          index={blockIndex}
+          name={item.region}
+        />
       </>
     );
   } else if (isType2(item)) {
     return (
       <>
         <View>
-          {item.mega_name !== ranking[index - 1]?.mega_name ? (
+          {item.mega_name !== ranking[idx - 1]?.mega_name ? (
             <Text style={[ItemStyles.nameAndCnt, ItemStyles.headerWrapper]}>
               {item.mega_name as string}
             </Text>
           ) : null}
         </View>
-        <StatisticItem name={item.dare_name} flag={item.dare_flag} cnt={item.cnt} index={index} />
+        <StatisticItem
+          name={item.dare_name}
+          flag={item.dare_flag}
+          cnt={item.cnt}
+          index={blockIndex}
+        />
       </>
     );
   } else if (isType7(item)) {
-    return <StatisticItem name={item.country_name} flag={item.flag} cnt={item.cnt} index={index} />;
+    return (
+      <StatisticItem name={item.country_name} flag={item.flag} cnt={item.cnt} index={blockIndex} />
+    );
   } else if (isType8(item)) {
     return (
       <>
         <View>
-          {item.mega_name !== ranking[index - 1]?.mega_name ? (
+          {item.mega_name !== ranking[idx - 1]?.mega_name ? (
             <Text style={[ItemStyles.nameAndCnt, ItemStyles.headerWrapper]}>
               {item.mega_name as string}
             </Text>
           ) : null}
-          <StatisticItem name={item.country} flag={item.flag} cnt={item.cnt} index={index} />
+          <StatisticItem name={item.country} flag={item.flag} cnt={item.cnt} index={blockIndex} />
         </View>
       </>
     );
   } else if (isType9(item)) {
-    return <StatisticItem name={item.nation} flag={item.flag} cnt={item.score} index={index} />;
+    return (
+      <StatisticItem name={item.nation} flag={item.flag} cnt={item.score} index={blockIndex} />
+    );
   } else if (isType10(item)) {
     return (
       <>
-        {item.series_name !== ranking[index - 1]?.series_name ? (
+        {item.series_name !== ranking[idx - 1]?.series_name ? (
           <View style={ItemStyles.headerSeriesWrapper}>
             <Image
-              key={index}
+              key={idx}
               style={{ width: 20, height: 20, borderRadius: 10 }}
               source={{ uri: API_HOST + '/static/img/series/' + item.series_icon }}
             />
@@ -104,19 +132,20 @@ export const RenderItem: FC<Props> = ({ item, index, ranking }) => {
           name={item.item_name}
           flags={item.flags as unknown as string}
           cnt={item.cnt}
-          index={index}
+          index={blockIndex}
         />
       </>
     );
-  } else
+  } else {
     return (
       <>
         <StatisticItem
           name={item.nation ? item.nation : `${item.first_name} ${item.last_name}`}
           flag={item.flag}
           cnt={item.score ? item.score : item.cnt}
-          index={index}
+          index={blockIndex}
         />
       </>
     );
+  }
 };

+ 24 - 7
src/screens/InAppScreens/TravellersScreen/Components/styles.ts

@@ -218,12 +218,14 @@ export const ItemStyles = StyleSheet.create({
     display: 'flex',
     flexDirection: 'row',
     justifyContent: 'space-between',
-    alignItems: 'center'
+    alignItems: 'center',
   },
   bigFlag: {
     width: 36,
     height: 36,
-    borderRadius: 18
+    borderRadius: 18,
+    borderWidth: 1,
+    borderColor: Colors.DARK_LIGHT
   },
   withFlagWrapper: {
     display: 'flex',
@@ -232,7 +234,22 @@ export const ItemStyles = StyleSheet.create({
   smallFlag: {
     width: 20,
     height: 20,
-    borderRadius: 10
+    borderRadius: 10,
+    borderWidth: 1,
+    borderColor: Colors.DARK_LIGHT
+  },
+  flagSquare: {
+    width: 20,
+    height: 20,
+    resizeMode: 'contain'
+  },
+  headerContainer: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    gap: 4,
+    justifyContent: 'center',
+    paddingBottom: 16,
+    paddingTop: 8
   },
   nameAndCnt: {
     color: Colors.DARK_BLUE,
@@ -240,8 +257,8 @@ export const ItemStyles = StyleSheet.create({
   },
   headerWrapper: {
     fontSize: getFontSize(14),
-    marginTop: 10,
-    marginBottom: 15,
+    paddingBottom: 16,
+    paddingTop: 8,
     textAlign: 'center'
   },
   headerSeriesWrapper: {
@@ -251,11 +268,11 @@ export const ItemStyles = StyleSheet.create({
     flexDirection: 'row',
     gap: 10,
     marginTop: 10,
-    marginBottom: 15
+    marginBottom: 16
   },
   comment: {
     color: Colors.DARK_BLUE,
     textAlign: 'center',
-    marginBottom: 15
+    marginBottom: 8,
   }
 });

+ 73 - 27
src/screens/InAppScreens/TravellersScreen/StatisticsListScreen/index.tsx

@@ -1,4 +1,4 @@
-import React, { FC, useCallback, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
 import { FlatList, Text } from 'react-native';
 import { useFocusEffect } from '@react-navigation/native';
 
@@ -6,54 +6,100 @@ import { RenderItem } from '../Components/StatisticRouter';
 
 import { Header, Loading, PageWrapper } from '../../../../components';
 import { getStatistic, StatisticType } from '../../../../database/statisticsService';
+import { isType1, isType5, isType7 } from '../StatisticsListScreen/funcs';
 
 import { ItemStyles } from '../Components/styles';
 
-type Props = {
-  route: { params: { title: string; url1: string; url2: string | null } };
-};
-
-//TODO: For future fix types
-
-const StatisticsListScreen: FC<Props> = ({ route }) => {
+const StatisticsListScreen = ({ route }: { route: any }) => {
   const title = route.params.title;
   const url1 = route.params.url1;
   const url2 = route.params.url2;
 
   const [isLoading, setIsLoading] = useState(true);
   const [statistic, setStatistic] = useState<StatisticType | null>(null);
+  const [blockIndexes, setBlockIndexes] = useState<{ [key: number]: number }>({});
 
   useFocusEffect(
     useCallback(() => {
-      function fetchStatistic() {
-        const data = getStatistic(url1, url2);
+      if (!statistic) return;
+      setIsLoading(false);
+    }, [statistic])
+  );
+
+  useEffect(() => {
+    function fetchStatistic() {
+      const data = getStatistic(url1, url2);
+
+      if (!data) return;
 
-        if (!data) return;
+      setStatistic(JSON.parse(data as unknown as string) as unknown as StatisticType);
+    }
 
-        setStatistic(JSON.parse(data as unknown as string) as unknown as StatisticType);
-        setIsLoading(false);
+    fetchStatistic();
+  }, [url1]);
+
+  const calculateBlockIndexes = (ranking: any) => {
+    let indexes: { [key: number]: number } = {};
+    let currentBlockIdentifier = '';
+    let indexInBlock = isType1(ranking[0]) || isType5(ranking[0]) || isType7(ranking[0]) ? -1 : 0;
+
+    ranking.forEach((item: any, index: number) => {
+      const blockIdentifier = item.megaregion || item.mega_name || item.series_name || item.country;
+
+      if (isType1(item) || isType5(item) || isType7(item)) {
+        indexInBlock++;
+      } else if (blockIdentifier !== currentBlockIdentifier) {
+        currentBlockIdentifier = blockIdentifier;
+        indexInBlock = 0;
+      } else {
+        indexInBlock++;
       }
 
-      fetchStatistic();
-    }, [url1])
-  );
+      indexes[index] = indexInBlock;
+    });
+
+    return indexes;
+  };
+
+  useEffect(() => {
+    if (statistic?.ranking) {
+      setBlockIndexes(calculateBlockIndexes(statistic.ranking));
+    }
+  }, [statistic]);
 
   if (isLoading) return <Loading />;
   if (!statistic) return null;
 
   return (
-    <PageWrapper>
+    <PageWrapper style={{ flex: 1 }}>
       <Header label={title} />
-      {statistic.comment && <Text style={ItemStyles.comment}>{statistic.comment}</Text>}
-      <FlatList
-        contentContainerStyle={{ gap: 10 }}
-        horizontal={false}
-        data={statistic.ranking}
-        renderItem={({ item, index }) => (
-          <RenderItem item={item} index={index} ranking={statistic?.ranking} />
-        )}
-        showsVerticalScrollIndicator={false}
-      />
+      {statistic.ranking ? (
+        <FlatList
+          contentContainerStyle={{ gap: 20, paddingBottom: 16 }}
+          style={{ flex: 1 }}
+          horizontal={false}
+          data={statistic.ranking as any}
+          keyExtractor={(item, index) => index.toString()}
+          initialNumToRender={20}
+          renderItem={({ item, index }) => {
+            const blockIndex: number = blockIndexes[index];
+            return (
+              <RenderItem
+                item={item}
+                blockIndex={blockIndex}
+                idx={index}
+                ranking={statistic?.ranking}
+              />
+            );
+          }}
+          showsVerticalScrollIndicator={false}
+          ListHeaderComponent={
+            statistic.comment ? (
+              <Text style={ItemStyles.comment}>{statistic.comment.replaceAll('<br/>', '\n')}</Text>
+            ) : null
+          }
+        />
+      ) : null}
     </PageWrapper>
   );
 };

+ 28 - 41
src/screens/InAppScreens/TravellersScreen/StatisticsScreen/index.tsx

@@ -5,12 +5,11 @@ import { Header, Loading, PageWrapper } from '../../../../components';
 import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
 import { useNavigation } from '@react-navigation/native';
 import { NAVIGATION_PAGES } from '../../../../types';
-import { Colors } from '../../../../theme';
 import ArrowIcon from '../../../../../assets/icons/mark-to-up.svg';
-import { getFontSize } from '../../../../utils';
+import { styles } from './styles';
 
 const StatisticsScreen = () => {
-  const [index, setIndex] = useState(null);
+  const [index, setIndex] = useState<number | null>(null);
   const [routes, setRoutes] = useState<{ key: string; title: string; list?: ListData[] }[]>([]);
   const [loading, setLoading] = useState(true);
 
@@ -37,7 +36,7 @@ const StatisticsScreen = () => {
       <PageWrapper>
         <ScrollView showsVerticalScrollIndicator={false}>
           <Header label={'Statistics'} />
-          <View style={{ gap: 20 }}>
+          <View style={{ gap: 8 }}>
             {routes.map((route, i) => {
               let name = route.title;
               let subname: string = '';
@@ -52,33 +51,14 @@ const StatisticsScreen = () => {
               }
 
               return (
-                <View>
+                <View key={i}>
                   <TouchableOpacity
                     onPress={() => setIndex(index === i ? null : i)}
-                    style={{
-                      display: 'flex',
-                      flexDirection: 'row',
-                      justifyContent: 'space-between',
-                      alignItems: 'center'
-                    }}
+                    style={styles.itemContainer}
                   >
                     <View>
-                      <Text
-                        style={[
-                          {
-                            fontSize: getFontSize(16),
-                            color: 'rgba(237, 147, 52, 1)',
-                            fontWeight: '700'
-                          }
-                        ]}
-                      >
-                        {route.title && name}
-                      </Text>
-                      {subname && (
-                        <Text style={{ color: Colors.DARK_BLUE, fontWeight: '700' }}>
-                          {subname}
-                        </Text>
-                      )}
+                      <Text style={styles.title}>{route.title && name}</Text>
+                      {subname && <Text style={styles.subtitle}>{subname}</Text>}
                     </View>
                     <View style={index === i ? { transform: 'rotate(180deg)' } : {}}>
                       <ArrowIcon height={18} width={18} stroke={'#B7C6CB'} />
@@ -89,25 +69,32 @@ const StatisticsScreen = () => {
                       {route.list?.map((item, index) => {
                         return (
                           <TouchableOpacity
+                            key={index}
                             onPress={() =>
-                              navigation.navigate(...([NAVIGATION_PAGES.STATISTICS_LIST_DATA, {
-                                title: item.name,
-                                type: route.title,
-                                url1: item.url1,
-                                url2: item.url2
-                              }] as never))
+                              navigation.navigate(
+                                ...([
+                                  NAVIGATION_PAGES.STATISTICS_LIST_DATA,
+                                  {
+                                    title: item.name,
+                                    type: route.title,
+                                    url1: item.url1,
+                                    url2: item.url2
+                                  }
+                                ] as never)
+                              )
                             }
+                            style={styles.listItem}
                           >
+                            <Text style={styles.subtitle}>• </Text>
                             <Text
-                              style={{
-                                marginTop: 12,
-                                marginBottom: 5,
-                                marginLeft: 10,
-                                color: Colors.DARK_BLUE,
-                                fontWeight: '700'
-                              }}
+                              style={[
+                                styles.subtitle,
+                                {
+                                  flexShrink: 1
+                                }
+                              ]}
                             >
-                              {item.name}
+                              {item.name}
                             </Text>
                           </TouchableOpacity>
                         );

+ 25 - 0
src/screens/InAppScreens/TravellersScreen/StatisticsScreen/styles.tsx

@@ -0,0 +1,25 @@
+import { Colors } from '../../../../theme';
+import { StyleSheet } from 'react-native';
+import { getFontSize } from '../../../../utils';
+
+export const styles = StyleSheet.create({
+  itemContainer: {
+    display: 'flex',
+    flexDirection: 'row',
+    justifyContent: 'space-between',
+    alignItems: 'center',
+    paddingVertical: 8,
+    paddingRight: 12
+  },
+  title: {
+    fontSize: getFontSize(16),
+    color: 'rgba(237, 147, 52, 1)',
+    fontWeight: '700'
+  },
+  subtitle: { color: Colors.DARK_BLUE, fontWeight: '700' },
+  listItem: {
+    paddingHorizontal: 12,
+    paddingVertical: 8,
+    flexDirection: 'row'
+  }
+});