feat(ui): added js routing, updated ingame ui, added tricktable (#50)

This merge request has full JS routing for calling specific endpoints. Game is fully playable but doesn't have polling yet. This version already has the UI changes adressed in MR #43 so first merge MR #43 and then this one or only merge this one because it already has the UI changes :)

Co-authored-by: LQ63 <lkhermann@web.de>
Reviewed-on: #50
Reviewed-by: Janis <janis-e@gmx.de>
This commit is contained in:
2025-11-12 11:44:21 +01:00
parent b847d3c054
commit c220e54bb8
12 changed files with 473 additions and 160 deletions

View File

@@ -3,7 +3,7 @@
--background-image: url('/assets/images/background.png') !important;
--color: #f8f9fa !important; /* Light text on dark bg */
--highlightscolor: rgba(131, 131, 131, 0.75) !important;
--background-color: #192734;
/* Bootstrap variable overrides for dark mode */
--bs-body-color: var(--color);
--bs-link-color: #66b2ff;

View File

@@ -2,4 +2,5 @@
--background-image: url('/assets/images/img.png');
--color: black;
--highlightscolor: rgba(0, 0, 0, 0.75);
--background-color: rgba(228, 232, 237, 1);
}

View File

@@ -14,7 +14,7 @@
--bs-border-color: rgba(0, 0, 0, 0.125) !important;
--bs-heading-color: var(--color) !important;
}
@background-color: var(--background-color);
@highlightcolor: var(--highlightscolor);
@background-image: var(--background-image);
@color: var(--color);
@@ -24,10 +24,14 @@
}
.game-field-background {
background-image: @background-image;
background-size: cover;
background-position: center center;
background-repeat: no-repeat;
background-attachment: fixed;
max-width: 1400px;
margin: 0 auto;
min-height: 100vh;
}
.lobby-background {
background-color: @background-color;
width: 100%;
height: 100vh;
}
.navbar-header{
@@ -45,8 +49,11 @@
.bottom-div {
position: fixed;
bottom: 0;
left: 0;
left: 50%;
transform: translateX(-50%);
max-width: 1400px;
width: 100%;
margin: 0;
text-align: center;
padding: 10px;
}
@@ -205,4 +212,20 @@ body {
color: @color;
font-size: 1.5em;
font-family: Arial, serif;
}
.score-table {
background-color: rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 10px;
margin-bottom: 20px;
backdrop-filter: blur(8px);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.score-header {
font-weight: bold;
color: #000000;
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
}
.score-row {
color: #000000;
}

View File

@@ -6,6 +6,7 @@ import exceptions.{CantPlayCardException, GameFullException, NotEnoughPlayersExc
import logic.PodManager
import model.sessions.{PlayerSession, UserSession}
import play.api.*
import play.api.libs.json.Json
import play.api.mvc.*
import java.util.UUID
@@ -64,30 +65,70 @@ class IngameController @Inject()(
}
}
if (result.isSuccess) {
Redirect(routes.IngameController.game(gameId))
Ok(Json.obj(
"status" -> "success",
"redirectUrl" -> routes.IngameController.game(gameId).url
))
} else {
val throwable = result.failed.get
throwable match {
case _: NotInThisGameException =>
BadRequest(throwable.getMessage)
BadRequest(Json.obj(
"status" -> "failure",
"errorMessage" -> throwable.getMessage
))
case _: NotHostException =>
Forbidden(throwable.getMessage)
Forbidden(Json.obj(
"status" -> "failure",
"errorMessage" -> throwable.getMessage
))
case _: NotEnoughPlayersException =>
BadRequest(throwable.getMessage)
BadRequest(Json.obj(
"status" -> "failure",
"errorMessage" -> throwable.getMessage
))
case _ =>
InternalServerError(throwable.getMessage)
InternalServerError(Json.obj(
"status" -> "failure",
"errorMessage" -> throwable.getMessage
))
}
}
}
def kickPlayer(gameId: String, playerToKick: UUID): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
def kickPlayer(gameId: String, playerToKick: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
val game = podManager.getGame(gameId)
game.get.leaveGame(playerToKick)
Redirect(routes.IngameController.game(gameId))
val playerToKickUUID = UUID.fromString(playerToKick)
val result = Try {
game.get.leaveGame(playerToKickUUID)
}
if(result.isSuccess) {
Ok(Json.obj(
"status" -> "success",
"redirectUrl" -> routes.IngameController.game(gameId).url
))
} else {
InternalServerError(Json.obj(
"status" -> "failure",
"errorMessage" -> "Something went wrong."
))
}
}
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())
val result = Try {
game.get.leaveGame(request.user.id)
}
if (result.isSuccess) {
Ok(Json.obj(
"status" -> "success",
"redirectUrl" -> routes.MainMenuController.mainMenu().url
))
} else {
InternalServerError(Json.obj(
"status" -> "failure",
"errorMessage" -> "Something went wrong."
))
}
}
def joinGame(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
val game = podManager.getGame(gameId)
@@ -119,7 +160,10 @@ class IngameController @Inject()(
val game = podManager.getGame(gameId)
game match {
case Some(g) =>
val cardIdOpt = request.body.asFormUrlEncoded.flatMap(_.get("cardId").flatMap(_.headOption))
val jsonBody = request.body.asJson
val cardIdOpt: Option[String] = jsonBody.flatMap { jsValue =>
(jsValue \ "cardID").asOpt[String]
}
cardIdOpt match {
case Some(cardId) =>
var optSession: Option[UserSession] = None
@@ -131,27 +175,51 @@ class IngameController @Inject()(
}
optSession.foreach(_.lock.unlock())
if (result.isSuccess) {
NoContent
Ok(Json.obj(
"status" -> "success",
"redirectUrl" -> routes.IngameController.game(gameId).url
))
} else {
val throwable = result.failed.get
throwable match {
case _: CantPlayCardException =>
BadRequest(throwable.getMessage)
BadRequest(Json.obj(
"status" -> "failure",
"errorMessage" -> throwable.getMessage
))
case _: NotInThisGameException =>
BadRequest(throwable.getMessage)
BadRequest(Json.obj(
"status" -> "failure",
"errorMessage" -> throwable.getMessage
))
case _: IllegalArgumentException =>
BadRequest(throwable.getMessage)
BadRequest(Json.obj(
"status" -> "failure",
"errorMessage" -> throwable.getMessage
))
case _: IllegalStateException =>
BadRequest(throwable.getMessage)
BadRequest(Json.obj(
"status" -> "failure",
"errorMessage" -> throwable.getMessage
))
case _ =>
InternalServerError(throwable.getMessage)
InternalServerError(Json.obj(
"status" -> "failure",
"errorMessage" -> throwable.getMessage
))
}
}
case None =>
BadRequest("cardId parameter is missing")
BadRequest(Json.obj(
"status" -> "failure",
"errorMessage" -> "cardId Parameter is missing"
))
}
case None =>
NotFound("Game not found")
NotFound(Json.obj(
"status" -> "failure",
"errorMessage" -> "Game not found"
))
}
}
}

