CampFireID/app/components/MainApp.tsx
degradin 0e304a8b2e init param
Добавление проверки isInitialized при инициализации приложения
Проверку наличия данных пользователя в initDataUnsafe
Вызов webApp.ready() после успешной инициализации
Улучшенную обработку ошибок и состояний загрузки
2025-03-16 12:39:38 +03:00

157 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import React, { useState, useEffect } from 'react';
import { Box, VStack, Spinner, Center, useToast } from '@chakra-ui/react';
import UserProfile from './UserProfile';
import Shop from './Shop';
import TransferBalance from './TransferBalance';
import { auth, getProfile, getShopItems, purchaseItem, transferBalance } from '../utils/api';
import { IUser } from '../../backend/models/User';
import { IShopItem } from '../../backend/models/ShopItem';
import { useTelegramWebApp } from '../hooks/useTelegramWebApp';
type SafeUser = Omit<IUser, keyof Document>;
export default function MainApp() {
const [isLoading, setIsLoading] = useState(true);
const [user, setUser] = useState<SafeUser | null>(null);
const [shopItems, setShopItems] = useState<IShopItem[]>([]);
const toast = useToast();
const { webApp, error: webAppError, isInitialized } = useTelegramWebApp();
useEffect(() => {
if (webApp && isInitialized) {
initApp();
}
}, [webApp, isInitialized]);
const initApp = async () => {
try {
setIsLoading(true);
if (!webApp) {
throw new Error('Telegram Web App не инициализирован');
}
// Авторизация пользователя
const { user: telegramUser } = webApp.initDataUnsafe;
if (!telegramUser) {
throw new Error('Пользователь не найден');
}
const authResponse = await auth(telegramUser.id.toString(), telegramUser.username || 'anonymous');
// Получение данных пользователя и магазина
const [profileData, shopData] = await Promise.all([
getProfile(),
getShopItems()
]);
setUser(profileData as SafeUser);
setShopItems(shopData);
// Сообщаем Telegram, что приложение готово
webApp.ready();
} catch (error: any) {
console.error('Initialization error:', error);
toast({
title: 'Ошибка инициализации',
description: error.message || 'Произошла ошибка при загрузке приложения',
status: 'error',
duration: 5000,
isClosable: true,
});
} finally {
setIsLoading(false);
}
};
if (webAppError) {
return (
<Center h="100vh">
<Box p={4} textAlign="center" color="red.500">
{webAppError}
</Box>
</Center>
);
}
if (isLoading || !webApp || !isInitialized) {
return (
<Center h="100vh">
<Spinner size="xl" />
</Center>
);
}
if (!user) {
return (
<Center h="100vh">
<Box p={4} textAlign="center">
Пожалуйста, авторизуйтесь через Telegram или используйте демо-режим
</Box>
</Center>
);
}
return (
<VStack spacing={6} p={4}>
<UserProfile
username={user.username}
level={user.level}
experience={user.experience}
balance={user.balance}
achievements={user.achievements}
/>
<Shop
items={shopItems}
userBalance={user.balance}
onPurchase={async (item: IShopItem) => {
try {
const response = await purchaseItem(item._id.toString());
setUser(response.user as SafeUser);
toast({
title: 'Успешная покупка',
description: `Вы приобрели ${item.name}`,
status: 'success',
duration: 3000,
isClosable: true,
});
} catch (error: any) {
toast({
title: 'Ошибка покупки',
description: error.message || 'Произошла ошибка при покупке',
status: 'error',
duration: 3000,
isClosable: true,
});
}
}}
/>
<TransferBalance
userBalance={user.balance}
onTransfer={async (username, amount) => {
try {
const response = await transferBalance(username, amount);
setUser(prev => ({ ...prev!, balance: response.balance } as SafeUser));
toast({
title: 'Успешный перевод',
description: `Вы перевели ${amount} монет пользователю ${username}`,
status: 'success',
duration: 3000,
isClosable: true,
});
} catch (error: any) {
toast({
title: 'Ошибка перевода',
description: error.message || 'Произошла ошибка при переводе',
status: 'error',
duration: 3000,
isClosable: true,
});
}
}}
/>
</VStack>
);
}