models: separate backend room model

not viewable on front end, since doesn't have json codecs, yay!
This commit is contained in:
efim 2023-04-27 22:15:13 +04:00
parent da9b96de84
commit ed6d30ec42
5 changed files with 55 additions and 59 deletions

View File

@ -20,30 +20,27 @@ object Models {
players: List[Player], players: List[Player],
me: PlayerID, me: PlayerID,
allowedCards: List[String], allowedCards: List[String],
round: RoundState, round: RoundStateView,
canCloseRound: Boolean = false canCloseRound: Boolean = false
) derives Codec.AsObject { ) derives Codec.AsObject
def playersCount: Int = players.size
}
object RoomStateView { object RoomStateView {
val empty = RoomStateView( val empty = RoomStateView(
List.empty, List.empty,
PlayerID(0), PlayerID(0),
List.empty, List.empty,
RoundState.Voting(None, List.empty), RoundStateView.Voting(None, List.empty),
false false
) )
} }
enum RoundState derives Codec.AsObject: enum RoundStateView derives Codec.AsObject:
/** view state for round before votes are open player can know their vote and who of the other /** view state for round before votes are open player can know their vote and who of the other
* players have voted * players have voted
*/ */
case Voting( case Voting(
myCard: Option[String], myCard: Option[String],
alreadyVoted: List[Player] alreadyVoted: List[PlayerID]
) )
/** view state for round after opening the votes /** view state for round after opening the votes
@ -51,15 +48,14 @@ object Models {
case Viewing( case Viewing(
votes: List[(PlayerID, String)] votes: List[(PlayerID, String)]
) )
final case class PlayerID(id: Long) derives Codec.AsObject
final case class PlayerID(id: Long) derives Codec.AsObject
final case class Player(name: String, id: PlayerID) derives Codec.AsObject final case class Player(name: String, id: PlayerID) derives Codec.AsObject
object Player { object Player {
def create(name: String) = Player(name, PlayerID(Random.nextLong)) def create(name: String) = Player(name, PlayerID(Random.nextLong))
} }
final case class RoomID(name: String) derives Codec.AsObject final case class RoomID(name: String) derives Codec.AsObject
final case class Room( final case class Room(
id: RoomID, id: RoomID,
players: List[Player], players: List[Player],
@ -69,7 +65,7 @@ object Models {
round: RoundState, round: RoundState,
playersPasswords: Map[String, String] = Map.empty // nickname into password playersPasswords: Map[String, String] = Map.empty // nickname into password
) { ) {
def toViewFor(playerId: PlayerID): RoomStateView = { def getViewFor(playerId: PlayerID): RoomStateView = {
players players
.find(_.id == playerId) .find(_.id == playerId)
.fold(ifEmpty = RoomStateView.empty)((me: Player) => .fold(ifEmpty = RoomStateView.empty)((me: Player) =>
@ -77,27 +73,28 @@ object Models {
players, players,
me.id, me.id,
allowedCards, allowedCards,
round, round.toViewFor(playerId),
playerId == owner playerId == owner
) )
) )
} }
} }
object Room {
val testRoom = Room(
id = RoomID("testroom"),
players = List(
Player("me", PlayerID(1L)),
Player("horsey", PlayerID(444L)),
Player("froggy", PlayerID(555L)),
Player("owley", PlayerID(777L))
),
owner = PlayerID(1L),
password = "password",
allowedCards = List("S", "M", "L"),
// TODO - this needs to be a different hting
round = RoundState.Voting(None, List.empty)
)
}
// no need to send to the front end, no deed to derive codec, cool
sealed trait RoundState {
def toViewFor(player: PlayerID): RoundStateView
}
object RoundState {
final case class Voting(votes: Map[PlayerID, String]) extends RoundState {
def toViewFor(playerId: PlayerID): RoundStateView.Voting = RoundStateView.Voting(
myCard = votes.get(playerId),
alreadyVoted = votes.filterKeys(id => id != playerId).keys.toList
)
}
final case class Viewing(votes: Map[PlayerID, String]) extends RoundState {
def toViewFor(player: PlayerID): RoundStateView.Viewing =
RoundStateView.Viewing(votes.toList)
}
}
} }

View File

@ -9,15 +9,15 @@ object TestModels {
val horsey = Player("horsey", PlayerID(12)) val horsey = Player("horsey", PlayerID(12))
val testRoomBackend = Room( val testRoomBackend = Room(
id = RoomID("testroom"), id = RoomID("testroom"),
players = List(me, birdy, pony, horsey), players = List(me, birdy, pony, horsey),
owner = me.id, owner = me.id,
password = "password", password = "password",
allowedCards = List("xs", "s", "m", "l", "xl"), allowedCards = List("xs", "s", "m", "l", "xl"),
round = RoundState.Viewing( round = RoundState.Viewing(
List(me.id -> "xs", pony.id -> "l", birdy.id -> "s", horsey.id -> "m") Map(me.id -> "xs", pony.id -> "l", birdy.id -> "s", horsey.id -> "m")
), ),
playersPasswords = Map("me" -> "nickpassword") // nickname into password playersPasswords = Map("me" -> "nickpassword") // nickname into password
) )
val testSessionId = 1L val testSessionId = 1L
@ -29,70 +29,71 @@ object TestModels {
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 = RoundState.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 = RoundState.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 = RoundState.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 = RoundState.Voting(myCard = None, alreadyVoted = List(birdy)), 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 = RoundState.Voting(myCard = Some("m"), alreadyVoted = List(birdy)), 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 = RoundState.Voting(myCard = Some("m"), alreadyVoted = List(birdy)), 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 = RoundState.Voting(myCard = Some("m"), alreadyVoted = List(birdy)), 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 = RoundState.Voting(myCard = Some("m"), alreadyVoted = List(birdy, horsey)), 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 = RoundState.Voting(myCard = Some("m"), alreadyVoted = List(birdy, horsey, pony)), round = RoundStateView
.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 = RoundState.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

@ -32,9 +32,9 @@ object OwnHandControls {
private def myUnselectedCards(state: RoomStateView): List[String] = { private def myUnselectedCards(state: RoomStateView): List[String] = {
state.round match { state.round match {
case RoundState.Voting(myCard, _) => case RoundStateView.Voting(myCard, _) =>
state.allowedCards.filterNot(value => myCard.contains(value)) state.allowedCards.filterNot(value => myCard.contains(value))
case RoundState.Viewing(votes) => case RoundStateView.Viewing(votes) =>
state.allowedCards.filterNot(value => votes.toMap.get(state.me).contains(value)) state.allowedCards.filterNot(value => votes.toMap.get(state.me).contains(value))
} }
} }

View File

@ -2,21 +2,19 @@ package industries.sunshine.planningpoker
import scala.scalajs.js import scala.scalajs.js
import com.raquo.laminar.api.L.{*, given} import com.raquo.laminar.api.L.{*, given}
import industries.sunshine.planningpoker.common.Models.RoundState import industries.sunshine.planningpoker.common.Models.RoundStateView
import com.raquo.airstream.core.Signal import com.raquo.airstream.core.Signal
import industries.sunshine.planningpoker.common.Models.RoundState
import industries.sunshine.planningpoker.common.Models.RoundState
import io.laminext.fetch.Fetch import io.laminext.fetch.Fetch
import scala.scalajs.js.Dynamic.{global => g} import scala.scalajs.js.Dynamic.{global => g}
import concurrent.ExecutionContext.Implicits.global import concurrent.ExecutionContext.Implicits.global
object TableControls { object TableControls {
def render(roundSignal: Signal[RoundState]): Element = { def render(roundSignal: Signal[RoundStateView]): Element = {
div( div(
child <-- roundSignal.map { child <-- roundSignal.map {
case RoundState.Viewing(_) => newPollButton() case RoundStateView.Viewing(_) => newPollButton()
case RoundState.Voting(_, _) => endPollButton() case RoundStateView.Voting(_, _) => endPollButton()
} }
) )
} }

View File

@ -39,16 +39,16 @@ object TableView {
def getPlayerCardType( def getPlayerCardType(
id: PlayerID, id: PlayerID,
state: RoundState, state: RoundStateView,
name: String, name: String,
myId: PlayerID myId: PlayerID
): CardState = { ): CardState = {
state match { state match {
case isOpen: RoundState.Voting => case isOpen: RoundStateView.Voting =>
if (myId == id) { if (myId == id) {
isOpen.myCard.fold(NoCard(name))(vote => Open(vote)) isOpen.myCard.fold(NoCard(name))(vote => Open(vote))
} else isOpen.alreadyVoted.find(_.id == id).fold(NoCard(name))(_ => CardBack) } else isOpen.alreadyVoted.find(_ == id).fold(NoCard(name))(_ => CardBack)
case isClosed: RoundState.Viewing => case isClosed: RoundStateView.Viewing =>
isClosed.votes isClosed.votes
.find(_._1 == id) .find(_._1 == id)
.fold { .fold {