View File

@@ -0,0 +1,26 @@
package controllers
import auth.{AuthAction, AuthenticatedRequest}
import logic.PodManager
import play.api.mvc.{Action, AnyContent, BaseController, ControllerComponents}
import play.api.routing.JavaScriptReverseRouter
import javax.inject.Inject
class JavaScriptRoutingController @Inject()(
val controllerComponents: ControllerComponents,
val authAction: AuthAction,
val podManager: PodManager
) extends BaseController {
def javascriptRoutes(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
Ok(
JavaScriptReverseRouter("jsRoutes")(
routes.javascript.MainMenuController.createGame,
routes.javascript.IngameController.startGame,
routes.javascript.IngameController.kickPlayer,
routes.javascript.IngameController.leaveGame,
routes.javascript.IngameController.playCard
)
).as("text/javascript")
}
}

View File

@@ -3,6 +3,7 @@ package controllers
import auth.{AuthAction, AuthenticatedRequest}
import logic.PodManager
import play.api.*
import play.api.libs.json.Json
import play.api.mvc.*
import javax.inject.*
@@ -29,18 +30,28 @@ class MainMenuController @Inject()(
}
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 jsonBody = request.body.asJson
if (jsonBody.isDefined) {
val gamename: String = (jsonBody.get \ "lobbyname").asOpt[String]
.getOrElse(s"${request.user.name}'s Game")
val playeramount: String = (jsonBody.get \ "playeramount").asOpt[String]
.getOrElse(throw new IllegalArgumentException("Player amount is required."))
val gameLobby = podManager.createGame(
host = request.user,
name = gamename,
maxPlayers = playeramount.toInt
)
Redirect(routes.IngameController.game(gameLobby.id))
Ok(Json.obj(
"status" -> "success",
"redirectUrl" -> routes.IngameController.game(gameLobby.id).url
))
} else {
BadRequest("Invalid form submission")
BadRequest(Json.obj(
"status" -> "failure",
"errorMessage" -> "Invalid form submission"
))
}
}

