Jelajahi Sumber

Merge branch 'nm-masters-sync' of SashaGoncharov19/nomadmania-app into dev

Viktoriia 1 tahun lalu
induk
melakukan
ba5549b4ef

+ 3 - 0
src/database/index.ts

@@ -4,6 +4,7 @@ import { StoreType, getOnlineStatus, storage } from 'src/storage';
 import { fetchLimitedRanking, fetchLpi, fetchInHistory, fetchInMemoriam } from '@api/ranking';
 import { initTilesDownload } from './tilesService';
 import { downloadFlags } from './flagsService';
+import { fetchAndSaveAllTypesAndMasters } from './unMastersService';
 
 const db = SQLite.openDatabase('nomadManiaDb.db');
 
@@ -82,6 +83,8 @@ const updateMasterRanking = async () => {
   if (dataInMemoriam && dataInMemoriam.data) {
     storage.set('inMemoriamRanking', JSON.stringify(dataInMemoriam.data));
   }
+
+  await fetchAndSaveAllTypesAndMasters();
 };
 
 export default setupDatabaseAndSync;

+ 208 - 0
src/database/unMastersService/index.ts

@@ -0,0 +1,208 @@
+import { fetchUNMastersType, fetchUNMastersTypes } from '@api/ranking';
+import { StoreType, storage } from 'src/storage';
+
+const NAMESPACE = 'UNMasters';
+
+function saveData<T>(key: string, data: T) {
+  const namespacedKey = `${NAMESPACE}:${key}`;
+  const jsonData = JSON.stringify(data);
+  storage.set(namespacedKey, jsonData);
+}
+
+function loadData<T>(key: string): T | null {
+  const namespacedKey = `${NAMESPACE}:${key}`;
+  const jsonData = storage.get(namespacedKey, StoreType.STRING) as string;
+
+  return jsonData ? JSON.parse(jsonData) : null;
+}
+
+function saveTypes(
+  typesData: { type: number; name: string }[],
+  startIndexes: Record<string, number>
+): void {
+  const types: TypeData[] = typesData.map((item) => ({
+    ...item,
+    start: startIndexes[item.type]
+  }));
+  saveData<TypeData[]>('types', types);
+}
+
+function saveTypeMasters(type: number, masters: Master[]): void {
+  let allMasters: Record<number, Master[]> = loadData<Record<number, Master[]>>('masters') || {};
+  allMasters[type] = masters;
+  saveData('masters', allMasters);
+}
+
+function saveSort(filtersData: any, filterType: 'yearOfCompletion' | 'countryOfOrigin'): void {
+  const filters = filtersData.reduce((obj: any, item: any) => {
+    const key = filterType === 'yearOfCompletion' ? item.year : item.country;
+    obj[key] = item.masters.map((master: Master) => master.id);
+
+    return obj;
+  }, {});
+  saveData<SortData>(filterType, filters);
+}
+
+async function handleGetTypeResponse(type: number): Promise<number | null> {
+  const responseData = await fetchUNMastersType(type);
+  if (responseData && responseData.result === 'OK') {
+    saveTypeMasters(type, (responseData.data as { data: Master[] }).data);
+
+    return (responseData.data as { start: number }).start;
+  }
+
+  return null;
+}
+
+export async function fetchAndSaveAllTypesAndMasters() {
+  const typesResponse = await fetchUNMastersTypes();
+  const startIndexes: Record<string, number> = {};
+
+  if (typesResponse && typesResponse.result === 'OK') {
+    const types = typesResponse.data.map((type) => type.type);
+    for (let type of types) {
+      const startIndex = await handleGetTypeResponse(type);
+      if (startIndex !== null) {
+        startIndexes[type] = startIndex;
+      }
+    }
+    saveTypes(typesResponse.data, startIndexes);
+  }
+
+  const countryResponse = await fetchUNMastersType(-1);
+  const yearResponse = await fetchUNMastersType(-2);
+
+  if (yearResponse && yearResponse.result === 'OK') {
+    saveSort(yearResponse.data, 'yearOfCompletion');
+  }
+
+  if (countryResponse && countryResponse.result === 'OK') {
+    saveSort(countryResponse.data, 'countryOfOrigin');
+  }
+}
+
+export function getMastersByCountryOfOrigin() {
+  const countrySortData = loadData('countryOfOrigin');
+  if (!countrySortData) return null;
+
+  const countryBlocks: { country: string; count: number; masters: Master[] }[] = [];
+
+  for (const [country, masterIds] of Object.entries(countrySortData)) {
+    const mastersList: Master[] = [];
+    masterIds.forEach((id: number) => {
+      const master = getMasterById(id);
+      if (master) {
+        mastersList.push(master);
+      }
+    });
+
+    countryBlocks.push({
+      country,
+      count: mastersList.length,
+      masters: mastersList
+    });
+  }
+
+  return countryBlocks;
+}
+
+export function getMastersByYearOfCompletion() {
+  const yearSortData = loadData('yearOfCompletion');
+  if (!yearSortData) return null;
+
+  const yearBlocks: { year: string; count: number; masters: Master[] }[] = [];
+
+  for (const [year, masterIds] of Object.entries(yearSortData)) {
+    const mastersList: Master[] = [];
+    masterIds.forEach((id: number) => {
+      const master = getMasterById(id);
+      if (master) {
+        mastersList.push(master);
+      }
+    });
+
+    yearBlocks.push({
+      year,
+      count: mastersList.length,
+      masters: mastersList
+    });
+  }
+
+  return yearBlocks;
+}
+
+export function getMastersByType(type: number) {
+  const allMasters: MastersData | null = loadData('masters');
+  if (!allMasters) return null;
+
+  return allMasters[type] || [];
+}
+
+function getMasterById(id: number) {
+  const allMasters = loadData('masters');
+  if (!allMasters) return null;
+
+  for (const typeMasters of Object.values(allMasters)) {
+    const master = typeMasters.find((m: Master) => m.id === id);
+    if (master) return master;
+  }
+
+  return null;
+}
+
+export function getUNMastersTypes() {
+  const types = loadData('types');
+  if (!types) return null;
+
+  return types;
+}
+
+interface Master {
+  id: number;
+  type: number;
+  origin: string;
+  origin1: string;
+  origin2: string;
+  full_name: string;
+  tbt_username: string;
+  born: string;
+  final: string | null;
+  final_year: string;
+  final_country: string;
+  age: string;
+  personal: string;
+  media: string;
+  media2: string;
+  media3: string;
+  wikipedia: string;
+  book: string;
+  book_url: string;
+  book2: string;
+  book_url2: string;
+  social: string;
+  twitter: string;
+  instagram: string;
+  youtube: string;
+  status: number;
+  sort: number;
+  last_country: string;
+  user_id: number | null;
+  origin1_flag: string;
+  origin2_flag: string | null;
+  final_flag: string;
+  interviews: string[];
+}
+
+interface TypeData {
+  name: string;
+  start: number;
+  type: number;
+}
+
+interface SortData {
+  [key: string]: number[];
+}
+
+interface MastersData {
+  [key: string]: Master[];
+}

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

