diff --git a/API/db/app.db b/API/db/app.db index bd8e54a3dade35f0526ed02551a1c6f44e96113f..a1e13e4fb81d71b67d2b35525e3b901708a8f4f0 100644 Binary files a/API/db/app.db and b/API/db/app.db differ diff --git a/API/db/create_db.sql b/API/db/create_db.sql index ceb75d81daa8edda83a5b5157a72f2c880c3f73c..8b5fc2f81d83856838dbd019cb18396bd5e893f8 100644 --- a/API/db/create_db.sql +++ b/API/db/create_db.sql @@ -27,6 +27,11 @@ CREATE TABLE answer ( CONSTRAINT FK_question FOREIGN KEY (id_question) REFERENCES questions(id) ON DELETE CASCADE ); +CREATE TABLE room ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT UNIQUE, + +); INSERT INTO users (username, password, admin) VALUES ('alec', 'alec', true); diff --git a/API/src/routes/BaseRoutes.ts b/API/src/routes/BaseRoutes.ts index cfb0c3108be14e548b27448468f23c88d14f254e..0b7dd5312b0a22635084f95e6ab968b7c4cd37ba 100644 --- a/API/src/routes/BaseRoutes.ts +++ b/API/src/routes/BaseRoutes.ts @@ -2,7 +2,6 @@ import express from 'express'; import { StatusCodes } from 'http-status-codes'; import DBHandler from '../database/Database'; import { User_t, Question_t } from '../database/Database'; -import { param } from 'express-validator'; const ROUTE:string = '/API/v1'; diff --git a/API/src/socket.io/ServerIO.ts b/API/src/socket.io/ServerIO.ts index 11e53d43f1eb05eefe03188960519b16ff155256..504ca90591ca93f370f6e14690c1289e70d87902 100644 --- a/API/src/socket.io/ServerIO.ts +++ b/API/src/socket.io/ServerIO.ts @@ -1,11 +1,16 @@ import * as IO from 'socket.io'; import logger from '../logging/WinstonLogger'; import http from 'http'; +import { User_t } from '../database/Database'; - +const jwt = require('jsonwebtoken'); //TODO: In this file you can add/edit all things about socket.io class ServerIO extends IO.Server { + + private playerNumber: number = 0; + private playerList: string[] = []; + constructor(server: http.Server) { super(server, { cors: { @@ -14,9 +19,17 @@ class ServerIO extends IO.Server { }); this.on('connection', (socket: IO.Socket) => { - logger.info(`Nouveau socket vers ${ socket.client.conn.remoteAddress }`); - + logger.info(`New socket on ${ socket.client.conn.remoteAddress }`); + const token = socket.handshake.query.Authorization; + + if (token === "null") { + logger.info(`User not logged in, disconnecting ${ socket.client.conn.remoteAddress }`) + socket.emit("Not Logged In"); + return socket.disconnect(); + } + this.registerEventsOnSocket(socket); + socket.emit('Players Waiting', this.playerNumber) }); } @@ -24,6 +37,66 @@ class ServerIO extends IO.Server { socket.on('Hello World', _ => { socket.emit('Bienvenue', socket.id); }); + + socket.on('Join Room', _ => { + const token = socket.handshake.query.Authorization; + + var user: User_t = null; + console.log(token) + jwt.verify(token, process.env.TOKEN_SECRET, (err:any, res:User_t) => { + // console.log(err); + if (err) return false; + user = res; + }); + + logger.info(user.username + ` joined the game`); + this.playerList.push(user.username); + + socket.emit('Players Waiting', this.playerList.length) + socket.broadcast.emit('Players Waiting', this.playerList.length); + }) + + socket.on('Leave Room', _ => { + const token = socket.handshake.query.Authorization; + + var index: number; + var username: string; + jwt.verify(token, process.env.TOKEN_SECRET, (err:any, user:User_t) => { + if (err) return; + index = this.playerList.indexOf(user.username, 0); + username = user.username; + }); + + if (index != -1) { + this.playerList.splice(index, 1); + logger.info(username + ` left the game`); + } + + socket.emit('Players Waiting', this.playerList.length) + socket.broadcast.emit('Players Waiting', this.playerList.length); + }) + + socket.on('disconnect', _ => { + + const token = socket.handshake.query.Authorization; + + var index = -1; + var username: string; + jwt.verify(token, process.env.TOKEN_SECRET, (err:any, user:User_t) => { + if (err) return; + index = this.playerList.indexOf(user.username, 0); + + username = user.username; + }); + + logger.info(`Socket disconnected on ${ socket.client.conn.remoteAddress }`); + if (index == -1) + return; + + this.playerList.splice(index, 1); + logger.info(username + ` left the game`); + socket.emit('Players Waiting', this.playerList.length); + }) } } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5e7d40c9ff93ec465480ae3b2737256d87f52855..8ea9a2a433151c465c2fc9c8134eb30af3e878f8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -17,7 +17,9 @@ "@angular/platform-browser-dynamic": "^16.0.0", "@angular/router": "^16.0.0", "jwt-decode": "^3.1.2", + "ngx-socket-io": "^4.5.1", "rxjs": "~7.8.0", + "socket.io-client": "^4.6.2", "tslib": "^2.3.0", "zone.js": "~0.13.0" }, @@ -3124,8 +3126,7 @@ "node_modules/@socket.io/component-emitter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, "node_modules/@tootallnate/once": { "version": "2.0.0", @@ -3242,14 +3243,12 @@ "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" }, "node_modules/@types/cors": { "version": "2.8.13", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -3334,8 +3333,7 @@ "node_modules/@types/node": { "version": "20.2.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", - "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==", - "dev": true + "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==" }, "node_modules/@types/qs": { "version": "6.9.7", @@ -3594,7 +3592,6 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -4006,7 +4003,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true, "engines": { "node": "^4.5.0 || >= 5.9" } @@ -4658,7 +4654,6 @@ "version": "0.4.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -4717,6 +4712,16 @@ "node": ">=10.13.0" } }, + "node_modules/core-js": { + "version": "3.30.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.2.tgz", + "integrity": "sha512-uBJiDmwqsbJCWHAwjrx3cvjbMXP7xD72Dmsn5LOJpiRmE3WbBbN5rCqQ2Qh6Ek6/eOrjlWngEynBWo4VxerQhg==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.30.2", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.2.tgz", @@ -4740,7 +4745,6 @@ "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, "dependencies": { "object-assign": "^4", "vary": "^1" @@ -5006,7 +5010,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -5277,7 +5280,6 @@ "version": "6.4.2", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz", "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==", - "dev": true, "dependencies": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", @@ -5294,11 +5296,22 @@ "node": ">=10.0.0" } }, + "node_modules/engine.io-client": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz", + "integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, "node_modules/engine.io-parser": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.7.tgz", "integrity": "sha512-P+jDFbvK6lE3n1OL+q9KuzdOFWkkZ/cMV9gol/SbVfpyqfvrfrFTOFJ6fQm2VC3PZHlU3QPhVwmbsCnauHF2MQ==", - "dev": true, "engines": { "node": ">=10.0.0" } @@ -7985,7 +7998,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -7994,7 +8006,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -8304,8 +8315,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -8400,7 +8410,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -8411,6 +8420,32 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/ngx-socket-io": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/ngx-socket-io/-/ngx-socket-io-4.5.1.tgz", + "integrity": "sha512-PNIXmL2NpBwytJLlsyERrf7bUni6ZYtir2LacHMXHFseUDOEnNE7G53kXR+6IKLsVGJlG5RbnplQujRcfMOVxA==", + "dependencies": { + "core-js": "^3.0.0", + "reflect-metadata": "^0.1.10", + "socket.io": "^4.5.1", + "socket.io-client": "^4.5.1", + "tslib": "^2.3.0", + "zone.js": "~0.11.4" + }, + "peerDependencies": { + "@angular/common": "^16.0.0", + "@angular/core": "^16.0.0", + "rxjs": "^7.0.0" + } + }, + "node_modules/ngx-socket-io/node_modules/zone.js": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz", + "integrity": "sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==", + "dependencies": { + "tslib": "^2.3.0" + } + }, "node_modules/nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", @@ -8777,7 +8812,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -9868,8 +9902,7 @@ "node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "dev": true + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, "node_modules/regenerate": { "version": "1.4.2", @@ -10674,7 +10707,6 @@ "version": "4.6.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", - "dev": true, "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", @@ -10691,16 +10723,28 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", - "dev": true, "dependencies": { "ws": "~8.11.0" } }, + "node_modules/socket.io-client": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.6.2.tgz", + "integrity": "sha512-OwWrMbbA8wSqhBAR0yoPK6EdQLERQAYjXb3A0zLpgxfM1ZGLKoxHx8gVmCHA6pcclRX5oA/zvQf7bghAS11jRA==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.4.0", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/socket.io-parser": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", - "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", - "dev": true, + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -11832,7 +11876,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -12356,7 +12399,6 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, "engines": { "node": ">=10.0.0" }, @@ -12373,6 +12415,14 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/frontend/package.json b/frontend/package.json index e8352546aa9e47a813ef3ea9aab66eac796e862f..2609f4da7964951d1b0bac2b5995224ad5c4b3f1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,7 +19,9 @@ "@angular/platform-browser-dynamic": "^16.0.0", "@angular/router": "^16.0.0", "jwt-decode": "^3.1.2", + "ngx-socket-io": "^4.5.1", "rxjs": "~7.8.0", + "socket.io-client": "^4.6.2", "tslib": "^2.3.0", "zone.js": "~0.13.0" }, diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index e738f97b52647680fa4c7582b0ad5f38e329c933..ea1657fef6f43d2abb9aacfebc1d4f1f18483176 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -20,6 +20,9 @@ <app-user-dropdown [username]="auth.getUsername()"></app-user-dropdown> </div> + <div *ngIf="auth.isLoggedIn()"> + <app-gameroom></app-gameroom> + </div> <main class="w-full h-full"> <router-outlet> diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 00645a00abca40a7ca32f0d35044870322f30638..d4efe2a51cbb92e7a341e0d6e7ba8e10591d3a26 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -14,9 +14,9 @@ export class AppComponent { constructor( public router: Router, - public auth: AuthenticationService) {} - - + public auth: AuthenticationService + ) {} + dashboardButton(): boolean { console.log(this.auth.isAdmin()) return this.auth.isAdmin(); @@ -25,5 +25,4 @@ export class AppComponent { userDropdown(): void { this.userDropdownModal = !this.userDropdownModal; } - } \ No newline at end of file diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 594924d84636b30dab1439421cc08ae3fecd6081..a25a4a63037a30611f55669652a3787536d32815 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -19,6 +19,10 @@ import { UserDropdownComponent } from './user-dropdown/user-dropdown.component'; import { CreateCategoryComponent } from './question-shit/create-category/create-category.component'; import { DeletionConfirmationComponent } from './deletion-confirmation/deletion-confirmation.component'; import { RemoveCategoryComponent } from './question-shit/remove-category/remove-category.component'; +import { SocketIoConfig, SocketIoModule } from 'ngx-socket-io'; +import { GameroomComponent } from './gameroom/gameroom.component'; + +const config: SocketIoConfig = { url: 'http://0.0.0.0:30992', options: { } }; @NgModule({ declarations: [ @@ -35,13 +39,15 @@ import { RemoveCategoryComponent } from './question-shit/remove-category/remove- UserDropdownComponent, CreateCategoryComponent, DeletionConfirmationComponent, - RemoveCategoryComponent + RemoveCategoryComponent, + GameroomComponent ], imports: [ BrowserModule, AppRoutingModule, HttpClientModule, - ReactiveFormsModule + ReactiveFormsModule, + SocketIoModule.forRoot(config) ], providers: [ { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true} diff --git a/frontend/src/app/gameroom/gameroom.component.css b/frontend/src/app/gameroom/gameroom.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/frontend/src/app/gameroom/gameroom.component.html b/frontend/src/app/gameroom/gameroom.component.html new file mode 100644 index 0000000000000000000000000000000000000000..7fc7963e3c52c76fb0cf93bc5f5ca976d76c192c --- /dev/null +++ b/frontend/src/app/gameroom/gameroom.component.html @@ -0,0 +1,6 @@ +<p>gameroom works!</p> + +<span>Player number : {{playerNumber}}/3</span> + +<button *ngIf="!inRoom" (click)="joinRoom()">Join Room</button> +<button *ngIf="inRoom" (click)="leaveRoom()">Leave Room</button> \ No newline at end of file diff --git a/frontend/src/app/gameroom/gameroom.component.spec.ts b/frontend/src/app/gameroom/gameroom.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..82f97e07b56093f2af12b5f984aece6e43b170fd --- /dev/null +++ b/frontend/src/app/gameroom/gameroom.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GameroomComponent } from './gameroom.component'; + +describe('GameroomComponent', () => { + let component: GameroomComponent; + let fixture: ComponentFixture<GameroomComponent>; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [GameroomComponent] + }); + fixture = TestBed.createComponent(GameroomComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/gameroom/gameroom.component.ts b/frontend/src/app/gameroom/gameroom.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..6f59da2747511d6ce215c4e963a7534d667ce26c --- /dev/null +++ b/frontend/src/app/gameroom/gameroom.component.ts @@ -0,0 +1,38 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Socket } from 'ngx-socket-io'; +import { SocketService } from '../services/socket.service'; + +@Component({ + selector: 'app-gameroom', + templateUrl: './gameroom.component.html', + styleUrls: ['./gameroom.component.css'] +}) +export class GameroomComponent implements OnInit, OnDestroy { + + playerNumber!: number; + + inRoom: boolean = false; + + constructor(private socket: SocketService) {} + + ngOnInit(): void { + this.socket.playerNumber.subscribe(number => { + this.playerNumber = number; + }) + } + + ngOnDestroy(): void { + // this.socket.disconnectSocket(); + } + + joinRoom(): void { + this.socket.joinRoom(); + this.inRoom = true; + } + + leaveRoom(): void { + this.socket.leaveRoom(); + this.inRoom = false; + } + +} diff --git a/frontend/src/app/question-shit/create-question/create-question.component.html b/frontend/src/app/question-shit/create-question/create-question.component.html index 5e1e49ec04319a860c3bf8387ce2d11ed953408e..ee3ede9f67b9ad138fb1bd7d7f510a2c5e8735d2 100644 --- a/frontend/src/app/question-shit/create-question/create-question.component.html +++ b/frontend/src/app/question-shit/create-question/create-question.component.html @@ -6,8 +6,20 @@ <h1 class="mt-2 mx-2 font-bold">Create Question</h1> <form [formGroup]="createQuestion" (ngSubmit)="onSubmit()" class="flex flex-col pb-2 mx-2"> - <input class="border-b-4 mt-2 w-full" type="text" placeholder="Text" formControlName="question"> - <select #select id="category" class="form-control" (change)="onSelected(select.value)"> + <div class="relative mb-3" data-te-input-wrapper-init> + <textarea + class="peer block min-h-[auto] w-full rounded border-2 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 data-[te-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-black dark:placeholder:text-neutral-200 [&:not([data-te-input-placeholder-active])]:placeholder:opacity-0" + id="exampleFormControlTextarea1" + rows="3" + placeholder="Your message" + type="text" placeholder="Text" formControlName="question"></textarea> + <label + for="exampleFormControlTextarea1" + class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-black transition-all duration-200 ease-out peer-focus:-translate-y-[0.5rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[te-input-state-active]:-translate-y-[0.9rem] peer-data-[te-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-200 dark:peer-focus:text-primary" + >Question</label> + </div> + + <select #select id="category" class="form-control mt-4" (change)="onSelected(select.value)"> <option default value="">Theme</option> <option *ngFor="let c of categories" [value]="c.title">{{c.title}}</option> </select> diff --git a/frontend/src/app/question-shit/update-question/update-question.component.html b/frontend/src/app/question-shit/update-question/update-question.component.html index 88cf75cca570bc85b58aaed756cd0c76c246281f..34025f3e7d9be8edae083fcf1d2a74688aed7cd0 100644 --- a/frontend/src/app/question-shit/update-question/update-question.component.html +++ b/frontend/src/app/question-shit/update-question/update-question.component.html @@ -6,7 +6,14 @@ <h1 class="mt-2 mx-2 font-bold">Update Question</h1> <form [formGroup]="updateQuestion" (ngSubmit)="onSubmit()" class="flex flex-col pb-2 mx-2"> - <input class="border-b-4 mt-2 w-full" type="text" placeholder="Text" formControlName="question"> + <div class="relative mb-3" data-te-input-wrapper-init> + <textarea + class="peer block min-h-[auto] w-full rounded border-2 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 data-[te-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-black dark:placeholder:text-neutral-200 [&:not([data-te-input-placeholder-active])]:placeholder:opacity-0" + id="exampleFormControlTextarea1" + rows="3" + placeholder="Your message" + type="text" placeholder="Text" formControlName="question"></textarea> + </div> <select #select id="category" class="form-control" (change)="onSelected(select.value)"> <option default value="">Theme</option> <option *ngFor="let c of categories" [value]="c.title">{{c.title}}</option> diff --git a/frontend/src/app/services/authentication.service.ts b/frontend/src/app/services/authentication.service.ts index da827c6553728c226707ece887a7d424bc04cd05..ea45926b44f89204a6f5062b36a2a1e927d38835 100644 --- a/frontend/src/app/services/authentication.service.ts +++ b/frontend/src/app/services/authentication.service.ts @@ -3,6 +3,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Router } from '@angular/router'; import jwtDecode from 'jwt-decode'; import { User } from '../Types/types'; +import { SocketService } from './socket.service'; @Injectable({ providedIn: 'root' @@ -11,7 +12,8 @@ export class AuthenticationService { constructor( private http: HttpClient, - private router: Router) { } + private router: Router, + private socket: SocketService) { } login(username:string, password:string): void { @@ -25,6 +27,7 @@ export class AuthenticationService { if(res.ok) { localStorage.setItem("token", res.body.toString()); + this.socket.refreshSocketToken(); this.router.navigateByUrl("/"); } else { @@ -39,9 +42,15 @@ export class AuthenticationService { logout(): void { localStorage.removeItem("token"); + this.socket.refreshSocketToken(); + this.socket.leaveRoom(); this.router.navigateByUrl("/"); } + getToken(): string { + return localStorage.getItem("token"); + } + private getDecodedAccessToken(): User { try { return jwtDecode(localStorage.getItem("token")) as User; diff --git a/frontend/src/app/services/socket.service.spec.ts b/frontend/src/app/services/socket.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..7ceaf59829f947e9f1da2271381aad9191d533f8 --- /dev/null +++ b/frontend/src/app/services/socket.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { SocketService } from './socket.service'; + +describe('SocketService', () => { + let service: SocketService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(SocketService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/services/socket.service.ts b/frontend/src/app/services/socket.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..cae437ea411f396991b7196f8aa73831b263b82d --- /dev/null +++ b/frontend/src/app/services/socket.service.ts @@ -0,0 +1,57 @@ +import { Injectable } from '@angular/core'; +import { Socket } from 'ngx-socket-io'; +import { AuthenticationService } from './authentication.service'; +import { BehaviorSubject } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class SocketService { + + private _playerNumber = new BehaviorSubject<number>(0);; + + constructor( + private socket: Socket + ) { + this.socket.ioSocket.io.opts.query = { Authorization: localStorage.getItem("token")}; + this.recievePlayerNumber(); + } + + get playerNumber() { + return this._playerNumber.asObservable(); + } + + refreshSocketToken(): void { + this.socket.ioSocket.io.opts.query = { Authorization: localStorage.getItem("token")}; + } + + joinRoom(): void { + this.socket.emit("Join Room"); + } + + leaveRoom(): void { + this.socket.emit("Leave Room"); + } + + recieveHelloWorld() { + this.socket.on("Bienvenue", ()=> { + console.log("Connected"); + }) + } + + recievePlayerNumber(): void { + this.socket.on("Players Waiting", number => { + this._playerNumber.next(number); + console.log(this.playerNumber); + }) + } + + disconnectSocket() { + this.socket.disconnect(); + } + + sendMessage(text: string): void { + this.socket.emit(text); + } + +}