View File

@@ -3,68 +3,86 @@
@(player: de.knockoutwhist.player.AbstractPlayer, gamelobby: logic.game.GameLobby)
@main("Ingame") {
<div class="py-5 ms-4 me-4">
<div class="lobby-background vh-100">
<main class="game-field-background vh-100">
<div class="py-5 container-xxl">
<div class="row ms-4 me-4">
<div class="col-4 mt-5 text-start">
<h4 class="fw-semibold mb-1">Current Player</h4>
<p class="fs-5 text-primary">@gamelobby.getLogic.getCurrentPlayer.get.name</p>
@if(!TrickUtil.isOver(gamelobby.getLogic.getCurrentMatch.get, gamelobby.getLogic.getPlayerQueue.get)) {
<div class="row ms-4 me-4">
<div class="col-4 mt-5 text-start">
<h4 class="fw-semibold mb-1">Current Player</h4>
<p class="fs-5 text-primary">@gamelobby.getLogic.getCurrentPlayer.get.name</p>
@if(!TrickUtil.isOver(gamelobby.getLogic.getCurrentMatch.get, gamelobby.getLogic.getPlayerQueue.get)) {
<h4 class="fw-semibold mb-1">Next Player</h4>
@for(nextplayer <- gamelobby.getLogic.getPlayerQueue.get.duplicate()) {
<p class="fs-5 text-primary">@nextplayer</p>
<p class="fs-5 text-primary">@nextplayer</p>
}
}
</div>
}
</div>
<div class="d-flex col-md-4 text-center justify-content-center g-3 mb-5">
@for((cardplayed, player) <- gamelobby.getLogic.getCurrentTrick.get.cards) {
<div class="col-auto">
<div class="card text-center shadow-sm border-0 bg-transparent" style="width: 7rem; backdrop-filter: blur(4px);">
<div class="p-2">
@util.WebUIUtils.cardtoImage(cardplayed) width="100%"/>
</div>
<div class="card-body p-2 bg-transparent">
<small class="fw-semibold text-secondary">@player</small>
<div class="col-4 text-center">
<div class="score-table mt-5">
<h4 class="fw-bold mb-3 text-black">Tricks Won</h4>
<div class="d-flex justify-content-between score-header pb-1">
<div style="width: 50%">PLAYER</div>
<div style="width: 50%">TRICKS</div>
</div>
@for(player <- gamelobby.getLogic.getPlayerQueue.get.toList.sortBy { p =>
-(gamelobby.getLogic.getCurrentRound.get.tricklist.filter { trick => trick.winner.contains(p) }.size)
}) {
<div class="d-flex justify-content-between score-row pt-1">
<div style="width: 50%" class="text-truncate">@player.name</div>
<div style="width: 50%">
@(gamelobby.getLogic.getCurrentRound.get.tricklist.filter { trick => trick.winner.contains(player) }.size)
</div>
</div>
}
</div>
}
<div class="d-flex justify-content-center g-3 mb-5">
@for((cardplayed, player) <- gamelobby.getLogic.getCurrentTrick.get.cards) {
<div class="col-auto">
<div class="card text-center shadow-sm border-0 bg-transparent" style="width: 7rem; backdrop-filter: blur(4px);">
<div class="p-2">
@util.WebUIUtils.cardtoImage(cardplayed) width="100%"/>
</div>
<div class="card-body p-2 bg-transparent">
<small class="fw-semibold text-secondary">@player</small>
</div>
</div>
</div>
}
</div>
</div>
<div class="col-4 mt-5 text-end">
<h4 class="fw-semibold mb-1">Trumpsuit</h4>
<p class="fs-5 text-primary">@gamelobby.getLogic.getCurrentRound.get.trumpSuit</p>
<h5 class="fw-semibold mt-4 mb-1">First Card</h5>
<div class="d-inline-block border rounded shadow-sm p-1 bg-light">
@if(gamelobby.getLogic.getCurrentTrick.get.firstCard.isDefined) {
@util.WebUIUtils.cardtoImage(gamelobby.getLogic.getCurrentTrick.get.firstCard.get) width="80px"/>
} else {
@views.html.render.card.apply("images/cards/1B.png")("Blank Card") width="80px"/>
}
</div>
</div>
</div>
<div class="col-md-4 text-end">
<h4 class="fw-semibold mb-1">Trumpsuit</h4>
<p class="fs-5 text-primary">@gamelobby.getLogic.getCurrentRound.get.trumpSuit</p>
<h5 class="fw-semibold mt-4 mb-1">First Card</h5>
<div class="d-inline-block border rounded shadow-sm p-1 bg-light">
@if(gamelobby.getLogic.getCurrentTrick.get.firstCard.isDefined) {
@util.WebUIUtils.cardtoImage(gamelobby.getLogic.getCurrentTrick.get.firstCard.get) width="80px"/>
} else {
@views.html.render.card.apply("images/cards/1B.png")("Blank Card") width="80px"/>
}
<div class="row justify-content-center g-2 mt-4 bottom-div" style="backdrop-filter: blur(4px); margin-left: 0; margin-right: 0;">
<div class="row justify-content-center" id="card-slide">
@for(i <- player.currentHand().get.cards.indices) {
<div class="col-auto handcard" style="border-radius: 6px">
<div class="btn btn-outline-light p-0 border-0 shadow-none" data-card-id="@i" style="border-radius: 6px" onclick="handlePlayCard(this, '@gamelobby.id')">
@util.WebUIUtils.cardtoImage(player.currentHand().get.cards(i)) width="120px" style="border-radius: 6px"/>
</div>
</div>
}
</div>
</div>
</div>
<div class="row">
</div>
<div class="row justify-content-center g-2 mt-4 bottom-div" style="backdrop-filter: blur(4px);">
<div class="row justify-content-center" id="card-slide">
@for(i <- player.currentHand().get.cards.indices) {
<div class="col-auto handcard" style="border-radius: 6px">
<form action="@(routes.IngameController.playCard(gamelobby.id))" method="post" class="m-0 p-0" style="border-radius: 6px">
<input type="hidden" name="cardId" value="@i" />
<button type="submit" class="btn btn-outline-light p-0 border-0 shadow-none" style="border-radius: 6px">
@util.WebUIUtils.cardtoImage(player.currentHand().get.cards(i)) width="120px" style="border-radius: 6px"/>
</button>
</form>
</div>
}
</div>
</div>
</div>
</main>
</div>
}

