CampFireID/app/components/MainApp.tsx
degradin 8af9c21cb1 add components
Безопасная работа с Telegram Web App SDK
Улучшенная обработка ошибок
Плавная загрузка с индикаторами
Корректная работа в демо-режиме
2025-03-16 12:35:18 +03:00

150 lines
4.5 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 } = useTelegramWebApp();
useEffect(() => {
if (webApp) {
initApp();
}
}, [webApp]);
const initApp = async () => {
try {
setIsLoading(true);
if (!webApp) {
throw new Error('Telegram Web App не инициализирован');
}
// Авторизация пользователя
const { user: telegramUser } = webApp.initDataUnsafe;
const authResponse = await auth(telegramUser.id.toString(), telegramUser.username || 'anonymous');
// Получение данных пользователя и магазина
const [profileData, shopData] = await Promise.all([
getProfile(),
getShopItems()
]);
setUser(profileData as SafeUser);
setShopItems(shopData);
} 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) {
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>
);
}