171 lines
5.2 KiB
TypeScript
171 lines
5.2 KiB
TypeScript
'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>
|
||
);
|
||
}
|