обавлена серия запросов на обновление баланса после генерации

This commit is contained in:
kazachilo 2025-03-28 12:31:32 +03:00
parent 83f4ccc7d0
commit 861670e481
5 changed files with 172 additions and 53 deletions

View File

@ -4,6 +4,7 @@ import Layout from './components/layout/Layout';
import Home from './screens/Home'; import Home from './screens/Home';
import { initializeUserId } from './constants/user'; import { initializeUserId } from './constants/user';
import { trackScreenView } from './services/analyticsService'; import { trackScreenView } from './services/analyticsService';
import { BalanceProvider } from './contexts/BalanceContext';
// Ленивая загрузка компонентов // Ленивая загрузка компонентов
const OnboardingWelcome = lazy(() => import('./screens/onboarding/OnboardingWelcome')); const OnboardingWelcome = lazy(() => import('./screens/onboarding/OnboardingWelcome'));
@ -147,7 +148,9 @@ const AppContent: React.FC = () => {
const App: React.FC = () => { const App: React.FC = () => {
return ( return (
<BrowserRouter> <BrowserRouter>
<BalanceProvider>
<AppContent /> <AppContent />
</BalanceProvider>
</BrowserRouter> </BrowserRouter>
); );
}; };

View File

@ -7,15 +7,16 @@ import TokenPacksModal from '../tokens/TokenPacksModal';
import { paymentService } from '../../services/paymentService'; 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 styles from './Header.module.css'; import styles from './Header.module.css';
const Header: React.FC = () => { const Header: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const { balance, updateBalance } = useBalance(); // Используем контекст баланса
const [user, setUser] = useState({ const [user, setUser] = useState({
telegramId: 0, telegramId: 0,
username: '', username: '',
avatarUrl: '', avatarUrl: ''
balance: 0
}); });
const [showTokensModal, setShowTokensModal] = useState(false); const [showTokensModal, setShowTokensModal] = useState(false);
const [lastPurchasedPack, setLastPurchasedPack] = useState<any>(null); const [lastPurchasedPack, setLastPurchasedPack] = useState<any>(null);
@ -27,34 +28,21 @@ const Header: React.FC = () => {
// Получаем информацию о пользователе // Получаем информацию о пользователе
const userInfo = getUserInfo(); const userInfo = getUserInfo();
const fetchData = async () => {
try {
// Получаем баланс пользователя
const balance = await apiService.getBalance(getCurrentUserId());
// Если есть данные из Telegram, обновляем состояние // Если есть данные из Telegram, обновляем состояние
if (isTelegramWebAppAvailable()) { if (isTelegramWebAppAvailable()) {
setUser({ setUser({
telegramId: userInfo.id, telegramId: userInfo.id,
username: userInfo.first_name + (userInfo.last_name ? ` ${userInfo.last_name}` : ''), username: userInfo.first_name + (userInfo.last_name ? ` ${userInfo.last_name}` : ''),
avatarUrl: userInfo.photo_url || '/ava.jpg', // Используем фото из Telegram или дефолтное avatarUrl: userInfo.photo_url || '/ava.jpg' // Используем фото из Telegram или дефолтное
balance: balance
}); });
} else { } else {
// Для локальной разработки // Для локальной разработки
setUser({ setUser({
telegramId: 12345678, telegramId: 12345678,
username: "TestUser", username: "TestUser",
avatarUrl: "/ava.jpg", avatarUrl: "/ava.jpg"
balance: balance
}); });
} }
} catch (error) {
console.error('Error fetching user data:', error);
}
};
fetchData();
}, []); }, []);
return ( return (
@ -89,7 +77,7 @@ const Header: React.FC = () => {
<img src={images.tokenIcon} alt="Токены" className={styles.tokenImage} /> <img src={images.tokenIcon} alt="Токены" className={styles.tokenImage} />
</span> </span>
<span className={styles.balanceValue}> <span className={styles.balanceValue}>
{user.balance} {balance}
</span> </span>
</button> </button>
</div> </div>
@ -110,13 +98,8 @@ const Header: React.FC = () => {
paymentService.showBuyTokensPopup(pack, async (userData) => { paymentService.showBuyTokensPopup(pack, async (userData) => {
if (userData) { if (userData) {
// Обновляем данные на основе полученной информации // Обновляем баланс через контекст
if (userData.balance !== undefined) { updateBalance();
setUser(prev => ({
...prev,
balance: userData.balance
}));
}
// Показываем модальное окно с информацией об успешной оплате // Показываем модальное окно с информацией об успешной оплате
setNotificationTitle('Оплата успешна!'); setNotificationTitle('Оплата успешна!');
@ -128,11 +111,8 @@ const Header: React.FC = () => {
// Получаем баланс пользователя // Получаем баланс пользователя
const balance = await apiService.getBalance(getCurrentUserId()); const balance = await apiService.getBalance(getCurrentUserId());
// Обновляем баланс пользователя // Обновляем баланс через контекст
setUser(prev => ({ updateBalance();
...prev,
balance: balance
}));
// Показываем модальное окно с информацией об успешной оплате // Показываем модальное окно с информацией об успешной оплате
setNotificationTitle('Оплата успешна!'); setNotificationTitle('Оплата успешна!');

View File

@ -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<void>;
}
const BalanceContext = createContext<BalanceContextType | undefined>(undefined);
export const BalanceProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [balance, setBalance] = useState<number>(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 (
<BalanceContext.Provider value={{ balance, updateBalance }}>
{children}
</BalanceContext.Provider>
);
};
// Хук для использования контекста баланса
export const useBalance = (): BalanceContextType => {
const context = useContext(BalanceContext);
if (context === undefined) {
throw new Error('useBalance must be used within a BalanceProvider');
}
return context;
};

View File

@ -11,6 +11,7 @@ import TokenPacksModal from '../components/tokens/TokenPacksModal';
import { paymentService } from '../services/paymentService'; import { paymentService } from '../services/paymentService';
import { tokenPacks } from '../constants/tokenPacks'; import { tokenPacks } from '../constants/tokenPacks';
import { getCurrentUserId } from '../constants/user'; import { getCurrentUserId } from '../constants/user';
import { useBalance } from '../contexts/BalanceContext';
// Интерфейс для хранения данных о последней генерации // Интерфейс для хранения данных о последней генерации
interface LastGenerationData { interface LastGenerationData {
@ -23,6 +24,7 @@ interface LastGenerationData {
const Home: React.FC = () => { const Home: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const feedbackHandlerRef = useRef<FeedbackHandlerRef>(null); const feedbackHandlerRef = useRef<FeedbackHandlerRef>(null);
const { updateBalance } = useBalance(); // Используем контекст баланса
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const [previewUrl, setPreviewUrl] = useState<string | undefined>(() => { const [previewUrl, setPreviewUrl] = useState<string | undefined>(() => {
@ -191,6 +193,30 @@ const Home: React.FC = () => {
customPrompt: userPrompt 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) { if (response.translationFailed) {
setNotificationTitle('Недопустимый промпт'); setNotificationTitle('Недопустимый промпт');
@ -385,7 +411,30 @@ const Home: React.FC = () => {
paymentService.showBuyTokensPopup(pack, async (userData) => { paymentService.showBuyTokensPopup(pack, async (userData) => {
if (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('Оплата успешна!'); setNotificationTitle('Оплата успешна!');
setNotificationMessage(`Вы успешно приобрели ${pack.tokens + pack.bonusTokens} токенов.`); setNotificationMessage(`Вы успешно приобрели ${pack.tokens + pack.bonusTokens} токенов.`);
@ -400,6 +449,30 @@ const Home: React.FC = () => {
// Получаем баланс пользователя // Получаем баланс пользователя
const balance = await apiService.getBalance(getCurrentUserId()); 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('Оплата успешна!'); setNotificationTitle('Оплата успешна!');
setNotificationMessage(`Вы успешно приобрели ${pack.tokens + pack.bonusTokens} токенов. Ваш текущий баланс: ${balance} токенов.`); setNotificationMessage(`Вы успешно приобрели ${pack.tokens + pack.bonusTokens} токенов. Ваш текущий баланс: ${balance} токенов.`);

View File

@ -24,25 +24,32 @@ 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') {
// Добавляем задержку перед запросом данных пользователя, // Функция для выполнения одной попытки получения данных пользователя
// чтобы дать серверу время на обработку транзакции и обновление баланса const fetchUserData = async (attempt: number) => {
setTimeout(async () => {
try { try {
// Получаем обновленную информацию о пользователе console.log(`Попытка ${attempt}/5 получения данных пользователя...`);
const userData = await apiService.getUserInfo(userId); const userData = await apiService.getUserInfo(userId);
if (onSuccess) { if (onSuccess) {
onSuccess(userData); onSuccess(userData);
} }
} catch (error) { } catch (error) {
console.error('Ошибка при получении данных пользователя:', error); console.error(`Ошибка при получении данных пользователя (попытка ${attempt}/5):`, error);
// Даже если не удалось получить данные, вызываем onSuccess // Если это последняя попытка и произошла ошибка, вызываем onSuccess без параметров
if (onSuccess) { if (attempt === 5 && onSuccess) {
onSuccess(); onSuccess();
} }
} }
}, 1000); // Задержка в 1 секунду };
// Выполняем первую попытку сразу
fetchUserData(1);
// Выполняем остальные попытки с интервалом в 1 секунду
for (let i = 2; i <= 5; i++) {
setTimeout(() => fetchUserData(i), (i - 1) * 1000);
}
} }
}); });
} catch (error) { } catch (error) {