View File

@@ -1,82 +1,69 @@
@(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
<main class="lobby-background vh-100">
<div class="container d-flex flex-column" style="height: calc(100vh - 1rem);">
<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>
<div class="btn btn-danger ms-auto" onclick="leaveGame('@gamelobby.id')">Exit</div>
</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 class="row">
<div class="col">
<div class="p-3 text-center fs-4">Playeramount: @gamelobby.getPlayers.size / @gamelobby.maxPlayers</div>
</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;">
<div class="row justify-content-center align-items-center flex-grow-1">
@if((gamelobby.getUserSession(user.get.id).host)) {
@for(playersession <- gamelobby.getPlayers.values) {
<div class="col-auto my-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>*@
<div class="btn btn-danger" onclick="removePlayer('@gamelobby.id', '@playersession.id')">Remove</div>
}
</div>
</div>
</div>
}
<div class="col-12 text-center mb-5">
<div class="btn btn-success" onclick="startGame('@gamelobby.id')">Start Game</div>
</div>
} else {
@for(playersession <- gamelobby.getPlayers.values) {
<div class="col-auto my-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>
<h5 class="card-title">@playersession.name (You)</h5>
} 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>
<h5 class="card-title">@playersession.name</h5>
}
</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>
} else {
<h5 class="card-title">@playersession.name</h5>
}
</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 class="col-12 text-center mt-3">
<p class="fs-4">Waiting for the host to start the game...</p>
<div class="spinner-border mt-1" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
}
}
</div>
</div>
</div>
</main>
}