@@ -3,3 +3,4 @@ export * from './use-post-get-full-ranking';
 export * from './use-post-get-lpi';
 export * from './use-post-get-in-history';
 export * from './use-post-get-in-memoriam';
+export * from './use-post-get-un-masters';

+ 39 - 0
src/modules/api/ranking/queries/use-post-get-un-masters.tsx

@@ -0,0 +1,39 @@
+import { rankingQueryKeys } from '../ranking-query-keys';
+import {
+  rankingApi,
+  type PostGetUNTypes,
+  type PostGetUNType,
+  type YearUNType,
+  type CountryUNType
+} from '../ranking-api';
+import { queryClient } from 'src/utils/queryClient';
+
+export const fetchUNMastersTypes = async () => {
+  try {
+    const data: PostGetUNTypes = await queryClient.fetchQuery({
+      queryKey: rankingQueryKeys.getUNMastersTypes(),
+      queryFn: () => rankingApi.getUNMastersTypes().then((res) => res.data),
+      gcTime: 0,
+      staleTime: 0
+    });
+
+    return data;
+  } catch (error) {
+    console.error('Failed to fetch UN masters types:', error);
+  }
+};
+
+export const fetchUNMastersType = async (type: number) => {
+  try {
+    const data: PostGetUNType | CountryUNType | YearUNType = await queryClient.fetchQuery({
+      queryKey: rankingQueryKeys.getUNMastersType(type),
+      queryFn: () => rankingApi.getUNMastersType(type).then((res) => res.data),
+      gcTime: 0,
+      staleTime: 0
+    });
+
+    return data;
+  } catch (error) {
+    console.error('Failed to fetch UN masters type:', error);
+  }
+};

+ 112 - 44
src/modules/api/ranking/ranking-api.tsx

