Resolve #48 - Added house rule support and a first implementation.

This commit is contained in:
Gareth Latty
2016-05-20 12:36:39 +01:00
parent 6b61056f30
commit b38faf92e3
25 changed files with 334 additions and 55 deletions
+10
View File
@@ -32,6 +32,16 @@
align-items: center;
position: relative;
.action-menu {
position: absolute;
top: 0px;
right: 5px;
.disabled {
opacity: 0.5;
}
}
.round-area {
display: flex;
flex-direction: row;
+6 -1
View File
@@ -16,7 +16,7 @@
text-align: center;
font-weight: bold;
padding: 0px 0px 10px 0px;
margin: 0px 0px 20px 0px;
margin: 0px 0px 10px 0px;
}
button {
@@ -24,6 +24,11 @@
margin: 15px auto 0px auto;
}
.info-message {
text-align: center;
padding: 0px 0px 10px 0px;
}
.mui-textfield {
width: 90%;
margin: 10px auto 0px auto;
@@ -30,6 +30,9 @@ object Actions {
case class GetLobbyAndHand(secret: Secret) extends Command
case class Skip(secret: Secret, players: List[Id]) extends Command
case class Back(secret: Secret) extends Command
case class EnableRule(secret: Secret, rule: String) extends Command
case class DisableRule(secret: Secret, rule: String) extends Command
case class Redraw(secret: Secret) extends Command
object Action {
def apply(json: JsValue): Option[Command] =
@@ -44,6 +47,9 @@ object Actions {
case "getLobbyAndHand" => Json.fromJson[GetLobbyAndHand](command).asOpt
case "skip" => Json.fromJson[Skip](command).asOpt
case "back" => Json.fromJson[Back](command).asOpt
case "enableRule" => Json.fromJson[EnableRule](command).asOpt
case "disableRule" => Json.fromJson[DisableRule](command).asOpt
case "redraw" => Json.fromJson[Redraw](command).asOpt
case _ => None
}
})
@@ -60,6 +66,9 @@ object Actions {
implicit val getLobbyAndHandFormat: Format[GetLobbyAndHand] = Json.format[GetLobbyAndHand]
implicit val skipFormat: Format[Skip] = Json.format[Skip]
implicit val backFormat: Format[Back] = Json.format[Back]
implicit val redrawFormat: Format[Redraw] = Json.format[Redraw]
implicit val enableRuleFormat: Format[EnableRule] = Json.format[EnableRule]
implicit val disableRuleFormat: Format[DisableRule] = Json.format[DisableRule]
}
}
@@ -118,6 +118,24 @@ class Game @Inject()(private val state: State, @Assisted private val id: String)
Json.toJson(state.lobbyAndHand(secret))
}
case Redraw(secret) =>
sender() ! Try {
state.redraw(secret)
Json.toJson(state.lobbyAndHand(secret))
}
case EnableRule(secret, rule) =>
sender() ! Try {
state.enableRule(secret, rule)
Json.toJson(state.lobbyAndHand(secret))
}
case DisableRule(secret, rule) =>
sender() ! Try {
state.disableRule(secret, rule)
Json.toJson(state.lobbyAndHand(secret))
}
case _ =>
sender() ! Try {
throw new Exception("Unknown message: " + message)
+27 -1
View File
@@ -22,6 +22,7 @@ import play.api.Play.current
class State @Inject()(private val cardCast: CardcastAPI, @Assisted val gameCode: String)(implicit ec: ExecutionContext) {
private var decks: Set[CardcastDeck] = Set()
private var rules: Set[String] = Set()
private var players: List[Player] = List()
private var lastPlayerId: Int = -1
private var secrets: Map[Id, Secret] = Map()
@@ -34,7 +35,7 @@ class State @Inject()(private val cardCast: CardcastAPI, @Assisted val gameCode:
private var ais: Set[Secret] = Set()
private var connected: Set[Id] = Set()
def config = Config(decks.map(deck => deck.info).toList)
def config = Config(decks.map(deck => deck.info).toList, rules)
def lobby = Lobby(gameCode, config, players, game.map(game => game.round))
def newAi(): Unit = {
@@ -251,6 +252,31 @@ class State @Inject()(private val cardCast: CardcastAPI, @Assisted val gameCode:
sendNotifications()
}
def redraw(secret: Secret): Unit = {
val id = validateSecretAndGetId(secret)
val state = validateInGameAndGetState()
verify(rules.contains("reboot"), "rule-not-enabled")
players = players.map(player => if (player.id == id) {
verify(player.score > 0, "not-enough-points-to-redraw")
player.copy(score = player.score - 1)
} else {
player
})
var hands = state.hands
hands += (id -> Hand(state.deck.drawResponses(Hand.size)))
game = Some(state.copy(hands = hands))
}
def enableRule(secret: Secret, rule: String): Unit = {
val id = validateSecretAndGetId(secret)
rules += rule
}
def disableRule(secret: Secret, rule: String): Unit = {
val id = validateSecretAndGetId(secret)
rules -= rule
}
def register(secret: Secret, socket: ActorRef): Unit = {
val id = validateSecretAndGetId(secret)
val player = playerForId(id)
+1 -1
View File
@@ -22,7 +22,7 @@ object Game {
case class DeckInfo(id: String, name: String, calls: Int, responses: Int)
case class Config(decks: List[DeckInfo])
case class Config(decks: List[DeckInfo], houseRules: Set[String])
case class Round(czar: Player.Id, call: Call, responses: Responses) {
require(responses.revealed.isEmpty ||
+24 -4
View File
@@ -4,6 +4,7 @@ import Json.Decode as Decode exposing ((:=))
import Json.Encode as Encode
import MassiveDecks.API.Request exposing (..)
import MassiveDecks.Scenes.Playing.HouseRule.Id as HouseRule
import MassiveDecks.Models.Game as Game
import MassiveDecks.Models.Player as Player exposing (Player)
import MassiveDecks.Models.JSON.Decode exposing (..)
@@ -22,7 +23,7 @@ createLobby = request "POST" "/lobbies" Nothing [] lobbyDecoder
-}
type NewPlayerError
= NameInUse
| LobbyNotFound
| NewPlayerLobbyNotFound
{-| Makes a request to add a new player to the given lobby. On success, returns a `Secret` for that player.
-}
@@ -33,15 +34,22 @@ newPlayer gameCode name =
("/lobbies/" ++ gameCode ++ "/players")
(Just (encodeName name))
[ ((400, "name-in-use"), Decode.succeed NameInUse)
, ((404, "lobby-not-found"), Decode.succeed LobbyNotFound)
, ((404, "lobby-not-found"), Decode.succeed NewPlayerLobbyNotFound)
]
playerSecretDecoder
{-| Errors specific to getting the lobby and hand.
* `LobbyNotFound` - The given lobby does not exist.
-}
type GetLobbyAndHandError
= LobbyNotFound
{-| Get the lobby and the hand for the player with the given secret (using it to authenticate).
-}
getLobbyAndHand : String -> Player.Secret -> Request Never Game.LobbyAndHand
getLobbyAndHand = commandRequest "getLobbyAndHand" [] []
getLobbyAndHand : String -> Player.Secret -> Request GetLobbyAndHandError Game.LobbyAndHand
getLobbyAndHand = commandRequest "getLobbyAndHand" [] [ ((404, "lobby-not-found"), Decode.succeed LobbyNotFound) ]
{-| Errors specific to add deck requests.
@@ -198,6 +206,18 @@ redraw =
]
{-| Make a request to enable a house rule.
-}
enableRule : HouseRule.Id -> String -> Player.Secret -> Request Never Game.LobbyAndHand
enableRule rule gameCode secret = commandRequest "enableRule" [ ("rule", rule |> HouseRule.toString |> Encode.string) ] [] gameCode secret
{-| Make a request to disable a house rule.
-}
disableRule : HouseRule.Id -> String -> Player.Secret -> Request Never Game.LobbyAndHand
disableRule rule gameCode secret = commandRequest "disableRule" [ ("rule", rule |> HouseRule.toString |> Encode.string) ] [] gameCode secret
{- Private -}
+2 -7
View File
@@ -8,6 +8,7 @@ import Dict exposing (Dict)
import Http exposing (Value(..))
import MassiveDecks.Components.Errors as Errors
import MassiveDecks.Util as Util
{-| Send a request to the server.
@@ -32,13 +33,7 @@ send request knownHandler errorMessageWrapper resultToMessage =
{-| Same as 'send', but for requests with no known errors.
-}
send' : Request Never result -> (Errors.Message -> message) -> (result -> message) -> Cmd message
send' request errorMessageWrapper resultToMessage = send request noKnownErrors errorMessageWrapper resultToMessage
{-| It's impossible to get an instance of Never, this will never happen, so just crash.
-}
noKnownErrors : Never -> message
noKnownErrors noPossibleValue = Debug.crash "Got an impossible response."
send' request errorMessageWrapper resultToMessage = send request Util.impossible errorMessageWrapper resultToMessage
{-| A request to the API.
+2
View File
@@ -2,6 +2,7 @@ module MassiveDecks.Models.Game exposing (..)
import MassiveDecks.Models.Card as Card
import MassiveDecks.Models.Player as Player exposing (Player)
import MassiveDecks.Scenes.Playing.HouseRule.Id as HouseRule
{-| The required information to rejoin a lobby - the ID and the secret.
@@ -22,6 +23,7 @@ type alias GameCode = String
-}
type alias Config =
{ decks : List DeckInfo
, houseRules : List HouseRule.Id
}
+14 -2
View File
@@ -5,6 +5,7 @@ import Json.Decode exposing (..)
import MassiveDecks.Models.Game exposing (..)
import MassiveDecks.Models.Player exposing (..)
import MassiveDecks.Models.Card exposing (..)
import MassiveDecks.Scenes.Playing.HouseRule.Id as HouseRule
lobbyAndHandDecoder : Decoder LobbyAndHand
@@ -30,8 +31,9 @@ deckInfoDecoder = object4 DeckInfo
configDecoder : Decoder Config
configDecoder = object1 Config
configDecoder = object2 Config
("decks" := (list deckInfoDecoder))
("houseRules" := (list houseRuleDecoder))
handDecoder : Decoder Hand
@@ -50,7 +52,7 @@ playerDecoder = object6 Player
playerStatusDecoder : Decoder Status
playerStatusDecoder = customDecoder (string) (\name -> Result.Ok (Maybe.withDefault NotPlayed (nameToStatus name)))
playerStatusDecoder = customDecoder (string) (\name -> nameToStatus name |> Result.fromMaybe ("Unknown player status '" ++ name ++ "'."))
roundDecoder : Decoder Round
@@ -104,6 +106,16 @@ playerSecretDecoder = object2 Secret
("secret" := string)
houseRuleDecoder : Decoder HouseRule.Id
houseRuleDecoder = customDecoder (string) (\name -> ruleNameToId name |> Result.fromMaybe ("Unknown house rule '" ++ name ++ "'."))
ruleNameToId : String -> Maybe HouseRule.Id
ruleNameToId name =
case name of
"reboot" -> Just HouseRule.Reboot
_ -> Nothing
responsesTransportDecoder : Decoder ResponsesTransport
responsesTransportDecoder = object2 ResponsesTransport
(maybe ("hidden" := int))
@@ -78,6 +78,12 @@ update message lobbyModel =
GameStarted lobbyAndHand ->
(model, Util.cmd (LobbyUpdate lobbyAndHand))
EnableRule rule ->
(model, Request.send' (API.enableRule rule gameCode secret) ErrorMessage LobbyUpdate)
DisableRule rule ->
(model, Request.send' (API.disableRule rule gameCode secret) ErrorMessage LobbyUpdate)
NoOp ->
(model, Cmd.none)
@@ -3,6 +3,7 @@ module MassiveDecks.Scenes.Config.Messages exposing (..)
import MassiveDecks.Components.Input as Input
import MassiveDecks.Components.Errors as Errors
import MassiveDecks.Models.Game as Game
import MassiveDecks.Scenes.Playing.HouseRule.Id as HouseRule
{-| This type is used for all sending of messages, allowing us to send messages handled outside this scene.
@@ -21,6 +22,8 @@ type Message
| AddAi
| StartGame
| GameStarted Game.LobbyAndHand
| EnableRule HouseRule.Id
| DisableRule HouseRule.Id
| NoOp
+23 -7
View File
@@ -10,6 +10,9 @@ import MassiveDecks.Components.Icon as Icon
import MassiveDecks.Components.Input as Input
import MassiveDecks.Models.Game as Game
import MassiveDecks.Scenes.Lobby.Models as Lobby
import MassiveDecks.Scenes.Playing.HouseRule as HouseRule exposing (HouseRule)
import MassiveDecks.Scenes.Playing.HouseRule.Id as HouseRule
import MassiveDecks.Scenes.Playing.HouseRule.Available exposing (houseRules)
import MassiveDecks.Scenes.Config.Messages exposing (Message(..), InputId(..), Deck(..))
import MassiveDecks.Util as Util
@@ -38,13 +41,14 @@ view lobbyModel =
]
, div [ id "decks", class "mui-tabs__pane mui--is-active" ]
[ deckList decks model.loadingDecks model.deckIdInput ]
, div [ id "house-rules", class "mui-tabs__pane" ] [ rando ]
, div [ id "house-rules", class "mui-tabs__pane" ]
([ rando ] ++ (List.map (\rule -> houseRule (List.member rule.id lobbyModel.lobby.config.houseRules) rule) houseRules))
, div [ class "mui-divider" ] []
, startGameButton enoughPlayers enoughCards
]
]
invite : String -> String -> Html Message
invite : String -> String -> Html msg
invite appUrl lobbyId =
let
url = Util.lobbyUrl appUrl lobbyId
@@ -99,7 +103,7 @@ deckList decks loadingDecks deckId =
]
deckLink : String -> Html Message
deckLink : String -> Html msg
deckLink id = a [ href ("https://www.cardcastgame.com/browse/deck/" ++ id), target "_blank" ] [ text id ]
@@ -125,8 +129,20 @@ emptyDeckListInfo display =
[]
houseRule : String -> String -> String -> String -> String -> Message -> Html Message
houseRule id' title icon description buttonText message =
houseRule : Bool -> HouseRule -> Html Message
houseRule enabled rule =
let
(buttonText, command) =
if enabled then
("Disable", DisableRule rule.id)
else
("Enable", EnableRule rule.id)
in
houseRuleTemplate (HouseRule.toString rule.id) rule.name rule.icon rule.description buttonText command
houseRuleTemplate : String -> String -> String -> String -> String -> msg -> Html msg
houseRuleTemplate id' title icon description buttonText message =
div [ id id', class "house-rule" ]
[ div [] [ h3 [] [ Icon.icon icon, text " ", text title ]
, button [ class "mui-btn mui-btn--small mui-btn--primary", onClick message ]
@@ -137,12 +153,12 @@ houseRule id' title icon description buttonText message =
rando : Html Message
rando = houseRule "rando" "Rando Cardrissian" "cogs"
rando = houseRuleTemplate "rando" "Rando Cardrissian" "cogs"
"Every round, one random card will be played for an imaginary player named Rando Cardrissian, if he wins, all players go home in a state of everlasting shame."
"Add an AI player" AddAi
startGameWarning : Bool -> Html Message
startGameWarning : Bool -> Html msg
startGameWarning canStart = if canStart then text "" else
span [] [ Icon.icon "info-circle", text " You will need at least two players to start the game." ]
+23 -5
View File
@@ -19,6 +19,9 @@ import MassiveDecks.Scenes.Lobby.Models as Lobby
import MassiveDecks.Scenes.Playing.UI as UI
import MassiveDecks.Scenes.Playing.Models exposing (Model, ShownPlayedCards)
import MassiveDecks.Scenes.Playing.Messages exposing (ConsumerMessage(..), Message(..))
import MassiveDecks.Scenes.Playing.HouseRule as HouseRule exposing (HouseRule)
import MassiveDecks.Scenes.Playing.HouseRule.Id as HouseRule
import MassiveDecks.Scenes.Playing.HouseRule.Reboot as Reboot
import MassiveDecks.Util as Util exposing ((:>))
@@ -34,6 +37,12 @@ init init =
}
houseRule : HouseRule.Id -> HouseRule
houseRule id =
case id of
HouseRule.Reboot -> Reboot.rule
{-| We shouldn't need to do this!
int flags blow up at the moment. For now, we pass a string, but we should take an int from JS in the future.
-}
@@ -129,13 +138,16 @@ update message lobbyModel =
(model, Request.send' (API.back gameCode secret) ErrorMessage (UpdateLobbyAndHand >> LocalMessage))
UpdateLobbyAndHand lobbyAndHand ->
lobbyModel |> updateLobbyAndHand lobbyAndHand
updateLobbyAndHand lobbyAndHand lobbyModel
Redraw ->
(model, Request.send (API.redraw lobbyModel.lobby.gameCode lobbyModel.secret) redrawErrorHandler ErrorMessage (UpdateLobbyAndHand >> LocalMessage))
NoOp ->
(model, Cmd.none)
type alias Update = Lobby.Model -> (Model, Cmd ConsumerMessage)
updateLobbyAndHand : Game.LobbyAndHand -> Update
updateLobbyAndHand : Game.LobbyAndHand -> Lobby.Model -> (Model, Cmd ConsumerMessage)
updateLobbyAndHand lobbyAndHand lobbyModel =
let
lobby = lobbyAndHand.lobby
@@ -162,6 +174,12 @@ updateLobbyAndHand lobbyAndHand lobbyModel =
(newModel, Util.cmd (LobbyUpdate lobbyAndHand))
redrawErrorHandler : API.RedrawError -> ConsumerMessage
redrawErrorHandler error =
case error of
API.NotEnoughPoints -> ErrorMessage <| Errors.New "You do not have enough points to redraw your hand." False
chooseErrorHandler : API.ChooseError -> ConsumerMessage
chooseErrorHandler error =
case error of
@@ -0,0 +1,23 @@
module MassiveDecks.Scenes.Playing.HouseRule exposing (HouseRule, Action)
import MassiveDecks.Scenes.Playing.HouseRule.Id exposing (Id)
import MassiveDecks.Scenes.Playing.Messages as Playing
import MassiveDecks.Scenes.Lobby.Models as Lobby
type alias HouseRule =
{ id : Id
, icon : String
, name : String
, description : String
, actions : List Action
}
type alias Action =
{ icon : String
, text : String
, description : String
, onClick : Playing.Message
, enabled : Lobby.Model -> Bool
}
@@ -0,0 +1,8 @@
module MassiveDecks.Scenes.Playing.HouseRule.Available exposing (houseRules)
import MassiveDecks.Scenes.Playing.HouseRule as HouseRule exposing (HouseRule)
import MassiveDecks.Scenes.Playing.HouseRule.Reboot as Reboot
houseRules : List HouseRule
houseRules = [ Reboot.rule ]
@@ -0,0 +1,11 @@
module MassiveDecks.Scenes.Playing.HouseRule.Id exposing (Id(..), toString)
type Id
= Reboot
toString : Id -> String
toString id =
case id of
Reboot -> "reboot"
@@ -0,0 +1,34 @@
module MassiveDecks.Scenes.Playing.HouseRule.Reboot exposing (rule)
import MassiveDecks.Models.Player as Player
import MassiveDecks.Scenes.Playing.HouseRule as HouseRule exposing (HouseRule)
import MassiveDecks.Scenes.Playing.HouseRule.Id as HouseRule
import MassiveDecks.Scenes.Playing.Messages as Playing
import MassiveDecks.Scenes.Lobby.Models as Lobby
rule : HouseRule
rule =
{ id = HouseRule.Reboot
, icon = "recycle"
, name = "Rebooting the Universe"
, description = "At any time, players may trade in a point to discard their hand and redraw."
, actions = [ rebootAction ]
}
rebootAction : HouseRule.Action
rebootAction =
{ icon = "recycle"
, text = "Redraw"
, description = "Lose one point to discard your hand and draw a new one."
, onClick = Playing.Redraw
, enabled = checkEnabled
}
checkEnabled : Lobby.Model -> Bool
checkEnabled lobbyModel =
Player.byId lobbyModel.secret.id lobbyModel.lobby.players
|> Maybe.map (.score >> ((>) 0))
|> Maybe.withDefault False
@@ -26,4 +26,6 @@ type Message
| CheckForPlayedCardsToAnimate
| Skip (List Player.Id)
| Back
| Redraw
| UpdateLobbyAndHand Game.LobbyAndHand
| NoOp
+44 -3
View File
@@ -10,6 +10,8 @@ import MassiveDecks.Models.Player as Player exposing (Player)
import MassiveDecks.Models.Card as Card
import MassiveDecks.Scenes.Lobby.Models as Lobby
import MassiveDecks.Scenes.Playing.Messages exposing (Message(..))
import MassiveDecks.Scenes.Playing.HouseRule as HouseRule exposing (HouseRule)
import MassiveDecks.Scenes.Playing.HouseRule.Available exposing (houseRules)
import MassiveDecks.Util as Util
@@ -32,6 +34,45 @@ view lobbyModel =
([], [])
houseRuleMenu : Lobby.Model -> List (Html Message)
houseRuleMenu lobbyModel =
let
enabled = List.filter (\rule -> List.member rule.id lobbyModel.lobby.config.houseRules) houseRules
in
if List.isEmpty enabled then
[]
else
[ div [ class "action-menu mui-dropdown"]
[ button [ class "mui-btn mui-btn--small mui-btn--fab"
, title "Game actions."
, attribute "data-mui-toggle" "dropdown"
]
[ Icon.icon "bars" ]
, ul [ class "mui-dropdown__menu mui-dropdown__menu--right" ]
(List.concatMap (houseRuleMenuItems lobbyModel) enabled)
]
]
houseRuleMenuItems : Lobby.Model -> HouseRule -> List (Html Message)
houseRuleMenuItems lobbyModel rule = List.map (houseRuleMenuItem lobbyModel rule) rule.actions
houseRuleMenuItem : Lobby.Model -> HouseRule -> HouseRule.Action -> Html Message
houseRuleMenuItem lobbyModel rule action =
let
enabled = action.enabled lobbyModel
message = if enabled then action.onClick else NoOp
in
li [] [ a [ classList [ ("link", True), ("disabled", not enabled) ]
, title action.description
, attribute "tabindex" "0"
, attribute "role" "button"
, onClick message
]
[ Icon.fwIcon action.icon, text " ", text action.text ]
]
roundContents : Lobby.Model -> Game.Round -> List (Html Message)
roundContents lobbyModel round =
let
@@ -63,9 +104,9 @@ roundContents lobbyModel round =
Card.Hidden _ -> handView model.picked (not canPlay) hand
in
[ playArea
[ div [ class "round-area" ] (List.concat [ [ call round.call callFill ], pickedOrChosen ])
, playedOrHand
]
([ div [ class "round-area" ] (List.concat [ [ call round.call callFill ], pickedOrChosen ])
, playedOrHand
] ++ houseRuleMenu lobbyModel)
]
+12 -2
View File
@@ -34,6 +34,7 @@ init init =
, init = init
, nameInput = Input.init Name "name-input" [ text "Your name in the game." ] "" "Nickname" InputMessage
, gameCodeInput = Input.init GameCode "game-code-input" [ text "The code for the game to join." ] (init.gameCode |> Maybe.withDefault "") "" InputMessage
, info = Nothing
, errors = Errors.init
}
, command)
@@ -80,6 +81,9 @@ update message model =
in
({ model | errors = newErrors }, Cmd.map ErrorMessage cmd)
ShowInfoMessage message ->
({ model | info = Just message }, Cmd.none)
CreateLobby ->
(model, Request.send' API.createLobby ErrorMessage (\lobby -> JoinLobbyAsNewPlayer lobby.gameCode))
@@ -90,7 +94,7 @@ update message model =
{- TODO: We send the secret to the websocket here for crazy reasons - remove - Bug workaround! -}
model !
[ Lobby.sendSecretToWebSocket model.init.url gameCode secret
, Request.send' (API.getLobbyAndHand gameCode secret) ErrorMessage (JoinLobby secret)
, Request.send (API.getLobbyAndHand gameCode secret) getLobbyAndHandErrorHandler ErrorMessage (JoinLobby secret)
, Storage.storeInGame (Game.GameCodeAndSecret gameCode secret)
]
@@ -133,4 +137,10 @@ newPlayerErrorHandler : API.NewPlayerError -> Message
newPlayerErrorHandler error =
case error of
API.NameInUse -> (Name, Just "This name is already in use in this game, try something else." |> Input.Error) |> InputMessage
API.LobbyNotFound -> (GameCode, Just "This game doesn't exist - check you have the right code." |> Input.Error) |> InputMessage
API.NewPlayerLobbyNotFound -> (GameCode, Just "This game doesn't exist - check you have the right code." |> Input.Error) |> InputMessage
getLobbyAndHandErrorHandler : API.GetLobbyAndHandError -> Message
getLobbyAndHandErrorHandler error =
case error of
API.LobbyNotFound -> "The game you were in has ended." |> ShowInfoMessage
@@ -11,6 +11,7 @@ import MassiveDecks.Models.Player as Player
-}
type Message
= CreateLobby
| ShowInfoMessage String
| JoinLobbyAsNewPlayer String
| JoinLobbyAsExistingPlayer Player.Secret String
| JoinLobby Player.Secret Game.LobbyAndHand
@@ -14,5 +14,6 @@ type alias Model =
, init : Init
, nameInput : Input.Model InputId Message
, gameCodeInput : Input.Model InputId Message
, info : Maybe String
, errors : Errors.Model
}
+23 -22
View File
@@ -6,8 +6,8 @@ import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import MassiveDecks.Components.Icon exposing (..)
import MassiveDecks.Components.About exposing (..)
import MassiveDecks.Components.Icon as Icon
import MassiveDecks.Components.About as About
import MassiveDecks.Components.Input as Input
import MassiveDecks.Scenes.Start.Models exposing (Model)
import MassiveDecks.Scenes.Start.Messages exposing (Message(..))
@@ -27,27 +27,28 @@ view model =
in
div [ id "start-screen" ]
[ div [ id "start-screen-content", class "mui-panel" ]
[ h1 [ class "mui--divider-bottom" ] [ text "Massive Decks" ]
, Input.view model.nameInput
, ul [ class "mui-tabs__bar mui-tabs__bar--justified" ]
[ li createLiClass [ a [ attribute "data-mui-toggle" "tab", attribute "data-mui-controls" "pane-new" ] [ text "Create" ] ]
, li joinLiClass [ a [ attribute "data-mui-toggle" "tab", attribute "data-mui-controls" "pane-existing" ] [ text "Join" ] ]
([ h1 [ class "mui--divider-bottom" ] [ text "Massive Decks" ]
] ++ (model.info |> Maybe.map (\message -> [ div [ class "info-message mui--divider-bottom" ] [ Icon.icon "info-circle", text " ", text message ] ]) |> Maybe.withDefault []) ++
[ Input.view model.nameInput
, ul [ class "mui-tabs__bar mui-tabs__bar--justified" ]
[ li createLiClass [ a [ attribute "data-mui-toggle" "tab", attribute "data-mui-controls" "pane-new" ] [ text "Create" ] ]
, li joinLiClass [ a [ attribute "data-mui-toggle" "tab", attribute "data-mui-controls" "pane-existing" ] [ text "Join" ] ]
]
, div [ class ("mui-tabs__pane" ++ createDivClass), id "pane-new" ] [ createLobbyButton nameEntered ]
, div [ class ("mui-tabs__pane" ++ joinDivClass), id "pane-existing" ]
[ Input.view model.gameCodeInput
, joinLobbyButton model.gameCodeInput.value (nameEntered && gameCodeEntered)
]
, a [ class "about-link mui--divider-top link"
, attribute "tabindex" "0"
, attribute "role" "button"
, attribute "onClick" "aboutOverlay()"
]
[ icon "question-circle" , text " About" ]
, aboutOverlay
, div [ id "forkongithub" ] [ div [] [ a [ href "https://github.com/lattyware/massivedecks", target "_blank" ]
[ icon "github", text " Fork me on GitHub" ] ] ]
]
, div [ class ("mui-tabs__pane" ++ createDivClass), id "pane-new" ] [ createLobbyButton nameEntered ]
, div [ class ("mui-tabs__pane" ++ joinDivClass), id "pane-existing" ]
[ Input.view model.gameCodeInput
, joinLobbyButton model.gameCodeInput.value (nameEntered && gameCodeEntered)
]
, a [ class "about-link mui--divider-top link"
, attribute "tabindex" "0"
, attribute "role" "button"
, attribute "onClick" "aboutOverlay()"
]
[ Icon.icon "question-circle" , text " About" ]
, About.aboutOverlay
, div [ id "forkongithub" ] [ div [] [ a [ href "https://github.com/lattyware/massivedecks", target "_blank" ]
[ Icon.icon "github", text " Fork me on GitHub" ] ] ]
])
]
+7
View File
@@ -4,6 +4,13 @@ import Task
import String
{-| Since a value of `Never` can never exist, this can't ever actually happen. Used to fill gaps where the type system
requires a method, but it'll never get called.
-}
impossible : Never -> a
impossible n = impossible n
{-| Add an item to a list if the value is not nothing.
-}
andMaybe : List a -> Maybe a -> List a