diff --git a/src/App.tsx b/src/App.tsx
index 043fc06..decbaff 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -4,6 +4,7 @@ import Layout from './components/layout/Layout';
import Home from './screens/Home';
import { initializeUserId } from './constants/user';
import { trackScreenView } from './services/analyticsService';
+import { BalanceProvider } from './contexts/BalanceContext';
// Ленивая загрузка компонентов
const OnboardingWelcome = lazy(() => import('./screens/onboarding/OnboardingWelcome'));
@@ -147,7 +148,9 @@ const AppContent: React.FC = () => {
const App: React.FC = () => {
return (
-
+
+
+
);
};
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
index 4451d30..386760c 100644
--- a/src/components/layout/Header.tsx
+++ b/src/components/layout/Header.tsx
@@ -7,15 +7,16 @@ import TokenPacksModal from '../tokens/TokenPacksModal';
import { paymentService } from '../../services/paymentService';
import { tokenPacks } from '../../constants/tokenPacks';
import NotificationModal from '../shared/NotificationModal';
+import { useBalance } from '../../contexts/BalanceContext';
import styles from './Header.module.css';
const Header: React.FC = () => {
const navigate = useNavigate();
+ const { balance, updateBalance } = useBalance(); // Используем контекст баланса
const [user, setUser] = useState({
telegramId: 0,
username: '',
- avatarUrl: '',
- balance: 0
+ avatarUrl: ''
});
const [showTokensModal, setShowTokensModal] = useState(false);
const [lastPurchasedPack, setLastPurchasedPack] = useState(null);
@@ -27,34 +28,21 @@ const Header: React.FC = () => {
// Получаем информацию о пользователе
const userInfo = getUserInfo();
- const fetchData = async () => {
- try {
- // Получаем баланс пользователя
- const balance = await apiService.getBalance(getCurrentUserId());
-
- // Если есть данные из Telegram, обновляем состояние
- if (isTelegramWebAppAvailable()) {
- setUser({
- telegramId: userInfo.id,
- username: userInfo.first_name + (userInfo.last_name ? ` ${userInfo.last_name}` : ''),
- avatarUrl: userInfo.photo_url || '/ava.jpg', // Используем фото из Telegram или дефолтное
- balance: balance
- });
- } else {
- // Для локальной разработки
- setUser({
- telegramId: 12345678,
- username: "TestUser",
- avatarUrl: "/ava.jpg",
- balance: balance
- });
- }
- } catch (error) {
- console.error('Error fetching user data:', error);
- }
- };
-
- fetchData();
+ // Если есть данные из Telegram, обновляем состояние
+ if (isTelegramWebAppAvailable()) {
+ setUser({
+ telegramId: userInfo.id,
+ username: userInfo.first_name + (userInfo.last_name ? ` ${userInfo.last_name}` : ''),
+ avatarUrl: userInfo.photo_url || '/ava.jpg' // Используем фото из Telegram или дефолтное
+ });
+ } else {
+ // Для локальной разработки
+ setUser({
+ telegramId: 12345678,
+ username: "TestUser",
+ avatarUrl: "/ava.jpg"
+ });
+ }
}, []);
return (
@@ -89,7 +77,7 @@ const Header: React.FC = () => {
- {user.balance}
+ {balance}
@@ -110,13 +98,8 @@ const Header: React.FC = () => {
paymentService.showBuyTokensPopup(pack, async (userData) => {
if (userData) {
- // Обновляем данные на основе полученной информации
- if (userData.balance !== undefined) {
- setUser(prev => ({
- ...prev,
- balance: userData.balance
- }));
- }
+ // Обновляем баланс через контекст
+ updateBalance();
// Показываем модальное окно с информацией об успешной оплате
setNotificationTitle('Оплата успешна!');
@@ -128,11 +111,8 @@ const Header: React.FC = () => {
// Получаем баланс пользователя
const balance = await apiService.getBalance(getCurrentUserId());
- // Обновляем баланс пользователя
- setUser(prev => ({
- ...prev,
- balance: balance
- }));
+ // Обновляем баланс через контекст
+ updateBalance();
// Показываем модальное окно с информацией об успешной оплате
setNotificationTitle('Оплата успешна!');
diff --git a/src/contexts/BalanceContext.tsx b/src/contexts/BalanceContext.tsx
new file mode 100644
index 0000000..4973f14
--- /dev/null
+++ b/src/contexts/BalanceContext.tsx
@@ -0,0 +1,56 @@
+import React, { createContext, useState, useEffect, useContext, useCallback } from 'react';
+import apiService from '../services/api';
+import { getCurrentUserId } from '../constants/user';
+
+interface BalanceContextType {
+ balance: number;
+ updateBalance: () => Promise;
+}
+
+const BalanceContext = createContext(undefined);
+
+export const BalanceProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
+ const [balance, setBalance] = useState(0);
+
+ // Функция для обновления баланса
+ const updateBalance = useCallback(async () => {
+ try {
+ console.log('Обновление баланса...');
+ const newBalance = await apiService.getBalance(getCurrentUserId());
+ console.log(`Текущий баланс: ${newBalance}`);
+ setBalance(newBalance);
+ } catch (error) {
+ console.error('Ошибка при обновлении баланса:', error);
+ }
+ }, []);
+
+ // Инициализация баланса при монтировании компонента
+ useEffect(() => {
+ updateBalance();
+ }, [updateBalance]);
+
+ // Настройка периодического обновления баланса каждые 45 секунд
+ useEffect(() => {
+ const intervalId = setInterval(() => {
+ updateBalance();
+ }, 45000); // 45 секунд
+
+ // Очистка интервала при размонтировании компонента
+ return () => clearInterval(intervalId);
+ }, [updateBalance]);
+
+ return (
+
+ {children}
+
+ );
+};
+
+// Хук для использования контекста баланса
+export const useBalance = (): BalanceContextType => {
+ const context = useContext(BalanceContext);
+ if (context === undefined) {
+ throw new Error('useBalance must be used within a BalanceProvider');
+ }
+ return context;
+};
diff --git a/src/screens/Home.tsx b/src/screens/Home.tsx
index 0dd32a4..2381b21 100644
--- a/src/screens/Home.tsx
+++ b/src/screens/Home.tsx
@@ -11,6 +11,7 @@ import TokenPacksModal from '../components/tokens/TokenPacksModal';
import { paymentService } from '../services/paymentService';
import { tokenPacks } from '../constants/tokenPacks';
import { getCurrentUserId } from '../constants/user';
+import { useBalance } from '../contexts/BalanceContext';
// Интерфейс для хранения данных о последней генерации
interface LastGenerationData {
@@ -23,6 +24,7 @@ interface LastGenerationData {
const Home: React.FC = () => {
const navigate = useNavigate();
const feedbackHandlerRef = useRef(null);
+ const { updateBalance } = useBalance(); // Используем контекст баланса
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [previewUrl, setPreviewUrl] = useState(() => {
@@ -191,6 +193,30 @@ const Home: React.FC = () => {
customPrompt: userPrompt
});
+ // Функция для выполнения серии запросов на обновление баланса
+ const updateBalanceWithRetries = () => {
+ // Функция для выполнения одной попытки обновления баланса
+ const fetchBalance = async (attempt: number) => {
+ try {
+ console.log(`Попытка ${attempt}/5 обновления баланса после генерации...`);
+ await updateBalance();
+ } catch (error) {
+ console.error(`Ошибка при обновлении баланса (попытка ${attempt}/5):`, error);
+ }
+ };
+
+ // Выполняем первую попытку сразу
+ fetchBalance(1);
+
+ // Выполняем остальные попытки с интервалом в 1 секунду
+ for (let i = 2; i <= 5; i++) {
+ setTimeout(() => fetchBalance(i), (i - 1) * 1000);
+ }
+ };
+
+ // Запускаем серию запросов на обновление баланса
+ updateBalanceWithRetries();
+
// Проверяем, была ли ошибка перевода
if (response.translationFailed) {
setNotificationTitle('Недопустимый промпт');
@@ -385,7 +411,30 @@ const Home: React.FC = () => {
paymentService.showBuyTokensPopup(pack, async (userData) => {
if (userData) {
- // Обновляем данные на основе полученной информации
+ // Функция для выполнения серии запросов на обновление баланса
+ const updateBalanceWithRetries = () => {
+ // Функция для выполнения одной попытки обновления баланса
+ const fetchBalance = async (attempt: number) => {
+ try {
+ console.log(`Попытка ${attempt}/5 обновления баланса после пополнения...`);
+ await updateBalance();
+ } catch (error) {
+ console.error(`Ошибка при обновлении баланса (попытка ${attempt}/5):`, error);
+ }
+ };
+
+ // Выполняем первую попытку сразу
+ fetchBalance(1);
+
+ // Выполняем остальные попытки с интервалом в 1 секунду
+ for (let i = 2; i <= 5; i++) {
+ setTimeout(() => fetchBalance(i), (i - 1) * 1000);
+ }
+ };
+
+ // Запускаем серию запросов на обновление баланса
+ updateBalanceWithRetries();
+
// Показываем модальное окно с информацией об успешной оплате
setNotificationTitle('Оплата успешна!');
setNotificationMessage(`Вы успешно приобрели ${pack.tokens + pack.bonusTokens} токенов.`);
@@ -400,6 +449,30 @@ const Home: React.FC = () => {
// Получаем баланс пользователя
const balance = await apiService.getBalance(getCurrentUserId());
+ // Функция для выполнения серии запросов на обновление баланса
+ const updateBalanceWithRetries = () => {
+ // Функция для выполнения одной попытки обновления баланса
+ const fetchBalance = async (attempt: number) => {
+ try {
+ console.log(`Попытка ${attempt}/5 обновления баланса после пополнения...`);
+ await updateBalance();
+ } catch (error) {
+ console.error(`Ошибка при обновлении баланса (попытка ${attempt}/5):`, error);
+ }
+ };
+
+ // Выполняем первую попытку сразу
+ fetchBalance(1);
+
+ // Выполняем остальные попытки с интервалом в 1 секунду
+ for (let i = 2; i <= 5; i++) {
+ setTimeout(() => fetchBalance(i), (i - 1) * 1000);
+ }
+ };
+
+ // Запускаем серию запросов на обновление баланса
+ updateBalanceWithRetries();
+
// Показываем модальное окно с информацией об успешной оплате
setNotificationTitle('Оплата успешна!');
setNotificationMessage(`Вы успешно приобрели ${pack.tokens + pack.bonusTokens} токенов. Ваш текущий баланс: ${balance} токенов.`);
diff --git a/src/services/paymentService.ts b/src/services/paymentService.ts
index 93a138b..5d10bee 100644
--- a/src/services/paymentService.ts
+++ b/src/services/paymentService.ts
@@ -24,25 +24,32 @@ export const paymentService = {
// Открываем встроенный платеж Telegram без предварительного подтверждения
webApp.openInvoice(invoiceLink, async (status: 'paid' | 'cancelled' | 'failed' | 'pending') => {
if (status === 'paid') {
- // Добавляем задержку перед запросом данных пользователя,
- // чтобы дать серверу время на обработку транзакции и обновление баланса
- setTimeout(async () => {
+ // Функция для выполнения одной попытки получения данных пользователя
+ const fetchUserData = async (attempt: number) => {
try {
- // Получаем обновленную информацию о пользователе
+ console.log(`Попытка ${attempt}/5 получения данных пользователя...`);
const userData = await apiService.getUserInfo(userId);
if (onSuccess) {
onSuccess(userData);
}
} catch (error) {
- console.error('Ошибка при получении данных пользователя:', error);
+ console.error(`Ошибка при получении данных пользователя (попытка ${attempt}/5):`, error);
- // Даже если не удалось получить данные, вызываем onSuccess
- if (onSuccess) {
+ // Если это последняя попытка и произошла ошибка, вызываем onSuccess без параметров
+ if (attempt === 5 && onSuccess) {
onSuccess();
}
}
- }, 1000); // Задержка в 1 секунду
+ };
+
+ // Выполняем первую попытку сразу
+ fetchUserData(1);
+
+ // Выполняем остальные попытки с интервалом в 1 секунду
+ for (let i = 2; i <= 5; i++) {
+ setTimeout(() => fetchUserData(i), (i - 1) * 1000);
+ }
}
});
} catch (error) {