diff --git a/app/components/MainApp.tsx b/app/components/MainApp.tsx
index 970bf01..f6b5adb 100644
--- a/app/components/MainApp.tsx
+++ b/app/components/MainApp.tsx
@@ -1,15 +1,29 @@
'use client';
+import dynamic from 'next/dynamic';
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';
+// Динамический импорт компонентов для клиентской стороны
+const UserProfile = dynamic(() => import('./UserProfile'), {
+ loading: () => ,
+ ssr: false
+});
+
+const Shop = dynamic(() => import('./Shop'), {
+ loading: () => ,
+ ssr: false
+});
+
+const TransferBalance = dynamic(() => import('./TransferBalance'), {
+ loading: () => ,
+ ssr: false
+});
+
type SafeUser = Omit;
export default function MainApp() {
diff --git a/app/hooks/useTelegramWebApp.ts b/app/hooks/useTelegramWebApp.ts
index b237ecc..834ec45 100644
--- a/app/hooks/useTelegramWebApp.ts
+++ b/app/hooks/useTelegramWebApp.ts
@@ -2,73 +2,72 @@
import { useEffect, useState } from 'react';
import { isDemoMode, getDemoWebApp } from '../utils/demo';
+import type { WebApp } from '@twa-dev/types';
-export type WebApp = {
- initData: string;
- initDataUnsafe: {
- query_id: string;
- user: {
- id: string;
- first_name: string;
- username?: string;
- language_code: string;
- };
- auth_date: number;
- hash: string;
- };
- platform: string;
- colorScheme: string;
- themeParams: {
- bg_color: string;
- text_color: string;
- hint_color: string;
- link_color: string;
- button_color: string;
- button_text_color: string;
- };
- isExpanded: boolean;
- viewportHeight: number;
- viewportStableHeight: number;
- headerColor: string;
- backgroundColor: string;
- ready: () => void;
- expand: () => void;
- close: () => void;
-};
+type SafeWebApp = Partial>;
export function useTelegramWebApp() {
- const [webApp, setWebApp] = useState(null);
+ const [webApp, setWebApp] = useState(null);
const [error, setError] = useState(null);
const [isInitialized, setIsInitialized] = useState(false);
useEffect(() => {
+ let isMounted = true;
+
const initWebApp = async () => {
try {
if (isDemoMode()) {
- const demoWebApp = getDemoWebApp();
- setWebApp(demoWebApp);
- setIsInitialized(true);
+ if (isMounted) {
+ const demoWebApp = getDemoWebApp();
+ setWebApp(demoWebApp);
+ setIsInitialized(true);
+ }
return;
}
if (typeof window !== 'undefined') {
const WebAppModule = await import('@twa-dev/sdk');
- if (WebAppModule.default) {
- setWebApp(WebAppModule.default);
- setIsInitialized(true);
- } else {
- throw new Error('WebApp не найден');
+ if (isMounted) {
+ if (WebAppModule.default) {
+ setWebApp(WebAppModule.default as SafeWebApp);
+ setIsInitialized(true);
+ } else {
+ throw new Error('WebApp не найден');
+ }
}
}
} catch (err) {
console.error('WebApp initialization error:', err);
- setError('Ошибка инициализации Telegram Web App');
+ if (isMounted) {
+ setError('Ошибка инициализации Telegram Web App');
+ }
}
};
if (!isInitialized) {
initWebApp();
}
+
+ return () => {
+ isMounted = false;
+ };
}, [isInitialized]);
return { webApp, error, isInitialized };
diff --git a/app/utils/demo.ts b/app/utils/demo.ts
index 65951bf..e438c64 100644
--- a/app/utils/demo.ts
+++ b/app/utils/demo.ts
@@ -1,28 +1,54 @@
'use client';
+import type { WebApp, WebAppUser, WebAppInitData, Platforms } from '@twa-dev/types';
+
// Демо данные для тестирования без Telegram
-const demoUser = {
- id: 'demo_user_123',
+const demoUser: WebAppUser = {
+ id: 12345,
first_name: 'Demo',
username: 'demo_user',
- language_code: 'ru'
+ language_code: 'ru',
+ is_premium: false
};
-const demoInitData = {
+const demoInitData: WebAppInitData = {
query_id: 'demo_query',
user: demoUser,
auth_date: Date.now(),
- hash: 'demo_hash'
+ hash: 'demo_hash',
+ start_param: ''
};
export const isDemoMode = () => {
return typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('demo');
};
-export const getDemoWebApp = () => ({
+// Создаем заглушки для методов WebApp
+const createNoopFunction = () => () => {};
+
+type SafeWebApp = Pick;
+
+export const getDemoWebApp = (): SafeWebApp => ({
initData: 'demo_mode',
initDataUnsafe: demoInitData,
- platform: 'demo',
+ platform: 'WEBVIEW' as Platforms,
colorScheme: 'light',
themeParams: {
bg_color: '#ffffff',
@@ -30,7 +56,8 @@ export const getDemoWebApp = () => ({
hint_color: '#999999',
link_color: '#2481cc',
button_color: '#2481cc',
- button_text_color: '#ffffff'
+ button_text_color: '#ffffff',
+ secondary_bg_color: '#f0f0f0'
},
isExpanded: true,
viewportHeight: typeof window !== 'undefined' ? window.innerHeight : 800,
@@ -40,7 +67,10 @@ export const getDemoWebApp = () => ({
isClosingConfirmationEnabled: true,
BackButton: {
isVisible: false,
- onClick: () => {},
+ onClick: createNoopFunction(),
+ offClick: createNoopFunction(),
+ show: createNoopFunction(),
+ hide: createNoopFunction()
},
MainButton: {
text: '',
@@ -49,16 +79,18 @@ export const getDemoWebApp = () => ({
isVisible: false,
isProgressVisible: false,
isActive: true,
- setText: () => {},
- onClick: () => {},
- show: () => {},
- hide: () => {},
- enable: () => {},
- disable: () => {},
- showProgress: () => {},
- hideProgress: () => {},
+ setText: createNoopFunction(),
+ onClick: createNoopFunction(),
+ offClick: createNoopFunction(),
+ show: createNoopFunction(),
+ hide: createNoopFunction(),
+ enable: createNoopFunction(),
+ disable: createNoopFunction(),
+ showProgress: createNoopFunction(),
+ hideProgress: createNoopFunction(),
+ setParams: createNoopFunction()
},
- ready: () => {},
- expand: () => {},
- close: () => {},
+ ready: createNoopFunction(),
+ expand: createNoopFunction(),
+ close: createNoopFunction(),
});
\ No newline at end of file