Files
thisORthat/frontend/src/context/AppContext.jsx
T
2026-08-13 11:35:00 +03:00

190 lines
5.2 KiB
React

import React, { createContext, useContext, useState, useCallback, useEffect } from 'react';
import { api } from '../services/api';
import { currentUser, initTelegramApp } from '../services/auth';
const AppContext = createContext(null);
export function AppProvider({ children }) {
// App state
const [isLoading, setIsLoading] = useState(true);
const [user, setUser] = useState(null);
const [error, setError] = useState(null);
// Card queue
const [cardQueue, setCardQueue] = useState([]);
const [currentCardIndex, setCurrentCardIndex] = useState(0);
const [chosenCard, setChosenCard] = useState(null); // null | "A" | "B"
// Panels
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isCommentsOpen, setIsCommentsOpen] = useState(false);
const [menuScreen, setMenuScreen] = useState('menu'); // 'menu' | 'about' | 'create'
// Toast
const [toast, setToast] = useState(null);
// Current card helper
const currentCard = cardQueue[currentCardIndex] || null;
// Initialize app
useEffect(() => {
async function init() {
try {
initTelegramApp();
// Check/register user
const checkResult = await api.checkUser(currentUser.id);
if (!checkResult.result) {
await api.addUser({
user_id: currentUser.id,
username: currentUser.username,
first_name: currentUser.first_name,
last_name: currentUser.last_name,
photo_url: currentUser.photo_url,
});
}
const userResult = await api.getUser(currentUser.id);
if (!userResult.error) {
setUser(userResult.result);
}
// Load first batch of cards
await loadCards();
} catch (err) {
setError('Не удалось загрузить приложение');
console.error('Init error:', err);
} finally {
setIsLoading(false);
}
}
init();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Load cards batch
const loadCards = useCallback(async () => {
try {
const result = await api.getRandomCards(currentUser.id);
if (!result.error && Array.isArray(result.result) && result.result.length > 0) {
setCardQueue(result.result);
setCurrentCardIndex(0);
setChosenCard(null);
} else {
// No more cards or error
setCardQueue([]);
setCurrentCardIndex(0);
}
} catch (err) {
console.error('Load cards error:', err);
setError('Ошибка загрузки карточек');
}
}, []);
// Choose a card (A or B)
const chooseCard = useCallback((choice) => {
if (chosenCard) {
// Second tap on chosen card — go next
if (choice === chosenCard) {
goToNextCard();
}
return;
}
setChosenCard(choice);
// Fire select_choice to backend
if (currentCard) {
api.selectChoice(currentUser.id, currentCard.card_id, choice).catch(console.error);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [chosenCard, currentCard]);
// Go to next card
const goToNextCard = useCallback(async () => {
const nextIndex = currentCardIndex + 1;
if (nextIndex < cardQueue.length) {
setCurrentCardIndex(nextIndex);
setChosenCard(null);
} else {
// Load next batch
await loadCards();
}
}, [currentCardIndex, cardQueue.length, loadCards]);
// Reactions
const likeCard = useCallback(async () => {
if (!currentCard || !chosenCard) return;
const result = await api.likeCard(currentUser.id, currentCard.card_id);
if (!result.error) {
// Refresh user data to get updated liked_card_ids
const userResult = await api.getUser(currentUser.id);
if (!userResult.error) setUser(userResult.result);
}
return result;
}, [currentCard, chosenCard]);
const dislikeCard = useCallback(async () => {
if (!currentCard || !chosenCard) return;
const result = await api.dislikeCard(currentUser.id, currentCard.card_id);
if (!result.error) {
const userResult = await api.getUser(currentUser.id);
if (!userResult.error) setUser(userResult.result);
}
return result;
}, [currentCard, chosenCard]);
// Toast helper
const showToast = useCallback((message, duration = 2500) => {
setToast(message);
setTimeout(() => setToast(null), duration);
}, []);
// Menu helpers
const openMenu = useCallback(() => {
setIsMenuOpen(true);
setMenuScreen('menu');
}, []);
const closeMenu = useCallback(() => {
setIsMenuOpen(false);
setMenuScreen('menu');
}, []);
const value = {
// State
isLoading,
user,
error,
currentCard,
chosenCard,
cardQueue,
currentCardIndex,
isMenuOpen,
isCommentsOpen,
menuScreen,
toast,
// Actions
chooseCard,
goToNextCard,
loadCards,
likeCard,
dislikeCard,
openMenu,
closeMenu,
setMenuScreen,
setIsCommentsOpen,
showToast,
setError,
};
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
}
export function useApp() {
const context = useContext(AppContext);
if (!context) {
throw new Error('useApp must be used within AppProvider');
}
return context;
}