diff --git a/backend/handlers/playerHandler.js b/backend/handlers/playerHandler.js
index 7640f16..fe8b453 100644
--- a/backend/handlers/playerHandler.js
+++ b/backend/handlers/playerHandler.js
@@ -115,6 +115,7 @@ module.exports = (io, socket) => {
socket.join(room._id.toString());
// Sending data to the user, after which player will be redirected to the game
socket.emit('player:data', JSON.stringify(req.session));
+ socket.emit('room:data', JSON.stringify(updatedRoom));
});
});
}
diff --git a/backend/handlers/roomHandler.js b/backend/handlers/roomHandler.js
index 962bea7..9a4817c 100644
--- a/backend/handlers/roomHandler.js
+++ b/backend/handlers/roomHandler.js
@@ -27,6 +27,8 @@ module.exports = (io, socket) => {
room.players[index + 1].nowMoving = true;
}
room.nextMoveTime = Date.now() + 15000;
+ if (this.timeoutID) clearTimeout(this.timeoutID);
+ this.timeoutID = null;
RoomModel.findOneAndUpdate({ _id: req.session.roomId }, room, function (err, updatedRoom) {
io.to(req.session.roomId).emit('room:data', JSON.stringify(updatedRoom));
});
diff --git a/backend/schemas/room.js b/backend/schemas/room.js
index c6118e4..d6887e4 100644
--- a/backend/schemas/room.js
+++ b/backend/schemas/room.js
@@ -36,6 +36,7 @@ RoomSchema.methods.changeMovingPlayer = function () {
this.nextMoveTime = Date.now() + 15000;
this.rolledNumber = null;
if (this.timeoutID) clearTimeout(this.timeoutID);
+ this.timeoutID = null;
};
RoomSchema.methods.movePawn = function (pawn) {
diff --git a/package-lock.json b/package-lock.json
index 12ed4c5..b39a8bf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,6 +20,7 @@
"react-loading": "^2.0.3",
"react-router-dom": "^5.2.0",
"react-scripts": "^5.0.1",
+ "react-transition-group": "^4.4.5",
"socket.io": "^4.5.1",
"socket.io-client": "^4.5.1",
"web-vitals": "^1.1.0"
@@ -16735,9 +16736,9 @@
}
},
"node_modules/react-transition-group": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
- "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==",
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
@@ -18664,16 +18665,16 @@
}
},
"node_modules/typescript": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
- "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+ "version": "4.9.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
+ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
- "node": ">=14.17"
+ "node": ">=4.2.0"
}
},
"node_modules/unbox-primitive": {
@@ -31757,9 +31758,9 @@
}
},
"react-transition-group": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
- "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==",
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"requires": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
@@ -33178,9 +33179,9 @@
}
},
"typescript": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
- "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+ "version": "4.9.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
+ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"peer": true
},
"unbox-primitive": {
diff --git a/package.json b/package.json
index 354a129..a3b0784 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"react-loading": "^2.0.3",
"react-router-dom": "^5.2.0",
"react-scripts": "^5.0.1",
+ "react-transition-group": "^4.4.5",
"socket.io": "^4.5.1",
"socket.io-client": "^4.5.1",
"web-vitals": "^1.1.0"
diff --git a/src/App.js b/src/App.js
index e69fe10..fa208b4 100644
--- a/src/App.js
+++ b/src/App.js
@@ -40,9 +40,6 @@ function App() {
) : null}
-
- Hand icons created by berkahicon - Flaticon
-
diff --git a/src/components/Gameboard.jsx b/src/components/Gameboard.jsx
index 000f284..8847ba9 100644
--- a/src/components/Gameboard.jsx
+++ b/src/components/Gameboard.jsx
@@ -67,8 +67,8 @@ const Gameboard = () => {
return (
<>
- {players ? (
- <>
+ {(players[0] && !started) || (time && started) ? (
+
{
rolledNumberCallback={rolledNumberCallback}
/>
- >
+
) : (
)}
diff --git a/src/components/Navbar.css b/src/components/Navbar.css
index 5bd52da..1358d4b 100644
--- a/src/components/Navbar.css
+++ b/src/components/Navbar.css
@@ -1,26 +1,3 @@
-.red {
- position: relative;
- left: 176px;
-}
-.yellow {
- position: relative;
- flex-direction: row-reverse;
- right: 170px;
-}
-.blue {
- position: relative;
- right: 28px;
- top: 538px;
-}
-.green {
- position: relative;
- flex-direction: row-reverse;
- top: 538px;
- left: 36px;
-}
-.player-container {
- display: flex;
-}
.dice-container {
margin-left: 20px;
margin-right: 20px;
@@ -30,3 +7,49 @@
.roll {
cursor: pointer;
}
+.ready-container {
+ display: flex;
+ width: 300px;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ flex-flow: row-reverse;
+ background-color: grey;
+ border-radius: 10px;
+ border: 2px solid white;
+}
+.ready-container > label {
+ margin-left: 10px;
+ margin-right: 10px;
+ width: 100px;
+ color: white;
+}
+.player-container {
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+ width: 100%;
+}
+.red {
+ margin-bottom: 50px;
+ grid-column: 1;
+ grid-row: 1;
+}
+.yellow {
+ margin-bottom: 50px;
+ flex-flow: row-reverse;
+ grid-column: 2;
+ grid-row: 1;
+}
+.blue {
+ margin-top: 50px;
+ grid-column: 1;
+ grid-row: 4;
+}
+.green {
+ margin-top: 50px;
+ flex-flow: row-reverse;
+ grid-column: 2;
+ grid-row: 4;
+}
+/* Styl dla overlay */
diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx
index 4a27c2d..0c67c8a 100644
--- a/src/components/Navbar.jsx
+++ b/src/components/Navbar.jsx
@@ -3,11 +3,13 @@ import Dice from './game-board-components/Dice';
import NameContainer from './navbar-components/NameContainer';
import ReadyButton from './navbar-components/ReadyButton';
import './Navbar.css';
-
+import { useContext } from 'react';
+import { PlayerDataContext } from '../App';
const Navbar = ({ players, started, time, isReady, rolledNumber, nowMoving, rolledNumberCallback, movingPlayer }) => {
+ const context = useContext(PlayerDataContext);
const colors = ['red', 'blue', 'green', 'yellow'];
return (
-
+ <>
{players.map((player, index) => (
@@ -18,10 +20,10 @@ const Navbar = ({ players, started, time, isReady, rolledNumber, nowMoving, roll
color={colors[index]}
rolledNumberCallback={rolledNumberCallback}
/>
+ {context.color !== player.color || started ? null : }
))}
- {started ? null :
}
-
+ >
);
};
export default Navbar;
diff --git a/src/components/navbar-components/AnimatedOverlay.jsx b/src/components/navbar-components/AnimatedOverlay.jsx
new file mode 100644
index 0000000..82b1507
--- /dev/null
+++ b/src/components/navbar-components/AnimatedOverlay.jsx
@@ -0,0 +1,25 @@
+import React, { useState, useEffect } from 'react';
+import { CSSTransition } from 'react-transition-group';
+import './TimerAnimation.js';
+
+const AnimatedOverlay = ({ time }) => {
+ const [animationDelay, setAnimationDelay] = useState();
+
+ useEffect(() => {
+ setAnimationDelay(15 - Math.ceil((time - Date.now()) / 1000));
+ }, [time]);
+
+ return (
+
+
+
+ );
+};
+
+export default AnimatedOverlay;
diff --git a/src/components/navbar-components/NameContainer.jsx b/src/components/navbar-components/NameContainer.jsx
index 53b17ff..b0a68cc 100644
--- a/src/components/navbar-components/NameContainer.jsx
+++ b/src/components/navbar-components/NameContainer.jsx
@@ -1,51 +1,15 @@
-import React, { useState, useEffect, useContext } from 'react';
+import React from 'react';
import PropTypes from 'prop-types';
-import { SocketContext } from '../../App';
-
-/*
- Component responsible for:
- - displaying the player's name
- - informing players about the readiness of other players by changing the color of container from gray to the player's color
- - counting time to the end of the move
-
- Props:
- - player (object):
- The player to whom the container belongs
- Player's properties used in this component:
- - ready (boolean):
- is the player ready for the start of the game, if so, change color from gray to the player's color
- when the game is started all players are ready not matter if they clicked ready button before
- - nowMoving (boolean) is this player move now, if true display timer
- - name (string)
- - time (number) - time remaining until the move is made in milliseconds
-*/
+import AnimatedOverlay from './AnimatedOverlay';
const NameContainer = ({ player, time }) => {
- const [remainingTime, setRemainingTime] = useState();
- const socket = useContext(SocketContext);
-
- // Function responsible for counting down to the end of time every second
- const countdown = () => {
- setRemainingTime(Math.ceil((time - Date.now()) / 1000));
- };
-
- useEffect(() => {
- // Starts the countdown from the beginning if the server returned information about skipping the turn
- socket.on('game:skip', () => {
- setRemainingTime(15);
- });
- setRemainingTime(Math.ceil((time - Date.now()) / 1000));
- const interval = setInterval(countdown, 1000);
- return () => clearInterval(interval);
- }, [countdown]);
-
return (
{player.name}
- {player.nowMoving ?
{remainingTime}
: null}
+ {player.nowMoving ?
: null}
);
};
diff --git a/src/components/navbar-components/ReadyButton.jsx b/src/components/navbar-components/ReadyButton.jsx
index 62ea815..6addc94 100644
--- a/src/components/navbar-components/ReadyButton.jsx
+++ b/src/components/navbar-components/ReadyButton.jsx
@@ -1,6 +1,8 @@
import React, { useState, useContext, useEffect } from 'react';
import { SocketContext } from '../../App';
import Switch from '@material-ui/core/Switch';
+import '../Navbar.css';
+import './TimerAnimation';
const ReadyButton = ({ isReady }) => {
const socket = useContext(SocketContext);
diff --git a/src/components/navbar-components/TimerAnimation.js b/src/components/navbar-components/TimerAnimation.js
new file mode 100644
index 0000000..f8b44e1
--- /dev/null
+++ b/src/components/navbar-components/TimerAnimation.js
@@ -0,0 +1,63 @@
+const keyframes = [];
+const steps = 86;
+
+let count = 0;
+let s = 'polygon(50% 50%, 50% 0%, 50% 0%';
+
+for (let i = 50; i <= 100; i += 5) {
+ s += `, ${i}% 0%`;
+ handle();
+}
+for (let i = 0; i <= 100; i += 5) {
+ s += `, 100% ${i}%`;
+ handle();
+}
+for (let i = 100; i >= 0; i -= 5) {
+ s += `, ${i}% 100%`;
+ handle();
+}
+
+for (let i = 100; i >= 0; i -= 5) {
+ s += `, 0% ${i}%`;
+ handle();
+}
+for (let i = 0; i <= 50; i += 5) {
+ s += `, ${i}% 0%`;
+ handle();
+}
+
+function handle() {
+ const percentage = (count / steps) * 100;
+ let step;
+ if (percentage <= 75 && percentage >= 73) {
+ step = `${percentage}% {
+ background-color: orange;
+ clip-path: ${s})
+ }`;
+ } else if (percentage > 97.5 && percentage < 100) {
+ step = `${percentage}% {
+ background-color: red;
+ clip-path: ${s})
+ }`;
+ } else if (percentage > 0 && percentage < 2.5) {
+ step = `${percentage}% {
+ background-color: green;
+ clip-path: ${s})
+ }`;
+ } else {
+ step = `${percentage}% {
+ clip-path: ${s})
+ }`;
+ }
+ keyframes.push(step);
+ count++;
+}
+
+const animation = document.styleSheets[0].insertRule(
+ `
+ @keyframes timerAnimation {
+ ${keyframes.join('\n')}
+ }
+`,
+ document.styleSheets[0].cssRules.length
+);
diff --git a/src/index.css b/src/index.css
index 97dff06..1346031 100644
--- a/src/index.css
+++ b/src/index.css
@@ -6,13 +6,16 @@ body {
rgba(0, 138, 255, 1) 16%,
rgba(9, 9, 121, 1) 81%
);
+ overflow: hidden;
+}
+#root {
display: flex;
+ justify-content: center;
flex-direction: column;
- align-items: center;
-}
-.canvas-container {
- margin: 10px;
+ height: 100vh;
+ width: 100vw;
}
+
canvas {
border-radius: 15px;
border: 2px solid black;
@@ -25,12 +28,14 @@ canvas {
display: flex;
flex-direction: row;
}
-.navbar-container > div {
- margin-right: 10px;
-}
+
.name-container {
- width: 100px;
- height: 50px;
+ position: relative;
+ min-width: 100px;
+ min-height: 50px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
border: 2px solid white;
border-radius: 5px;
color: white;
@@ -49,9 +54,33 @@ canvas {
height: 20px;
border-radius: 5px;
}
+.overlay {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ opacity: 0.9;
+ animation: timerAnimation 15s linear infinite;
+ transition-duration: 15s;
+}
#root {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
+
+.container {
+ display: grid;
+ align-items: center;
+ justify-items: center;
+ grid-template-columns: 230px 230px;
+ grid-template-rows: 50px 250px 250px 50px;
+}
+
+.canvas-container {
+ place-self: center;
+ grid-column: 1 / span 2;
+ grid-row: 2 / span 2;
+}