react update

This commit is contained in:
degradin 2025-03-16 13:03:09 +03:00
parent 65a143ef4d
commit 6fbe286ca2
4 changed files with 129 additions and 102 deletions

View File

@ -1,8 +1,8 @@
'use client';
import { useEffect, useState } from 'react';
import { isDemoMode, getDemoWebApp } from '../utils/demo';
import { useEffect, useState, useCallback } from 'react';
import type { WebApp } from '@twa-dev/types';
import { isDemoMode, getDemoWebApp } from '../utils/demo';
declare global {
interface Window {
@ -12,84 +12,68 @@ declare global {
}
}
type SafeWebApp = Partial<Pick<WebApp,
| 'initData'
| 'initDataUnsafe'
| 'platform'
| 'colorScheme'
| 'themeParams'
| 'isExpanded'
| 'viewportHeight'
| 'viewportStableHeight'
| 'headerColor'
| 'backgroundColor'
| 'isClosingConfirmationEnabled'
| 'BackButton'
| 'MainButton'
| 'ready'
| 'expand'
| 'close'
>>;
export interface UseTelegramWebAppResult {
webApp: WebApp | null;
error: Error | null;
isLoading: boolean;
isInitialized: boolean;
}
export function useTelegramWebApp() {
const [webApp, setWebApp] = useState<SafeWebApp | null>(null);
const [error, setError] = useState<string | null>(null);
export function useTelegramWebApp(): UseTelegramWebAppResult {
const [webApp, setWebApp] = useState<WebApp | null>(null);
const [error, setError] = useState<Error | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [isInitialized, setIsInitialized] = useState(false);
useEffect(() => {
let isMounted = true;
const initWebApp = useCallback(async () => {
try {
setIsLoading(true);
setError(null);
const initWebApp = async () => {
try {
if (isDemoMode()) {
if (isMounted) {
const demoWebApp = getDemoWebApp();
setWebApp(demoWebApp);
setIsInitialized(true);
}
return;
}
if (isDemoMode()) {
const demoWebApp = getDemoWebApp();
setWebApp(demoWebApp);
setIsInitialized(true);
return;
}
if (typeof window !== 'undefined') {
// Проверяем наличие Telegram WebApp в глобальном объекте
if (window.Telegram?.WebApp) {
if (isMounted) {
setWebApp(window.Telegram.WebApp as SafeWebApp);
setIsInitialized(true);
}
return;
}
try {
// Если WebApp не найден в глобальном объекте, пробуем импортировать SDK
const WebAppModule = await import('@twa-dev/sdk');
if (isMounted && WebAppModule.default) {
setWebApp(WebAppModule.default as SafeWebApp);
setIsInitialized(true);
} else {
throw new Error('WebApp не найден');
}
} catch (importError) {
console.error('SDK import error:', importError);
throw new Error('Ошибка импорта Telegram Web App SDK');
}
}
} catch (err) {
console.error('WebApp initialization error:', err);
if (isMounted) {
setError('Ошибка инициализации Telegram Web App');
if (typeof window !== 'undefined') {
if (window.Telegram?.WebApp) {
setWebApp(window.Telegram.WebApp);
setIsInitialized(true);
} else {
throw new Error('Telegram WebApp не найден');
}
}
} catch (err) {
console.error('Ошибка инициализации WebApp:', err);
setError(err instanceof Error ? err : new Error('Неизвестная ошибка'));
} finally {
setIsLoading(false);
}
}, []);
useEffect(() => {
initWebApp();
}, [initWebApp]);
useEffect(() => {
if (!webApp) return;
const handleThemeChange = () => {
// Обновляем состояние при изменении темы
setWebApp(prevWebApp => {
if (!prevWebApp) return null;
return { ...prevWebApp };
});
};
if (!isInitialized) {
initWebApp();
}
webApp.onEvent('themeChanged', handleThemeChange);
return () => {
isMounted = false;
webApp.offEvent('themeChanged', handleThemeChange);
};
}, [isInitialized]);
}, [webApp]);
return { webApp, error, isInitialized };
return { webApp, error, isLoading, isInitialized };
}

View File

@ -1,6 +1,6 @@
'use client';
import type { WebApp, WebAppUser, WebAppInitData, Platforms } from '@twa-dev/types';
import type { WebApp, WebAppUser, WebAppInitData, Platforms, HapticFeedback } from '@twa-dev/types';
// Демо данные для тестирования без Telegram
const demoUser: WebAppUser = {
@ -28,26 +28,14 @@ export const isDemoMode = () => {
// Создаем заглушки для методов WebApp
const createNoopFunction = () => () => {};
type SafeWebApp = Pick<WebApp,
| 'initData'
| 'initDataUnsafe'
| 'platform'
| 'colorScheme'
| 'themeParams'
| 'isExpanded'
| 'viewportHeight'
| 'viewportStableHeight'
| 'headerColor'
| 'backgroundColor'
| 'isClosingConfirmationEnabled'
| 'BackButton'
| 'MainButton'
| 'ready'
| 'expand'
| 'close'
>;
// Создаем заглушку для HapticFeedback
const demoHapticFeedback: HapticFeedback = {
impactOccurred: (_style: "light" | "medium" | "heavy" | "rigid" | "soft") => demoHapticFeedback,
notificationOccurred: (_type: "error" | "success" | "warning") => demoHapticFeedback,
selectionChanged: () => demoHapticFeedback
};
export const getDemoWebApp = (): SafeWebApp => ({
export const getDemoWebApp = (): WebApp => ({
initData: JSON.stringify(demoInitData),
initDataUnsafe: demoInitData,
platform: 'WEBVIEW' as Platforms,
@ -92,6 +80,36 @@ export const getDemoWebApp = (): SafeWebApp => ({
hideProgress: createNoopFunction(),
setParams: createNoopFunction()
},
HapticFeedback: demoHapticFeedback,
CloudStorage: {
setItem: createNoopFunction(),
getItem: createNoopFunction(),
getItems: createNoopFunction(),
removeItem: createNoopFunction(),
removeItems: createNoopFunction(),
getKeys: createNoopFunction()
},
version: '7.0',
isVersionAtLeast: (version: string) => true,
setHeaderColor: createNoopFunction(),
setBackgroundColor: createNoopFunction(),
enableClosingConfirmation: createNoopFunction(),
disableClosingConfirmation: createNoopFunction(),
onEvent: createNoopFunction(),
offEvent: createNoopFunction(),
sendData: createNoopFunction(),
switchInlineQuery: createNoopFunction(),
openLink: createNoopFunction(),
openTelegramLink: createNoopFunction(),
openInvoice: createNoopFunction(),
showPopup: createNoopFunction(),
showAlert: createNoopFunction(),
showConfirm: createNoopFunction(),
showScanQrPopup: createNoopFunction(),
closeScanQrPopup: createNoopFunction(),
readTextFromClipboard: createNoopFunction(),
requestWriteAccess: createNoopFunction(),
requestContact: createNoopFunction(),
ready: createNoopFunction(),
expand: createNoopFunction(),
close: createNoopFunction(),

20
next.config.js Normal file
View File

@ -0,0 +1,20 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
output: 'standalone',
experimental: {
serverActions: {
allowedOrigins: ['telegram.org', 't.me'],
},
},
webpack: (config) => {
config.externals.push({
'utf-8-validate': 'commonjs utf-8-validate',
'bufferutil': 'commonjs bufferutil',
});
return config;
},
}
module.exports = nextConfig

View File

@ -1,6 +1,7 @@
{
"name": "campfire-id",
"version": "1.0.0",
"private": true,
"description": "Telegram Mini App for user achievements and virtual economy",
"scripts": {
"dev": "next dev",
@ -9,30 +10,34 @@
"lint": "next lint"
},
"dependencies": {
"@chakra-ui/react": "^2.8.1",
"@emotion/react": "^11.11.1",
"@chakra-ui/next-js": "^2.2.0",
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@twa-dev/sdk": "^6.9.0",
"@twa-dev/sdk": "^7.0.0",
"@twa-dev/types": "^7.0.0",
"@types/cors": "^2.8.17",
"axios": "^1.5.1",
"axios": "^1.6.7",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"framer-motion": "^10.16.4",
"framer-motion": "^11.0.8",
"jsonwebtoken": "^9.0.2",
"mongoose": "^7.5.3",
"next": "^13.5.4",
"mongoose": "^8.2.1",
"next": "15.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/express": "^4.17.18",
"@types/node": "^20.8.3",
"@types/react": "^18.2.25",
"eslint": "^8.51.0",
"eslint-config-next": "13.5.4",
"@types/node": "^20.11.24",
"@types/react": "^18.2.61",
"@types/react-dom": "^18.2.19",
"eslint": "^8.57.0",
"eslint-config-next": "15.2.2",
"prettier": "^3.0.3",
"typescript": "^5.2.2"
"typescript": "^5.3.3"
}
}