Selaa lähdekoodia

master ranking pagination

Viktoriia 1 kuukausi sitten
vanhempi
commit
38d6153588

+ 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 { fetchLimitedRanking, fetchLpi, fetchInHistory, fetchInMemoriam } from '@api/ranking';
+import { fetchLpi, fetchInHistory, fetchInMemoriam, updateMaster } from '@api/ranking';
 import { initOfflineSetup } from './tilesService';
 import { downloadFlags } from './flagsService';
 import { fetchAndSaveAllTypesAndMasters } from './unMastersService';
@@ -40,11 +40,8 @@ export const setupDatabaseAndSync = async (): Promise<void> => {
 
 export const updateMasterRanking = async () => {
   const token = (storage.get('token', StoreType.STRING) as string) || '';
-  const dataLimitedRanking = await fetchLimitedRanking();
-
-  if (dataLimitedRanking && dataLimitedRanking?.data) {
-    storage.set('masterRanking', JSON.stringify(dataLimitedRanking.data));
-  }
+  await updateMaster();
+  storage.remove('masterRanking');
 
   const dataLpi = await fetchLpi();
 

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

@@ -1,4 +1,3 @@
-export * from './use-post-get-limited-ranking';
 export * from './use-post-get-full-ranking';
 export * from './use-post-get-lpi';
 export * from './use-post-get-in-history';
@@ -7,3 +6,5 @@ export * from './use-post-get-un-masters';
 export * from './use-post-get-countries-ranking';
 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';

+ 0 - 21
src/modules/api/ranking/queries/use-post-get-limited-ranking.tsx

@@ -1,21 +0,0 @@
-import { rankingQueryKeys } from '../ranking-query-keys';
-import { type PostGetRanking, rankingApi } from '../ranking-api';
-import { queryClient } from 'src/utils/queryClient';
-
-export const fetchLimitedRanking = async () => {
-  try {
-    const data: PostGetRanking = await queryClient.fetchQuery({
-      queryKey: rankingQueryKeys.getLimitedRanking(),
-      queryFn: async () => {
-        const response = await rankingApi.getLimitedRanking();
-        return response.data;
-      },
-      gcTime: 0,
-      staleTime: 0
-    });
-
-    return data;
-  } catch (error) {
-    console.error('Failed to fetch master ranking data:', error);
-  }
-};

+ 38 - 0
src/modules/api/ranking/queries/use-post-get-master.tsx

@@ -0,0 +1,38 @@
+import { useQuery } from '@tanstack/react-query';
+
+import { rankingQueryKeys } from '../ranking-query-keys';
+import { type PostGetMaster, rankingApi } from '../ranking-api';
+
+import type { BaseAxiosError } from '../../../../types';
+import { storage, StoreType } from 'src/storage';
+
+export const useGetMaster = (
+  country: string,
+  age: number,
+  ranking: string,
+  rows: number,
+  page: number,
+  enabled: boolean
+) => {
+  const storageKey = `masterRanking-${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.getMaster(country, age, ranking, rows, page),
+    queryFn: async () => {
+      const response = await rankingApi.getMaster(country, age, ranking, rows, page);
+      const result = response.data;
+
+      if (result && page <= 20) {
+        storage.set(storageKey, JSON.stringify(result));
+      }
+
+      return result;
+    },
+    initialData,
+    gcTime: 0,
+    staleTime: 0,
+    enabled
+  });
+};

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

@@ -0,0 +1,22 @@
+import { rankingApi } from '../ranking-api';
+import { storage } from 'src/storage';
+
+export const updateMaster = 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.getMaster(country, age, ranking, pageSize, page);
+      if (response.data) {
+        const key = `masterRanking-${page}`;
+        storage.set(key, JSON.stringify(response.data));
+      }
+    } catch (err) {
+      break;
+    }
+  }
+};

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

@@ -55,6 +55,65 @@ export interface PostGetRanking extends ResponseType {
   }[];
 }
 
+export interface PostGetMaster extends ResponseType {
+  data: {
+    age: number;
+    first: number;
+    max: number;
+    ranking: {
+      user_id: number;
+      score_dare: number;
+      score_nm: number;
+      score_un: number;
+      score_unp: number;
+      score_tcc: number;
+      score_deep: number;
+      score_whs: number;
+      score_kye: number;
+      score_tbt: number;
+      score_yes: number;
+      score_slow: number;
+      rank_tbt: number;
+      avatar: string;
+      first_name: string;
+      last_name: string;
+      age: number;
+      flag1: string;
+      flag2: string;
+      badge_1281: number;
+      badge_un: number;
+      badge_un_25: 0 | 1;
+      badge_un_50: 0 | 1;
+      badge_un_75: 0 | 1;
+      badge_un_100: 0 | 1;
+      badge_un_150: 0 | 1;
+      badge_un_193: 0 | 1;
+      badge_supreme: number;
+      badge_tbt: number;
+      badge_offline: number;
+      patreon: number;
+      country: string;
+      auth: number;
+      rank: number;
+      country_rank: number;
+      dod: number;
+      ukr: number;
+      badges: number;
+      arrow_nm: number;
+      arrow_un: number;
+      arrow_unp: number;
+      arrow_dare: number;
+      arrow_yes: number;
+      arrow_whs: number;
+      arrow_tcc: number;
+      arrow_tbt: number;
+      arrow_slow: number;
+      arrow_kye: number;
+      homebase: number;
+    }[];
+  };
+}
+
 export interface PostGetUNTypes extends ResponseType {
   data: {
     type: number;
@@ -137,5 +196,13 @@ export const rankingApi = {
   getCountriesRanking: () => request.get<PostGetCountriesRanking>(API.GET_COUNTRIES_RANKING),
   getCountriesRankingLPI: () => request.get<PostGetCountriesRanking>(API.GET_COUNTRIES_RANKING_LPI),
   getCountriesRankingMemoriam: () =>
-    request.get<PostGetCountriesRanking>(API.GET_COUNTRIES_RANKING_MEMORIAM)
+    request.get<PostGetCountriesRanking>(API.GET_COUNTRIES_RANKING_MEMORIAM),
+  getMaster: (country: string, age: number, ranking: string, rows: number, page: number) =>
+    request.postForm<PostGetMaster>(API.GET_MASTER, {
+      country,
+      age,
+      ranking,
+      rows,
+      page
+    })
 };

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

@@ -8,5 +8,7 @@ export const rankingQueryKeys = {
   getUNMastersType: (type: number) => ['getUNMastersType', { type }] as const,
   getCountriesRanking: () => ['getCountriesRanking'] as const,
   getCountriesRankingLPI: () => ['getCountriesRankingLPI'] as const,
-  getCountriesRankingMemoriam: () => ['getCountriesRankingMemoriam'] as const
+  getCountriesRankingMemoriam: () => ['getCountriesRankingMemoriam'] as const,
+  getMaster: (country: string, age: number, ranking: string, rows: number, page: number) =>
+    ['getMaster', { country, age, ranking, rows, page }] as const
 };

+ 10 - 5
src/screens/InAppScreens/TravellersScreen/Components/Profile.tsx

@@ -95,9 +95,7 @@ export const Profile: FC<Props> = ({
     }
   };
 
-  const avatarBaseUri = netInfo?.isInternetReachable
-    ? `${API_HOST}/img/avatars/`
-    : null;
+  const avatarBaseUri = netInfo?.isInternetReachable ? `${API_HOST}/img/avatars/` : null;
 
   const flagBaseUri = netInfo?.isInternetReachable
     ? `${API_HOST}/img/flags_new/`
@@ -228,14 +226,21 @@ export const Profile: FC<Props> = ({
         style={[
           ProfileStyles.index,
           {
-            width: index + 1 < 100 ? 26 : 38
+            width: index + 1 < 100 ? 26 : index + 1 < 1000 ? 38 : index + 1 < 10000 ? 44 : 46
           }
         ]}
       >
         {index === -1 ? (
           <MonumentIcon fill={Colors.DARK_BLUE} />
         ) : (
-          <Text style={adaptiveStyle(ScoreStyles.rankText, {})}>{index + 1}</Text>
+          <Text
+            style={[
+              adaptiveStyle(ScoreStyles.rankText, {}),
+              { fontSize: index + 1 < 100 ? 18 : index + 1 < 1000 ? 17 : 14 }
+            ]}
+          >
+            {index + 1}
+          </Text>
         )}
       </View>
       <View style={{ flex: 1, paddingLeft: 8, paddingRight: 3, paddingBottom: 3 }}>

+ 144 - 135
src/screens/InAppScreens/TravellersScreen/MasterRankingScreen/index.tsx

@@ -1,104 +1,54 @@
-import React, { useCallback, useEffect, useState } from 'react';
-import { useFocusEffect } from '@react-navigation/native';
-import { FlatList } from 'react-native';
-
-import { useGetFullRanking, usePostGetCountriesRanking } from '@api/ranking';
+import React, { useEffect, useState } from 'react';
+import { TouchableOpacity, View, Text } from 'react-native';
+import { FlashList } from '@shopify/flash-list';
 
+import { useGetMaster, usePostGetCountriesRanking } from '@api/ranking';
 import { Header, Loading, PageWrapper } from '../../../../components';
-import { storage, StoreType } from '../../../../storage';
-
 import { Profile } from '../Components/Profile';
 import { FilterModal, FilterButton } from '../Components/FilterModal';
 
-import { applyModalSort, dataRanking } from '../utils';
+import { dataRanking } from '../utils';
 import type { RankingDropdown } from '../utils/types';
 
 import type { Ranking } from '..';
-import { useConnection } from 'src/contexts/ConnectionContext';
-import { updateMasterRanking } from 'src/database';
+
+import ChevronLeft from 'assets/icons/chevron-left.svg';
+import { Colors } from 'src/theme';
+import { getFontSize } from 'src/utils';
 
 const MasterRankingScreen = () => {
-  const { data: fullData } = useGetFullRanking();
   const { data: masterCountries } = usePostGetCountriesRanking();
-  const netInfo = useConnection();
 
   const [masterRanking, setMasterRanking] = useState<Ranking[]>([]);
-  const [fullRanking, setFullRanking] = useState<Ranking[]>([]);
-  const [isLoading, setIsLoading] = useState(true);
-
-  const [filteredData, setFilteredData] = useState<Ranking[]>([]);
   const [isModalVisible, setModalVisible] = useState(false);
   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);
 
-  useFocusEffect(
-    useCallback(() => {
-      const fetchRanking = async () => {
-        const ranking = storage.get('masterRanking', StoreType.STRING) as string;
-        let indexCounter = 0;
-        if (!ranking) {
-          updateMasterRanking();
-          setMasterRanking(
-            fullData
-              ? fullData?.data
-                  ?.sort(
-                    (a: Omit<Ranking, 'displayIndex'>, b: Omit<Ranking, 'displayIndex'>) =>
-                      b.score_nm - a.score_nm || b.score_un - a.score_un || a.age - b.age
-                  )
-                  .map((item) => {
-                    return item.dod !== 1
-                      ? { ...item, displayIndex: indexCounter++ }
-                      : { ...item, displayIndex: -1 };
-                  })
-              : []
-          );
-
-          return;
-        }
-
-        setMasterRanking(
-          JSON.parse(ranking)
-            .sort(
-              (a: Ranking, b: Ranking) =>
-                b.score_nm - a.score_nm || b.score_un - a.score_un || a.age - b.age
-            )
-            .map((item: Ranking) => {
-              return item.dod !== 1
-                ? { ...item, displayIndex: indexCounter++ }
-                : { ...item, displayIndex: -1 };
-            })
-        );
-        setIsLoading(false);
-      };
-
-      fetchRanking();
-    }, [])
-  );
+  const { data } = useGetMaster(filter.country, filter.age, filter.ranking, 50, page, true);
 
   useEffect(() => {
-    if (fullData) {
-      let indexCounter = 0;
-      setFullRanking(
-        fullData?.data
-          ?.sort(
-            (a: Omit<Ranking, 'displayIndex'>, b: Omit<Ranking, 'displayIndex'>) =>
-              b.score_nm - a.score_nm || b.score_un - a.score_un || a.age - b.age
-          )
-          .map((item) => {
-            return item.dod !== 1
-              ? { ...item, displayIndex: indexCounter++ }
-              : { ...item, displayIndex: -1 };
-          })
+    if (data && data.data) {
+      let indexCounter = data.data.first - 1;
+
+      setMasterRanking(
+        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);
     }
-  }, [fullData]);
-
-  if (isLoading) return <Loading />;
-
-  const getFullRanking = async () => {
-    if (netInfo?.isInternetReachable && fullRanking) {
-      setMasterRanking(fullRanking);
-    }
-  };
+  }, [data]);
 
   return (
     <PageWrapper>
@@ -107,14 +57,12 @@ const MasterRankingScreen = () => {
         setModalVisible={(value) => setModalVisible(value)}
         applyFilter={(filterAge, filterRanking, filterCountry) => {
           setConfirmedValueRanking(filterRanking);
-          setFilteredData(
-            applyModalSort(
-              netInfo?.isInternetReachable && fullRanking ? fullRanking : masterRanking,
-              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);
           setModalVisible(false);
         }}
         countriesData={masterCountries ? masterCountries.data : []}
@@ -124,53 +72,114 @@ const MasterRankingScreen = () => {
         label="Master Ranking"
         rightElement={<FilterButton onPress={() => setModalVisible(!isModalVisible)} />}
       />
-
-      <FlatList
-        showsVerticalScrollIndicator={false}
-        data={filteredData.length != 0 ? filteredData : masterRanking}
-        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}
-            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' }] }}
           />
-        )}
-        onEndReached={() => getFullRanking()}
-        onEndReachedThreshold={0.5}
-      />
+        </TouchableOpacity>
+      </View>
+      {data && data.data ? (
+        <FlashList
+          showsVerticalScrollIndicator={false}
+          data={masterRanking}
+          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}
+              auth={item.auth}
+            />
+          )}
+          estimatedItemSize={80}
+          viewabilityConfig={{
+            waitForInteraction: true,
+            itemVisiblePercentThreshold: 50,
+            minimumViewTime: 1000
+          }}
+          contentContainerStyle={{ paddingTop: 8 }}
+        />
+      ) : (
+        <View style={{ flex: 1 }}>
+          <Loading />
+        </View>
+      )}
     </PageWrapper>
   );
 };

+ 1 - 0
src/screens/InAppScreens/TravellersScreen/index.tsx

@@ -168,6 +168,7 @@ export interface Ranking {
   arrow_slow: number;
   arrow_kye: number;
   displayIndex: number;
+  homebase: number;
 }
 
 export default TravellersScreen;

+ 4 - 2
src/types/api.ts

@@ -199,7 +199,8 @@ export enum API_ENDPOINT {
   CAN_ADD_EVENT = 'can-add-event',
   GET_PHOTOS_FOR_REGION = 'get-photos-for-region',
   ADD_EVENT = 'add-event',
-  SET_LOCATION_REGIONS = 'set-settings-regions'
+  SET_LOCATION_REGIONS = 'set-settings-regions',
+  GET_MASTER = 'get-master'
 }
 
 export enum API {
@@ -371,7 +372,8 @@ export enum API {
   CAN_ADD_EVENT = `${API_ROUTE.EVENTS}/${API_ENDPOINT.CAN_ADD_EVENT}`,
   GET_PHOTOS_FOR_REGION = `${API_ROUTE.EVENTS}/${API_ENDPOINT.GET_PHOTOS_FOR_REGION}`,
   ADD_EVENT = `${API_ROUTE.EVENTS}/${API_ENDPOINT.ADD_EVENT}`,
-  SET_LOCATION_REGIONS = `${API_ROUTE.LOCATION}/${API_ENDPOINT.SET_LOCATION_REGIONS}`
+  SET_LOCATION_REGIONS = `${API_ROUTE.LOCATION}/${API_ENDPOINT.SET_LOCATION_REGIONS}`,
+  GET_MASTER = `${API_ROUTE.RANKING}/${API_ENDPOINT.GET_MASTER}`
 }
 
 export type BaseAxiosError = AxiosError;