Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/pages/BattlePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Footer from './components/layout/Footer';
import SettingsModal from './components/ui/SettingsModal';
import QueueModal from './components/ui/QueueModal';
import { useQueueSSE } from './useQueueSSE';
import {useMatchStore} from "./useMatchStore.js";
import './BattlePage.css';

if (typeof global === 'undefined') {
Expand All @@ -16,10 +17,15 @@ export default function BattlePage() {
const navigate = useNavigate();
const { logout } = useUser();
const [showSettings, setShowSettings] = useState(false);

const resetStore = useMatchStore((state) => state.resetStore);
const { isModalOpen, queueSize, waitSeconds, isConnecting, enterQueue, leaveQueue } =
useQueueSSE();

const enterQueueAndResetStore = () => {
resetStore();
enterQueue();
}

const handleLogout = () => {
logout();
navigate('/login');
Expand All @@ -37,7 +43,7 @@ export default function BattlePage() {

<button
className="btn-battle"
onClick={enterQueue}
onClick={enterQueueAndResetStore}
disabled={isConnecting}
>
{isConnecting ? 'Подключение...' : 'В БОЙ!'}
Expand Down
50 changes: 25 additions & 25 deletions src/pages/DraftPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const DraftPage = () => {
whiteSpace: 'nowrap'
}}
>
{isMyTurn ? "Ходите вы" : "Ходит оппонент"}
{isMyTurn ? "Вы баните опцию" : "Оппонент банит опцию"}
</div>
</header>

Expand All @@ -103,30 +103,30 @@ const DraftPage = () => {

<div className="draft-options-list">
{options && options.length > 0 ? (
options.map(({ option, banned }) => {
const isClickable = active && !banned;

// Определяем классы стилей для кнопки
let btnClass = "draft-ban-btn";

if (banned) {
btnClass += " banned";
} else if (isCategoryFinished) {
// Если категория завершена и опция не забанена — это наш победитель!
btnClass += " selected-winner";
}

return (
<button
key={option}
disabled={!isClickable}
onClick={() => sendBan(category, option)}
className={btnClass}
>
{banned ? `${option}` : isCategoryFinished ? `${option}` : `${option}`}
</button>
);
})
// Копируем массив через [...] и сортируем по свойству option (строка с именем)
[...options]
.sort((a, b) => a.option.localeCompare(b.option))
.map(({ option, banned }) => {
const isClickable = active && !banned;

let btnClass = "draft-ban-btn";
if (banned) {
btnClass += " banned";
} else if (isCategoryFinished) {
btnClass += " selected-winner";
}

return (
<button
key={option}
disabled={!isClickable}
onClick={() => sendBan(category, option)}
className={btnClass}
>
{option}
</button>
);
})
) : (
<p className="muted">Опций не осталось</p>
)}
Expand Down
78 changes: 39 additions & 39 deletions src/pages/WorkspacePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import SettingsModal from './components/ui/SettingsModal';
import { useState, useEffect } from 'react';
import { PanelGroup, PanelResizeHandle } from "react-resizable-panels";
import {useNavigate, useParams} from 'react-router-dom';
import { useParcelPolling } from './useParcelPolling';
import Header from './components/layout/Header';
import Footer from './components/layout/Footer';
import LeftWorkspace from './components/workspace/LeftWorkspace';
import RightWorkspace from './components/workspace/RightWorkspace';
import './WorkspacePage.css';
import MatchResultOverlay from './MatchResultOverlay';
import { useMatchStore } from "./useMatchStore.js";
import {useMatchSession} from "./useMatchSession.js";

function WorkspacePage() {
const {matchId} = useParams();
Expand All @@ -19,7 +19,7 @@ function WorkspacePage() {
const [isDarkMode, setIsDarkMode] = useState(true);
const [isSwapped, setIsSwapped] = useState(false);
const [showSettings, setShowSettings] = useState(false);
const { submissions } = useParcelPolling();
const { userSubmissions } = useMatchSession();

const matchResult = useMatchStore((state) => state.matchResult);

Expand All @@ -37,48 +37,48 @@ function WorkspacePage() {
};

return (
<div className="layout-container">
<Header onSettingsClick={() => setShowSettings(true)} />
<div className="layout-container">
<Header onSettingsClick={() => setShowSettings(true)} />

<main className="workspace">
<PanelGroup direction="horizontal">
{isSwapped ? (
<RightWorkspace position="left" submissions={submissions} />
) : (
<LeftWorkspace isDarkMode={isDarkMode} position="left" submissions = {submissions} matchId = {matchId} />
)}

<PanelResizeHandle className="resizer-vertical">
<div className="resizer-line-vertical"></div>
</PanelResizeHandle>

{isSwapped ? (
<LeftWorkspace isDarkMode={isDarkMode} position="right" />
) : (
<RightWorkspace position="right" submissions={submissions} />
)}
</PanelGroup>
</main>
<main className="workspace">
<PanelGroup direction="horizontal">
{isSwapped ? (
<RightWorkspace position="left" submissions={userSubmissions} />
) : (
<LeftWorkspace isDarkMode={isDarkMode} position="left" submissions = {userSubmissions} matchId = {matchId} />
)}

<Footer />
<PanelResizeHandle className="resizer-vertical">
<div className="resizer-line-vertical"></div>
</PanelResizeHandle>

<SettingsModal
isOpen={showSettings}
onClose={() => setShowSettings(false)}
onLogout={handleLogout}
themeConfig={{ isDarkMode, setIsDarkMode }}
workspaceConfig={{ isSwapped, setIsSwapped }}
/>
{isSwapped ? (
<LeftWorkspace isDarkMode={isDarkMode} position="right" />
) : (
<RightWorkspace position="right" submissions={userSubmissions} />
)}
</PanelGroup>
</main>

{matchResult && (
<MatchResultOverlay
outcome={matchResult.outcome}
newRating={matchResult.newRating}
ratingDelta={matchResult.ratingDelta}
/>
)}
<Footer />

</div>
<SettingsModal
isOpen={showSettings}
onClose={() => setShowSettings(false)}
onLogout={handleLogout}
themeConfig={{ isDarkMode, setIsDarkMode }}
workspaceConfig={{ isSwapped, setIsSwapped }}
/>

{matchResult && (
<MatchResultOverlay
outcome={matchResult.outcome}
newRating={matchResult.newRating}
ratingDelta={matchResult.ratingDelta}
/>
)}

</div>
);
}

Expand Down
25 changes: 23 additions & 2 deletions src/pages/useMatchSession.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import {useUser} from "../context/UserContext.jsx";

export const useMatchSession = (matchId) => {
const {isConnected, publish} = useWebSocket();

Check failure on line 10 in src/pages/useMatchSession.js

View workflow job for this annotation

GitHub Actions / test

src/pages/WorkspacePage.test.jsx > WorkspacePage > открытие настроек и нажатие выхода вызывает logout и перенаправляет на /login

TypeError: Cannot destructure property 'isConnected' of '(0 , __vite_ssr_import_1__.useWebSocket)(...)' as it is null. ❯ useMatchSession src/pages/useMatchSession.js:10:12 ❯ WorkspacePage src/pages/WorkspacePage.jsx:22:31 ❯ Object.react_stack_bottom_frame node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 ❯ updateFunctionComponent node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 ❯ beginWork node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 ❯ runWithFiberInDEV node_modules/react-dom/cjs/react-dom-client.development.js:874:13 ❯ performUnitOfWork node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 ❯ workLoopSync node_modules/react-dom/cjs/react-dom-client.development.js:17469:41 ❯ renderRootSync node_modules/react-dom/cjs/react-dom-client.development.js:17450:11

Check failure on line 10 in src/pages/useMatchSession.js

View workflow job for this annotation

GitHub Actions / test

src/pages/WorkspacePage.test.jsx > WorkspacePage > при нажатии на PVP появляется confirm, при отмене редирект не происходит

TypeError: Cannot destructure property 'isConnected' of '(0 , __vite_ssr_import_1__.useWebSocket)(...)' as it is null. ❯ useMatchSession src/pages/useMatchSession.js:10:12 ❯ WorkspacePage src/pages/WorkspacePage.jsx:22:31 ❯ Object.react_stack_bottom_frame node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 ❯ updateFunctionComponent node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 ❯ beginWork node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 ❯ runWithFiberInDEV node_modules/react-dom/cjs/react-dom-client.development.js:874:13 ❯ performUnitOfWork node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 ❯ workLoopSync node_modules/react-dom/cjs/react-dom-client.development.js:17469:41 ❯ renderRootSync node_modules/react-dom/cjs/react-dom-client.development.js:17450:11

Check failure on line 10 in src/pages/useMatchSession.js

View workflow job for this annotation

GitHub Actions / test

src/pages/WorkspacePage.test.jsx > WorkspacePage > при нажатии на логотип появляется confirm, при согласии кидает на /battle

TypeError: Cannot destructure property 'isConnected' of '(0 , __vite_ssr_import_1__.useWebSocket)(...)' as it is null. ❯ useMatchSession src/pages/useMatchSession.js:10:12 ❯ WorkspacePage src/pages/WorkspacePage.jsx:22:31 ❯ Object.react_stack_bottom_frame node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 ❯ updateFunctionComponent node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 ❯ beginWork node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 ❯ runWithFiberInDEV node_modules/react-dom/cjs/react-dom-client.development.js:874:13 ❯ performUnitOfWork node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 ❯ workLoopSync node_modules/react-dom/cjs/react-dom-client.development.js:17469:41 ❯ renderRootSync node_modules/react-dom/cjs/react-dom-client.development.js:17450:11
const navigate = useNavigate();
const draftSessionDTO = useMatchStore((state) => state.draftSessionDTO);
const isRedirect = useMatchStore((state) => state.matchStarted);
Expand All @@ -16,6 +16,11 @@
const [opponent, setOpponent] = useState(null);
const {user} = useUser();
const [sessionData, setSessionData] = useState(null);
const matchSubmissions = useMatchStore((state) => state.matchSubmissions);
const userSubmissions = matchSubmissions
.filter(sub => sub.userId === user?.id);
console.log(`user id : ${user?.id}`);

useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
setSessionData(draftSessionDTO?.draftSession);
Expand All @@ -27,7 +32,11 @@
const amIFirstUser = user.id === sessionData.firstUserId;
const opponentId = amIFirstUser ? sessionData.secondUserId : sessionData.firstUserId;

if (!opponentId) return;
if (!opponentId) {
console.log(`session data: ${sessionData}`);
console.log(`opponent id : ${opponentId}`);
return;
};

api.get(`user/info/${opponentId}`)
.then(response => {
Expand All @@ -51,9 +60,21 @@
}
}, [isRedirect, matchId, navigate, resetStore]);

useEffect(() => {
if (matchSubmissions.length === 0) return;

const lastSubmission = matchSubmissions[0];
console.log(`submission id: ${lastSubmission.userId}`);
if (lastSubmission.userId !== user?.id) {

toast.success("Оппонент попытался решить задачу");
}

}, [matchSubmissions.length]);

Check warning on line 73 in src/pages/useMatchSession.js

View workflow job for this annotation

GitHub Actions / test

React Hook useEffect has missing dependencies: 'matchSubmissions' and 'user?.id'. Either include them or remove the dependency array

const sendBan = (category, value) => {
publish(`/app/${matchId}/ban`, {category: category, banObject: value});
};

return {sessionData, opponent, error, sendBan, isConnected};
return {sessionData, opponent, error, sendBan, isConnected, userSubmissions};
};
9 changes: 8 additions & 1 deletion src/pages/useMatchStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const useMatchStore = create((set) => ({
error: { stage: null, message: null },
matchStarted: false,
matchResult: null,
matchSubmissions : [],

handleIncomingMessage: (payload) => {
switch (payload.status) {
Expand All @@ -18,12 +19,18 @@ export const useMatchStore = create((set) => ({
case 'MATCH_FINISHED':
set({ matchResult: payload.payload });
break;

case 'SUBMISSION':
set((state) => ({
matchSubmissions: [ payload.payload, ...state.matchSubmissions]
}));
break;
}
},

handleErrorMessage: (payload) => {
set({ error: { stage: null, message: payload.message } });
},

resetStore: () => set({ matchStarted: null, error: null, matchResult: null }),
resetStore: () => set({ matchStarted: false, error: {stage : null, message : null}, matchResult: null, draftSessionDTO : null, matchSubmissions : [] }),
}));
Loading