View File

@@ -18,16 +18,11 @@
<link href="https://cdn.jsdelivr.net/npm/bootstrap@@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
</head>
<body class="d-flex flex-column min-vh-100 game-field-background">
<main>
<body class="d-flex flex-column min-vh-100">
@* And here's where we render the `Html` object containing
* the page content. *@
@content
</main>
<footer class="footer">
</footer>
<script src="@routes.JavaScriptRoutingController.javascriptRoutes()" type="text/javascript"></script>
<script src="@routes.Assets.versioned("javascripts/main.js")" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
</body>

View File

@@ -2,7 +2,7 @@
@main("Create Game") {
@navbar(user)
<form action="@routes.MainMenuController.createGame()" method="post" class="game-field-background">
<main class="lobby-background flex-grow-1">
<div class="w-50 mx-auto">
<div class="mt-3">
<label for="lobbyname" class="form-label">Lobby-Name</label>
@@ -25,8 +25,8 @@
</div>
</div>
<div class="mt-3 text-center">
<button type="submit" class="btn btn-success">Create Game</button>
<div class="btn btn-success" onclick="createGameJS()">Create Game</div>
</div>
</div>
</form>
</main>
}

View File

@@ -3,7 +3,8 @@
# https://www.playframework.com/documentation/latest/ScalaRouting
# ~~~~
# For the javascript routing
GET /assets/js/routes controllers.JavaScriptRoutingController.javascriptRoutes()
# Primary routes
GET / controllers.MainMenuController.index()
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
@@ -25,6 +26,6 @@ GET /logout controllers.UserController.logout()
GET /game/:id controllers.IngameController.game(id: String)
GET /game/:id/join controllers.IngameController.joinGame(id: String)
GET /game/:id/start controllers.IngameController.startGame(id: String)
POST /game/:id/kickPlayer controllers.IngameController.kickPlayer(id: String, playerId: java.util.UUID)
POST /game/:id/kickPlayer controllers.IngameController.kickPlayer(id: String, playerId: String)
GET /game/:id/leaveGame controllers.IngameController.leaveGame(id: String)
POST /game/:id/playCard controllers.IngameController.playCard(id: String)

View File

