Bläddra i källkod

paginated LPI ranking

Viktoriia 3 veckor sedan
förälder
incheckning
dbb6c5c895

+ 3 - 6
src/database/index.ts

@@ -1,6 +1,6 @@
 import NetInfo, { NetInfoState } from '@react-native-community/netinfo';
 import { StoreType, storage } from 'src/storage';
-import { fetchLpi, fetchInHistory, fetchInMemoriam, updateMaster } from '@api/ranking';
+import { fetchInHistory, fetchInMemoriam, updateMaster, updateLpi } from '@api/ranking';
 import { initOfflineSetup, updateMapsCache } from './tilesService';
 import { downloadFlags } from './flagsService';
 import { fetchAndSaveAllTypesAndMasters } from './unMastersService';
@@ -55,11 +55,8 @@ export const updateMasterRanking = async () => {
   await updateMaster();
   storage.remove('masterRanking');
 
-  const dataLpi = await fetchLpi();
-
-  if (dataLpi && dataLpi?.data) {
-    storage.set('lpiRanking', JSON.stringify(dataLpi.data));
-  }
+  await updateLpi();
+  storage.remove('lpiRanking');
 
   const dataInHistory = await fetchInHistory();
 

+ 1 - 0
src/modules/api/ranking/queries/index.ts

@@ -8,3 +8,4 @@ export * from './use-post-get-countries-ranking-lpi';
 export * from './use-post-get-countries-ranking-memoriam';
 export * from './use-post-get-master';
 export * from './use-post-update-master';
+export * from './use-post-update-lpi';

+ 34 - 17
src/modules/api/ranking/queries/use-post-get-lpi.tsx

@@ -1,21 +1,38 @@
+import { useQuery } from '@tanstack/react-query';
+
 import { rankingQueryKeys } from '../ranking-query-keys';
-import { type PostGetRanking, rankingApi } from '../ranking-api';
-import { queryClient } from 'src/utils/queryClient';
+import { type PostGetMaster, rankingApi } from '../ranking-api';
+
+import type { BaseAxiosError } from '../../../../types';
+import { storage, StoreType } from 'src/storage';
+
+export const useGetLpi = (
+  country: string,
+  age: number,
+  ranking: string,
+  rows: number,
+  page: number,
+  enabled: boolean
+) => {
+  const storageKey = `lpiRanking-${page}`;
+  const fallbackData = storage.get(storageKey, StoreType.STRING) as string;
+  const initialData = fallbackData ? (JSON.parse(fallbackData) as PostGetMaster) : undefined;
+
+  return useQuery<PostGetMaster, BaseAxiosError>({
+    queryKey: rankingQueryKeys.getLpi(country, age, ranking, rows, page),
+    queryFn: async () => {
+      const response = await rankingApi.getLpi(country, age, ranking, rows, page);
+      const result = response.data;
 
-export const fetchLpi = async () => {
-  try {
-    const data: PostGetRanking = await queryClient.fetchQuery({
-      queryKey: rankingQueryKeys.getLpi(),
-      queryFn: async () => {
-        const response = await rankingApi.getLpi();
-        return response.data;
-      },
-      gcTime: 0,
-      staleTime: 0
-    });
+      if (result && page <= 20) {
+        storage.set(storageKey, JSON.stringify(result));
+      }
 
-    return data;
-  } catch (error) {
-    console.error('Failed to fetch lpi data:', error);
-  }
+      return result;
+    },
+    initialData,
+    gcTime: 0,
+    staleTime: 0,
+    enabled
+  });
 };

+ 22 - 0
src/modules/api/ranking/queries/use-post-update-lpi.tsx

@@ -0,0 +1,22 @@
+import { rankingApi } from '../ranking-api';
+import { storage } from 'src/storage';
+
+export const updateLpi = async () => {
+  const country = 'ALL';
+  const age = 0;
+  const ranking = 'nm';
+  const pageSize = 50;
+  const totalPages = 5;
+
+  for (let page = 1; page <= totalPages; page++) {
+    try {
+      const response = await rankingApi.getLpi(country, age, ranking, pageSize, page);
+      if (response.data) {
+        const key = `lpiRanking-${page}`;
+        storage.set(key, JSON.stringify(response.data));
+      }
+    } catch (err) {
+      break;
+    }
+  }
+};

+ 9 - 1
src/modules/api/ranking/ranking-api.tsx

@@ -89,6 +89,7 @@ export interface PostGetMaster extends ResponseType {
       badge_un_100: 0 | 1;
       badge_un_150: 0 | 1;
       badge_un_193: 0 | 1;
+      badge_premium: 0 | 1;
       badge_supreme: number;
       badge_tbt: number;
       badge_offline: number;
@@ -188,7 +189,14 @@ export interface PostGetCountriesRanking extends ResponseType {
 export const rankingApi = {
   getLimitedRanking: () => request.postForm<PostGetRanking>(API.GET_LIMITED_RANKING),
   getFullRanking: () => request.postForm<PostGetRanking>(API.GET_FULL_RANKING),
-  getLpi: () => request.postForm<PostGetRanking>(API.GET_LPI),
+  getLpi: (country: string, age: number, ranking: string, rows: number, page: number) =>
+    request.postForm<PostGetMaster>(API.GET_LPI, {
+      country,
+      age,
+      ranking,
+      rows,
+      page
+    }),
   getInHistory: () => request.postForm<PostGetRanking>(API.GET_IN_HISTORY),
   getInMemoriam: () => request.postForm<PostGetRanking>(API.GET_IN_MEMORIAM),
   getUNMastersTypes: () => request.postForm<PostGetUNTypes>(API.GET_UN_MASTERS_TYPES),

+ 2 - 1
src/modules/api/ranking/ranking-query-keys.tsx

@@ -1,7 +1,8 @@
 export const rankingQueryKeys = {
   getLimitedRanking: () => ['getLimitedRanking'] as const,
   getFullRanking: () => ['getFullRanking'] as const,
-  getLpi: () => ['getLpi'] as const,
+  getLpi: (country: string, age: number, ranking: string, rows: number, page: number) =>
+    ['getLpi', { country, age, ranking, rows, page }] as const,
   getInHistory: () => ['getInHistory'] as const,
   getInMemoriam: () => ['getInMemoriam'] as const,
   getUNMastersTypes: () => ['getUNMastersTypes'] as const,

+ 152 - 85
src/screens/InAppScreens/TravellersScreen/LPIRankingScreen/index.tsx

@@ -1,48 +1,54 @@
-import React, { useCallback, useState, useRef } from 'react';
-import { FlatList, View, Dimensions } from 'react-native';
-import { useFocusEffect } from '@react-navigation/native';
-import { usePostGetCountriesRankingLPI } from '@api/ranking';
+import React, { useEffect, useState, useRef } from 'react';
+import { TouchableOpacity, View, Text, Dimensions } from 'react-native';
+import { FlashList } from '@shopify/flash-list';
 
+import { useGetLpi, usePostGetCountriesRankingLPI } from '@api/ranking';
 import { Header, Loading, PageWrapper } from '../../../../components';
-import { storage, StoreType } from '../../../../storage';
-
 import { Profile } from '../Components/Profile';
-import { FilterButton, FilterModal } from '../Components/FilterModal';
+import { FilterModal, FilterButton } from '../Components/FilterModal';
 
-import { applyModalSort, dataRanking } from '../utils';
-import { RankingDropdown, FilterModalRef } from '../utils/types';
+import { dataRanking } from '../utils';
+import type { RankingDropdown, FilterModalRef } from '../utils/types';
 
 import type { Ranking } from '..';
 
+import ChevronLeft from 'assets/icons/chevron-left.svg';
+import { Colors } from 'src/theme';
+import { getFontSize } from 'src/utils';
+
 const LPIRankingScreen = () => {
-  const [LPIRanking, setLPIRanking] = useState<Ranking[]>([]);
-  const [isLoading, setIsLoading] = useState(true);
+  const { data: lpiCountries } = usePostGetCountriesRankingLPI();
 
-  const [filteredData, setFilteredData] = useState<Ranking[]>([]);
+  const [LPIRanking, setLPIRanking] = useState<Ranking[]>([]);
   const filterRef = useRef<FilterModalRef>(null);
   const [confirmedValueRanking, setConfirmedValueRanking] = useState<RankingDropdown | null>();
+  const [filter, setFilter] = useState({
+    age: 0,
+    ranking: dataRanking[0].name,
+    country: 'ALL'
+  });
+  const [page, setPage] = useState(0);
+  const [first, setFirst] = useState(0);
+  const [last, setLast] = useState(0);
 
-  const { data: lpiCountries } = usePostGetCountriesRankingLPI();
+  const { data } = useGetLpi(filter.country, filter.age, filter.ranking, 50, page, true);
 
-  useFocusEffect(
-    useCallback(() => {
-      const fetchRanking = async () => {
-        const lpi: string = storage.get('lpiRanking', StoreType.STRING) as string;
-        try {
-          const parsedLpi = JSON.parse(lpi);
-          setLPIRanking(parsedLpi.sort((a: Ranking, b: Ranking) => b.score_nm - a.score_nm));
-        } catch (error) {
-          console.error('Failed to parse LPI ranking:', error);
-          setIsLoading(false);
-        }
-        setIsLoading(false);
-      };
-
-      fetchRanking();
-    }, [])
-  );
+  useEffect(() => {
+    if (data && data.data) {
+      let indexCounter = data.data.first - 1;
 
-  if (isLoading) return <Loading />;
+      setLPIRanking(
+        data.data.ranking.map((item) => {
+          return item.dod !== 1
+            ? { ...item, displayIndex: indexCounter++ }
+            : { ...item, displayIndex: -1 };
+        })
+      );
+      const filtered = data.data.ranking.filter((item) => item.dod !== 1);
+      setFirst(data.data.ranking.length ? data.data.first : 0);
+      setLast(filtered.length + data.data.first - 1);
+    }
+  }, [data]);
 
   return (
     <PageWrapper>
@@ -55,7 +61,7 @@ const LPIRankingScreen = () => {
         <View
           style={{
             position: 'absolute',
-            top: 0,
+            top: -10,
             left: -Dimensions.get('window').width * 0.05,
             width: Dimensions.get('window').width,
             height: Dimensions.get('window').height,
@@ -67,66 +73,127 @@ const LPIRankingScreen = () => {
             ref={filterRef}
             applyFilter={(filterAge, filterRanking, filterCountry) => {
               setConfirmedValueRanking(filterRanking);
-              setFilteredData(
-                applyModalSort(
-                  LPIRanking,
-                  filterAge,
-                  filterRanking ?? dataRanking[0],
-                  filterCountry
-                )
-              );
+              setFilter({
+                age: filterAge?.value ? +filterAge?.value : 0,
+                ranking: filterRanking ? filterRanking.name : dataRanking[0].name,
+                country: filterCountry?.two ? filterCountry.two?.toLowerCase() : 'ALL'
+              });
+              setPage(0);
             }}
             countriesData={lpiCountries ? lpiCountries.data : []}
           />
         </View>
       </View>
 
-      <FlatList
-        data={filteredData.length > 0 ? filteredData : LPIRanking}
-        showsVerticalScrollIndicator={false}
-        keyExtractor={(item) => item.user_id.toString()}
-        onEndReachedThreshold={0.1}
-        renderItem={({ item, index }) => (
-          <Profile
-            userId={item.user_id}
-            key={index}
-            index={index}
-            first_name={item.first_name}
-            last_name={item.last_name}
-            avatar={item.avatar}
-            date_of_birth={item.age}
-            homebase_flag={item.flag1}
-            homebase2_flag={item.flag2}
-            score={[
-              item.score_nm,
-              item.score_un,
-              item.score_unp,
-              item.score_dare,
-              item.score_tcc,
-              item.score_deep,
-              item.score_slow,
-              item.score_yes,
-              item.score_kye,
-              item.score_whs
-            ]}
-            active_score={
-              confirmedValueRanking ? confirmedValueRanking.value - 1 : dataRanking[0].value - 1
-            }
-            tbt_score={item.score_tbt}
-            tbt_rank={item.rank_tbt}
-            badge_tbt={item.badge_tbt}
-            badge_1281={item.badge_1281}
-            badge_un={item.badge_un}
-            badge_un_25={item.badge_un_25}
-            badge_un_50={item.badge_un_50}
-            badge_un_75={item.badge_un_75}
-            badge_un_100={item.badge_un_100}
-            badge_un_150={item.badge_un_150}
-            badge_premium={item.badge_premium}
-            auth={item.auth}
+      <View
+        style={{
+          marginTop: '-5%',
+          flexDirection: 'row',
+          alignItems: 'center',
+          justifyContent: 'center'
+        }}
+      >
+        <TouchableOpacity
+          style={{
+            width: 52,
+            height: 30,
+            display: 'flex',
+            justifyContent: 'center',
+            alignItems: 'center'
+          }}
+          onPress={() => setPage((prev) => prev - 1)}
+          disabled={page === 0}
+        >
+          <ChevronLeft fill={page === 0 ? Colors.LIGHT_GRAY : Colors.DARK_BLUE} height={12} />
+        </TouchableOpacity>
+
+        <Text
+          style={{
+            fontFamily: 'redhat-700',
+            fontSize: getFontSize(14),
+            color: Colors.DARK_BLUE,
+            textAlign: 'center'
+          }}
+        >
+          {first} - {last}
+        </Text>
+
+        <TouchableOpacity
+          style={{
+            width: 52,
+            height: 30,
+            display: 'flex',
+            justifyContent: 'center',
+            alignItems: 'center'
+          }}
+          onPress={() => setPage((prev) => prev + 1)}
+          disabled={data?.data?.max === page + 1}
+        >
+          <ChevronLeft
+            fill={data?.data?.max === page + 1 ? Colors.LIGHT_GRAY : Colors.DARK_BLUE}
+            height={12}
+            style={{ transform: [{ rotate: '180deg' }] }}
           />
-        )}
-      />
+        </TouchableOpacity>
+      </View>
+
+      {data && data.data ? (
+        <FlashList
+          showsVerticalScrollIndicator={false}
+          data={LPIRanking}
+          keyExtractor={(item) => item.user_id.toString()}
+          renderItem={({ item, index }) => (
+            <Profile
+              userId={item.user_id}
+              key={index}
+              index={item.displayIndex}
+              first_name={item.first_name}
+              last_name={item.last_name}
+              avatar={item.avatar}
+              date_of_birth={item.age}
+              homebase_flag={item.flag1}
+              homebase2_flag={item.flag2}
+              score={[
+                item.score_nm,
+                item.score_un,
+                item.score_unp,
+                item.score_dare,
+                item.score_tcc,
+                item.score_deep,
+                item.score_slow,
+                item.score_yes,
+                item.score_kye,
+                item.score_whs
+              ]}
+              active_score={
+                confirmedValueRanking ? confirmedValueRanking.value - 1 : dataRanking[0].value - 1
+              }
+              tbt_score={item.score_tbt}
+              tbt_rank={item.rank_tbt}
+              badge_tbt={item.badge_tbt}
+              badge_1281={item.badge_1281}
+              badge_un={item.badge_un}
+              badge_un_25={item.badge_un_25}
+              badge_un_50={item.badge_un_50}
+              badge_un_75={item.badge_un_75}
+              badge_un_100={item.badge_un_100}
+              badge_un_150={item.badge_un_150}
+              badge_premium={item.badge_premium}
+              auth={item.auth}
+            />
+          )}
+          viewabilityConfig={{
+            waitForInteraction: true,
+            itemVisiblePercentThreshold: 50,
+            minimumViewTime: 1000
+          }}
+          contentContainerStyle={{ paddingTop: 8 }}
+        />
+      ) : (
+        <View style={{ flex: 1 }}>
+          <Loading />
+        </View>
+      )}
     </PageWrapper>
   );
 };

+ 1 - 1
src/types/api.ts

@@ -43,7 +43,7 @@ export enum API_ENDPOINT {
   PROFILE_INFO_PUBLIC = 'profile-info-public',
   GET_LIMITED_RANKING = 'get-app-limited',
   GET_FULL_RANKING = 'get-app-full',
-  GET_LPI = 'get-app-lpi',
+  GET_LPI = 'get-lpi',
   GET_IN_HISTORY = 'get-app-in-history',
   GET_IN_MEMORIAM = 'get-app-in-memoriam',
   GET_UN_MASTERS_TYPES = 'get-types',