ChakraUI Added
This commit is contained in:
parent
27280c831a
commit
015d3eb5d1
@ -1,96 +1,18 @@
|
||||
'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 { useEffect, useState } from 'react';
|
||||
import { Center, Spinner, Text, VStack } from '@chakra-ui/react';
|
||||
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();
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
const { webApp, error, isLoading } = useTelegramWebApp();
|
||||
|
||||
useEffect(() => {
|
||||
if (webApp && isInitialized) {
|
||||
initApp();
|
||||
}
|
||||
}, [webApp, isInitialized]);
|
||||
setIsMounted(true);
|
||||
}, []);
|
||||
|
||||
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) {
|
||||
if (!isMounted || isLoading) {
|
||||
return (
|
||||
<Center h="100vh">
|
||||
<Spinner size="xl" />
|
||||
@ -98,74 +20,28 @@ export default function MainApp() {
|
||||
);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
if (error) {
|
||||
return (
|
||||
<Center h="100vh">
|
||||
<Box p={4} textAlign="center">
|
||||
Пожалуйста, авторизуйтесь через Telegram или используйте демо-режим
|
||||
</Box>
|
||||
<VStack>
|
||||
<Text color="red.500">Ошибка инициализации</Text>
|
||||
<Text>{error.message}</Text>
|
||||
</VStack>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
if (!webApp) {
|
||||
return (
|
||||
<Center h="100vh">
|
||||
<Text>WebApp не инициализирован</Text>
|
||||
</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>
|
||||
<Center h="100vh">
|
||||
<Text>Добро пожаловать, {webApp.initDataUnsafe.user?.first_name}!</Text>
|
||||
</Center>
|
||||
);
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Center, Spinner } from '@chakra-ui/react';
|
||||
|
||||
const MainApp = dynamic(() => import('./MainApp'), {
|
||||
loading: () => (
|
||||
<Center h="100vh">
|
||||
<Spinner size="xl" />
|
||||
</Center>
|
||||
),
|
||||
});
|
||||
|
||||
export default function MainAppWrapper() {
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsMounted(true);
|
||||
}, []);
|
||||
|
||||
if (!isMounted) {
|
||||
return (
|
||||
<Center h="100vh">
|
||||
<Spinner size="xl" />
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
return <MainApp />;
|
||||
}
|
@ -1,12 +1,9 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { Inter } from 'next/font/google';
|
||||
import { Providers } from './providers';
|
||||
|
||||
const inter = Inter({ subsets: ['latin', 'cyrillic'] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Campfire ID',
|
||||
description: 'Ваш цифровой профиль в Telegram',
|
||||
title: 'CampFire ID',
|
||||
description: 'Telegram Mini App for user achievements and virtual economy',
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
@ -16,7 +13,7 @@ export default function RootLayout({
|
||||
}) {
|
||||
return (
|
||||
<html lang="ru">
|
||||
<body className={inter.className}>
|
||||
<body>
|
||||
<Providers>
|
||||
{children}
|
||||
</Providers>
|
||||
|
20
app/page.tsx
20
app/page.tsx
@ -1,17 +1,9 @@
|
||||
import { Suspense } from 'react';
|
||||
import { Center, Spinner } from '@chakra-ui/react';
|
||||
import MainAppWrapper from './components/MainAppWrapper';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const MainAppWithNoSSR = dynamic(() => import('./components/MainApp'), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<Center h="100vh">
|
||||
<Spinner size="xl" />
|
||||
</Center>
|
||||
}
|
||||
>
|
||||
<MainAppWrapper />
|
||||
</Suspense>
|
||||
);
|
||||
return <MainAppWithNoSSR />;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { CacheProvider } from '@chakra-ui/next-js';
|
||||
import { ChakraProvider, extendTheme } from '@chakra-ui/react';
|
||||
|
||||
const theme = extendTheme({
|
||||
@ -15,8 +16,10 @@ const theme = extendTheme({
|
||||
|
||||
export function Providers({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<ChakraProvider theme={theme}>
|
||||
{children}
|
||||
</ChakraProvider>
|
||||
<CacheProvider>
|
||||
<ChakraProvider theme={theme}>
|
||||
{children}
|
||||
</ChakraProvider>
|
||||
</CacheProvider>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user