CampFireID/app/components/MainApp.tsx
2025-03-16 12:55:51 +03:00

171 lines
5.2 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 dynamic from 'next/dynamic';
import React, { useState, useEffect } from 'react';
import { Box, VStack, Spinner, Center, useToast } from '@chakra-ui/react';
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';
// Динамический импорт компонентов для клиентской стороны
const UserProfile = dynamic(() => import('./UserProfile'), {
loading: () => <Spinner />,
ssr: false
});
const Shop = dynamic(() => import('./Shop'), {
loading: () => <Spinner />,
ssr: false
});
const TransferBalance = dynamic(() => import('./TransferBalance'), {
loading: () => <Spinner />,
ssr: false
});
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 telegramUser = webApp.initDataUnsafe?.user;
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>
);
}