sto-k-odnomu/src/pages/RoomPage.jsx
2026-01-07 17:54:32 +03:00

300 lines
9.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useEffect, useState, useCallback } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';
import { useRoom } from '../hooks/useRoom';
import { questionsApi } from '../services/api';
import QRCode from 'qrcode';
import QRModal from '../components/QRModal';
import NameInputModal from '../components/NameInputModal';
const RoomPage = () => {
const { roomCode } = useParams();
const navigate = useNavigate();
const { user, loginAnonymous, loading: authLoading } = useAuth();
// Callback для автоматической навигации при старте игры
const handleGameStartedEvent = useCallback(() => {
navigate(`/game/${roomCode}`);
}, [navigate, roomCode]);
const {
room,
participants,
loading,
error,
joinRoom,
startGame,
updateQuestionPack,
} = useRoom(roomCode, handleGameStartedEvent);
const [qrCode, setQrCode] = useState('');
const [joined, setJoined] = useState(false);
const [isQRModalOpen, setIsQRModalOpen] = useState(false);
const [isNameModalOpen, setIsNameModalOpen] = useState(false);
const [questionPacks, setQuestionPacks] = useState([]);
const [selectedPackId, setSelectedPackId] = useState('');
const [loadingPacks, setLoadingPacks] = useState(false);
const [updatingPack, setUpdatingPack] = useState(false);
useEffect(() => {
const generateQR = async () => {
try {
// Используем абсолютный URL с протоколом для работы из любого приложения
const origin = window.location.origin ||
`${window.location.protocol}//${window.location.host}`;
const url = `${origin}/join-room?code=${roomCode}`;
const qr = await QRCode.toDataURL(url, {
errorCorrectionLevel: 'M',
type: 'image/png',
quality: 0.92,
margin: 1,
});
setQrCode(qr);
} catch (err) {
console.error('QR generation error:', err);
}
};
if (roomCode) {
generateQR();
}
}, [roomCode]);
// Проверка авторизации и показ модального окна для ввода имени
useEffect(() => {
if (!authLoading && !user && room && !loading) {
setIsNameModalOpen(true);
} else if (user) {
setIsNameModalOpen(false);
}
}, [authLoading, user, room, loading]);
// Обработка ввода имени и авторизация
const handleNameSubmit = async (name) => {
try {
await loginAnonymous(name);
setIsNameModalOpen(false);
} catch (error) {
console.error('Login error:', error);
alert('Ошибка при авторизации. Попробуйте еще раз.');
}
};
useEffect(() => {
const handleJoin = async () => {
if (room && user && !joined) {
const isParticipant = participants.some((p) => p.userId === user.id);
if (!isParticipant) {
try {
await joinRoom(room.id, user.id, user.name || 'Гость', 'PLAYER');
setJoined(true);
} catch (error) {
console.error('Join error:', error);
}
} else {
setJoined(true);
}
}
};
handleJoin();
}, [room, user, participants, joined, joinRoom]);
useEffect(() => {
const fetchPacks = async () => {
if (user) {
try {
setLoadingPacks(true);
const response = await questionsApi.getPacks(user.id);
setQuestionPacks(response.data);
} catch (error) {
console.error('Error fetching question packs:', error);
} finally {
setLoadingPacks(false);
}
}
};
if (room && user && room.hostId === user.id) {
fetchPacks();
}
}, [room, user]);
useEffect(() => {
if (room && room.questionPackId) {
setSelectedPackId(room.questionPackId);
} else {
setSelectedPackId('');
}
}, [room]);
// Автоматически перенаправляем на страницу игры, если игра уже началась
useEffect(() => {
if (room && room.status === 'PLAYING') {
navigate(`/game/${roomCode}`);
}
}, [room, roomCode, navigate]);
const handleStartGame = () => {
startGame();
navigate(`/game/${roomCode}`);
};
const handleUpdateQuestionPack = async () => {
if (!selectedPackId) {
alert('Выберите пак вопросов');
return;
}
try {
setUpdatingPack(true);
await updateQuestionPack(selectedPackId);
alert('Пак вопросов успешно добавлен');
} catch (error) {
console.error('Error updating question pack:', error);
alert('Ошибка при обновлении пака вопросов');
} finally {
setUpdatingPack(false);
}
};
if (loading) {
return <div className="loading">Загрузка комнаты...</div>;
}
if (error) {
return (
<div className="error-page">
<h1>Ошибка</h1>
<p>{error}</p>
<button onClick={() => navigate('/')}>На главную</button>
</div>
);
}
if (!room) {
return (
<div className="error-page">
<h1>Комната не найдена</h1>
<button onClick={() => navigate('/')}>На главную</button>
</div>
);
}
const isHost = user && room.hostId === user.id;
return (
<div className="room-page">
<div className="room-container">
<h1>Комната: {room.code}</h1>
<div className="room-info">
<p>Статус: {room.status === 'WAITING' ? 'Ожидание игроков' : room.status}</p>
<p>Игроков: {participants.length}/{room.maxPlayers}</p>
</div>
<div className="participants-list">
<h3>Участники:</h3>
<ul>
{participants.map((participant) => (
<li key={participant.id}>
{participant.name} {participant.role === 'HOST' && '(Ведущий)'}
{participant.role === 'SPECTATOR' && '(Зритель)'}
</li>
))}
</ul>
</div>
<div className="question-pack-section">
<h3>Пак вопросов:</h3>
{room.questionPack ? (
<div className="pack-info">
<p>
<strong>{room.questionPack.name}</strong> (
{room.questionPack.questionCount || 0} вопросов)
</p>
{isHost && (
<p className="pack-hint">
Можете изменить пак вопросов в любой момент
</p>
)}
</div>
) : (
<div className="pack-info">
<p className="pack-hint">
Пак вопросов не выбран. Вы можете начать игру без пака и
добавить его позже.
</p>
</div>
)}
{isHost && (
<div className="pack-selector">
<select
value={selectedPackId}
onChange={(e) => setSelectedPackId(e.target.value)}
disabled={loadingPacks || updatingPack}
>
<option value="">Выберите пак вопросов</option>
{questionPacks.map((pack) => (
<option key={pack.id} value={pack.id}>
{pack.name} ({pack.questionCount} вопросов)
</option>
))}
</select>
<button
onClick={handleUpdateQuestionPack}
disabled={
!selectedPackId ||
selectedPackId === room.questionPackId ||
updatingPack ||
loadingPacks
}
className="secondary"
>
{updatingPack ? 'Сохранение...' : 'Сохранить пак'}
</button>
</div>
)}
</div>
<div className="button-group">
<button
onClick={() => setIsQRModalOpen(true)}
className="secondary"
>
Показать QR-код
</button>
{isHost &&
(room.status === 'WAITING' ||
(room.status === 'PLAYING' &&
(!room.questionPack ||
room.questionPack.questionCount === 0 ||
room.currentQuestionIndex === 0))) && (
<button
onClick={handleStartGame}
className="primary"
>
Начать игру
</button>
)}
<button onClick={() => navigate('/')}>Покинуть комнату</button>
</div>
</div>
<QRModal
isOpen={isQRModalOpen}
onClose={() => setIsQRModalOpen(false)}
qrCode={qrCode}
roomCode={roomCode}
/>
<NameInputModal
isOpen={isNameModalOpen}
onSubmit={handleNameSubmit}
onCancel={null}
/>
</div>
);
};
export default RoomPage;