Compare commits

..

3 Commits

Author SHA1 Message Date
efim db65777780 feat!: display room name
BREAKING CHANGE: protocol now expects room name attribute
2023-04-30 09:55:49 +04:00
efim e2795edbe6 bugfix: (not full) overlapping other players 2023-04-30 09:55:25 +04:00
efim 346069ae96 bugfix: disallow joining room without nickname
and also creating a room without a name
maybe maybe spectatorship sometimes later
2023-04-30 09:39:41 +04:00
6 changed files with 117 additions and 85 deletions

View File

@ -34,7 +34,8 @@ lazy val frontend = project
libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.4.0", libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.4.0",
libraryDependencies += "com.raquo" %%% "laminar" % "15.0.1", libraryDependencies += "com.raquo" %%% "laminar" % "15.0.1",
libraryDependencies += "io.laminext" %%% "websocket-circe" % "0.15.0", libraryDependencies += "io.laminext" %%% "websocket-circe" % "0.15.0",
libraryDependencies += "io.laminext" %%% "fetch-circe" % "0.15.0" libraryDependencies += "io.laminext" %%% "fetch-circe" % "0.15.0",
libraryDependencies += "io.laminext" %%% "validation-cats" % "0.15.0"
) )
.dependsOn(common.js) .dependsOn(common.js)

View File