@@ -77,4 +77,187 @@
})
})
})
})()
})()
function createGameJS() {
let lobbyName = document.getElementById("lobbyname").value;
if (lobbyName === "") {
lobbyName = "DefaultLobby"
}
const playerAmount = document.getElementById("playeramount").value;
const jsonObj = {
lobbyname: lobbyName,
playeramount: playerAmount
}
sendGameCreationRequest(jsonObj);
}
function sendGameCreationRequest(dataObject) {
const route = jsRoutes.controllers.MainMenuController.createGame();
fetch(route.url, {
method: route.type,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(dataObject)
})
.then(response => {
return response.json().then(data => {
if (!response.ok) {
return Promise.reject(data);
}
return data;
});
})
.then(data => {
if (data.status === 'success') {
window.location.href = data.redirectUrl;
}
})
.catch(error => {
if (error && error.errorMessage) {
alert(`${error.errorMessage}`);
} else {
alert('An unexpected error occurred. Please try again.');
}
});
}
function startGame(gameId) {
sendGameStartRequest(gameId)
}
function sendGameStartRequest(gameId) {
const route = jsRoutes.controllers.IngameController.startGame(gameId);
fetch(route.url, {
method: route.type,
})
.then(response => {
return response.json().then(data => {
if (!response.ok) {
return Promise.reject(data);
}
return data;
});
})
.then(data => {
// SUCCESS BLOCK: data is the { status: 'success', ... } object
if (data.status === 'success') {
window.location.href = data.redirectUrl;
}
})
.catch(error => {
if (error && error.errorMessage) {
alert(`${error.errorMessage}`);
} else {
alert('An unexpected error occurred. Please try again.');
}
});
}
function removePlayer(gameid, playersessionId) {
sendRemovePlayerRequest(gameid, playersessionId)
}
function sendRemovePlayerRequest(gameId, playersessionId) {
const route = jsRoutes.controllers.IngameController.kickPlayer(gameId, playersessionId);
fetch(route.url, {
method: route.type,
headers: {
'Content-Type': 'application/json',
}
})
.then(response => {
return response.json().then(data => {
if (!response.ok) {
return Promise.reject(data);
}
return data;
});
})
.then(data => {
// SUCCESS BLOCK: data is the { status: 'success', ... } object
if (data.status === 'success') {
window.location.href = data.redirectUrl;
}
})
.catch(error => {
if (error && error.errorMessage) {
alert(`${error.errorMessage}`);
} else {
alert('An unexpected error occurred. Please try again.');
}
});
}
function leaveGame(gameId) {
sendLeavePlayerRequest(gameId)
}
function sendLeavePlayerRequest(gameId) {
const route = jsRoutes.controllers.IngameController.leaveGame(gameId);
fetch(route.url, {
method: route.type,
})
.then(response => {
return response.json().then(data => {
if (!response.ok) {
return Promise.reject(data);
}
return data;
});
})
.then(data => {
// SUCCESS BLOCK: data is the { status: 'success', ... } object
if (data.status === 'success') {
window.location.href = data.redirectUrl;
}
})
.catch(error => {
if (error && error.errorMessage) {
alert(`${error.errorMessage}`);
} else {
alert('An unexpected error occurred. Please try again.');
}
});
}
function handlePlayCard(cardobject, gameId) {
const cardId = cardobject.dataset.cardId;
const jsonObj = {
cardID: cardId
}
sendPlayCardRequest(jsonObj, gameId)
}
function sendPlayCardRequest(jsonObj, gameId) {
const route = jsRoutes.controllers.IngameController.playCard(gameId);
fetch(route.url, {
method: route.type,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(jsonObj)
})
.then(response => {
return response.json().then(data => {
if (!response.ok) {
return Promise.reject(data);
}
return data;
});
})
.then(data => {
if (data.status === 'success') {
window.location.href = data.redirectUrl;
}
})
.catch(error => {
if (error && error.errorMessage) {
alert(`${error.errorMessage}`);
} else {
alert('An unexpected error occurred. Please try again.');
}
});
}