sto-k-odnomu/src/pages/RoomPage.jsx

283 lines
8.5 KiB
React
Raw Normal View History

2026-01-03 14:07:04 +00:00
import React, { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';
import { useRoom } from '../hooks/useRoom';
2026-01-06 20:27:50 +00:00
import { questionsApi } from '../services/api';
2026-01-03 14:07:04 +00:00
import QRCode from 'qrcode';
2026-01-06 20:12:36 +00:00
import QRModal from '../components/QRModal';
2026-01-07 13:24:30 +00:00
import NameInputModal from '../components/NameInputModal';
2026-01-03 14:07:04 +00:00
const RoomPage = () => {
const { roomCode } = useParams();
const navigate = useNavigate();
2026-01-07 13:24:30 +00:00
const { user, loginAnonymous, loading: authLoading } = useAuth();
2026-01-06 20:27:50 +00:00
const {
room,
participants,
loading,
error,
joinRoom,
startGame,
updateQuestionPack,
} = useRoom(roomCode);
2026-01-03 14:07:04 +00:00
const [qrCode, setQrCode] = useState('');
const [joined, setJoined] = useState(false);
2026-01-06 20:12:36 +00:00
const [isQRModalOpen, setIsQRModalOpen] = useState(false);
2026-01-07 13:24:30 +00:00
const [isNameModalOpen, setIsNameModalOpen] = useState(false);
2026-01-06 20:27:50 +00:00
const [questionPacks, setQuestionPacks] = useState([]);
const [selectedPackId, setSelectedPackId] = useState('');
const [loadingPacks, setLoadingPacks] = useState(false);
const [updatingPack, setUpdatingPack] = useState(false);
2026-01-03 14:07:04 +00:00
useEffect(() => {
const generateQR = async () => {
try {
2026-01-06 20:12:36 +00:00
// Используем абсолютный 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,
});
2026-01-03 14:07:04 +00:00
setQrCode(qr);
} catch (err) {
console.error('QR generation error:', err);
}
};
if (roomCode) {
generateQR();
}
}, [roomCode]);
2026-01-07 13:24:30 +00:00
// Проверка авторизации и показ модального окна для ввода имени
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('Ошибка при авторизации. Попробуйте еще раз.');
}
};
2026-01-03 14:07:04 +00:00
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]);
2026-01-06 20:27:50 +00:00
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]);
2026-01-03 14:07:04 +00:00
const handleStartGame = () => {
startGame();
navigate(`/game/${roomCode}`);
};
2026-01-06 20:27:50 +00:00
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);
}
};
2026-01-03 14:07:04 +00:00
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>
2026-01-06 20:27:50 +00:00
<div className="question-pack-section">
<h3>Пак вопросов:</h3>
{room.questionPack ? (
<div className="pack-info">
<p>
<strong>{room.questionPack.name}</strong> (
{room.questionPack.questionCount || 0} вопросов)
</p>
2026-01-06 20:46:39 +00:00
{isHost && (
2026-01-06 20:27:50 +00:00
<p className="pack-hint">
2026-01-06 20:46:39 +00:00
Можете изменить пак вопросов в любой момент
2026-01-06 20:27:50 +00:00
</p>
)}
</div>
) : (
<div className="pack-info">
2026-01-06 20:46:39 +00:00
<p className="pack-hint">
Пак вопросов не выбран. Вы можете начать игру без пака и
добавить его позже.
2026-01-06 20:27:50 +00:00
</p>
</div>
)}
2026-01-06 20:46:39 +00:00
{isHost && (
2026-01-06 20:27:50 +00:00
<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>
2026-01-03 14:07:04 +00:00
<div className="button-group">
2026-01-06 20:12:36 +00:00
<button
onClick={() => setIsQRModalOpen(true)}
className="secondary"
>
Показать QR-код
</button>
2026-01-03 14:07:04 +00:00
{isHost && room.status === 'WAITING' && (
<button
onClick={handleStartGame}
className="primary"
>
Начать игру
</button>
)}
<button onClick={() => navigate('/')}>Покинуть комнату</button>
</div>
</div>
2026-01-06 20:12:36 +00:00
<QRModal
isOpen={isQRModalOpen}
onClose={() => setIsQRModalOpen(false)}
qrCode={qrCode}
roomCode={roomCode}
/>
2026-01-07 13:24:30 +00:00
<NameInputModal
isOpen={isNameModalOpen}
onSubmit={handleNameSubmit}
onCancel={null}
/>
2026-01-03 14:07:04 +00:00
</div>
);
};
export default RoomPage;