Finish game password protection.
This commit is contained in:
@@ -25,18 +25,20 @@ createLobby name =
|
||||
-}
|
||||
type NewPlayerError
|
||||
= NameInUse
|
||||
| PasswordWrong
|
||||
| NewPlayerLobbyNotFound
|
||||
|
||||
|
||||
{-| Makes a request to add a new player to the given lobby. On success, returns a `Secret` for that player.
|
||||
-}
|
||||
newPlayer : String -> String -> Request NewPlayerError Player.Secret
|
||||
newPlayer gameCode name =
|
||||
newPlayer : String -> String -> String -> Request NewPlayerError Player.Secret
|
||||
newPlayer gameCode name password =
|
||||
request
|
||||
"POST"
|
||||
("/api/lobbies/" ++ gameCode ++ "/players")
|
||||
(Just (encodeName name))
|
||||
(Just (encodeNameAndPassword name password))
|
||||
[ ( ( 400, "name-in-use" ), Decode.succeed NameInUse )
|
||||
, ( ( 403, "password-wrong" ), Decode.succeed PasswordWrong )
|
||||
, ( ( 404, "lobby-not-found" ), Decode.succeed NewPlayerLobbyNotFound )
|
||||
]
|
||||
playerSecretDecoder
|
||||
@@ -279,6 +281,13 @@ disableRule rule gameCode secret =
|
||||
commandRequest "disableRule" [ ( "rule", rule |> HouseRule.toString |> Encode.string ) ] [] (Decode.succeed ()) gameCode secret
|
||||
|
||||
|
||||
{-| Make a request to set the lobby password
|
||||
-}
|
||||
setPassword : String -> Player.Secret -> String -> Request Never ()
|
||||
setPassword gameCode secret password =
|
||||
commandRequest "setPassword" [ ( "password", password |> Encode.string ) ] [] (Decode.succeed ()) gameCode secret
|
||||
|
||||
|
||||
|
||||
{- Private -}
|
||||
|
||||
|
||||
@@ -128,13 +128,18 @@ update message model =
|
||||
if (identity == model.identity) then
|
||||
case change of
|
||||
Changed value ->
|
||||
( { model | value = value }, Cmd.none )
|
||||
( { model
|
||||
| value = value
|
||||
, error = Nothing
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
Error error ->
|
||||
( { model | error = error }, Cmd.none )
|
||||
|
||||
Submit ->
|
||||
( model, model.submit )
|
||||
( { model | error = Nothing }, model.submit )
|
||||
|
||||
SetEnabled enabled ->
|
||||
( { model | enabled = enabled }, Cmd.none )
|
||||
@@ -145,4 +150,4 @@ update message model =
|
||||
NoOp ->
|
||||
( model, Cmd.none )
|
||||
else
|
||||
( model, Cmd.none )
|
||||
( { model | error = Nothing }, Cmd.none )
|
||||
|
||||
@@ -25,7 +25,7 @@ type alias GameCode =
|
||||
type alias Config =
|
||||
{ decks : List DeckInfo
|
||||
, houseRules : List HouseRule.Id
|
||||
, pasword : Maybe String
|
||||
, password : Maybe String
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -36,3 +36,11 @@ encodePlayerId playerId =
|
||||
encodeName : String -> Json.Value
|
||||
encodeName name =
|
||||
Json.object [ ( "name", Json.string name ) ]
|
||||
|
||||
|
||||
encodeNameAndPassword : String -> String -> Json.Value
|
||||
encodeNameAndPassword name password =
|
||||
Json.object
|
||||
[ ( "name", Json.string name )
|
||||
, ( "password", Json.string password )
|
||||
]
|
||||
|
||||
@@ -10,18 +10,34 @@ import MassiveDecks.Scenes.Lobby.Models as Lobby
|
||||
import MassiveDecks.Scenes.Config.Messages exposing (ConsumerMessage(..), Message(..), InputId(..), Deck(..))
|
||||
import MassiveDecks.Scenes.Config.Models exposing (Model)
|
||||
import MassiveDecks.Scenes.Config.UI as UI
|
||||
import MassiveDecks.Models.Game as Game
|
||||
import MassiveDecks.Models.Player as Player
|
||||
import MassiveDecks.Util as Util exposing ((:>))
|
||||
|
||||
|
||||
{-| Create the initial model for the config screen.
|
||||
-}
|
||||
init : Model
|
||||
init =
|
||||
{ decks = []
|
||||
, deckIdInput = Input.initWithExtra DeckId "deck-id-input" UI.deckIdInputLabel "" "Play Code" UI.addDeckButton (Util.cmd AddDeck) InputMessage
|
||||
, passwordInput = Input.init Password "password-input" UI.passwordInputLabel "" "Password" (Cmd.none) InputMessage
|
||||
, loadingDecks = []
|
||||
}
|
||||
init : Game.Lobby -> Player.Secret -> ( Model, Cmd ConsumerMessage )
|
||||
init lobby secret =
|
||||
let
|
||||
canChangeConfig =
|
||||
lobby.owner == secret.id
|
||||
|
||||
config =
|
||||
lobby.config
|
||||
|
||||
setPasswordButton =
|
||||
if canChangeConfig then
|
||||
UI.setPasswordButton
|
||||
else
|
||||
(\_ -> [])
|
||||
in
|
||||
{ decks = []
|
||||
, deckIdInput = Input.initWithExtra DeckId "input-with-button" UI.deckIdInputLabel "" "Play Code" UI.addDeckButton (Util.cmd AddDeck) InputMessage
|
||||
, passwordInput = Input.initWithExtra Password "input-with-button" UI.passwordInputLabel (config.password |> Maybe.withDefault "") "Password" setPasswordButton (Util.cmd SetPassword) InputMessage
|
||||
, loadingDecks = []
|
||||
}
|
||||
! [ ( Password, Input.SetEnabled canChangeConfig ) |> InputMessage |> LocalMessage |> Util.cmd ]
|
||||
|
||||
|
||||
{-| Subscriptions for the config screen.
|
||||
@@ -77,10 +93,17 @@ update message lobbyModel =
|
||||
|
||||
InputMessage message ->
|
||||
let
|
||||
( deckIdInput, msg ) =
|
||||
( deckIdInput, deckIdMsg ) =
|
||||
Input.update message lobbyModel.config.deckIdInput
|
||||
|
||||
( passwordInput, passwordMsg ) =
|
||||
Input.update message lobbyModel.config.passwordInput
|
||||
in
|
||||
( { model | deckIdInput = deckIdInput }, Cmd.map LocalMessage msg )
|
||||
{ model
|
||||
| deckIdInput = deckIdInput
|
||||
, passwordInput = passwordInput
|
||||
}
|
||||
! [ Cmd.map LocalMessage deckIdMsg, Cmd.map LocalMessage passwordMsg ]
|
||||
|
||||
AddAi ->
|
||||
( model, Request.send_ (API.newAi gameCode secret) ErrorMessage (\_ -> LocalMessage NoOp) )
|
||||
@@ -94,6 +117,9 @@ update message lobbyModel =
|
||||
DisableRule rule ->
|
||||
( model, Request.send_ (API.disableRule rule gameCode secret) ErrorMessage ignore )
|
||||
|
||||
SetPassword ->
|
||||
( model, Request.send_ (API.setPassword gameCode secret model.passwordInput.value) ErrorMessage ignore )
|
||||
|
||||
NoOp ->
|
||||
( model, Cmd.none )
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ type Message
|
||||
| StartGame
|
||||
| EnableRule HouseRule.Id
|
||||
| DisableRule HouseRule.Id
|
||||
| SetPassword
|
||||
| NoOp
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module MassiveDecks.Scenes.Config.UI exposing (view, deckIdInputLabel, passwordInputLabel, addDeckButton)
|
||||
module MassiveDecks.Scenes.Config.UI exposing (view, deckIdInputLabel, passwordInputLabel, addDeckButton, setPasswordButton)
|
||||
|
||||
import String
|
||||
import Html exposing (..)
|
||||
@@ -94,18 +94,29 @@ infoBar =
|
||||
|
||||
passwordInputLabel : List (Html msg)
|
||||
passwordInputLabel =
|
||||
[ text "If blank, no password will be needed - anyone with the game code can play." ]
|
||||
[ text "If blank, anyone with the game code can join." ]
|
||||
|
||||
|
||||
password : Input.Model InputId Message -> Html Message
|
||||
password passwordInputModel =
|
||||
div []
|
||||
[ h3 [] [ Icon.icon "key", text " Privacy" ]
|
||||
, p [] [ text "A password that players will need to enter to get in the game." ]
|
||||
, p [] [ text "A password that players will need to enter to get in the game. People already in the game will not need to enter it, and anyone in the game will be able to see it." ]
|
||||
, Input.view passwordInputModel
|
||||
]
|
||||
|
||||
|
||||
setPasswordButton : String -> List (Html Message)
|
||||
setPasswordButton password =
|
||||
[ button
|
||||
[ class "mui-btn mui-btn--small mui-btn--primary"
|
||||
, onClick SetPassword
|
||||
, title "Set the password."
|
||||
]
|
||||
[ Icon.icon "lock" ]
|
||||
]
|
||||
|
||||
|
||||
invite : String -> String -> Html msg
|
||||
invite appUrl lobbyId =
|
||||
let
|
||||
|
||||
@@ -34,19 +34,23 @@ import MassiveDecks.Util as Util exposing ((:>))
|
||||
-}
|
||||
init : Init -> Game.LobbyAndHand -> Player.Secret -> ( Model, Cmd ConsumerMessage )
|
||||
init init lobbyAndHand secret =
|
||||
{ lobby = lobbyAndHand.lobby
|
||||
, hand = lobbyAndHand.hand
|
||||
, config = Config.init
|
||||
, playing = Playing.init init
|
||||
, browserNotifications = BrowserNotifications.init init.browserNotificationsSupported False
|
||||
, secret = secret
|
||||
, init = init
|
||||
, notification = Nothing
|
||||
, qrNeedsRendering = False
|
||||
, sidebar = Sidebar.init 768 {- Should match the enhance-width in less. -}
|
||||
, tts = TTS.init
|
||||
}
|
||||
! []
|
||||
let
|
||||
( config, configCmd ) =
|
||||
Config.init lobbyAndHand.lobby secret
|
||||
in
|
||||
{ lobby = lobbyAndHand.lobby
|
||||
, hand = lobbyAndHand.hand
|
||||
, config = config
|
||||
, playing = Playing.init init
|
||||
, browserNotifications = BrowserNotifications.init init.browserNotificationsSupported False
|
||||
, secret = secret
|
||||
, init = init
|
||||
, notification = Nothing
|
||||
, qrNeedsRendering = False
|
||||
, sidebar = Sidebar.init 768 {- Should match the enhance-width in less. -}
|
||||
, tts = TTS.init
|
||||
}
|
||||
! [ configCmd |> Cmd.map (ConfigMessage >> LocalMessage) ]
|
||||
|
||||
|
||||
{-| Subscriptions for the lobby.
|
||||
|
||||
@@ -130,20 +130,24 @@ score client owner player =
|
||||
[ ( Player.statusName player.status, True )
|
||||
, ( "disconnected", player.disconnected )
|
||||
, ( "left", player.left )
|
||||
, ( "you", player.id == client )
|
||||
]
|
||||
|
||||
prename =
|
||||
((if player.id == owner then
|
||||
[ Icon.icon "star", text " " ]
|
||||
else
|
||||
[]
|
||||
)
|
||||
++ (if player.disconnected then
|
||||
[ Icon.icon "minus-circle", text " " ]
|
||||
else
|
||||
[]
|
||||
)
|
||||
)
|
||||
List.concat
|
||||
[ if player.id == owner then
|
||||
[ Icon.icon "star", text " " ]
|
||||
else
|
||||
[]
|
||||
, if player.id == client then
|
||||
[ Icon.icon "user-circle", text " " ]
|
||||
else
|
||||
[]
|
||||
, if player.disconnected then
|
||||
[ Icon.icon "minus-circle", text " " ]
|
||||
else
|
||||
[]
|
||||
]
|
||||
|
||||
afterNameTitle =
|
||||
(if player.id == owner then
|
||||
|
||||
@@ -40,6 +40,8 @@ init init location =
|
||||
, path = path
|
||||
, nameInput = Input.init Name "name-input" [ text "Your name in the game." ] "" "Nickname" (Util.cmd SubmitCurrentTab) InputMessage
|
||||
, gameCodeInput = Input.init GameCode "game-code-input" [ text "The code for the game to join." ] (path.gameCode |> Maybe.withDefault "") "" (Util.cmd JoinLobbyAsNewPlayer) InputMessage
|
||||
, passwordInput = Input.init Password "password-input" [ text "The password for the game to join." ] "" "Password" (Util.cmd JoinLobbyAsNewPlayer) InputMessage
|
||||
, passwordRequired = Nothing
|
||||
, errors = Errors.init
|
||||
, overlay = Overlay.init OverlayMessage
|
||||
, buttonsEnabled = True
|
||||
@@ -182,6 +184,9 @@ update message model =
|
||||
SetButtonsEnabled enabled ->
|
||||
( { model | buttonsEnabled = enabled }, Cmd.none )
|
||||
|
||||
SetPasswordRequired ->
|
||||
( { model | passwordRequired = Just model.gameCodeInput.value }, Cmd.none )
|
||||
|
||||
JoinLobbyAsNewPlayer ->
|
||||
( { model | buttonsEnabled = False }, Util.cmd (JoinGivenLobbyAsNewPlayer model.gameCodeInput.value) )
|
||||
|
||||
@@ -189,7 +194,7 @@ update message model =
|
||||
case List.filter (.gameCode >> ((==) gameCode)) model.storage |> List.head of
|
||||
Nothing ->
|
||||
model
|
||||
! [ Request.send (API.newPlayer gameCode model.nameInput.value)
|
||||
! [ Request.send (API.newPlayer gameCode model.nameInput.value model.passwordInput.value)
|
||||
newPlayerErrorHandler
|
||||
ErrorMessage
|
||||
(StoreCredentialsAndMoveToLobby gameCode)
|
||||
@@ -237,12 +242,16 @@ update message model =
|
||||
|
||||
( gameCodeInput, gameCodeCmd ) =
|
||||
Input.update message model.gameCodeInput
|
||||
|
||||
( passwordInput, passwordCmd ) =
|
||||
Input.update message model.passwordInput
|
||||
in
|
||||
( { model
|
||||
| nameInput = nameInput
|
||||
, gameCodeInput = gameCodeInput
|
||||
, passwordInput = passwordInput
|
||||
}
|
||||
, Cmd.batch [ nameCmd, gameCodeCmd ]
|
||||
, Cmd.batch [ nameCmd, gameCodeCmd, passwordCmd ]
|
||||
)
|
||||
|
||||
OverlayMessage overlayMessage ->
|
||||
@@ -319,6 +328,12 @@ newPlayerErrorHandler error =
|
||||
API.NameInUse ->
|
||||
( Name, Just "This name is already in use in this game, try something else." |> Input.Error ) |> InputMessage
|
||||
|
||||
API.PasswordWrong ->
|
||||
Batch
|
||||
[ SetPasswordRequired
|
||||
, ( Password, Just "This game requires a password, please check you have the right one." |> Input.Error ) |> InputMessage
|
||||
]
|
||||
|
||||
API.NewPlayerLobbyNotFound ->
|
||||
( GameCode, Just "This game doesn't exist - check you have the right code." |> Input.Error ) |> InputMessage
|
||||
in
|
||||
|
||||
@@ -32,6 +32,7 @@ type Message
|
||||
| OverlayMessage (Overlay.Message Message)
|
||||
| TabsMessage (Tabs.Message Tab)
|
||||
| StorageMessage Storage.Message
|
||||
| SetPasswordRequired
|
||||
| Batch (List Message)
|
||||
| NoOp
|
||||
|
||||
@@ -41,6 +42,7 @@ type Message
|
||||
type InputId
|
||||
= Name
|
||||
| GameCode
|
||||
| Password
|
||||
|
||||
|
||||
{-| Tabs for the start page.
|
||||
|
||||
@@ -18,6 +18,8 @@ type alias Model =
|
||||
, path : Path
|
||||
, nameInput : Input.Model InputId Message
|
||||
, gameCodeInput : Input.Model InputId Message
|
||||
, passwordInput : Input.Model InputId Message
|
||||
, passwordRequired : Maybe String
|
||||
, errors : Errors.Model
|
||||
, overlay : Overlay.Model Message
|
||||
, buttonsEnabled : Bool
|
||||
|
||||
@@ -97,9 +97,17 @@ renderTab nameEntered gameCodeEntered model tab =
|
||||
[ createLobbyButton (nameEntered && model.buttonsEnabled) ]
|
||||
|
||||
Join ->
|
||||
[ Input.view model.gameCodeInput
|
||||
, joinLobbyButton (nameEntered && gameCodeEntered && model.buttonsEnabled)
|
||||
]
|
||||
List.concat
|
||||
[ [ Input.view model.gameCodeInput
|
||||
]
|
||||
, (if model.passwordRequired == (Just model.gameCodeInput.value) then
|
||||
[ Input.view model.passwordInput ]
|
||||
else
|
||||
[]
|
||||
)
|
||||
, [ joinLobbyButton (nameEntered && gameCodeEntered && model.buttonsEnabled)
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
existingGames : List Game.GameCodeAndSecret -> List (Html msg)
|
||||
|
||||
Reference in New Issue
Block a user