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