From ffaaf0b6f34224cf81493768d7eb730aed8690c2 Mon Sep 17 00:00:00 2001 From: Wenszel Date: Thu, 23 Nov 2023 21:20:44 +0100 Subject: [PATCH] added LoginPage --- backend/handlers/roomHandler.js | 20 +++++ backend/schemas/room.js | 2 + backend/server.js | 2 +- src/App.js | 10 ++- src/components/LoginPage/LoginPage.css | 76 ++++++++++++++++++ src/components/LoginPage/LoginPage.jsx | 54 +++++++++++++ .../LoginPage/NameInput/NameInput.css | 47 +++++++++++ .../LoginPage/NameInput/NameInput.jsx | 21 +++++ src/components/NameInput.jsx | 25 ------ src/images/login-page/user.png | Bin 0 -> 16826 bytes 10 files changed, 228 insertions(+), 29 deletions(-) create mode 100644 src/components/LoginPage/LoginPage.css create mode 100644 src/components/LoginPage/LoginPage.jsx create mode 100644 src/components/LoginPage/NameInput/NameInput.css create mode 100644 src/components/LoginPage/NameInput/NameInput.jsx delete mode 100644 src/components/NameInput.jsx create mode 100644 src/images/login-page/user.png diff --git a/backend/handlers/roomHandler.js b/backend/handlers/roomHandler.js index bfdeef3..5630cc0 100644 --- a/backend/handlers/roomHandler.js +++ b/backend/handlers/roomHandler.js @@ -15,5 +15,25 @@ module.exports = (io, socket) => { io.to(socket.id).emit('room:data', JSON.stringify(room)); } }; + + const getRooms = async () => { + let rooms = await RoomModel.find({}); + const response = []; + rooms.forEach(room => { + if (!room.isStarted && !room.isFull()) { + response.push({ + _id: room._id, + name: room.name, + players: room.players, + isStarted: room.isStarted, + }); + } + }); + io.to(socket.id).emit('room:rooms', JSON.stringify(response)); + }; + + + socket.on('room:data', getData); + socket.on('room:rooms', getRooms); }; diff --git a/backend/schemas/room.js b/backend/schemas/room.js index e171950..90379b6 100644 --- a/backend/schemas/room.js +++ b/backend/schemas/room.js @@ -6,6 +6,7 @@ const PawnSchema = require('./pawn'); const PlayerSchema = require('./player'); const RoomSchema = new Schema({ + name: String, createDate: { type: Date, default: Date.now }, started: { type: Boolean, default: false }, full: { type: Boolean, default: false }, @@ -66,6 +67,7 @@ RoomSchema.methods.startGame = function () { this.nextMoveTime = Date.now() + 15000; this.players.forEach(player => (player.ready = true)); this.players[0].nowMoving = true; + this.timeoutID = setTimeout(makeRandomMove, 15000, this); }; RoomSchema.methods.isFull = function () { diff --git a/backend/server.js b/backend/server.js index fb85acb..21341f1 100644 --- a/backend/server.js +++ b/backend/server.js @@ -91,4 +91,4 @@ if (process.env.NODE_ENV === 'production') { }); } -module.exports = { server }; \ No newline at end of file +module.exports = { server }; diff --git a/src/App.js b/src/App.js index fa208b4..770998b 100644 --- a/src/App.js +++ b/src/App.js @@ -1,9 +1,9 @@ import React, { useEffect, useState, createContext } from 'react'; import { io } from 'socket.io-client'; import { BrowserRouter as Router, Route, Redirect, Switch } from 'react-router-dom'; - +import ReactLoading from 'react-loading'; import Gameboard from './components/Gameboard'; -import NameInput from './components/NameInput'; +import LoginPage from './components/LoginPage/LoginPage'; export const PlayerDataContext = createContext(); export const SocketContext = createContext(); @@ -32,7 +32,11 @@ function App() { LOADING... - + {playerSocket ? ( + + ) : ( + + )} {playerData ? ( diff --git a/src/components/LoginPage/LoginPage.css b/src/components/LoginPage/LoginPage.css new file mode 100644 index 0000000..62099aa --- /dev/null +++ b/src/components/LoginPage/LoginPage.css @@ -0,0 +1,76 @@ +.login-page-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 50vh; + width: 400px; + position: relative; + padding: 20px; + border-radius: 5%; + border: 5px solid white; + background-color: rgba(0, 0, 0, 0.5); +} +h1 { + margin-right: 10px; + align-self: flex-start; + top: 0; + position: absolute; + color: white; +} +.rooms { + width: 98%; + height: 80%; + overflow-y: scroll; + overflow-x: hidden; +} +.room { + cursor: pointer; + justify-content: space-between; + display: flex; + flex-direction: row; + align-items: center; + color: white; + width: 90%; + margin: 10px; + padding: 10px; + border: 1px solid black; +} +.room-selected { + border: 1px solid white; +} +.room-selected, +.room:hover { + background-color: rgba(0, 0, 0, 0.5); +} +.number-of-players { + display: flex; + flex-direction: row; + align-items: center; +} +.number-of-players > img { + margin-right: 5px; + width: 20px; + height: 20px; +} + +/* Firefox */ +* { + scrollbar-width: auto; + scrollbar-color: #ffffff rgba(0, 0, 0, 0.1); +} + +/* Chrome, Edge, and Safari */ +*::-webkit-scrollbar { + width: 8px; +} + +*::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0); +} + +*::-webkit-scrollbar-thumb { + background-color: #ffffff; + border-radius: 10px; + border: 3px none #ffffff; +} diff --git a/src/components/LoginPage/LoginPage.jsx b/src/components/LoginPage/LoginPage.jsx new file mode 100644 index 0000000..835787d --- /dev/null +++ b/src/components/LoginPage/LoginPage.jsx @@ -0,0 +1,54 @@ +import React, { useContext, useEffect, useState } from 'react'; +import NameInput from './NameInput/NameInput'; +import { SocketContext } from '../../App'; +import './LoginPage.css'; +import userImage from '../../images/login-page/user.png'; +const LoginPage = () => { + const socket = useContext(SocketContext); + const [rooms, setRooms] = useState([]); + const [selectedRoom, setSelectedRoom] = useState(null); + + useEffect(async () => { + socket.emit('room:rooms'); + socket.on('room:rooms', data => { + data = JSON.parse(data); + console.log(data); + setRooms(data); + }); + }, []); + + return ( +
+

Select room:

+
+ {rooms.map(room => { + return ( +
{ + if (selectedRoom && selectedRoom == room._id) { + setSelectedRoom(null); + } else { + setSelectedRoom(room._id); + } + }} + key={room.id} + > +
+

{room.name}

+ {room.players.map(player => player.name + ' ')} +
+ +
+ + {room.players.length}/4 +
+
+ ); + })} +
+ +
+ ); +}; +export default LoginPage; diff --git a/src/components/LoginPage/NameInput/NameInput.css b/src/components/LoginPage/NameInput/NameInput.css new file mode 100644 index 0000000..f7509fe --- /dev/null +++ b/src/components/LoginPage/NameInput/NameInput.css @@ -0,0 +1,47 @@ +.name-input-container { + display: flex; + position: absolute; + bottom: 0; + flex-direction: row; + width: 80%; + margin: 20px; +} +input, +button { + padding: 0; + border: none; + outline: none; + box-sizing: border-box; +} + +input { + width: 100%; + padding: 12px; + font-size: 16px; + border-radius: 8px; + color: white; + border: 1px solid #ccc; + background-color: rgba(0, 0, 0, 0.5); + transition: border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; +} + +input:focus { + color: black; + border-color: #4a90e2; + background-color: #fff; +} + +button { + padding: 12px 20px; + font-size: 16px; + border-radius: 8px; + border: none; + color: #fff; + background-color: rgba(0, 0, 0, 0.5); + cursor: pointer; + transition: background-color 0.3s ease-in-out; +} + +button:hover { + background-color: rgba(0, 0, 0, 1); +} diff --git a/src/components/LoginPage/NameInput/NameInput.jsx b/src/components/LoginPage/NameInput/NameInput.jsx new file mode 100644 index 0000000..491c56b --- /dev/null +++ b/src/components/LoginPage/NameInput/NameInput.jsx @@ -0,0 +1,21 @@ +import React, { useState, useContext } from 'react'; +import { SocketContext } from '../../../App'; +import './NameInput.css'; +const NameInput = () => { + const socket = useContext(SocketContext); + const [inputValue, setInputValue] = useState(''); + const handleInputChange = e => { + setInputValue(e.target.value); + }; + const handleButtonClick = () => { + socket.emit('player:login', { name: inputValue }); + }; + return ( +
+ + +
+ ); +}; + +export default NameInput; diff --git a/src/components/NameInput.jsx b/src/components/NameInput.jsx deleted file mode 100644 index 9f4d1eb..0000000 --- a/src/components/NameInput.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, { useState, useContext } from "react"; -import { SocketContext } from "../App"; - -const NameInput = () => { - const socket = useContext(SocketContext); - const [inputValue, setInputValue] = useState(""); - const handleInputChange = (e) => { - setInputValue(e.target.value); - }; - const handleButtonClick = () => { - socket.emit("player:login", { name: inputValue }); - }; - return ( -
- - -
- ); -}; - -export default NameInput; diff --git a/src/images/login-page/user.png b/src/images/login-page/user.png new file mode 100644 index 0000000000000000000000000000000000000000..6d15e9e62526ea987387257509b31beb06e18793 GIT binary patch literal 16826 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

