sto-k-odnomu/src/pages/RoomPage.jsx
2026-01-06 23:27:50 +03:00

258 lines
7.7 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 } 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';
const RoomPage = () => {
const { roomCode } = useParams();
const navigate = useNavigate();
const { user } = useAuth();
const {
room,
participants,
loading,
error,
joinRoom,
startGame,
updateQuestionPack,
} = useRoom(roomCode);
const [qrCode, setQrCode] = useState('');
const [joined, setJoined] = useState(false);
const [isQRModalOpen, setIsQRModalOpen] = 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(() => {
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]);
const handleStartGame = () => {
if (!room.questionPackId) {
alert('Выберите пак вопросов перед началом игры');
return;
}
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 && room.status === 'WAITING' && (
<p className="pack-hint">
Можете изменить пак вопросов перед началом игры
</p>
)}
</div>
) : (
<div className="pack-info">
<p className="pack-warning">
Пак вопросов не выбран. Выберите пак для начала игры.
</p>
</div>
)}
{isHost && room.status === 'WAITING' && (
<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' && (
<button
onClick={handleStartGame}
disabled={participants.length < 2 || !room.questionPackId}
className="primary"
>
Начать игру
</button>
)}
<button onClick={() => navigate('/')}>Покинуть комнату</button>
</div>
</div>
<QRModal
isOpen={isQRModalOpen}
onClose={() => setIsQRModalOpen(false)}
qrCode={qrCode}
roomCode={roomCode}
/>
</div>
);
};
export default RoomPage;