瀏覽代碼

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

Viktoriia 1 年之前
父節點
當前提交
4bd422bb2b

+ 3 - 0
src/database/index.ts

@@ -6,6 +6,7 @@ import { initTilesDownload } from './tilesService';
 import { downloadFlags } from './flagsService';
 import { fetchAndSaveAllTypesAndMasters } from './unMastersService';
 import { updateAvatars } from './avatarsService';
+import { fetchAndSaveStatistics } from './statisticsService';
 
 const db = SQLite.openDatabase('nomadManiaDb.db');
 const lastUpdateDate = storage.get('lastUpdateDate', StoreType.STRING) as string || '1990-01-01';
@@ -59,6 +60,7 @@ export const setupDatabaseAndSync = async (): Promise<void> => {
 };
 
 const updateMasterRanking = async () => {
+  const token = storage.get('token', StoreType.STRING) as string || '';
   const dataLimitedRanking = await fetchLimitedRanking();
 
   if (dataLimitedRanking && dataLimitedRanking.data) {
@@ -83,6 +85,7 @@ const updateMasterRanking = async () => {
     storage.set('inMemoriamRanking', JSON.stringify(dataInMemoriam.data));
   }
 
+  await fetchAndSaveStatistics(token);
   await fetchAndSaveAllTypesAndMasters();
 };
 

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

@@ -0,0 +1,146 @@
+import { fetchList, fetchStatistic } from '@api/statistics';
+import { StoreType, storage } from 'src/storage';
+
+const NAMESPACE = 'statistics';
+
+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 structureList(listData: Sublist[]) {
+  const structuredData: StructuredData = {};
+  const baseCategoryName = 'NOMADS STATISTICS';
+  const list = [];
+
+  listData.forEach((category) => {
+    const categoryName = category.name.split('<br/>')[0];
+
+    if (categoryName === baseCategoryName) {
+      if (!structuredData[baseCategoryName]) {
+        structuredData[baseCategoryName] = {
+          name: baseCategoryName,
+          sublists: []
+        };
+      }
+
+      const descriptor = category.name.split('<br/>')[1].replace(/<[^>]*>?/gm, '');
+
+      const sublist: Sublist | null =
+        structuredData[baseCategoryName].sublists?.find((el) => el.name === descriptor) || null;
+
+      if (!sublist) {
+        structuredData[baseCategoryName].sublists?.push({
+          name: descriptor,
+          list: category.list.map((item) => ({
+            name: item.name,
+            url1: item.url1,
+            url2: item.url2 || null
+          }))
+        });
+      }
+    } else {
+      list.push(category);
+    }
+  });
+
+  structuredData[baseCategoryName] && list.push(structuredData[baseCategoryName]);
+  saveData('list', list);
+}
+
+export async function fetchAndSaveStatistics(token: string) {
+  const list = await fetchList(token);
+
+  if (list && list.result === 'OK') {
+    structureList(list.data);
+
+    for (const category of list.data) {
+      for (const item of category.list) {
+        const statsData = await fetchStatistic(token, item.url1, item.url2 ?? 'null');
+
+        if (statsData && statsData.result === 'OK') {
+          const statsKey = item.url2
+            ? `stats_${item.url1}_${item.url2}`
+            : `stats_${item.url1}`;
+
+          saveData(statsKey, JSON.stringify(statsData.data));
+        }
+      }
+    }
+  }
+}
+
+export function getStatistic(url1: string, url2?: string | null): Statistic | null {
+  const statsKey = `stats_${url1}${url2 ? `_${url2}` : ''}`;
+  return loadData<Statistic>(statsKey);
+}
+
+export function getList(): List | null {
+  return loadData<List>('list');
+}
+
+interface ListData {
+  name: string;
+  url1: string;
+  url2?: string | null;
+}
+
+interface List {
+  name: string;
+  list?: ListData[];
+  sublists?: Sublist[];
+}
+
+interface Sublist {
+  name: string;
+  list: ListData[];
+}
+
+interface StructuredData {
+  [key: string]: List;
+}
+
+interface Statistic {
+  data: {
+    url1: string;
+    url2: string;
+    type: number;
+    name: string;
+    comment: string;
+    ranking: Type1[] | Type2[] | Type3[];
+  }[];
+}
+
+interface Type1 {
+  region_id: number;
+  cnt: number;
+  region_name: string;
+  country: string;
+  flag: string;
+}
+
+interface Type2 {
+  cnt: number;
+  mega_id: number;
+  mega_name: string;
+  dare_name: string;
+  dare_flag: string;
+  dare_id: number;
+}
+
+interface Type3 {
+  year: number;
+  user: number;
+  cnt: number;
+  first_name: string;
+  last_name: string;
+  flag: string;
+}

+ 3 - 0
src/modules/api/statistics/index.ts

@@ -0,0 +1,3 @@
+export * from './queries';
+export * from './statistics-api';
+export * from './statistics-query-keys';

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

@@ -0,0 +1 @@
+export * from './use-post-get-statistics';

+ 33 - 0
src/modules/api/statistics/queries/use-post-get-statistics.tsx

@@ -0,0 +1,33 @@
+import { statisticsQueryKeys } from '../statistics-query-keys';
+import { type PostGetList, type PostGetStat, statisticsApi } from '../statistics-api';
+import { queryClient } from 'src/utils/queryClient';
+
+export const fetchList = async (token: string) => {
+  try {
+    const data: PostGetList = await queryClient.fetchQuery({
+      queryKey: statisticsQueryKeys.getList(token),
+      queryFn: () => statisticsApi.getList(token).then((res) => res.data),
+      gcTime: 0,
+      staleTime: 0
+    });
+
+    return data;
+  } catch (error) {
+    console.error('Failed to fetch statistics list:', error);
+  }
+};
+
+export const fetchStatistic = async (token: string, url1: string, url2: string) => {
+  try {
+    const data: PostGetStat = await queryClient.fetchQuery({
+      queryKey: statisticsQueryKeys.getStatistic(token, url1, url2),
+      queryFn: () => statisticsApi.getStatistic(token, url1, url2).then((res) => res.data),
+      gcTime: 0,
+      staleTime: 0
+    });
+
+    return data;
+  } catch (error) {
+    console.error('Failed to fetch statistic:', error);
+  }
+};

+ 75 - 0
src/modules/api/statistics/statistics-api.tsx

@@ -0,0 +1,75 @@
+import { request } from '../../../utils';
+import { API } from '../../../types';
+import { ResponseType } from '../response-type';
+
+export interface PostGetList extends ResponseType {
+  data: {
+    name: string;
+    list: List[];
+  }[];
+}
+
+export interface PostGetStat extends ResponseType {
+  data: {
+    url1: string;
+    url2: string;
+    type: number;
+    name: string;
+    comment: string;
+    ranking: Type1[] | Type2[] | Type3[];
+  }[];
+}
+
+export interface List {
+  name: string;
+  url1: string;
+  url2?: string;
+}
+
+export interface Type1 {
+  region_id: number;
+  cnt: number;
+  region_name: string;
+  country: string;
+  flag: string;
+}
+
+export interface Type2 {
+  cnt: number;
+  mega_id: number;
+  mega_name: string;
+  dare_name: string;
+  dare_flag: string;
+  dare_id: number;
+}
+
+export interface Type3 {
+  year: number;
+  user: number;
+  cnt: number;
+  first_name: string;
+  last_name: string;
+  flag: string;
+}
+
+export const statisticsApi = {
+  getList: (token: string) => request.post<PostGetList>(
+      API.GET_LIST,
+      {},
+      {
+        headers: {
+          Nmtoken: token
+        }
+      }
+    ),
+  getStatistic: (token: string, url1: string, url2: string) =>
+    request.postForm<PostGetStat>(
+      API.GET_STATISTIC,
+      { url1, url2 },
+      {
+        headers: {
+          Nmtoken: token
+        }
+      }
+    )
+};

+ 4 - 0
src/modules/api/statistics/statistics-query-keys.tsx

@@ -0,0 +1,4 @@
+export const statisticsQueryKeys = {
+  getList: (token: string) => ['getList', { token }] as const,
+  getStatistic: (token: string, url1: string, url2: string) => ['getStatistic', { token, url1, url2 }] as const,
+};

+ 6 - 0
src/screens/LoginScreen/index.tsx

@@ -11,6 +11,7 @@ import { storage } from '../../storage';
 import { NAVIGATION_PAGES } from '../../types';
 
 import { useLoginMutation } from '@api/auth';
+import { fetchAndSaveStatistics } from 'src/database/statisticsService';
 
 type Props = {
   navigation: NavigationProp<any>;
@@ -26,10 +27,15 @@ const LoginScreen: FC<Props> = ({ navigation }) => {
 
   const { data, mutate: userLogin } = useLoginMutation();
 
+  const updateLocalData = async (token: string) => {
+    await fetchAndSaveStatistics(token);
+  }
+
   if (data) {
     if (data.token) {
       storage.set('token', data.token);
       storage.set('uid', data.uid);
+      updateLocalData(data.token);
       dispatch(
         CommonActions.reset({
           index: 1,

+ 6 - 0
src/screens/RegisterScreen/EditAccount/index.tsx

@@ -13,6 +13,7 @@ import { storage } from '../../../storage';
 
 import store from '../../../storage/zustand';
 import { NAVIGATION_PAGES } from '../../../types';
+import { fetchAndSaveStatistics } from 'src/database/statisticsService';
 
 const SignUpSchema = yup.object({
   first_name: yup.string().required(),
@@ -46,10 +47,15 @@ const EditAccount = () => {
     return null;
   }
 
+  const updateLocalData = async (token: string) => {
+    await fetchAndSaveStatistics(token);
+  }
+
   if (data) {
     if (data.token) {
       storage.set('token', data.token);
       storage.set('uid', data.uid.toString());
+      updateLocalData(data.token);
       dispatch(
         CommonActions.reset({
           index: 1,

+ 5 - 0
src/types/api.ts

@@ -7,6 +7,7 @@ export enum API_ROUTE {
   RANKING = 'ranking',
   UN_MASTERS = 'un-masters',
   AVATARS = 'avatars',
+  STATISTICS = 'statistics',
 }
 
 export enum API_ENDPOINT {
@@ -28,6 +29,8 @@ export enum API_ENDPOINT {
   GET_UN_MASTERS_TYPES = 'get-types',
   GET_UN_MASTERS_TYPE = 'get-type',
   GET_UPDATED_AVATARS = 'get-updates',
+  GET_LIST = 'get-list',
+  GET_STATISTIC = 'get-stat',
 }
 
 export enum API {
@@ -48,6 +51,8 @@ export enum API {
   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}`,
   GET_UPDATED_AVATARS = `${API_ROUTE.AVATARS}/${API_ENDPOINT.GET_UPDATED_AVATARS}`,
+  GET_LIST = `${API_ROUTE.STATISTICS}/${API_ENDPOINT.GET_LIST}`,
+  GET_STATISTIC = `${API_ROUTE.STATISTICS}/${API_ENDPOINT.GET_STATISTIC}`,
 }
 
 export type BaseAxiosError = AxiosError;