diff --git a/app/src/app.service.ts b/app/src/app.service.ts index 3024d5d..674685b 100644 --- a/app/src/app.service.ts +++ b/app/src/app.service.ts @@ -14,10 +14,10 @@ import { CreateLobbyEvent, emitUpdateLobbyEvent, JoinLobbyEvent, - ServerEvent, } from 'interface'; import { createWsExceptionFilter } from './websocket-exception-filter'; import { + InvalidPlayerNameException, MissingPlayerNameException, PlayerNotFoundException, } from './exceptions'; @@ -39,6 +39,7 @@ export class AppService implements OnGatewayConnection { createWsExceptionFilter([ PlayerNotFoundException, MissingPlayerNameException, + InvalidPlayerNameException, ]), ) @SubscribeMessage(ClientEvent.CREATE_LOBBY) @@ -60,6 +61,7 @@ export class AppService implements OnGatewayConnection { createWsExceptionFilter([ PlayerNotFoundException, MissingPlayerNameException, + InvalidPlayerNameException, ]), ) @SubscribeMessage(ClientEvent.JOIN_LOBBY) @@ -73,7 +75,7 @@ export class AppService implements OnGatewayConnection { event.lobbyId, ); const playerNames = game.players.map((player) => player.userName); - game.players.forEach((player) => + game.players.forEach((player) => emitUpdateLobbyEvent(player.socket, { playerNames: playerNames, gameCode: game.gameCode, diff --git a/app/src/exceptions.ts b/app/src/exceptions.ts index 886ad1b..67ad805 100644 --- a/app/src/exceptions.ts +++ b/app/src/exceptions.ts @@ -16,6 +16,12 @@ export class MissingPlayerNameException extends WebSocketException { } } +export class InvalidPlayerNameException extends WebSocketException { + constructor(userName: string) { + super('Invalid player name: ' + userName, ErrorCode.INVALID_USER_NAME); + } +} + export class PlayerNotFoundException extends WebSocketException { constructor(playerId: string) { super( diff --git a/app/src/players/player.service.ts b/app/src/players/player.service.ts index 16fbf82..b6c4b3c 100644 --- a/app/src/players/player.service.ts +++ b/app/src/players/player.service.ts @@ -1,6 +1,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { Player } from './player'; import { + InvalidPlayerNameException, MissingPlayerNameException, PlayerNotFoundException, } from 'src/exceptions'; @@ -10,6 +11,7 @@ import { Socket } from 'socket.io/dist/socket'; export class PlayerService { private readonly logger = new Logger(PlayerService.name); private readonly players: Map = new Map(); + private readonly userNameValidator: RegExp = /(^[a-zA-Z]){1,20}$/; createPlayer(socket: Socket) { const player: Player = new Player(socket); @@ -20,6 +22,9 @@ export class PlayerService { if (!userName) { throw new MissingPlayerNameException(); } + if (!this.userNameValidator.test(userName)) { + throw new InvalidPlayerNameException(userName); + } this.players.get(socketId).userName = userName; } diff --git a/app/src/players/player.ts b/app/src/players/player.ts index 0ca0e3e..64e1abf 100644 --- a/app/src/players/player.ts +++ b/app/src/players/player.ts @@ -1,4 +1,4 @@ -import { Socket } from "socket.io"; +import { Socket } from 'socket.io'; export class Player { socketId: string; diff --git a/interface/server-events/ServerError.ts b/interface/server-events/ServerError.ts index 8ba99a3..c1789c1 100644 --- a/interface/server-events/ServerError.ts +++ b/interface/server-events/ServerError.ts @@ -6,5 +6,6 @@ export enum ServerError { export enum ErrorCode { PLAYER_NOT_FOUND = "player-not-found", GAME_NOT_FOUND = "game-not-found", - MISSING_USER_NAME = "player-user-name-not-found", + MISSING_USER_NAME = "missing-player-name", + INVALID_USER_NAME = "invalid-player-name", } diff --git a/web/src/pages/landing/LandingPage.scss b/web/src/pages/landing/LandingPage.scss index ff0c9c7..4b01e2c 100644 --- a/web/src/pages/landing/LandingPage.scss +++ b/web/src/pages/landing/LandingPage.scss @@ -26,6 +26,10 @@ margin: auto; width: 20%; min-width: 300px; + + #error-message { + color: #222; + } } > div { diff --git a/web/src/pages/landing/LandingPage.tsx b/web/src/pages/landing/LandingPage.tsx index 2b7abd5..255318d 100644 --- a/web/src/pages/landing/LandingPage.tsx +++ b/web/src/pages/landing/LandingPage.tsx @@ -1,10 +1,12 @@ import { + attachHandlerToCreateLobbyError, CreateLobbyEvent, + ErrorCode, handleCreateLobby, handleJoinLobby, JoinLobbyEvent, } from "interface"; -import React, { ChangeEvent } from "react"; +import React, { ChangeEvent, useState } from "react"; import { Socket } from "socket.io-client"; import "./LandingPage.scss"; @@ -24,6 +26,21 @@ const LandingPage = (props: LandingPageProps) => { const registerLobbyId = (event: ChangeEvent) => (joinLobbyPayload.lobbyId = event.target.value); + const [receivedError, setReceivedError] = useState("" as ErrorCode); + const userNameErrorCodesToMessageMap = new Map([ + [ErrorCode.MISSING_USER_NAME, "Introduce your player name"], + [ + ErrorCode.INVALID_USER_NAME, + "Player name must be letters or spaces only and up to 25 characters", + ], + ]); + const userNameErrorMessage = + userNameErrorCodesToMessageMap.get(receivedError); + + attachHandlerToCreateLobbyError(socket, (event) => + setReceivedError(event.error), + ); + return (
@@ -34,7 +51,11 @@ const LandingPage = (props: LandingPageProps) => { + {userNameErrorMessage && ( + {userNameErrorMessage} + )} @@ -43,6 +64,7 @@ const LandingPage = (props: LandingPageProps) => {