Skip to content

Commit

Permalink
add win/lost alert and sizes for TaskAssignment
Browse files Browse the repository at this point in the history
  • Loading branch information
ReDBrother committed Nov 2, 2023
1 parent 09fb956 commit d3a280b
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 76 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import React, { useCallback } from 'react';

import cn from 'classnames';
import isEmpty from 'lodash/isEmpty';
import PropTypes from 'prop-types';
import { useDispatch } from 'react-redux';
Expand Down Expand Up @@ -47,21 +48,36 @@ function ShowGuideButton() {
function TaskAssignment({
task,
taskLanguage,
taskSize = 0,
handleSetLanguage,
changeTaskDescriptionSizes,
hideGuide = false,
hideContribution = false,
}) {
const [avaibleLanguages, displayLanguage, description] = useTaskDescriptionParams(
task,
taskLanguage,
);
const [avaibleLanguages, displayLanguage, description] = useTaskDescriptionParams(task, taskLanguage);

const handleTaskSizeIncrease = useCallback(() => {
changeTaskDescriptionSizes(taskSize + 1);
}, [taskSize, changeTaskDescriptionSizes]);

const handleTaskSizeDecrease = useCallback(() => {
changeTaskDescriptionSizes(taskSize - 1);
}, [taskSize, changeTaskDescriptionSizes]);

if (isEmpty(task)) {
return null;
}

const cardClassName = cn('card h-100 border-0 shadow-sm', {
h5: taskSize === 1,
h4: taskSize === 2,
h3: taskSize === 3,
h2: taskSize === 4,
h1: taskSize > 4,
});

return (
<div className="card h-100 border-0 shadow-sm">
<div className={cardClassName}>
<div className="px-3 py-3 h-100 overflow-auto" data-guide-id="Task">
<div className="d-flex align-items-begin flex-column flex-sm-row justify-content-between">
<h6 className="card-text d-flex align-items-center">
Expand All @@ -77,6 +93,20 @@ function TaskAssignment({
/>

{!hideGuide && <ShowGuideButton />}
{changeTaskDescriptionSizes && (
<div
className="btn-group align-items-center ml-2 mr-auto"
role="group"
aria-label="Editor size controls"
>
<button type="button" className="btn btn-sm btn-light rounded-left" onClick={handleTaskSizeIncrease}>
-
</button>
<button type="button" className="btn btn-sm mr-2 btn-light border-left rounded-right" onClick={handleTaskSizeDecrease}>
+
</button>
</div>
)}
</div>
</div>
<div className="d-flex align-items-stretch flex-column user-select-none">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ import { useInterpret } from '@xstate/react';
import cn from 'classnames';
import groupBy from 'lodash/groupBy';
import reverse from 'lodash/reverse';
import Modal from 'react-bootstrap/Modal';
import { useDispatch, useSelector } from 'react-redux';

import CountdownTimer from '@/components/CountdownTimer';
import { connectToEditor, connectToGame, updateGameChannel } from '@/middlewares/Game';
import {
connectToEditor,
connectToGame,
updateGameChannel,
} from '@/middlewares/Game';

import EditorUserTypes from '../../config/editorUserTypes';
import GameStateCodes from '../../config/gameStateCodes';
Expand All @@ -24,6 +29,38 @@ import TaskAssignment from '../game/TaskAssignment';

import SpectatorEditor from './SpectatorEditor';

const ResultModal = ({ solutionStatus, isWinner }) => {
const [showModal, setShowModal] = useState(false);

useEffect(() => {
if (solutionStatus) {
setTimeout(() => {
setShowModal(true);
}, 100);
}
}, [solutionStatus]);

useEffect(() => {
if (showModal) {
setTimeout(() => {
setShowModal(false);
}, 4000);
}
}, [showModal]);

return (
<Modal centered show={showModal}>
<Modal.Body className="bg-light rounded-lg">
<div className="d-flex bg-light justify-content-center align-items-center">
<span className={`h3 ${isWinner ? 'text-success' : 'text-danger'}`}>
{isWinner ? 'Won battle' : 'Lost battle'}
</span>
</div>
</Modal.Body>
</Modal>
);
};

const RoundStatus = ({ playerId, matches }) => {
const [
// player = {
Expand All @@ -34,10 +71,7 @@ const RoundStatus = ({ playerId, matches }) => {
// },
player,
opponent,
] = useRoundStatistics(
playerId,
matches,
);
] = useRoundStatistics(playerId, matches);

const RoundStatistics = () => (
<div className="d-flex text-center align-items-center justify-content-center">
Expand All @@ -63,30 +97,26 @@ const RoundStatus = ({ playerId, matches }) => {
);

const RoundResultIcon = () => {
if ((player.winMatches.length === opponent.winMatches.length)
&& (player.score === opponent.score)
&& (player.avgTests === opponent.avgTests)
&& (player.avgDuration === opponent.avgDuration)
if (
player.winMatches.length === opponent.winMatches.length
&& player.score === opponent.score
&& player.avgTests === opponent.avgTests
&& player.avgDuration === opponent.avgDuration
) {
return <FontAwesomeIcon className="ml-2 text-primary" icon="handshake" />;
}

if (
(player.score > opponent.score)
|| (
(player.score === opponent.score)
&& (player.winMatches.length > opponent.winMatches.length)
)
|| (
(player.winMatches.length === opponent.winMatches.length)
&& (player.score === opponent.score)
&& (player.avgTests > opponent.avgTests)
)
|| ((player.winMatches.length === opponent.winMatches.length)
&& (player.score === opponent.score)
&& (player.avgTests === opponent.avgTests)
&& (player.avgDuration > opponent.avgDuration)
)
player.score > opponent.score
|| (player.score === opponent.score
&& player.winMatches.length > opponent.winMatches.length)
|| (player.winMatches.length === opponent.winMatches.length
&& player.score === opponent.score
&& player.avgTests > opponent.avgTests)
|| (player.winMatches.length === opponent.winMatches.length
&& player.score === opponent.score
&& player.avgTests === opponent.avgTests
&& player.avgDuration > opponent.avgDuration)
) {
return <FontAwesomeIcon className="ml-2 text-warning" icon="trophy" />;
}
Expand All @@ -105,7 +135,10 @@ const RoundStatus = ({ playerId, matches }) => {
};

const getMatchIcon = (playerId, match) => {
if (match.state === MatchStatesCodes.timeout || match.state === MatchStatesCodes.canceled) {
if (
match.state === MatchStatesCodes.timeout
|| match.state === MatchStatesCodes.canceled
) {
return <FontAwesomeIcon className="text-dark" icon="stopwatch" />;
}

Expand Down Expand Up @@ -139,26 +172,36 @@ const getSpectatorStatus = (state, task, gameId) => {
return '';
};

function TournamentPlayer({
spectatorMachine,
}) {
function TournamentPlayer({ spectatorMachine }) {
const dispatch = useDispatch();

const [switchedWidgetsStatus, setSwitchedWidgetsStatus] = useState(false);
const [taskSize, setTaskSize] = useState(0);

const changeTaskDescriptionSizes = useCallback(
size => {
setTaskSize(size);
},
[setTaskSize],
);

const {
startsAt,
timeoutSeconds,
state: gameState,
solutionStatus,
} = useSelector(selectors.gameStatusSelector);
const tournament = useSelector(selectors.tournamentSelector);
const task = useSelector(selectors.gameTaskSelector);
const taskLanguage = useSelector(selectors.taskDescriptionLanguageselector);
const { playerId, gameId } = useSelector(
state => state.tournamentPlayer,
);
const { playerId, gameId } = useSelector(state => state.tournamentPlayer);

const output = useSelector(selectors.executionOutputSelector(playerId));

const isGameWinner = useSelector(state => (
state.executionOutput.results[playerId]?.status === 'ok'
));

const spectatorStatus = getSpectatorStatus(tournament.state, task, gameId);
// TODO: if there is not active_match set html, LOADING

Expand All @@ -169,7 +212,10 @@ function TournamentPlayer({
actions: {},
});

const handleSwitchWidgets = useCallback(() => setSwitchedWidgetsStatus(state => !state), [setSwitchedWidgetsStatus]);
const handleSwitchWidgets = useCallback(
() => setSwitchedWidgetsStatus(state => !state),
[setSwitchedWidgetsStatus],
);
const handleSetLanguage = lang => () => dispatch(actions.setTaskDescriptionLanguage(lang));

useEffect(() => {
Expand All @@ -194,58 +240,62 @@ function TournamentPlayer({
};
}

return () => { };
return () => {};
}, [gameId, spectatorService, dispatch]);

const spectatorDisplayClassName = cn('d-flex flex-column', {
'flex-xl-row flex-lg-row': !switchedWidgetsStatus,
'flex-xl-row-reverse flex-lg-row-reverse': switchedWidgetsStatus,
});

const spectatorGameStatusClassName = cn('d-flex justify-content-around align-items-center w-100 p-2', {
'flex-row-reverse': switchedWidgetsStatus,
});
const spectatorGameStatusClassName = cn(
'd-flex justify-content-around align-items-center w-100 p-2',
{
'flex-row-reverse': switchedWidgetsStatus,
},
);

const GamePanel = () => (
!spectatorStatus ? (
<>
<div>
<TaskAssignment
task={task}
taskLanguage={taskLanguage}
handleSetLanguage={handleSetLanguage}
hideGuide
hideContribution
/>
const GamePanel = () => (!spectatorStatus ? (
<>
<div>
<TaskAssignment
task={task}
taskSize={taskSize}
taskLanguage={taskLanguage}
handleSetLanguage={handleSetLanguage}
changeTaskDescriptionSizes={changeTaskDescriptionSizes}
hideGuide
hideContribution
/>
</div>
<div
className="card border-0 shadow-sm h-50 mt-1"
style={{ minHeight: '490px' }}
>
<div className={spectatorGameStatusClassName}>
{GameStateCodes.playing !== gameState && <h3>Game Over</h3>}
{startsAt && gameState === GameStateCodes.playing && (
<CountdownTimer time={startsAt} timeoutSeconds={timeoutSeconds} />
)}
<OutputTab sideOutput={output} large />
</div>
<div
className="card border-0 shadow-sm h-50 mt-1"
style={{ minHeight: '490px' }}
style={{ minHeight: '400px' }}
className="position-relative overflow-auto w-100 h-100"
>
<div className={spectatorGameStatusClassName}>
{GameStateCodes.playing !== gameState && (
<h3>Game Over</h3>
)}
{startsAt && gameState === GameStateCodes.playing && (
<CountdownTimer time={startsAt} timeoutSeconds={timeoutSeconds} />
)}
<OutputTab sideOutput={output} large />
</div>
<div style={{ minHeight: '400px' }} className="position-relative overflow-auto w-100 h-100">
<div className="position-absolute w-100">
<Output sideOutput={output} />
</div>
<div className="position-absolute w-100">
<Output sideOutput={output} />
</div>
</div>
</>
</div>
</>
) : (
<div className="card border-0 h-100 w-100">
<div className="d-flex justify-content-center align-items-center w-100 h-100">
{spectatorStatus}
</div>
</div>
)
);
));

const MatchesPannel = () => {
const groupedMatches = groupBy(Object.values(tournament.matches), 'round');
Expand All @@ -255,9 +305,7 @@ function TournamentPlayer({

if (!lastRound || !groupedMatches[lastRound]) {
return (
<div
className="card bg-white rounded-lg flex justify-content-center align-items-center w-100 h-100"
>
<div className="card bg-white rounded-lg flex justify-content-center align-items-center w-100 h-100">
No statistics
</div>
);
Expand All @@ -278,7 +326,10 @@ function TournamentPlayer({
<h2 className="mb-4 mt-2 border-top">Matches:</h2>
<div>
{groupedMatches[lastRound].map(match => (
<div className="d-flex text-center align-items-center" key={match.id}>
<div
className="d-flex text-center align-items-center"
key={match.id}
>
<span className="h3">{getMatchIcon(playerId, match)}</span>
{match.playerResults[playerId] ? (
<div className="d-flex flex-column align-items-baseline">
Expand Down Expand Up @@ -310,9 +361,15 @@ function TournamentPlayer({
return (
<>
<div className="container-fluid d-flex flex-column min-vh-100">
<ResultModal isWinner={isGameWinner} solutionStatus={solutionStatus} />
<div className={spectatorDisplayClassName} style={{ flex: '1 1 auto' }}>
<div className="d-flex flex-column col-12 col-xl-4 col-lg-6 p-1">
{(tournament.breakState === 'off' && tournament.state === TournamentStates.active) ? <GamePanel /> : <MatchesPannel />}
{tournament.breakState === 'off'
&& tournament.state === TournamentStates.active ? (
<GamePanel />
) : (
<MatchesPannel />
)}
</div>
<SpectatorEditor
switchedWidgetsStatus={switchedWidgetsStatus}
Expand Down

0 comments on commit d3a280b

Please sign in to comment.