feat(ui): add Lobby and Main Menu Body
Added main Menu body and a Lobby with Bootstrap
This commit is contained in:
@@ -8,6 +8,7 @@ import model.sessions.{PlayerSession, UserSession}
|
|||||||
import play.api.*
|
import play.api.*
|
||||||
import play.api.mvc.*
|
import play.api.mvc.*
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
import javax.inject.*
|
import javax.inject.*
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ class IngameController @Inject()(
|
|||||||
game match {
|
game match {
|
||||||
case Some(g) =>
|
case Some(g) =>
|
||||||
g.logic.getCurrentState match {
|
g.logic.getCurrentState match {
|
||||||
case Lobby => Ok("Lobby: " + gameId)
|
case Lobby => Ok(views.html.lobby.lobby(Some(request.user), g))
|
||||||
case InGame =>
|
case InGame =>
|
||||||
Ok(views.html.ingame.ingame(
|
Ok(views.html.ingame.ingame(
|
||||||
g.getPlayerByUser(request.user),
|
g.getPlayerByUser(request.user),
|
||||||
@@ -63,7 +64,7 @@ class IngameController @Inject()(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
NoContent
|
Redirect(routes.IngameController.game(gameId))
|
||||||
} else {
|
} else {
|
||||||
val throwable = result.failed.get
|
val throwable = result.failed.get
|
||||||
throwable match {
|
throwable match {
|
||||||
@@ -78,6 +79,16 @@ class IngameController @Inject()(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
def kickPlayer(gameId: String, playerToKick: UUID): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||||
|
val game = podManager.getGame(gameId)
|
||||||
|
game.get.leaveGame(playerToKick)
|
||||||
|
Redirect(routes.IngameController.game(gameId))
|
||||||
|
}
|
||||||
|
def leaveGame(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||||
|
val game = podManager.getGame(gameId)
|
||||||
|
game.get.leaveGame(request.user.id)
|
||||||
|
Redirect(routes.MainMenuController.mainMenu())
|
||||||
|
}
|
||||||
def joinGame(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
def joinGame(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||||
val game = podManager.getGame(gameId)
|
val game = podManager.getGame(gameId)
|
||||||
val result = Try {
|
val result = Try {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class MainMenuController @Inject()(
|
|||||||
|
|
||||||
// Pass the request-handling function directly to authAction (no nested Action)
|
// Pass the request-handling function directly to authAction (no nested Action)
|
||||||
def mainMenu(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
def mainMenu(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||||
Ok(views.html.mainmenu.navbar(Some(request.user)))
|
Ok(views.html.mainmenu.creategame(Some(request.user)))
|
||||||
}
|
}
|
||||||
|
|
||||||
def index(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
def index(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||||
@@ -29,12 +29,20 @@ class MainMenuController @Inject()(
|
|||||||
}
|
}
|
||||||
|
|
||||||
def createGame(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
def createGame(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||||
|
val postData = request.body.asFormUrlEncoded
|
||||||
|
if (postData.isDefined) {
|
||||||
|
val gamename = postData.get.get("lobbyname").flatMap(_.headOption).getOrElse(s"${request.user.name}'s Game")
|
||||||
|
val playeramount = postData.get.get("playeramount").flatMap(_.headOption).getOrElse("")
|
||||||
val gameLobby = podManager.createGame(
|
val gameLobby = podManager.createGame(
|
||||||
host = request.user,
|
host = request.user,
|
||||||
name = s"${request.user.name}'s Game",
|
name = gamename,
|
||||||
maxPlayers = 4
|
maxPlayers = playeramount.toInt
|
||||||
)
|
)
|
||||||
Redirect(routes.IngameController.game(gameLobby.id))
|
Redirect(routes.IngameController.game(gameLobby.id))
|
||||||
|
} else {
|
||||||
|
BadRequest("Invalid form submission")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def joinGame(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
def joinGame(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||||
|
|||||||
@@ -88,12 +88,12 @@ class GameLobby private(
|
|||||||
* Remove the user from the game lobby.
|
* Remove the user from the game lobby.
|
||||||
* @param user the user who wants to leave the game.
|
* @param user the user who wants to leave the game.
|
||||||
*/
|
*/
|
||||||
def leaveGame(user: User): Unit = {
|
def leaveGame(userId: UUID): Unit = {
|
||||||
val sessionOpt = users.get(user.id)
|
val sessionOpt = users.get(userId)
|
||||||
if (sessionOpt.isEmpty) {
|
if (sessionOpt.isEmpty) {
|
||||||
throw new NotInThisGameException("You are not in this game!")
|
throw new NotInThisGameException("You are not in this game!")
|
||||||
}
|
}
|
||||||
users.remove(user.id)
|
users.remove(userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -176,6 +176,10 @@ class GameLobby private(
|
|||||||
getPlayerBySession(getUserSession(user.id))
|
getPlayerBySession(getUserSession(user.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getPlayers: mutable.Map[UUID, UserSession] = {
|
||||||
|
users.clone()
|
||||||
|
}
|
||||||
|
|
||||||
private def getPlayerBySession(userSession: UserSession): AbstractPlayer = {
|
private def getPlayerBySession(userSession: UserSession): AbstractPlayer = {
|
||||||
val playerOption = getMatch.totalplayers.find(_.id == userSession.id)
|
val playerOption = getMatch.totalplayers.find(_.id == userSession.id)
|
||||||
if (playerOption.isEmpty) {
|
if (playerOption.isEmpty) {
|
||||||
|
|||||||
84
knockoutwhistweb/app/views/lobby/lobby.scala.html
Normal file
84
knockoutwhistweb/app/views/lobby/lobby.scala.html
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
@(user: Option[model.users.User], gamelobby: logic.game.GameLobby)
|
||||||
|
|
||||||
|
@main("Lobby") {
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="p-3 fs-1 d-flex align-items-center">
|
||||||
|
<div class="text-center" style="flex-grow: 1;">
|
||||||
|
Lobby-Name: @gamelobby.name
|
||||||
|
</div>
|
||||||
|
<form action="@(routes.IngameController.leaveGame(gamelobby.id))">
|
||||||
|
<button type="submit" class="btn btn-danger ms-auto">Exit</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="p-3 text-center fs-4">Playeramount: @gamelobby.getPlayers.size / @gamelobby.maxPlayers</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
|
||||||
|
@if((gamelobby.getUserSession(user.get.id).host)) {
|
||||||
|
@for(playersession <- gamelobby.getPlayers.values) {
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="card" style="width: 18rem;">
|
||||||
|
<img src="@routes.Assets.versioned("images/profile.png")" alt="Profile" class="card-img-top w-50 mx-auto mt-3" />
|
||||||
|
<div class="card-body">
|
||||||
|
@if(playersession.id == user.get.id) {
|
||||||
|
<h5 class="card-title">@playersession.name (You)</h5>
|
||||||
|
<p class="card-text">Your text could be here!</p>
|
||||||
|
<a href="#" class="btn btn-danger disabled" aria-disabled="true" tabindex="-1">Remove</a>
|
||||||
|
} else {
|
||||||
|
<h5 class="card-title">@playersession.name</h5>
|
||||||
|
<p class="card-text">Your text could be here!</p>
|
||||||
|
<form action="@(routes.IngameController.kickPlayer(gamelobby.id, playersession.id))" method="post">
|
||||||
|
<button type="submit" class="btn btn-danger">Remove</button>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col text-center mt-3">
|
||||||
|
<a href="@(routes.IngameController.startGame(gamelobby.id))" class="btn btn-success">Start Game</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
} else {
|
||||||
|
@for(playersession <- gamelobby.getPlayers.values) {
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="card" style="width: 18rem;">
|
||||||
|
<img src="@routes.Assets.versioned("images/profile.png")" alt="Profile" class="card-img-top w-50 mx-auto mt-3" />
|
||||||
|
<div class="card-body">
|
||||||
|
@if(playersession.id == user.get.id) {
|
||||||
|
<h5 class="card-title">@playersession.name (You)</h5>
|
||||||
|
<p class="card-text">Your text could be here!</p>
|
||||||
|
} else {
|
||||||
|
<h5 class="card-title">@playersession.name</h5>
|
||||||
|
<p class="card-text">Your text could be here!</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col mt-3">
|
||||||
|
<p class="text-center fs-4">Waiting for the host to start the game...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col mt-1">
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="spinner-border" role="status">
|
||||||
|
<span class="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
31
knockoutwhistweb/app/views/mainmenu/creategame.scala.html
Normal file
31
knockoutwhistweb/app/views/mainmenu/creategame.scala.html
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
@(user: Option[model.users.User])
|
||||||
|
@navbar(user)
|
||||||
|
@main("Create Game") {
|
||||||
|
<form action="@routes.MainMenuController.createGame()" method="post">
|
||||||
|
<div class="w-50 mx-auto">
|
||||||
|
<div class="mt-3">
|
||||||
|
<label for="lobbyname" class="form-label">Lobby-Name</label>
|
||||||
|
<input type="text" class="form-control" id="lobbyname" name="lobbyname" placeholder="Lobby 1" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-switch mt-3">
|
||||||
|
<input class="form-check-input" type="checkbox" id="visibilityswitch" disabled>
|
||||||
|
<label class="form-check-label" for="visibilityswitch">public/private</label>
|
||||||
|
</div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<label for="playeramount" class="form-label">Playeramount:</label>
|
||||||
|
<input type="range" class="form-range" min="2" max="7" value="2" id="playeramount" name="playeramount">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<span>2</span>
|
||||||
|
<span>3</span>
|
||||||
|
<span>4</span>
|
||||||
|
<span>5</span>
|
||||||
|
<span>6</span>
|
||||||
|
<span>7</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-3 text-center">
|
||||||
|
<button type="submit" class="btn btn-success">Create Game</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
@(user: Option[model.users.User])
|
@(user: Option[model.users.User])
|
||||||
@main("Knockout Whist - Main Menu") {
|
|
||||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navBar" aria-controls="navBar" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navBar" aria-controls="navBar" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
@@ -53,4 +52,3 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
}
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ GET /logout controllers.UserController.logout()
|
|||||||
# In-game routes
|
# In-game routes
|
||||||
GET /game/:id controllers.IngameController.game(id: String)
|
GET /game/:id controllers.IngameController.game(id: String)
|
||||||
GET /game/:id/join controllers.IngameController.joinGame(id: String)
|
GET /game/:id/join controllers.IngameController.joinGame(id: String)
|
||||||
POST /game/:id/start controllers.IngameController.startGame(id: String)
|
GET /game/:id/start controllers.IngameController.startGame(id: String)
|
||||||
|
POST /game/:id/kickPlayer controllers.IngameController.kickPlayer(id: String, playerId: java.util.UUID)
|
||||||
|
GET /game/:id/leaveGame controllers.IngameController.leaveGame(id: String)
|
||||||
POST /game/:id/playCard controllers.IngameController.playCard(id: String)
|
POST /game/:id/playCard controllers.IngameController.playCard(id: String)
|
||||||
Reference in New Issue
Block a user