2025-11-04 22:31:04 +00:00
|
|
|
|
import { BrowserRouter, Routes, Route, Navigate, useNavigate } from 'react-router-dom'
|
|
|
|
|
|
import { useState, useEffect, useRef } from 'react'
|
2025-11-04 21:51:05 +00:00
|
|
|
|
import { initTelegramApp, getTelegramUser, isThirdPartyClient } from './utils/telegram'
|
|
|
|
|
|
import { verifyAuth, authWithTelegramOAuth } from './utils/api'
|
2025-11-03 20:35:01 +00:00
|
|
|
|
import { initTheme } from './utils/theme'
|
|
|
|
|
|
import Layout from './components/Layout'
|
|
|
|
|
|
import Feed from './pages/Feed'
|
|
|
|
|
|
import Search from './pages/Search'
|
|
|
|
|
|
import Notifications from './pages/Notifications'
|
|
|
|
|
|
import Profile from './pages/Profile'
|
|
|
|
|
|
import UserProfile from './pages/UserProfile'
|
2025-11-03 22:51:17 +00:00
|
|
|
|
import CommentsPage from './pages/CommentsPage'
|
|
|
|
|
|
import PostMenuPage from './pages/PostMenuPage'
|
2025-11-04 21:51:05 +00:00
|
|
|
|
import TelegramLogin from './components/TelegramLogin'
|
2025-11-03 20:35:01 +00:00
|
|
|
|
import './styles/index.css'
|
|
|
|
|
|
|
2025-11-04 22:31:04 +00:00
|
|
|
|
function AppContent() {
|
2025-11-03 20:35:01 +00:00
|
|
|
|
const [user, setUser] = useState(null)
|
|
|
|
|
|
const [loading, setLoading] = useState(true)
|
|
|
|
|
|
const [error, setError] = useState(null)
|
2025-11-04 21:51:05 +00:00
|
|
|
|
const [showLogin, setShowLogin] = useState(false)
|
2025-11-04 22:31:04 +00:00
|
|
|
|
const navigate = useNavigate()
|
|
|
|
|
|
const startParamProcessed = useRef(false) // Флаг для обработки startParam только один раз
|
2025-11-03 20:35:01 +00:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
// Инициализировать тему
|
|
|
|
|
|
initTheme()
|
|
|
|
|
|
initApp()
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
|
|
|
|
const initApp = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// Инициализация Telegram Web App
|
|
|
|
|
|
initTelegramApp()
|
|
|
|
|
|
|
2025-11-04 22:02:23 +00:00
|
|
|
|
// Проверить наличие Telegram Web App API
|
|
|
|
|
|
const tg = window.Telegram?.WebApp
|
2025-11-03 20:35:01 +00:00
|
|
|
|
|
2025-11-04 22:02:23 +00:00
|
|
|
|
// Если нет Telegram Web App API вообще, показываем Login Widget
|
|
|
|
|
|
if (!tg) {
|
2025-11-04 21:51:05 +00:00
|
|
|
|
setShowLogin(true)
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
return
|
2025-11-03 20:35:01 +00:00
|
|
|
|
}
|
2025-11-04 22:02:23 +00:00
|
|
|
|
|
2025-11-04 22:23:33 +00:00
|
|
|
|
// В официальном клиенте Telegram есть initData (даже если user еще не распарсен)
|
|
|
|
|
|
// Пробуем авторизоваться через API - backend распарсит initData
|
2025-11-04 22:02:23 +00:00
|
|
|
|
|
2025-11-04 22:23:33 +00:00
|
|
|
|
// Дать время на полную инициализацию Telegram Web App
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300))
|
|
|
|
|
|
|
|
|
|
|
|
// Проверить наличие initData (главный индикатор что мы в Telegram)
|
|
|
|
|
|
const initData = tg.initData || ''
|
|
|
|
|
|
|
|
|
|
|
|
if (initData) {
|
|
|
|
|
|
// Есть initData - пробуем авторизоваться через API
|
|
|
|
|
|
try {
|
|
|
|
|
|
const userData = await verifyAuth()
|
|
|
|
|
|
setUser(userData)
|
|
|
|
|
|
|
2025-11-04 22:31:04 +00:00
|
|
|
|
// Обработать параметр start из Telegram (только один раз)
|
|
|
|
|
|
if (!startParamProcessed.current && tg?.startParam?.startsWith('post_')) {
|
|
|
|
|
|
startParamProcessed.current = true // Пометить как обработанный
|
2025-11-04 22:23:33 +00:00
|
|
|
|
const postId = tg.startParam.replace('post_', '')
|
2025-11-04 22:31:04 +00:00
|
|
|
|
// Использовать navigate вместо window.location.href (не вызывает перезагрузку)
|
|
|
|
|
|
navigate(`/feed?post=${postId}`, { replace: true })
|
2025-11-04 22:23:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
} catch (authError) {
|
|
|
|
|
|
console.error('Ошибка авторизации через API:', authError)
|
|
|
|
|
|
// Если авторизация не удалась, показываем Login Widget
|
2025-11-04 22:02:23 +00:00
|
|
|
|
setShowLogin(true)
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-04 21:51:05 +00:00
|
|
|
|
|
2025-11-04 22:23:33 +00:00
|
|
|
|
// Если нет initData, но есть WebApp API - это странно
|
|
|
|
|
|
// Показываем Login Widget
|
|
|
|
|
|
setShowLogin(true)
|
|
|
|
|
|
setLoading(false)
|
2025-11-03 20:35:01 +00:00
|
|
|
|
} catch (err) {
|
|
|
|
|
|
console.error('Ошибка инициализации:', err)
|
|
|
|
|
|
setError(err.message)
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-04 21:51:05 +00:00
|
|
|
|
const handleTelegramAuth = async (telegramUser) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
setLoading(true)
|
|
|
|
|
|
// Отправить данные OAuth на backend
|
|
|
|
|
|
const userData = await authWithTelegramOAuth(telegramUser)
|
|
|
|
|
|
setUser(userData)
|
|
|
|
|
|
setShowLogin(false)
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
console.error('Ошибка авторизации:', err)
|
|
|
|
|
|
setError(err.message || 'Ошибка авторизации через Telegram')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-03 20:35:01 +00:00
|
|
|
|
if (loading) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div style={{
|
|
|
|
|
|
display: 'flex',
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
height: '100vh',
|
|
|
|
|
|
flexDirection: 'column',
|
|
|
|
|
|
gap: '16px'
|
|
|
|
|
|
}}>
|
|
|
|
|
|
<div className="spinner" />
|
|
|
|
|
|
<p style={{ color: 'var(--text-secondary)' }}>Загрузка...</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-04 21:51:05 +00:00
|
|
|
|
// Показать Login Widget если нет авторизации
|
|
|
|
|
|
if (showLogin) {
|
|
|
|
|
|
// Получить имя бота из конфига или переменных окружения
|
|
|
|
|
|
const botName = import.meta.env.VITE_TELEGRAM_BOT_NAME || 'nakama_bot'
|
|
|
|
|
|
return <TelegramLogin botName={botName} onAuth={handleTelegramAuth} />
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-03 20:35:01 +00:00
|
|
|
|
if (error) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div style={{
|
|
|
|
|
|
display: 'flex',
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
height: '100vh',
|
|
|
|
|
|
flexDirection: 'column',
|
|
|
|
|
|
gap: '16px',
|
|
|
|
|
|
padding: '20px'
|
|
|
|
|
|
}}>
|
|
|
|
|
|
<p style={{ color: 'var(--text-primary)', textAlign: 'center' }}>
|
|
|
|
|
|
Ошибка загрузки приложения
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p style={{ color: 'var(--text-secondary)', textAlign: 'center' }}>
|
|
|
|
|
|
{error}
|
|
|
|
|
|
</p>
|
2025-11-04 21:51:05 +00:00
|
|
|
|
<button
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setError(null)
|
|
|
|
|
|
setShowLogin(true)
|
|
|
|
|
|
}}
|
|
|
|
|
|
style={{
|
|
|
|
|
|
padding: '12px 24px',
|
|
|
|
|
|
borderRadius: '12px',
|
|
|
|
|
|
background: 'var(--button-accent)',
|
|
|
|
|
|
color: 'white',
|
|
|
|
|
|
border: 'none',
|
|
|
|
|
|
cursor: 'pointer',
|
|
|
|
|
|
fontSize: '16px'
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
Попробовать снова
|
|
|
|
|
|
</button>
|
2025-11-03 20:35:01 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-04 21:51:05 +00:00
|
|
|
|
if (!user) {
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-04 22:31:04 +00:00
|
|
|
|
return (
|
|
|
|
|
|
<Routes>
|
|
|
|
|
|
<Route path="/" element={<Layout user={user} />}>
|
|
|
|
|
|
<Route index element={<Navigate to="/feed" replace />} />
|
|
|
|
|
|
<Route path="feed" element={<Feed user={user} />} />
|
|
|
|
|
|
<Route path="search" element={<Search user={user} />} />
|
|
|
|
|
|
<Route path="notifications" element={<Notifications user={user} />} />
|
|
|
|
|
|
<Route path="profile" element={<Profile user={user} setUser={setUser} />} />
|
|
|
|
|
|
<Route path="user/:id" element={<UserProfile currentUser={user} />} />
|
|
|
|
|
|
<Route path="post/:postId/comments" element={<CommentsPage user={user} />} />
|
|
|
|
|
|
<Route path="post/:postId/menu" element={<PostMenuPage user={user} />} />
|
|
|
|
|
|
</Route>
|
|
|
|
|
|
</Routes>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function App() {
|
2025-11-03 20:35:01 +00:00
|
|
|
|
return (
|
|
|
|
|
|
<BrowserRouter>
|
2025-11-04 22:31:04 +00:00
|
|
|
|
<AppContent />
|
2025-11-03 20:35:01 +00:00
|
|
|
|
</BrowserRouter>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default App
|
|
|
|
|
|
|