175 lines
7.5 KiB
TypeScript
175 lines
7.5 KiB
TypeScript
import React, { lazy, Suspense, useEffect } from 'react';
|
|
import { BrowserRouter, Routes, Route, Navigate, Outlet, useNavigate, useLocation } from 'react-router-dom';
|
|
import Layout from './components/layout/Layout';
|
|
import Home from './screens/Home';
|
|
import { initializeUserId, getCurrentUserId } from './constants/user';
|
|
import { trackScreenView } from './services/analyticsService';
|
|
import customAnalyticsService from './services/customAnalyticsService';
|
|
import { BalanceProvider } from './contexts/BalanceContext';
|
|
import { getTranslation } from './constants/translations';
|
|
|
|
// Ленивая загрузка компонентов
|
|
const OnboardingWelcome = lazy(() => import('./screens/onboarding/OnboardingWelcome'));
|
|
const OnboardingHowTo = lazy(() => import('./screens/onboarding/OnboardingHowTo'));
|
|
const OnboardingStickerPacks = lazy(() => import('./screens/onboarding/OnboardingStickerPacks'));
|
|
const TermsAndConditions = lazy(() => import('./screens/onboarding/TermsAndConditions'));
|
|
const CreateSticker = lazy(() => import('./screens/CreateSticker'));
|
|
const Gallery = lazy(() => import('./screens/Gallery'));
|
|
const Profile = lazy(() => import('./screens/Profile'));
|
|
const StickerPacks = lazy(() => import('./screens/StickerPacks'));
|
|
const CreateStickerPack = lazy(() => import('./screens/CreateStickerPack'));
|
|
const AddStickerToPackScreen = lazy(() => import('./screens/AddStickerToPackScreen'));
|
|
const History = lazy(() => import('./screens/History'));
|
|
const CropPhoto = lazy(() => import('./screens/CropPhoto'));
|
|
|
|
// Компонент для отображения состояния загрузки
|
|
const LoadingScreen = () => (
|
|
<div style={{
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
height: '100vh'
|
|
}}>
|
|
{getTranslation('loading')}
|
|
</div>
|
|
);
|
|
|
|
// Компонент для проверки онбординга
|
|
const AppContent: React.FC = () => {
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
|
|
useEffect(() => {
|
|
// Инициализируем ID пользователя при запуске приложения
|
|
initializeUserId()
|
|
.then(() => {
|
|
// Отправляем событие открытия приложения после успешной инициализации
|
|
customAnalyticsService.trackEvent({
|
|
telegram_id: getCurrentUserId(),
|
|
event_category: 'app',
|
|
event_name: 'app_open'
|
|
});
|
|
})
|
|
.catch(error => {
|
|
console.error('Ошибка при инициализации пользователя:', error);
|
|
});
|
|
|
|
// Стабилизируем окно и отключаем вертикальные свайпы
|
|
if (window.Telegram?.WebApp) {
|
|
// Стабилизация окна (особенно важно для iPhone)
|
|
window.Telegram.WebApp.expand();
|
|
|
|
// Отключаем вертикальные свайпы для предотвращения случайного сворачивания WebApp
|
|
if (window.Telegram.WebApp.isVersionAtLeast('6.9')) {
|
|
window.Telegram.WebApp.disableVerticalSwipes();
|
|
}
|
|
}
|
|
|
|
// Проверяем, видел ли пользователь онбординг и принял ли условия
|
|
const hasSeenOnboarding = localStorage.getItem('hasSeenOnboarding') === 'true';
|
|
const hasAcceptedTerms = localStorage.getItem('hasAcceptedTerms') === 'true';
|
|
|
|
// Если не видел онбординг и не на странице онбординга или условий
|
|
if (!hasSeenOnboarding && !location.pathname.includes('/onboarding')) {
|
|
navigate('/onboarding/welcome');
|
|
}
|
|
// Если видел онбординг, но не принял условия и не на странице условий
|
|
else if (hasSeenOnboarding && !hasAcceptedTerms && !location.pathname.includes('/onboarding/terms')) {
|
|
navigate('/onboarding/terms');
|
|
}
|
|
}, [navigate, location.pathname]);
|
|
|
|
// Отслеживаем изменение маршрута для аналитики
|
|
useEffect(() => {
|
|
// Получаем название экрана из пути
|
|
let screenName = location.pathname;
|
|
if (screenName === '/') {
|
|
screenName = 'Главная';
|
|
} else {
|
|
// Преобразуем путь в более читаемое название
|
|
screenName = screenName
|
|
.replace('/onboarding/welcome', 'Онбординг: Приветствие')
|
|
.replace('/onboarding/how-to', 'Онбординг: Как использовать')
|
|
.replace('/onboarding/sticker-packs', 'Онбординг: Стикер-паки')
|
|
.replace('/onboarding/terms', 'Условия использования')
|
|
.replace('/create', 'Создание стикера')
|
|
.replace('/gallery', 'Галерея')
|
|
.replace('/packs', 'Стикер-паки')
|
|
.replace('/create-sticker-pack', 'Создание стикер-пака')
|
|
.replace('/profile', 'Профиль')
|
|
.replace('/history', 'История')
|
|
.replace('/crop-photo', 'Обрезка фото');
|
|
|
|
// Если это добавление стикера в пак, извлекаем название пака
|
|
if (screenName.includes('/add-sticker/')) {
|
|
const packName = screenName.split('/add-sticker/')[1];
|
|
screenName = `Добавление стикера в пак: ${packName}`;
|
|
}
|
|
}
|
|
|
|
// Отправляем событие просмотра экрана
|
|
trackScreenView(screenName);
|
|
|
|
// Отправляем событие навигации в нашу новую аналитику
|
|
customAnalyticsService.trackNavigation(location.pathname.replace('/', ''));
|
|
}, [location.pathname]);
|
|
|
|
return (
|
|
<Routes>
|
|
{/* Онбординг */}
|
|
<Route path="/onboarding">
|
|
<Route index element={<Navigate to="/onboarding/welcome" replace />} />
|
|
<Route path="welcome" element={
|
|
<Suspense fallback={<LoadingScreen />}>
|
|
<OnboardingWelcome />
|
|
</Suspense>
|
|
} />
|
|
<Route path="how-to" element={
|
|
<Suspense fallback={<LoadingScreen />}>
|
|
<OnboardingHowTo />
|
|
</Suspense>
|
|
} />
|
|
<Route path="sticker-packs" element={
|
|
<Suspense fallback={<LoadingScreen />}>
|
|
<OnboardingStickerPacks />
|
|
</Suspense>
|
|
} />
|
|
<Route path="terms" element={
|
|
<Suspense fallback={<LoadingScreen />}>
|
|
<TermsAndConditions />
|
|
</Suspense>
|
|
} />
|
|
</Route>
|
|
|
|
{/* Основные экраны */}
|
|
<Route element={<Layout>
|
|
<Suspense fallback={<LoadingScreen />}>
|
|
<Outlet />
|
|
</Suspense>
|
|
</Layout>}>
|
|
<Route path="/" element={<Home />} />
|
|
<Route path="/create" element={<CreateSticker />} />
|
|
<Route path="/gallery" element={<Gallery />} />
|
|
<Route path="/packs" element={<StickerPacks />} />
|
|
<Route path="/create-sticker-pack" element={<CreateStickerPack />} />
|
|
<Route path="/add-sticker/:packName" element={<AddStickerToPackScreen />} />
|
|
<Route path="/profile" element={<Profile />} />
|
|
<Route path="/history" element={<History />} />
|
|
<Route path="/crop-photo" element={<CropPhoto />} />
|
|
</Route>
|
|
</Routes>
|
|
);
|
|
};
|
|
|
|
const App: React.FC = () => {
|
|
return (
|
|
<BrowserRouter>
|
|
<BalanceProvider>
|
|
<AppContent />
|
|
</BalanceProvider>
|
|
</BrowserRouter>
|
|
);
|
|
};
|
|
|
|
export default App;
|