@ -17,6 +17,7 @@ object Models {
* \- whether current player has access to button to finish the round * \- whether current player has access to button to finish the round
*/ */
final case class RoomStateView( final case class RoomStateView(
roomName: String,
players: List[Player], players: List[Player],
me: PlayerID, me: PlayerID,
allowedCards: List[String], allowedCards: List[String],
@ -26,6 +27,7 @@ object Models {
object RoomStateView { object RoomStateView {
val empty = RoomStateView( val empty = RoomStateView(
"",
List.empty, List.empty,
PlayerID(0), PlayerID(0),
List.empty, List.empty,
@ -70,6 +72,7 @@ object Models {
.find(_.id == playerId) .find(_.id == playerId)
.fold(ifEmpty = RoomStateView.empty)((me: Player) => .fold(ifEmpty = RoomStateView.empty)((me: Player) =>
RoomStateView( RoomStateView(
id.name,
players, players,
me.id, me.id,
allowedCards, allowedCards,

View File

@ -24,79 +24,79 @@ object TestModels {
// val testSessions = Map(testSessionId -> (testRoomBackend.id, me.id)) // val testSessions = Map(testSessionId -> (testRoomBackend.id, me.id))
// val testRooms = Map(testRoomBackend.id -> testRoomBackend) // val testRooms = Map(testRoomBackend.id -> testRoomBackend)
val testChangesList = List( // val testChangesList = List(
RoomStateView( // RoomStateView(
players = List(me), // players = List(me),
me = me.id, // me = me.id,
allowedCards = List("xs", "s", "m", "l", "xl"), // allowedCards = List("xs", "s", "m", "l", "xl"),
round = RoundStateView.Voting(myCard = None, alreadyVoted = List.empty), // round = RoundStateView.Voting(myCard = None, alreadyVoted = List.empty),
canCloseRound = true // canCloseRound = true
), // ),
RoomStateView( // RoomStateView(
players = List(me, pony), // players = List(me, pony),
me = me.id, // me = me.id,
allowedCards = List("xs", "s", "m", "l", "xl"), // allowedCards = List("xs", "s", "m", "l", "xl"),
round = RoundStateView.Voting(myCard = None, alreadyVoted = List.empty), // round = RoundStateView.Voting(myCard = None, alreadyVoted = List.empty),
canCloseRound = true // canCloseRound = true
), // ),
RoomStateView( // RoomStateView(
players = List(me, birdy, pony), // players = List(me, birdy, pony),
me = me.id, // me = me.id,
allowedCards = List("xs", "s", "m", "l", "xl"), // allowedCards = List("xs", "s", "m", "l", "xl"),
round = RoundStateView.Voting(myCard = None, alreadyVoted = List.empty), // round = RoundStateView.Voting(myCard = None, alreadyVoted = List.empty),
canCloseRound = true // canCloseRound = true
), // ),
RoomStateView( // RoomStateView(
players = List(me, birdy, pony), // players = List(me, birdy, pony),
me = me.id, // me = me.id,
allowedCards = List("xs", "s", "m", "l", "xl"), // allowedCards = List("xs", "s", "m", "l", "xl"),
round = RoundStateView.Voting(myCard = None, alreadyVoted = List(birdy.id)), // round = RoundStateView.Voting(myCard = None, alreadyVoted = List(birdy.id)),
canCloseRound = true // canCloseRound = true
), // ),
RoomStateView( // RoomStateView(
players = List(me, birdy, pony), // players = List(me, birdy, pony),
me = me.id, // me = me.id,
allowedCards = List("xs", "s", "m", "l", "xl"), // allowedCards = List("xs", "s", "m", "l", "xl"),
round = RoundStateView.Voting(myCard = Some("m"), alreadyVoted = List(birdy.id)), // round = RoundStateView.Voting(myCard = Some("m"), alreadyVoted = List(birdy.id)),
canCloseRound = true // canCloseRound = true
), // ),
RoomStateView( // RoomStateView(
players = List(me, birdy, pony), // players = List(me, birdy, pony),
me = me.id, // me = me.id,
allowedCards = List("xs", "s", "m", "l", "xl"), // allowedCards = List("xs", "s", "m", "l", "xl"),
round = RoundStateView.Voting(myCard = Some("m"), alreadyVoted = List(birdy.id)), // round = RoundStateView.Voting(myCard = Some("m"), alreadyVoted = List(birdy.id)),
canCloseRound = true // canCloseRound = true
), // ),
RoomStateView( // RoomStateView(
players = List(me, birdy, pony, horsey), // players = List(me, birdy, pony, horsey),
me = me.id, // me = me.id,
allowedCards = List("xs", "s", "m", "l", "xl"), // allowedCards = List("xs", "s", "m", "l", "xl"),
round = RoundStateView.Voting(myCard = Some("m"), alreadyVoted = List(birdy.id)), // round = RoundStateView.Voting(myCard = Some("m"), alreadyVoted = List(birdy.id)),
canCloseRound = true // canCloseRound = true
), // ),
RoomStateView( // RoomStateView(
players = List(me, birdy, pony, horsey), // players = List(me, birdy, pony, horsey),
me = me.id, // me = me.id,
allowedCards = List("xs", "s", "m", "l", "xl"), // allowedCards = List("xs", "s", "m", "l", "xl"),
round = RoundStateView.Voting(myCard = Some("m"), alreadyVoted = List(birdy.id, horsey.id)), // round = RoundStateView.Voting(myCard = Some("m"), alreadyVoted = List(birdy.id, horsey.id)),
canCloseRound = true // canCloseRound = true
), // ),
RoomStateView( // RoomStateView(
players = List(me, birdy, pony, horsey), // players = List(me, birdy, pony, horsey),
me = me.id, // me = me.id,
allowedCards = List("xs", "s", "m", "l", "xl"), // allowedCards = List("xs", "s", "m", "l", "xl"),
round = RoundStateView // round = RoundStateView
.Voting(myCard = Some("m"), alreadyVoted = List(birdy.id, horsey.id, pony.id)), // .Voting(myCard = Some("m"), alreadyVoted = List(birdy.id, horsey.id, pony.id)),
canCloseRound = true // canCloseRound = true
), // ),
RoomStateView( // RoomStateView(
players = List(me, birdy, pony, horsey), // players = List(me, birdy, pony, horsey),
me = me.id, // me = me.id,
allowedCards = List("xs", "s", "m", "l", "xl"), // allowedCards = List("xs", "s", "m", "l", "xl"),
round = RoundStateView.Viewing( // round = RoundStateView.Viewing(
List(me.id -> "m", pony.id -> "l", birdy.id -> "s", horsey.id -> "m") // List(me.id -> "m", pony.id -> "l", birdy.id -> "s", horsey.id -> "m")
), // ),
canCloseRound = true // canCloseRound = true
) // )
) // )
} }

View File

@ -6,6 +6,9 @@ import com.raquo.laminar.api.L.{*, given}
import io.laminext.fetch.Fetch import io.laminext.fetch.Fetch
import io.laminext.fetch.circe._ import io.laminext.fetch.circe._
import io.laminext.syntax.core._
import io.laminext.syntax.validation._
import scala.util.Failure import scala.util.Failure
import scala.util.Success import scala.util.Success
@ -29,7 +32,8 @@ object JoinRoomComponent {
value <-- data.signal, value <-- data.signal,
onInput.mapToValue --> data.writer onInput.mapToValue --> data.writer
) )
) ).validated(V.nonBlank("name must not be blank"))
def passInput(data: Var[String], placeholderText: String) = input( def passInput(data: Var[String], placeholderText: String) = input(
tpe := "password", tpe := "password",
className := "border-2 m-1 rounded", className := "border-2 m-1 rounded",
@ -104,16 +108,36 @@ object JoinRoomComponent {
} --> responseReceived } --> responseReceived
) )
val roomNameInput = nameInput(roomNameVar, "Enter room name:")
val roomPassInput = passInput(roomPassVar, "room password")
val nickNameInput = nameInput(nicknameVar, "Enter your nickname:")
val nickPassInput = passInput(nicknamePass, "nickname pass")
div( div(
className := "bg-green-50 w-full h-full flex justify-center", className := "bg-green-50 w-full h-full flex justify-center",
div( div(
className := "w-60 flex flex-col justify-center", className := "w-60 flex flex-col justify-center",
"Logging in:", "Logging in:",
nameInput(roomNameVar, "Enter room name:"), roomNameInput,
passInput(roomPassVar, "room password"), div(
label("Alert: no https, please don't use good passwords"), child.maybe <-- roomNameInput.validationError.optionMap(errors =>
nameInput(nicknameVar, "Enter your nickname:"), span(
passInput(nicknamePass, "nickname pass:"), cls := "text-red-700 text-sm",
errors.map(error => div(error))
)
)
),
roomPassInput,
label("Alert: no https, please use throwaway passwords"),
nickNameInput,
div(
child.maybe <-- nickNameInput.validationError.optionMap(errors =>
span(
cls := "text-red-700 text-sm",
errors.map(error => div(error))
)
)
),
nickPassInput,
submitButton, submitButton,
newRoomButton, newRoomButton,
div( div(

View File

@ -33,9 +33,9 @@ object OtherPlayers {
def renderPlayer(p: Signal[(Player, Int)], cardsAmount: Signal[Int]): Element = { def renderPlayer(p: Signal[(Player, Int)], cardsAmount: Signal[Int]): Element = {
val xOffsetStyleSignal = p.map(_._2) val xOffsetStyleSignal = p.map(_._2)
div( div(
className := "bg-green-200 border-b-2 border-black rounded p-2 m-2 absolute drop-shadow-xl", className := "bg-green-200 border-b-2 border-black rounded p-2 m-2 absolute drop-shadow-xl m-3",
// left <-- p.map(tuple => ((1 + tuple._2) * 10000).toString()), // left <-- p.map(tuple => ((1 + tuple._2) * 10000).toString()),
styleAttr <-- p.map(tuple => s"left: ${(1 + tuple._2) * 100}px"), styleAttr <-- p.map(tuple => s"left: ${(0 + tuple._2) * 200}px"),
child.text <-- p.map(_._1.name), child.text <-- p.map(_._1.name),
renderHandCardBacks(cardsAmount) renderHandCardBacks(cardsAmount)
) )

View File

@ -35,7 +35,11 @@ object RoomView {
val wsFinalDeathSignal = wsStream.closed.collect { case (_, false) => () } val wsFinalDeathSignal = wsStream.closed.collect { case (_, false) => () }
div( div(
className := "w-full h-full border-4 border-amber-900 flex flex-col", className := "w-full h-full border-4 border-amber-900 flex flex-col relative",
div(
className := "absolute top-2 right-2",
child.text <-- roomStateSignal.map(st => s"Room name: '${st.roomName}'"),
),
OtherPlayers.render(roomStateSignal), OtherPlayers.render(roomStateSignal),
TableView.renderTable(roomStateSignal), TableView.renderTable(roomStateSignal),
OwnHandControls.render(roomStateSignal), OwnHandControls.render(roomStateSignal),