@@ -4,55 +4,123 @@ import { ResponseType } from '../response-type';
 
 export interface PostGetRanking extends ResponseType {
   data: {
-    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_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,
+    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_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;
   }[];
-};
+}
+
+export interface PostGetUNTypes extends ResponseType {
+  data: {
+    type: number;
+    name: string;
+  }[];
+}
+
+export interface Masters {
+  id: number;
+  type: number;
+  origin: string;
+  origin1: string;
+  origin2: string;
+  full_name: string;
+  tbt_username: string;
+  born: string;
+  final: string | null;
+  final_year: string;
+  final_country: string;
+  age: string;
+  personal: string;
+  media: string;
+  media2: string;
+  media3: string;
+  wikipedia: string;
+  book: string;
+  book_url: string;
+  book2: string;
+  book_url2: string;
+  social: string;
+  twitter: string;
+  instagram: string;
+  youtube: string;
+  status: number;
+  sort: number;
+  last_country: string;
+  user_id: number | null;
+  origin1_flag: string;
+  origin2_flag: string | null;
+  final_flag: string;
+  interviews: string[];
+}
+
+export interface YearUNType extends ResponseType {
+  data: {
+    year: number,
+    masters: Masters[];
+  }[];
+}
+
+export interface CountryUNType extends ResponseType {
+  data: {
+    country: string;
+    code: string;
+    masters: Masters[];
+  }[];
+}
+
+export interface PostGetUNType extends ResponseType {
+  data: {
+    data: Masters[];
+    start: number;
+  };
+}
 
 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),
   getInHistory: () => request.postForm<PostGetRanking>(API.GET_IN_HISTORY),
-  getInMemoriam: () => request.postForm<PostGetRanking>(API.GET_IN_MEMORIAM)
+  getInMemoriam: () => request.postForm<PostGetRanking>(API.GET_IN_MEMORIAM),
+  getUNMastersTypes: () => request.postForm<PostGetUNTypes>(API.GET_UN_MASTERS_TYPES),
+  getUNMastersType: (type: number) =>
+    request.postForm<PostGetUNType>(API.GET_UN_MASTERS_TYPE, { type })
 };

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

@@ -3,5 +3,7 @@ export const rankingQueryKeys = {
   getFullRanking: () => ['getFullRanking'] as const,
   getLpi: () => ['getLpi'] as const,
   getInHistory: () => ['getInHistory'] as const,
-  getInMemoriam: () => ['getInMemoriam'] as const
+  getInMemoriam: () => ['getInMemoriam'] as const,
+  getUNMastersTypes: () => ['getUNMastersTypes'] as const,
+  getUNMastersType: (type: number) => ['getUNMastersType', { type }] as const,
 };

+ 8 - 3
src/types/api.ts

@@ -4,7 +4,8 @@ export enum API_ROUTE {
   USER = 'user',
   REGIONS = 'regions',
   SERIES = 'series',
-  RANKING = 'ranking'
+  RANKING = 'ranking',
+  UN_MASTERS = 'un-masters',
 }
 
 export enum API_ENDPOINT {
@@ -22,7 +23,9 @@ export enum API_ENDPOINT {
   GET_FULL_RANKING = 'get-app-full',
   GET_LPI = 'get-app-lpi',
   GET_IN_HISTORY = 'get-app-in-history',
-  GET_IN_MEMORIAM = 'get-app-in-memoriam'
+  GET_IN_MEMORIAM = 'get-app-in-memoriam',
+  GET_UN_MASTERS_TYPES = 'get-types',
+  GET_UN_MASTERS_TYPE = 'get-type',
 }
 
 export enum API {
@@ -39,7 +42,9 @@ export enum API {
   GET_FULL_RANKING = `${API_ROUTE.RANKING}/${API_ENDPOINT.GET_FULL_RANKING}`,
   GET_LPI = `${API_ROUTE.RANKING}/${API_ENDPOINT.GET_LPI}`,
   GET_IN_HISTORY = `${API_ROUTE.RANKING}/${API_ENDPOINT.GET_IN_HISTORY}`,
-  GET_IN_MEMORIAM = `${API_ROUTE.RANKING}/${API_ENDPOINT.GET_IN_MEMORIAM}`
+  GET_IN_MEMORIAM = `${API_ROUTE.RANKING}/${API_ENDPOINT.GET_IN_MEMORIAM}`,
+  GET_UN_MASTERS_TYPES = `${API_ROUTE.UN_MASTERS}/${API_ENDPOINT.GET_UN_MASTERS_TYPES}`,
+  GET_UN_MASTERS_TYPE = `${API_ROUTE.UN_MASTERS}/${API_ENDPOINT.GET_UN_MASTERS_TYPE}`,
 }
 
 export type BaseAxiosError = AxiosError;