Massive fixes

This commit is contained in:
degradin 2025-03-16 12:47:27 +03:00
parent 0e304a8b2e
commit 33248392ff
3 changed files with 110 additions and 65 deletions

View File

@ -1,15 +1,29 @@
'use client'; 'use client';
import dynamic from 'next/dynamic';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Box, VStack, Spinner, Center, useToast } from '@chakra-ui/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 { auth, getProfile, getShopItems, purchaseItem, transferBalance } from '../utils/api';
import { IUser } from '../../backend/models/User'; import { IUser } from '../../backend/models/User';
import { IShopItem } from '../../backend/models/ShopItem'; 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>; type SafeUser = Omit<IUser, keyof Document>;
export default function MainApp() { export default function MainApp() {

View File

@ -2,73 +2,72 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { isDemoMode, getDemoWebApp } from '../utils/demo'; import { isDemoMode, getDemoWebApp } from '../utils/demo';
import type { WebApp } from '@twa-dev/types';
export type WebApp = { type SafeWebApp = Partial<Pick<WebApp,
initData: string; | 'initData'
initDataUnsafe: { | 'initDataUnsafe'
query_id: string; | 'platform'
user: { | 'colorScheme'
id: string; | 'themeParams'
first_name: string; | 'isExpanded'
username?: string; | 'viewportHeight'
language_code: string; | 'viewportStableHeight'
}; | 'headerColor'
auth_date: number; | 'backgroundColor'
hash: string; | 'isClosingConfirmationEnabled'
}; | 'BackButton'
platform: string; | 'MainButton'
colorScheme: string; | 'ready'
themeParams: { | 'expand'
bg_color: string; | 'close'
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;
};
export function useTelegramWebApp() { export function useTelegramWebApp() {
const [webApp, setWebApp] = useState<WebApp | null>(null); const [webApp, setWebApp] = useState<SafeWebApp | null>(null);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [isInitialized, setIsInitialized] = useState(false); const [isInitialized, setIsInitialized] = useState(false);
useEffect(() => { useEffect(() => {
let isMounted = true;
const initWebApp = async () => { const initWebApp = async () => {
try { try {
if (isDemoMode()) { if (isDemoMode()) {
const demoWebApp = getDemoWebApp(); if (isMounted) {
setWebApp(demoWebApp); const demoWebApp = getDemoWebApp();
setIsInitialized(true); setWebApp(demoWebApp);
setIsInitialized(true);
}
return; return;
} }
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
const WebAppModule = await import('@twa-dev/sdk'); const WebAppModule = await import('@twa-dev/sdk');
if (WebAppModule.default) { if (isMounted) {
setWebApp(WebAppModule.default); if (WebAppModule.default) {
setIsInitialized(true); setWebApp(WebAppModule.default as SafeWebApp);
} else { setIsInitialized(true);
throw new Error('WebApp не найден'); } else {
throw new Error('WebApp не найден');
}
} }
} }
} catch (err) { } catch (err) {
console.error('WebApp initialization error:', err); console.error('WebApp initialization error:', err);
setError('Ошибка инициализации Telegram Web App'); if (isMounted) {
setError('Ошибка инициализации Telegram Web App');
}
} }
}; };
if (!isInitialized) { if (!isInitialized) {
initWebApp(); initWebApp();
} }
return () => {
isMounted = false;
};
}, [isInitialized]); }, [isInitialized]);
return { webApp, error, isInitialized }; return { webApp, error, isInitialized };

View File

@ -1,28 +1,54 @@
'use client'; 'use client';
import type { WebApp, WebAppUser, WebAppInitData, Platforms } from '@twa-dev/types';
// Демо данные для тестирования без Telegram // Демо данные для тестирования без Telegram
const demoUser = { const demoUser: WebAppUser = {
id: 'demo_user_123', id: 12345,
first_name: 'Demo', first_name: 'Demo',
username: 'demo_user', username: 'demo_user',
language_code: 'ru' language_code: 'ru',
is_premium: false
}; };
const demoInitData = { const demoInitData: WebAppInitData = {
query_id: 'demo_query', query_id: 'demo_query',
user: demoUser, user: demoUser,
auth_date: Date.now(), auth_date: Date.now(),
hash: 'demo_hash' hash: 'demo_hash',
start_param: ''
}; };
export const isDemoMode = () => { export const isDemoMode = () => {
return typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('demo'); return typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('demo');
}; };
export const getDemoWebApp = () => ({ // Создаем заглушки для методов WebApp
const createNoopFunction = () => () => {};
type SafeWebApp = Pick<WebApp,
| 'initData'
| 'initDataUnsafe'
| 'platform'
| 'colorScheme'
| 'themeParams'
| 'isExpanded'
| 'viewportHeight'
| 'viewportStableHeight'
| 'headerColor'
| 'backgroundColor'
| 'isClosingConfirmationEnabled'
| 'BackButton'
| 'MainButton'
| 'ready'
| 'expand'
| 'close'
>;
export const getDemoWebApp = (): SafeWebApp => ({
initData: 'demo_mode', initData: 'demo_mode',
initDataUnsafe: demoInitData, initDataUnsafe: demoInitData,
platform: 'demo', platform: 'WEBVIEW' as Platforms,
colorScheme: 'light', colorScheme: 'light',
themeParams: { themeParams: {
bg_color: '#ffffff', bg_color: '#ffffff',
@ -30,7 +56,8 @@ export const getDemoWebApp = () => ({
hint_color: '#999999', hint_color: '#999999',
link_color: '#2481cc', link_color: '#2481cc',
button_color: '#2481cc', button_color: '#2481cc',
button_text_color: '#ffffff' button_text_color: '#ffffff',
secondary_bg_color: '#f0f0f0'
}, },
isExpanded: true, isExpanded: true,
viewportHeight: typeof window !== 'undefined' ? window.innerHeight : 800, viewportHeight: typeof window !== 'undefined' ? window.innerHeight : 800,
@ -40,7 +67,10 @@ export const getDemoWebApp = () => ({
isClosingConfirmationEnabled: true, isClosingConfirmationEnabled: true,
BackButton: { BackButton: {
isVisible: false, isVisible: false,
onClick: () => {}, onClick: createNoopFunction(),
offClick: createNoopFunction(),
show: createNoopFunction(),
hide: createNoopFunction()
}, },
MainButton: { MainButton: {
text: '', text: '',
@ -49,16 +79,18 @@ export const getDemoWebApp = () => ({
isVisible: false, isVisible: false,
isProgressVisible: false, isProgressVisible: false,
isActive: true, isActive: true,
setText: () => {}, setText: createNoopFunction(),
onClick: () => {}, onClick: createNoopFunction(),
show: () => {}, offClick: createNoopFunction(),
hide: () => {}, show: createNoopFunction(),
enable: () => {}, hide: createNoopFunction(),
disable: () => {}, enable: createNoopFunction(),
showProgress: () => {}, disable: createNoopFunction(),
hideProgress: () => {}, showProgress: createNoopFunction(),
hideProgress: createNoopFunction(),
setParams: createNoopFunction()
}, },
ready: () => {}, ready: createNoopFunction(),
expand: () => {}, expand: createNoopFunction(),
close: () => {}, close: createNoopFunction(),
}); });