добавлена разметка событий аналитики
This commit is contained in:
parent
825be2d0a6
commit
2bf8cb05b2
187
ANALYTICS_EVENTS.md
Normal file
187
ANALYTICS_EVENTS.md
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
# Документация по аналитическим событиям
|
||||||
|
|
||||||
|
В этом документе представлен полный список событий для отслеживания в приложении с использованием сервиса `customAnalyticsService.ts`. События сгруппированы по экранам и функциональным блокам.
|
||||||
|
|
||||||
|
## 1. Общие события приложения
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Открытие мини-приложения | `app` | `app_open` | - | - |
|
||||||
|
|
||||||
|
## 2. Навигация
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Открытие экрана Главная | `navigation` | `view_home` | - | - |
|
||||||
|
| Открытие экрана Галерея | `navigation` | `view_gallery` | - | - |
|
||||||
|
| Открытие экрана Стикерпаки | `navigation` | `view_sticker_packs` | - | - |
|
||||||
|
| Открытие экрана Профиль | `navigation` | `view_profile` | - | - |
|
||||||
|
| Открытие экрана Обрезка фото | `navigation` | `view_crop_photo` | - | - |
|
||||||
|
| Открытие экрана Создание стикерпака | `navigation` | `view_create_sticker_pack` | - | - |
|
||||||
|
| Открытие экрана Добавление стикера в пак | `navigation` | `view_add_sticker_to_pack` | - | - |
|
||||||
|
| Открытие экрана Политика конфиденциальности | `navigation` | `view_terms_and_conditions` | - | - |
|
||||||
|
| Открытие экрана Инструкция | `navigation` | `view_how_to` | - | - |
|
||||||
|
|
||||||
|
## 3. Политика конфиденциальности
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Нажатие на ссылку политики | `terms` | `policy_link_click` | - | `link_url` |
|
||||||
|
| Принятие политики | `terms` | `accept` | - | - |
|
||||||
|
| Отклонение политики | `terms` | `decline` | - | - |
|
||||||
|
|
||||||
|
## 4. Главный экран (Home)
|
||||||
|
|
||||||
|
### 4.1 Верхний блок кнопок
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Нажатие на кнопку обратной связи | `ui_interaction` | `feedback_button_click` | - | - |
|
||||||
|
| Нажатие на кнопку инструкции | `ui_interaction` | `instruction_button_click` | - | - |
|
||||||
|
| Нажатие на кнопку другого бота | `ui_interaction` | `other_bot_button_click` | - | `bot_url` |
|
||||||
|
|
||||||
|
### 4.2 Хедер
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Нажатие на кнопку баланса | `ui_interaction` | `balance_button_click` | - | - |
|
||||||
|
|
||||||
|
### 4.3 Загрузка и обработка фото
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Нажатие на загрузить фото | `photo` | `upload_photo_click` | - | - |
|
||||||
|
| Применение обрезки фото | `photo` | `crop_photo_apply` | - | - |
|
||||||
|
|
||||||
|
### 4.4 Выбор стиля и параметров
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Нажатие кнопки Чиби стиль | `style` | `chibi_style_click` | - | - |
|
||||||
|
| Нажатие кнопки Эмодзи стиль | `style` | `emoji_style_click` | - | - |
|
||||||
|
| Выбор подкатегории Мем | `style` | `meme_subcategory_select` | - | `meme_id` |
|
||||||
|
| Выбор подкатегории Коллекция | `style` | `collection_subcategory_select` | - | `collection_id` |
|
||||||
|
|
||||||
|
### 4.5 Генерация стикера
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Удачная отправка на генерацию | `generation` | `generation_success` | 1 | `preset_name` |
|
||||||
|
| Неудачная отправка на генерацию | `generation` | `generation_failure` | - | `error_type` |
|
||||||
|
|
||||||
|
### 4.6 Футер
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Нажатие на кнопку Главная | `footer` | `home_button_click` | - | - |
|
||||||
|
| Нажатие на кнопку Галерея | `footer` | `gallery_button_click` | - | - |
|
||||||
|
| Нажатие на кнопку Стикерпаки | `footer` | `sticker_packs_button_click` | - | - |
|
||||||
|
| Нажатие на кнопку Профиль | `footer` | `profile_button_click` | - | - |
|
||||||
|
|
||||||
|
## 5. Экран Галерея
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Долгое удерживание на изображении | `gallery` | `image_long_press` | - | `image_id` |
|
||||||
|
| Удаление изображения | `gallery` | `image_delete` | - | `image_id` |
|
||||||
|
| Нажатие кнопки "Создать стикерпак" | `gallery` | `create_sticker_pack_click` | - | `from_gallery` |
|
||||||
|
|
||||||
|
## 6. Экран Стикерпаки
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Нажатие кнопки "Создать стикерпак" | `sticker_packs` | `create_sticker_pack_click` | - | `from_sticker_packs` |
|
||||||
|
| Создание стикерпака | `sticker_packs` | `sticker_pack_created` | - | `pack_url` |
|
||||||
|
| Ошибка создания стикерпака | `sticker_packs` | `sticker_pack_creation_error` | - | `error_type` |
|
||||||
|
| Удаление стикерпака | `sticker_packs` | `sticker_pack_deleted` | - | `pack_id` |
|
||||||
|
|
||||||
|
## 7. Экран Профиль и попап с офферами
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Нажатие на оффер на экране профиля | `offers` | `profile_offer_click` | - | `offer_id` |
|
||||||
|
| Нажатие на оффер в попапе | `offers` | `popup_offer_click` | - | `offer_id` |
|
||||||
|
| Успешная покупка | `payment` | `purchase_success` | `stars_amount` | `star` |
|
||||||
|
|
||||||
|
## 8. Экраны онбординга
|
||||||
|
|
||||||
|
### 8.1 Экран приветствия (OnboardingWelcome)
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Открытие экрана приветствия | `navigation` | `view_onboarding_welcome` | - | - |
|
||||||
|
| Нажатие кнопки "Далее" | `onboarding` | `welcome_next_click` | - | - |
|
||||||
|
| Нажатие кнопки "Пропустить" | `onboarding` | `welcome_skip_click` | - | - |
|
||||||
|
|
||||||
|
### 8.2 Экран инструкции (OnboardingHowTo)
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Открытие экрана инструкции | `navigation` | `view_onboarding_how_to` | - | - |
|
||||||
|
| Нажатие кнопки "Далее" | `onboarding` | `how_to_next_click` | - | - |
|
||||||
|
| Нажатие кнопки "Назад" | `onboarding` | `how_to_back_click` | - | - |
|
||||||
|
| Нажатие кнопки "Пропустить" | `onboarding` | `how_to_skip_click` | - | - |
|
||||||
|
| Переключение слайда инструкции | `onboarding` | `how_to_slide_change` | - | `slide_index` |
|
||||||
|
|
||||||
|
### 8.3 Экран стикерпаков (OnboardingStickerPacks)
|
||||||
|
|
||||||
|
| Событие | Категория | Название события | Значение | Единица измерения |
|
||||||
|
|---------|-----------|------------------|----------|-------------------|
|
||||||
|
| Открытие экрана стикерпаков | `navigation` | `view_onboarding_sticker_packs` | - | - |
|
||||||
|
| Нажатие кнопки "Начать" | `onboarding` | `sticker_packs_start_click` | - | - |
|
||||||
|
| Нажатие кнопки "Назад" | `onboarding` | `sticker_packs_back_click` | - | - |
|
||||||
|
|
||||||
|
## Примеры использования
|
||||||
|
|
||||||
|
### Отслеживание открытия приложения
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import customAnalyticsService from '../services/customAnalyticsService';
|
||||||
|
|
||||||
|
// В компоненте App.tsx при монтировании
|
||||||
|
useEffect(() => {
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'app',
|
||||||
|
event_name: 'app_open'
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Отслеживание навигации
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import customAnalyticsService from '../services/customAnalyticsService';
|
||||||
|
|
||||||
|
// В компоненте страницы
|
||||||
|
useEffect(() => {
|
||||||
|
customAnalyticsService.trackNavigation('home');
|
||||||
|
}, []);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Отслеживание генерации стикера
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import customAnalyticsService from '../services/customAnalyticsService';
|
||||||
|
|
||||||
|
// При успешной генерации стикера
|
||||||
|
const handleGenerationSuccess = (presetName) => {
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'generation',
|
||||||
|
event_name: 'generation_success',
|
||||||
|
value: 1,
|
||||||
|
unit: presetName
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Отслеживание покупки
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import customAnalyticsService from '../services/customAnalyticsService';
|
||||||
|
|
||||||
|
// При успешной покупке
|
||||||
|
const handlePurchaseSuccess = (starsAmount) => {
|
||||||
|
customAnalyticsService.trackPayment('purchase_success', starsAmount, 'star');
|
||||||
|
};
|
||||||
17
src/App.tsx
17
src/App.tsx
@ -2,8 +2,9 @@ import React, { lazy, Suspense, useEffect } from 'react';
|
|||||||
import { BrowserRouter, Routes, Route, Navigate, Outlet, useNavigate, useLocation } from 'react-router-dom';
|
import { BrowserRouter, Routes, Route, Navigate, Outlet, useNavigate, useLocation } from 'react-router-dom';
|
||||||
import Layout from './components/layout/Layout';
|
import Layout from './components/layout/Layout';
|
||||||
import Home from './screens/Home';
|
import Home from './screens/Home';
|
||||||
import { initializeUserId } from './constants/user';
|
import { initializeUserId, getCurrentUserId } from './constants/user';
|
||||||
import { trackScreenView } from './services/analyticsService';
|
import { trackScreenView } from './services/analyticsService';
|
||||||
|
import customAnalyticsService from './services/customAnalyticsService';
|
||||||
import { BalanceProvider } from './contexts/BalanceContext';
|
import { BalanceProvider } from './contexts/BalanceContext';
|
||||||
|
|
||||||
// Ленивая загрузка компонентов
|
// Ленивая загрузка компонентов
|
||||||
@ -39,7 +40,16 @@ const AppContent: React.FC = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Инициализируем ID пользователя при запуске приложения
|
// Инициализируем ID пользователя при запуске приложения
|
||||||
initializeUserId().catch(error => {
|
initializeUserId()
|
||||||
|
.then(() => {
|
||||||
|
// Отправляем событие открытия приложения после успешной инициализации
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'app',
|
||||||
|
event_name: 'app_open'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
console.error('Ошибка при инициализации пользователя:', error);
|
console.error('Ошибка при инициализации пользователя:', error);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -98,6 +108,9 @@ const AppContent: React.FC = () => {
|
|||||||
|
|
||||||
// Отправляем событие просмотра экрана
|
// Отправляем событие просмотра экрана
|
||||||
trackScreenView(screenName);
|
trackScreenView(screenName);
|
||||||
|
|
||||||
|
// Отправляем событие навигации в нашу новую аналитику
|
||||||
|
customAnalyticsService.trackNavigation(location.pathname.replace('/', ''));
|
||||||
}, [location.pathname]);
|
}, [location.pathname]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import BlockRenderer from '../blocks/BlockRenderer';
|
import BlockRenderer from '../blocks/BlockRenderer';
|
||||||
import { homeScreenConfig } from '../../config/homeScreen';
|
import { homeScreenConfig } from '../../config/homeScreen';
|
||||||
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
|
||||||
interface EmotionTypeSelectorProps {
|
interface EmotionTypeSelectorProps {
|
||||||
selectedEmotionTypeButtonId?: string;
|
selectedEmotionTypeButtonId?: string;
|
||||||
@ -30,6 +32,14 @@ const EmotionTypeSelector: React.FC<EmotionTypeSelectorProps> = ({
|
|||||||
// Обработчик выбора типа эмоций
|
// Обработчик выбора типа эмоций
|
||||||
const handleAction = (actionType: string, actionValue: string, blockId?: string, buttonId?: string) => {
|
const handleAction = (actionType: string, actionValue: string, blockId?: string, buttonId?: string) => {
|
||||||
if (actionType === 'selectEmotionType') {
|
if (actionType === 'selectEmotionType') {
|
||||||
|
// Отслеживаем событие выбора типа эмоций
|
||||||
|
const eventName = actionValue === 'memes' ? 'memes_category_click' : 'prompts_category_click';
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'emotion_type',
|
||||||
|
event_name: eventName
|
||||||
|
});
|
||||||
|
|
||||||
onEmotionTypeSelect(actionValue as 'memes' | 'prompts', buttonId);
|
onEmotionTypeSelect(actionValue as 'memes' | 'prompts', buttonId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import React from 'react';
|
|||||||
import BlockRenderer from '../blocks/BlockRenderer';
|
import BlockRenderer from '../blocks/BlockRenderer';
|
||||||
import { homeScreenConfig } from '../../config/homeScreen';
|
import { homeScreenConfig } from '../../config/homeScreen';
|
||||||
import styles from '../../screens/Home.module.css';
|
import styles from '../../screens/Home.module.css';
|
||||||
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
|
||||||
interface GenerationButtonProps {
|
interface GenerationButtonProps {
|
||||||
onStartGeneration: () => void;
|
onStartGeneration: () => void;
|
||||||
@ -25,6 +27,13 @@ const GenerationButton: React.FC<GenerationButtonProps> = ({
|
|||||||
// Обработчик нажатия на кнопку генерации
|
// Обработчик нажатия на кнопку генерации
|
||||||
const handleAction = (actionType: string, actionValue: string) => {
|
const handleAction = (actionType: string, actionValue: string) => {
|
||||||
if (actionType === 'function' && actionValue === 'startGeneration' && !isGenerating) {
|
if (actionType === 'function' && actionValue === 'startGeneration' && !isGenerating) {
|
||||||
|
// Отслеживаем событие нажатия на кнопку генерации
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'generation',
|
||||||
|
event_name: 'generate_button_click'
|
||||||
|
});
|
||||||
|
|
||||||
onStartGeneration();
|
onStartGeneration();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import BlockRenderer from '../blocks/BlockRenderer';
|
import BlockRenderer from '../blocks/BlockRenderer';
|
||||||
import { homeScreenConfig } from '../../config/homeScreen';
|
import { homeScreenConfig } from '../../config/homeScreen';
|
||||||
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
|
||||||
interface MainActionsProps {
|
interface MainActionsProps {
|
||||||
onSendFeedback: () => void;
|
onSendFeedback: () => void;
|
||||||
@ -26,8 +28,21 @@ const MainActions: React.FC<MainActionsProps> = ({
|
|||||||
const handleAction = (actionType: string, actionValue: string) => {
|
const handleAction = (actionType: string, actionValue: string) => {
|
||||||
if (actionType === 'function') {
|
if (actionType === 'function') {
|
||||||
if (actionValue === 'sendFeedback') {
|
if (actionValue === 'sendFeedback') {
|
||||||
|
// Отслеживаем событие нажатия на кнопку обратной связи
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'ui_interaction',
|
||||||
|
event_name: 'feedback_button_click'
|
||||||
|
});
|
||||||
onSendFeedback();
|
onSendFeedback();
|
||||||
} else if (actionValue === 'openTelegramBot') {
|
} else if (actionValue === 'openTelegramBot') {
|
||||||
|
// Отслеживаем событие нажатия на кнопку другого бота
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'ui_interaction',
|
||||||
|
event_name: 'other_bot_button_click',
|
||||||
|
unit: 'https://t.me/youtube_s_loader_bot'
|
||||||
|
});
|
||||||
onOpenTelegramBot();
|
onOpenTelegramBot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import BlockRenderer from '../blocks/BlockRenderer';
|
import BlockRenderer from '../blocks/BlockRenderer';
|
||||||
import { homeScreenConfig } from '../../config/homeScreen';
|
import { homeScreenConfig } from '../../config/homeScreen';
|
||||||
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
|
||||||
interface PhotoUploadProps {
|
interface PhotoUploadProps {
|
||||||
onImageDataChange: (imageData: string) => void;
|
onImageDataChange: (imageData: string) => void;
|
||||||
@ -22,9 +24,18 @@ const PhotoUpload: React.FC<PhotoUploadProps> = ({
|
|||||||
|
|
||||||
// Обработчик загрузки фото
|
// Обработчик загрузки фото
|
||||||
const handleAction = (actionType: string, actionValue: string, blockId?: string, buttonId?: string, extraData?: any) => {
|
const handleAction = (actionType: string, actionValue: string, blockId?: string, buttonId?: string, extraData?: any) => {
|
||||||
if (actionType === 'uploadPhoto' && extraData?.imageData) {
|
if (actionType === 'uploadPhoto') {
|
||||||
|
// Отслеживаем событие нажатия на загрузить фото
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'photo',
|
||||||
|
event_name: 'upload_photo_click'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (extraData?.imageData) {
|
||||||
onImageDataChange(extraData.imageData);
|
onImageDataChange(extraData.imageData);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import BlockRenderer from '../blocks/BlockRenderer';
|
import BlockRenderer from '../blocks/BlockRenderer';
|
||||||
import { homeScreenConfig } from '../../config/homeScreen';
|
import { homeScreenConfig } from '../../config/homeScreen';
|
||||||
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
|
||||||
interface StyleSelectorProps {
|
interface StyleSelectorProps {
|
||||||
selectedStyleButtonId?: string;
|
selectedStyleButtonId?: string;
|
||||||
@ -25,6 +27,14 @@ const StyleSelector: React.FC<StyleSelectorProps> = ({
|
|||||||
// Обработчик выбора стиля
|
// Обработчик выбора стиля
|
||||||
const handleAction = (actionType: string, actionValue: string, blockId?: string, buttonId?: string) => {
|
const handleAction = (actionType: string, actionValue: string, blockId?: string, buttonId?: string) => {
|
||||||
if (actionType === 'selectStyle') {
|
if (actionType === 'selectStyle') {
|
||||||
|
// Отслеживаем событие выбора стиля
|
||||||
|
const eventName = actionValue === 'chibi' ? 'chibi_style_click' : 'emoji_style_click';
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'style',
|
||||||
|
event_name: eventName
|
||||||
|
});
|
||||||
|
|
||||||
onStyleSelect(actionValue, buttonId);
|
onStyleSelect(actionValue, buttonId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { paymentService } from '../../services/paymentService';
|
|||||||
import { tokenPacks } from '../../constants/tokenPacks';
|
import { tokenPacks } from '../../constants/tokenPacks';
|
||||||
import NotificationModal from '../shared/NotificationModal';
|
import NotificationModal from '../shared/NotificationModal';
|
||||||
import { useBalance } from '../../contexts/BalanceContext';
|
import { useBalance } from '../../contexts/BalanceContext';
|
||||||
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
import styles from './Header.module.css';
|
import styles from './Header.module.css';
|
||||||
|
|
||||||
const Header: React.FC = () => {
|
const Header: React.FC = () => {
|
||||||
@ -70,7 +71,15 @@ const Header: React.FC = () => {
|
|||||||
{/* Баланс токенов */}
|
{/* Баланс токенов */}
|
||||||
<button
|
<button
|
||||||
className={styles.balance}
|
className={styles.balance}
|
||||||
onClick={() => setShowTokensModal(true)}
|
onClick={() => {
|
||||||
|
// Отслеживаем событие нажатия на кнопку баланса
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'ui_interaction',
|
||||||
|
event_name: 'balance_button_click'
|
||||||
|
});
|
||||||
|
setShowTokensModal(true);
|
||||||
|
}}
|
||||||
title="Нажмите чтобы пополнить баланс"
|
title="Нажмите чтобы пополнить баланс"
|
||||||
>
|
>
|
||||||
<span className={styles.balanceIcon}>
|
<span className={styles.balanceIcon}>
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useNavigate, useLocation } from 'react-router-dom';
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import styles from './Navigation.module.css';
|
import styles from './Navigation.module.css';
|
||||||
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
|
||||||
const NavigationComponent: React.FC = () => {
|
const NavigationComponent: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -13,7 +15,15 @@ const NavigationComponent: React.FC = () => {
|
|||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.list}>
|
<div className={styles.list}>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/')}
|
onClick={() => {
|
||||||
|
// Отслеживаем событие нажатия на кнопку Главная
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'footer',
|
||||||
|
event_name: 'home_button_click'
|
||||||
|
});
|
||||||
|
navigate('/');
|
||||||
|
}}
|
||||||
className={`${styles.item} ${isActive('/') ? styles.active : ''}`}
|
className={`${styles.item} ${isActive('/') ? styles.active : ''}`}
|
||||||
>
|
>
|
||||||
<span className={styles.icon}>
|
<span className={styles.icon}>
|
||||||
@ -26,7 +36,15 @@ const NavigationComponent: React.FC = () => {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/gallery')}
|
onClick={() => {
|
||||||
|
// Отслеживаем событие нажатия на кнопку Галерея
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'footer',
|
||||||
|
event_name: 'gallery_button_click'
|
||||||
|
});
|
||||||
|
navigate('/gallery');
|
||||||
|
}}
|
||||||
className={`${styles.item} ${isActive('/gallery') ? styles.active : ''}`}
|
className={`${styles.item} ${isActive('/gallery') ? styles.active : ''}`}
|
||||||
>
|
>
|
||||||
<span className={styles.icon}>
|
<span className={styles.icon}>
|
||||||
@ -40,7 +58,15 @@ const NavigationComponent: React.FC = () => {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/packs')}
|
onClick={() => {
|
||||||
|
// Отслеживаем событие нажатия на кнопку Стикерпаки
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'footer',
|
||||||
|
event_name: 'sticker_packs_button_click'
|
||||||
|
});
|
||||||
|
navigate('/packs');
|
||||||
|
}}
|
||||||
className={`${styles.item} ${isActive('/packs') ? styles.active : ''}`}
|
className={`${styles.item} ${isActive('/packs') ? styles.active : ''}`}
|
||||||
>
|
>
|
||||||
<span className={styles.icon}>
|
<span className={styles.icon}>
|
||||||
@ -53,7 +79,15 @@ const NavigationComponent: React.FC = () => {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/profile')}
|
onClick={() => {
|
||||||
|
// Отслеживаем событие нажатия на кнопку Профиль
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'footer',
|
||||||
|
event_name: 'profile_button_click'
|
||||||
|
});
|
||||||
|
navigate('/profile');
|
||||||
|
}}
|
||||||
className={`${styles.item} ${isActive('/profile') ? styles.active : ''}`}
|
className={`${styles.item} ${isActive('/profile') ? styles.active : ''}`}
|
||||||
>
|
>
|
||||||
<span className={styles.icon}>
|
<span className={styles.icon}>
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { images } from '../../assets';
|
import { images } from '../../assets';
|
||||||
import { TokenPack } from '../../constants/tokenPacks';
|
import { TokenPack } from '../../constants/tokenPacks';
|
||||||
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
import styles from './TokenPackCard.module.css';
|
import styles from './TokenPackCard.module.css';
|
||||||
|
|
||||||
interface TokenPackCardProps extends TokenPack {
|
interface TokenPackCardProps extends TokenPack {
|
||||||
onBuy: () => void;
|
onBuy: () => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
|
source?: 'popup' | 'profile'; // Источник отображения карточки
|
||||||
}
|
}
|
||||||
|
|
||||||
const TokenPackCard: React.FC<TokenPackCardProps> = ({
|
const TokenPackCard: React.FC<TokenPackCardProps> = ({
|
||||||
@ -20,12 +23,22 @@ const TokenPackCard: React.FC<TokenPackCardProps> = ({
|
|||||||
isPopular,
|
isPopular,
|
||||||
isBestValue,
|
isBestValue,
|
||||||
onBuy,
|
onBuy,
|
||||||
className = ''
|
className = '',
|
||||||
|
source = 'popup' // По умолчанию считаем, что карточка отображается в попапе
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`${styles.card} ${isPopular ? styles.popular : ''} ${isBestValue ? styles.bestValue : ''} ${className}`}
|
className={`${styles.card} ${isPopular ? styles.popular : ''} ${isBestValue ? styles.bestValue : ''} ${className}`}
|
||||||
onClick={onBuy} // Добавляем обработчик клика на всю карточку
|
onClick={() => {
|
||||||
|
// Отслеживаем событие нажатия на оффер
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'offers',
|
||||||
|
event_name: source === 'popup' ? 'popup_offer_click' : 'profile_offer_click',
|
||||||
|
unit: title
|
||||||
|
});
|
||||||
|
onBuy();
|
||||||
|
}} // Добавляем обработчик клика на всю карточку
|
||||||
style={{ cursor: 'pointer' }} // Добавляем стиль курсора, чтобы показать, что карточка кликабельна
|
style={{ cursor: 'pointer' }} // Добавляем стиль курсора, чтобы показать, что карточка кликабельна
|
||||||
>
|
>
|
||||||
{isPopular && (
|
{isPopular && (
|
||||||
@ -67,6 +80,13 @@ const TokenPackCard: React.FC<TokenPackCardProps> = ({
|
|||||||
className={styles.buyButton}
|
className={styles.buyButton}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation(); // Предотвращаем всплытие события
|
e.stopPropagation(); // Предотвращаем всплытие события
|
||||||
|
// Отслеживаем событие нажатия на кнопку "КУПИТЬ"
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'offers',
|
||||||
|
event_name: source === 'popup' ? 'popup_offer_click' : 'profile_offer_click',
|
||||||
|
unit: title
|
||||||
|
});
|
||||||
onBuy();
|
onBuy();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -7,12 +7,14 @@ interface TokenPacksListProps {
|
|||||||
onBuyPack: (packId: string) => void;
|
onBuyPack: (packId: string) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
|
source?: 'popup' | 'profile'; // Источник отображения списка
|
||||||
}
|
}
|
||||||
|
|
||||||
const TokenPacksList: React.FC<TokenPacksListProps> = ({
|
const TokenPacksList: React.FC<TokenPacksListProps> = ({
|
||||||
onBuyPack,
|
onBuyPack,
|
||||||
className = '',
|
className = '',
|
||||||
compact
|
compact,
|
||||||
|
source = 'popup' // По умолчанию считаем, что список отображается в попапе
|
||||||
}) => {
|
}) => {
|
||||||
const listRef = useRef<HTMLDivElement>(null);
|
const listRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -40,6 +42,7 @@ const TokenPacksList: React.FC<TokenPacksListProps> = ({
|
|||||||
key={pack.id}
|
key={pack.id}
|
||||||
{...pack}
|
{...pack}
|
||||||
compact={compact}
|
compact={compact}
|
||||||
|
source={source}
|
||||||
onBuy={() => onBuyPack(pack.id)}
|
onBuy={() => onBuyPack(pack.id)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import { checkSufficientBalance, updateBalanceWithRetries } from '../utils/balan
|
|||||||
import apiService from '../services/api';
|
import apiService from '../services/api';
|
||||||
import { useBalance } from '../contexts/BalanceContext';
|
import { useBalance } from '../contexts/BalanceContext';
|
||||||
import { WorkflowType } from '../constants/workflows';
|
import { WorkflowType } from '../constants/workflows';
|
||||||
|
import customAnalyticsService from '../services/customAnalyticsService';
|
||||||
|
import { getCurrentUserId } from '../constants/user';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Хук для управления состоянием генерации
|
* Хук для управления состоянием генерации
|
||||||
@ -232,6 +234,14 @@ export const useGenerationState = (
|
|||||||
// Устанавливаем таймер для обнаружения проблем с подключением (6 секунд)
|
// Устанавливаем таймер для обнаружения проблем с подключением (6 секунд)
|
||||||
connectionTimeoutId = setTimeout(() => {
|
connectionTimeoutId = setTimeout(() => {
|
||||||
console.log('Connection timeout triggered');
|
console.log('Connection timeout triggered');
|
||||||
|
|
||||||
|
// Отслеживаем событие проблем с подключением
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'generation',
|
||||||
|
event_name: 'connection_timeout'
|
||||||
|
});
|
||||||
|
|
||||||
// Вызываем handleRequestTimeout из useNotifications через showNotification
|
// Вызываем handleRequestTimeout из useNotifications через showNotification
|
||||||
showNotification(
|
showNotification(
|
||||||
'Генерация стикера',
|
'Генерация стикера',
|
||||||
@ -318,6 +328,15 @@ export const useGenerationState = (
|
|||||||
// Получаем результат
|
// Получаем результат
|
||||||
const { result } = response;
|
const { result } = response;
|
||||||
|
|
||||||
|
// Отслеживаем событие успешной отправки на генерацию
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'generation',
|
||||||
|
event_name: 'generation_request_success',
|
||||||
|
value: 1,
|
||||||
|
unit: workflowType
|
||||||
|
});
|
||||||
|
|
||||||
// Обновляем уведомление с информацией о позиции в очереди
|
// Обновляем уведомление с информацией о позиции в очереди
|
||||||
if (result.queue_position !== undefined) {
|
if (result.queue_position !== undefined) {
|
||||||
const estimatedTime = apiService.calculateEstimatedWaitTime(result.queue_position);
|
const estimatedTime = apiService.calculateEstimatedWaitTime(result.queue_position);
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { GeneratedImage } from '../types/api';
|
|||||||
import { getCurrentUserId } from '../constants/user';
|
import { getCurrentUserId } from '../constants/user';
|
||||||
import EmojiPickerModal from '../components/shared/EmojiPickerModal';
|
import EmojiPickerModal from '../components/shared/EmojiPickerModal';
|
||||||
import ValidationModal from '../components/shared/ValidationModal';
|
import ValidationModal from '../components/shared/ValidationModal';
|
||||||
|
import customAnalyticsService from '../services/customAnalyticsService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Транслитерирует кириллический текст в латиницу.
|
* Транслитерирует кириллический текст в латиницу.
|
||||||
@ -201,6 +202,14 @@ const CreateStickerPack: React.FC = () => {
|
|||||||
packName
|
packName
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Отслеживаем событие успешного создания стикерпака
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'sticker_packs',
|
||||||
|
event_name: 'sticker_pack_created',
|
||||||
|
unit: packName
|
||||||
|
});
|
||||||
|
|
||||||
// Переходим на страницу стикерпаков
|
// Переходим на страницу стикерпаков
|
||||||
navigate('/packs');
|
navigate('/packs');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -209,6 +218,14 @@ const CreateStickerPack: React.FC = () => {
|
|||||||
// Преобразуем ошибку в строку для поиска
|
// Преобразуем ошибку в строку для поиска
|
||||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||||
|
|
||||||
|
// Отслеживаем событие ошибки создания стикерпака
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'sticker_packs',
|
||||||
|
event_name: 'sticker_pack_creation_error',
|
||||||
|
unit: errorMessage
|
||||||
|
});
|
||||||
|
|
||||||
// Проверяем, содержит ли сообщение об ошибке информацию о занятом имени
|
// Проверяем, содержит ли сообщение об ошибке информацию о занятом имени
|
||||||
if (errorMessage.includes('sticker set name is already occupied')) {
|
if (errorMessage.includes('sticker set name is already occupied')) {
|
||||||
setValidationTitle('Имя стикерпака уже занято');
|
setValidationTitle('Имя стикерпака уже занято');
|
||||||
|
|||||||
@ -6,6 +6,8 @@ import { GeneratedImage, PendingTask } from '../types/api';
|
|||||||
import ImageViewer from '../components/shared/ImageViewer';
|
import ImageViewer from '../components/shared/ImageViewer';
|
||||||
import ImageWithFallback from '../components/shared/ImageWithFallback';
|
import ImageWithFallback from '../components/shared/ImageWithFallback';
|
||||||
import NotificationModal from '../components/shared/NotificationModal';
|
import NotificationModal from '../components/shared/NotificationModal';
|
||||||
|
import customAnalyticsService from '../services/customAnalyticsService';
|
||||||
|
import { getCurrentUserId } from '../constants/user';
|
||||||
|
|
||||||
const GalleryScreen: React.FC = () => {
|
const GalleryScreen: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -38,6 +40,14 @@ const GalleryScreen: React.FC = () => {
|
|||||||
|
|
||||||
// Обработчики для режима удаления
|
// Обработчики для режима удаления
|
||||||
const handleLongPress = useCallback((image: GeneratedImage) => {
|
const handleLongPress = useCallback((image: GeneratedImage) => {
|
||||||
|
// Отслеживаем событие долгого нажатия на изображение
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'gallery',
|
||||||
|
event_name: 'image_long_press',
|
||||||
|
unit: String(image.id)
|
||||||
|
});
|
||||||
|
|
||||||
setIsDeleteMode(true);
|
setIsDeleteMode(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -64,6 +74,14 @@ const GalleryScreen: React.FC = () => {
|
|||||||
setIsDeleting(true);
|
setIsDeleting(true);
|
||||||
await apiService.deleteImage(selectedForDelete.link);
|
await apiService.deleteImage(selectedForDelete.link);
|
||||||
|
|
||||||
|
// Отслеживаем событие удаления изображения
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'gallery',
|
||||||
|
event_name: 'image_delete',
|
||||||
|
unit: String(selectedForDelete.id)
|
||||||
|
});
|
||||||
|
|
||||||
// Обновляем список изображений
|
// Обновляем список изображений
|
||||||
setImages(prevImages =>
|
setImages(prevImages =>
|
||||||
prevImages.filter(img => img.id !== selectedForDelete.id)
|
prevImages.filter(img => img.id !== selectedForDelete.id)
|
||||||
@ -85,6 +103,14 @@ const GalleryScreen: React.FC = () => {
|
|||||||
|
|
||||||
// Обработчик для кнопки "Создать стикерпак"
|
// Обработчик для кнопки "Создать стикерпак"
|
||||||
const handleCreateStickerPack = useCallback(() => {
|
const handleCreateStickerPack = useCallback(() => {
|
||||||
|
// Отслеживаем событие нажатия на кнопку "Создать стикерпак"
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'gallery',
|
||||||
|
event_name: 'create_sticker_pack_click',
|
||||||
|
unit: 'from_gallery'
|
||||||
|
});
|
||||||
|
|
||||||
navigate('/create-sticker-pack');
|
navigate('/create-sticker-pack');
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
|
|||||||
@ -105,6 +105,7 @@ const Profile: React.FC = () => {
|
|||||||
<TokenPacksList
|
<TokenPacksList
|
||||||
onBuyPack={handleBuyPack}
|
onBuyPack={handleBuyPack}
|
||||||
className={styles.tokenPacks}
|
className={styles.tokenPacks}
|
||||||
|
source="profile"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Модальное окно успешной оплаты */}
|
{/* Модальное окно успешной оплаты */}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import styles from './StickerPacks.module.css';
|
|||||||
import { stickerService } from '../services/stickerService';
|
import { stickerService } from '../services/stickerService';
|
||||||
import { getCurrentUserId } from '../constants/user';
|
import { getCurrentUserId } from '../constants/user';
|
||||||
import NotificationModal from '../components/shared/NotificationModal';
|
import NotificationModal from '../components/shared/NotificationModal';
|
||||||
|
import customAnalyticsService from '../services/customAnalyticsService';
|
||||||
|
|
||||||
// Функция для удаления дописанной части из названия стикерпака
|
// Функция для удаления дописанной части из названия стикерпака
|
||||||
const cleanPackTitle = (title: string): string => {
|
const cleanPackTitle = (title: string): string => {
|
||||||
@ -70,6 +71,14 @@ const StickerPacks: React.FC = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleCreateStickerPack = () => {
|
const handleCreateStickerPack = () => {
|
||||||
|
// Отслеживаем событие нажатия на кнопку "Создать стикерпак"
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'sticker_packs',
|
||||||
|
event_name: 'create_sticker_pack_click',
|
||||||
|
unit: 'from_sticker_packs'
|
||||||
|
});
|
||||||
|
|
||||||
navigate('/create-sticker-pack');
|
navigate('/create-sticker-pack');
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,6 +99,15 @@ const StickerPacks: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
setIsDeleting(true);
|
setIsDeleting(true);
|
||||||
await stickerService.deleteStickerPack(packToDelete);
|
await stickerService.deleteStickerPack(packToDelete);
|
||||||
|
|
||||||
|
// Отслеживаем событие удаления стикерпака
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'sticker_packs',
|
||||||
|
event_name: 'sticker_pack_deleted',
|
||||||
|
unit: packToDelete
|
||||||
|
});
|
||||||
|
|
||||||
setStickerPacks(prevPacks => prevPacks.filter(pack => pack.name !== packToDelete));
|
setStickerPacks(prevPacks => prevPacks.filter(pack => pack.name !== packToDelete));
|
||||||
setSelectedPack(null);
|
setSelectedPack(null);
|
||||||
setIsDeleting(false);
|
setIsDeleting(false);
|
||||||
|
|||||||
@ -1,16 +1,41 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import OnboardingLayout from '../../components/shared/OnboardingLayout';
|
import OnboardingLayout from '../../components/shared/OnboardingLayout';
|
||||||
import styles from './OnboardingHowTo.module.css';
|
import styles from './OnboardingHowTo.module.css';
|
||||||
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
|
||||||
const OnboardingHowTo: React.FC = () => {
|
const OnboardingHowTo: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Отслеживаем событие открытия экрана инструкции
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'navigation',
|
||||||
|
event_name: 'view_onboarding_how_to'
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
|
// Отслеживаем событие нажатия кнопки "Далее"
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'onboarding',
|
||||||
|
event_name: 'how_to_next_click'
|
||||||
|
});
|
||||||
|
|
||||||
navigate('/onboarding/sticker-packs');
|
navigate('/onboarding/sticker-packs');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSkip = () => {
|
const handleSkip = () => {
|
||||||
|
// Отслеживаем событие нажатия кнопки "Пропустить"
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'onboarding',
|
||||||
|
event_name: 'how_to_skip_click'
|
||||||
|
});
|
||||||
|
|
||||||
localStorage.setItem('hasSeenOnboarding', 'true');
|
localStorage.setItem('hasSeenOnboarding', 'true');
|
||||||
navigate('/');
|
navigate('/');
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,12 +1,30 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import OnboardingLayout from '../../components/shared/OnboardingLayout';
|
import OnboardingLayout from '../../components/shared/OnboardingLayout';
|
||||||
import styles from './OnboardingStickerPacks.module.css';
|
import styles from './OnboardingStickerPacks.module.css';
|
||||||
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
|
||||||
const OnboardingStickerPacks: React.FC = () => {
|
const OnboardingStickerPacks: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Отслеживаем событие открытия экрана стикерпаков
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'navigation',
|
||||||
|
event_name: 'view_onboarding_sticker_packs'
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleStart = () => {
|
const handleStart = () => {
|
||||||
|
// Отслеживаем событие нажатия кнопки "Начать"
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'onboarding',
|
||||||
|
event_name: 'sticker_packs_start_click'
|
||||||
|
});
|
||||||
|
|
||||||
// Устанавливаем флаг, что пользователь видел онбординг
|
// Устанавливаем флаг, что пользователь видел онбординг
|
||||||
localStorage.setItem('hasSeenOnboarding', 'true');
|
localStorage.setItem('hasSeenOnboarding', 'true');
|
||||||
|
|
||||||
|
|||||||
@ -1,16 +1,41 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import OnboardingLayout from '../../components/shared/OnboardingLayout';
|
import OnboardingLayout from '../../components/shared/OnboardingLayout';
|
||||||
import { images } from '../../assets';
|
import { images } from '../../assets';
|
||||||
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
|
||||||
const OnboardingWelcome: React.FC = () => {
|
const OnboardingWelcome: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Отслеживаем событие открытия экрана приветствия
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'navigation',
|
||||||
|
event_name: 'view_onboarding_welcome'
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
|
// Отслеживаем событие нажатия кнопки "Далее"
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'onboarding',
|
||||||
|
event_name: 'welcome_next_click'
|
||||||
|
});
|
||||||
|
|
||||||
navigate('/onboarding/how-to');
|
navigate('/onboarding/how-to');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSkip = () => {
|
const handleSkip = () => {
|
||||||
|
// Отслеживаем событие нажатия кнопки "Пропустить"
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'onboarding',
|
||||||
|
event_name: 'welcome_skip_click'
|
||||||
|
});
|
||||||
|
|
||||||
localStorage.setItem('hasSeenOnboarding', 'true');
|
localStorage.setItem('hasSeenOnboarding', 'true');
|
||||||
navigate('/');
|
navigate('/');
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,12 +1,30 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import styles from './TermsAndConditions.module.css';
|
import styles from './TermsAndConditions.module.css';
|
||||||
import { images } from '../../assets';
|
import { images } from '../../assets';
|
||||||
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
|
||||||
const TermsAndConditions: React.FC = () => {
|
const TermsAndConditions: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Отслеживаем событие открытия экрана условий
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'navigation',
|
||||||
|
event_name: 'view_terms_and_conditions'
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleAccept = () => {
|
const handleAccept = () => {
|
||||||
|
// Отслеживаем событие принятия условий
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'terms',
|
||||||
|
event_name: 'accept'
|
||||||
|
});
|
||||||
|
|
||||||
// Устанавливаем флаги, что пользователь видел онбординг и принял условия
|
// Устанавливаем флаги, что пользователь видел онбординг и принял условия
|
||||||
localStorage.setItem('hasSeenOnboarding', 'true');
|
localStorage.setItem('hasSeenOnboarding', 'true');
|
||||||
localStorage.setItem('hasAcceptedTerms', 'true');
|
localStorage.setItem('hasAcceptedTerms', 'true');
|
||||||
@ -15,12 +33,29 @@ const TermsAndConditions: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDecline = () => {
|
const handleDecline = () => {
|
||||||
|
// Отслеживаем событие отклонения условий
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'terms',
|
||||||
|
event_name: 'decline'
|
||||||
|
});
|
||||||
|
|
||||||
// Закрываем приложение
|
// Закрываем приложение
|
||||||
if (window.Telegram?.WebApp?.close) {
|
if (window.Telegram?.WebApp?.close) {
|
||||||
window.Telegram.WebApp.close();
|
window.Telegram.WebApp.close();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePolicyLinkClick = (linkUrl: string) => {
|
||||||
|
// Отслеживаем событие нажатия на ссылку политики
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'terms',
|
||||||
|
event_name: 'policy_link_click',
|
||||||
|
unit: linkUrl
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
@ -40,6 +75,7 @@ const TermsAndConditions: React.FC = () => {
|
|||||||
className={styles.link}
|
className={styles.link}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
|
onClick={() => handlePolicyLinkClick("https://telegra.ph/Polzovatelskoe-soglashenie-03-19-13")}
|
||||||
>
|
>
|
||||||
Условия использования
|
Условия использования
|
||||||
</a>
|
</a>
|
||||||
@ -48,6 +84,7 @@ const TermsAndConditions: React.FC = () => {
|
|||||||
className={styles.link}
|
className={styles.link}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
|
onClick={() => handlePolicyLinkClick("https://telegra.ph/Politika-konfidencialnosti-03-19-10")}
|
||||||
>
|
>
|
||||||
Политика конфиденциальности
|
Политика конфиденциальности
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
107
src/services/customAnalyticsService.ts
Normal file
107
src/services/customAnalyticsService.ts
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import { getCurrentUserId } from '../constants/user';
|
||||||
|
|
||||||
|
// Базовый URL API
|
||||||
|
const API_BASE_URL = 'https://stickerserver.gymnasticstuff.uk';
|
||||||
|
|
||||||
|
// Интерфейс для данных события
|
||||||
|
export interface EventData {
|
||||||
|
telegram_id: number;
|
||||||
|
event_category: string;
|
||||||
|
event_name: string;
|
||||||
|
value?: number;
|
||||||
|
unit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Отправляет событие аналитики на сервер
|
||||||
|
* @param eventData Данные события
|
||||||
|
* @returns Promise<boolean> Успешность операции
|
||||||
|
*/
|
||||||
|
export const trackEvent = async (eventData: EventData): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/events/create`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(eventData)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
console.log('Событие аналитики успешно отправлено:', result);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка при отправке события аналитики:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Отслеживает событие использования функционала
|
||||||
|
* @param eventName Название события
|
||||||
|
* @param value Числовое значение (опционально)
|
||||||
|
* @param unit Единица измерения (опционально)
|
||||||
|
* @returns Promise<boolean> Успешность операции
|
||||||
|
*/
|
||||||
|
export const trackUsage = async (
|
||||||
|
eventName: string,
|
||||||
|
value?: number,
|
||||||
|
unit?: string
|
||||||
|
): Promise<boolean> => {
|
||||||
|
return trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'usage',
|
||||||
|
event_name: eventName,
|
||||||
|
value,
|
||||||
|
unit
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Отслеживает событие платежа
|
||||||
|
* @param eventName Название события
|
||||||
|
* @param amount Сумма платежа
|
||||||
|
* @param currency Валюта платежа (например, 'stars', 'USD')
|
||||||
|
* @returns Promise<boolean> Успешность операции
|
||||||
|
*/
|
||||||
|
export const trackPayment = async (
|
||||||
|
eventName: string,
|
||||||
|
amount: number,
|
||||||
|
currency: string
|
||||||
|
): Promise<boolean> => {
|
||||||
|
return trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'payment',
|
||||||
|
event_name: eventName,
|
||||||
|
value: amount,
|
||||||
|
unit: currency
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Отслеживает событие навигации по приложению
|
||||||
|
* @param screenName Название экрана
|
||||||
|
* @returns Promise<boolean> Успешность операции
|
||||||
|
*/
|
||||||
|
export const trackNavigation = async (screenName: string): Promise<boolean> => {
|
||||||
|
return trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'navigation',
|
||||||
|
event_name: `view_${screenName}`
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Экспортируем все функции по умолчанию
|
||||||
|
const customAnalyticsService = {
|
||||||
|
trackEvent,
|
||||||
|
trackUsage,
|
||||||
|
trackPayment,
|
||||||
|
trackNavigation
|
||||||
|
};
|
||||||
|
|
||||||
|
export default customAnalyticsService;
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { TokenPack } from '../constants/tokenPacks';
|
import { TokenPack } from '../constants/tokenPacks';
|
||||||
import apiService from '../services/api';
|
import apiService from '../services/api';
|
||||||
import { getCurrentUserId } from '../constants/user';
|
import { getCurrentUserId } from '../constants/user';
|
||||||
import { sendTargetEvent } from './analyticsService';
|
import customAnalyticsService from './customAnalyticsService';
|
||||||
|
|
||||||
export const paymentService = {
|
export const paymentService = {
|
||||||
showBuyTokensPopup: async (pack: TokenPack, onSuccess?: (userData?: any) => void) => {
|
showBuyTokensPopup: async (pack: TokenPack, onSuccess?: (userData?: any) => void) => {
|
||||||
@ -25,6 +25,15 @@ export const paymentService = {
|
|||||||
// Открываем встроенный платеж Telegram без предварительного подтверждения
|
// Открываем встроенный платеж Telegram без предварительного подтверждения
|
||||||
webApp.openInvoice(invoiceLink, async (status: 'paid' | 'cancelled' | 'failed' | 'pending') => {
|
webApp.openInvoice(invoiceLink, async (status: 'paid' | 'cancelled' | 'failed' | 'pending') => {
|
||||||
if (status === 'paid') {
|
if (status === 'paid') {
|
||||||
|
// Отслеживаем событие успешной покупки
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'payment',
|
||||||
|
event_name: 'purchase_success',
|
||||||
|
value: pack.tokens + pack.bonusTokens,
|
||||||
|
unit: 'star'
|
||||||
|
});
|
||||||
|
|
||||||
// Функция для выполнения одной попытки получения данных пользователя
|
// Функция для выполнения одной попытки получения данных пользователя
|
||||||
const fetchUserData = async (attempt: number) => {
|
const fetchUserData = async (attempt: number) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user