@@ -52,10 +48,6 @@ const Home = () => {
Присоединиться к комнате
-
-
- Локальная игра
-
{user && (
diff --git a/src/pages/LocalGame.jsx b/src/pages/LocalGame.jsx
deleted file mode 100644
index 99f5f59..0000000
--- a/src/pages/LocalGame.jsx
+++ /dev/null
@@ -1,8 +0,0 @@
-import React from 'react';
-import LocalGameApp from '../components/LocalGameApp';
-
-const LocalGame = () => {
- return
;
-};
-
-export default LocalGame;
diff --git a/src/pages/RoomPage.jsx b/src/pages/RoomPage.jsx
index 1282e77..4d15da3 100644
--- a/src/pages/RoomPage.jsx
+++ b/src/pages/RoomPage.jsx
@@ -4,9 +4,12 @@ import { useAuth } from '../context/AuthContext';
import { useRoom } from '../hooks/useRoom';
import { questionsApi } from '../services/api';
import QRCode from 'qrcode';
+import socketService from '../services/socket';
import QRModal from '../components/QRModal';
import NameInputModal from '../components/NameInputModal';
import PasswordModal from '../components/PasswordModal';
+import RoleSelectionModal from '../components/RoleSelectionModal';
+import GameManagementModal from '../components/GameManagementModal';
const RoomPage = () => {
const { roomCode } = useParams();
@@ -28,18 +31,18 @@ const RoomPage = () => {
fetchRoomWithPassword,
joinRoom,
startGame,
- updateQuestionPack,
} = useRoom(roomCode, handleGameStartedEvent, password);
const [qrCode, setQrCode] = useState('');
const [joined, setJoined] = useState(false);
const [isQRModalOpen, setIsQRModalOpen] = useState(false);
const [isNameModalOpen, setIsNameModalOpen] = useState(false);
const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false);
+ const [isRoleSelectionModalOpen, setIsRoleSelectionModalOpen] = useState(false);
+ const [isQuestionsModalOpen, setIsQuestionsModalOpen] = useState(false);
const [passwordError, setPasswordError] = useState(null);
+ const [joinError, setJoinError] = useState(null);
+ const [selectedRole, setSelectedRole] = useState('PLAYER');
const [questionPacks, setQuestionPacks] = useState([]);
- const [selectedPackId, setSelectedPackId] = useState('');
- const [loadingPacks, setLoadingPacks] = useState(false);
- const [updatingPack, setUpdatingPack] = useState(false);
useEffect(() => {
const generateQR = async () => {
@@ -116,16 +119,38 @@ const RoomPage = () => {
}
};
+ // Показываем модальное окно выбора роли, если allowSpectators === true и пользователь авторизован
+ useEffect(() => {
+ if (
+ room &&
+ user &&
+ !joined &&
+ !isRoleSelectionModalOpen &&
+ room.allowSpectators &&
+ !participants.some((p) => p.userId === user.id)
+ ) {
+ setIsRoleSelectionModalOpen(true);
+ }
+ }, [room, user, joined, participants, isRoleSelectionModalOpen]);
+
+ // Автоматическое присоединение как PLAYER, если зрители не разрешены
useEffect(() => {
const handleJoin = async () => {
- if (room && user && !joined) {
+ if (room && user && !joined && !isRoleSelectionModalOpen) {
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);
+ // Если зрители не разрешены, присоединяемся как PLAYER автоматически
+ if (!room.allowSpectators) {
+ try {
+ setJoinError(null);
+ await joinRoom(room.id, user.id, user.name || 'Гость', 'PLAYER');
+ setJoined(true);
+ } catch (error) {
+ console.error('Join error:', error);
+ const errorMessage = error.response?.data?.message || error.message || 'Ошибка при присоединении к комнате';
+ setJoinError(errorMessage);
+ alert(errorMessage);
+ }
}
} else {
setJoined(true);
@@ -134,35 +159,50 @@ const RoomPage = () => {
};
handleJoin();
- }, [room, user, participants, joined, joinRoom]);
+ }, [room, user, participants, joined, joinRoom, isRoleSelectionModalOpen]);
+
+ // Обработка выбора роли
+ const handleRoleSubmit = async (role) => {
+ if (!room || !user) return;
+
+ try {
+ setJoinError(null);
+ setIsRoleSelectionModalOpen(false);
+ await joinRoom(room.id, user.id, user.name || 'Гость', role);
+ setSelectedRole(role);
+ setJoined(true);
+ } catch (error) {
+ console.error('Join error:', error);
+ const errorMessage = error.response?.data?.message || error.message || 'Ошибка при присоединении к комнате';
+ setJoinError(errorMessage);
+ alert(errorMessage);
+ // Открываем модальное окно снова при ошибке
+ setIsRoleSelectionModalOpen(true);
+ }
+ };
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) {
+ // Проверяем роль участника для поддержки нескольких хостов
+ const currentUserParticipant = user && participants
+ ? participants.find(p => p.userId === user.id)
+ : null;
+ const isHost = currentUserParticipant?.role === 'HOST';
+
+ if (room && user && isHost) {
fetchPacks();
}
- }, [room, user]);
-
- useEffect(() => {
- if (room && room.questionPackId) {
- setSelectedPackId(room.questionPackId);
- } else {
- setSelectedPackId('');
- }
- }, [room]);
+ }, [room, user, participants]);
// Автоматически перенаправляем на страницу игры, если игра уже началась
useEffect(() => {
@@ -176,23 +216,61 @@ const RoomPage = () => {
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);
+ // Получаем вопросы из roomPack (может быть JSON строкой или массивом)
+ const getRoomQuestions = () => {
+ if (!room?.roomPack?.questions) return [];
+ const questions = room.roomPack.questions;
+ if (typeof questions === 'string') {
+ try {
+ return JSON.parse(questions);
+ } catch {
+ return [];
+ }
}
+ return Array.isArray(questions) ? questions : [];
};
+ const roomQuestions = getRoomQuestions();
+
+ // Обновление вопросов через WebSocket
+ const handleUpdateRoomQuestions = useCallback(
+ async (newQuestions) => {
+ if (!room?.id || !user) return;
+
+ try {
+ socketService.updateRoomPack(
+ room.id,
+ room.code,
+ user.id,
+ newQuestions
+ );
+ } catch (error) {
+ console.error('Error updating room questions:', error);
+ alert('Ошибка при сохранении вопросов');
+ }
+ },
+ [room, user]
+ );
+
+ // Изменение роли участника через WebSocket
+ const handleChangeParticipantRole = useCallback(
+ (participantId, newRole) => {
+ if (!room?.id || !user) return;
+
+ try {
+ socketService.changeParticipantRole(
+ room.id,
+ room.code,
+ user.id,
+ participantId,
+ newRole
+ );
+ } catch (error) {
+ console.error('Error changing participant role:', error);
+ alert('Ошибка при изменении роли участника');
+ }
+ },
+ [room, user]
+ );
if (loading) {
return
Загрузка комнаты...
;
@@ -218,7 +296,11 @@ const RoomPage = () => {
);
}
- const isHost = user && room && room.hostId === user.id;
+ // Проверяем роль участника для поддержки нескольких хостов
+ const currentUserParticipant = user && participants
+ ? participants.find(p => p.userId === user.id)
+ : null;
+ const isHost = currentUserParticipant?.role === 'HOST';
// Если требуется пароль, показываем только модальное окно
if (requiresPassword && !room) {
@@ -258,55 +340,25 @@ const RoomPage = () => {