IA?)MjP!%=gz=kK*5%-P zGP#1@`MIw2dKAE5{sQ*`Gu3Z^?saPH8Fz3u#F+xH^>W%~&nrF9(1iL9XzmhsJDC9P zp{aQx$p?`!_jzcoe|$vO@42Z#5JLre*3zyT-vmqG15848sc~L0DpmdeC=uH+8T)}uOL7~j&bD)?_NzY>ynF-H zCm*my$vl|aY+dxpYU>{L)J*tGJ#Di?mA+_DJ7JE;R0Q3ooaS|%7?r0&_N#HC8Ap>@ z$%r#{BLKs7&PROx-b3D=_yADuPOjS?4J1IlfGb%Unaf$MHig3Hpi=9$M-b`t{Bvji zOOLUgkU_BH+s&>obnYFtp_bli6!{!AUR7<9pX_CROyI@!HGo>&`uOd&+80@Y6V zpFXW)mAd{#tW??Vv0igEj=-JD>#SKs3~HE}TWC0LlU(vGvbf3|;*hf>Uu?V5seFy2 zFm4$msHsbwSaE!i*+-Z0!9+o;hnkH&ECVFnzKo#yjz^S&RF1|HMt(xx8kZAEkEBUL z`nTzkpzoX6mdKK&T~k2C2)Q3|w@vcX*Vi`F-)V$`q`n6;$1Sow<>pMeSGkq+7wTHc znIeodOh~D-He$!PTvm#;af5J$;Zh5H6fh+TiEC~DM^?kmxA~a(3nk(sxp7qv;*@ep z-JURPIiXy&pzG97z`nUG($I=V)^RWBTcMb`(+^Wrqo6TR=oKne`j?ea*Ux9Ybt0>1 zNQ#(**2~R(3nrP+q|}4b9cUhbd#0muARa7@M%H+x;MPRi4D`^NyUCFV{rgVoXEHMT z2CuzZOK~^+U41Q8{DD@=&HWqLu#f1N)|OC3T-O}}uNI6R%h>>M8dWupYUKW~Yewgp z;;gb73`K=FO5(b~p!x|m>+Oqpw#3yxq5(+*Oj)dWtfV_{L=w23x_-@VO##FqMB*dU zhHAw^v|d_nV{7Y8b@A0b02l{ukXi_Z3Bus06wurk<`%d|O)au#g!bIDp{nnAR)ZSk zx(ojUdVg0+wwt9cy2RND{%+}Mzovj2Udx77e5ogFfzCp`FuZ)X3Y#&Oo#u^MKyE~{+@@Gbum$pmD) zorE|Dk!D_?L5HOsw94c*EBlS{^W*g1gih#uIv}0>d4|=>_MLcL{|m&!(hMUlZSF_- zY*PRTiM~dIZu~hO$iU*tft`s!R`p5R$y#3(&9x z?wkS?L9$FKZVFSg!;z?!l<6d{gUx}WK6WhpNGH(v82j!i2~G)m1DsE*(%W1(lqPA? z@V3bb%^=V&Ge}5hms%1)2vK{?pCbzGpO_ObIRxg#f<~^1OzAsL8{7leyi90qm75YR zJ`VETEb(`FW9WiM;KtGvZfd(Xg3Zi+-^!L2M4Yk3O>4;#X8?L42q@428D&9lQlnQ> z*C~()%c0KLq1O+_9t22F^3o-WsNn}v%Fom;*%V%yk}60GxeYQKVY-QfDy zldElDnHw@~g6vj+r@0&ZH@HuIqvT0Y-9GfblXP!t?T?*@t0HvOzvjo+J%WBfp^H