Restructure styles, add finished round advance button.

This commit is contained in:
Gareth Latty
2019-07-14 13:47:51 +01:00
parent 12ef605479
commit aaf3f2bd09
45 changed files with 1078 additions and 583 deletions
+12 -1
View File
@@ -163,6 +163,14 @@ update msg model =
, Cmd.batch [ lobbyCmd, settingsCmd ]
)
UpdateToken auth ->
let
shared =
model.shared
in
Util.modelLift (\settings -> { model | shared = { shared | settings = settings } })
(Settings.onTokenUpdate auth shared.settings)
StartMsg startMsg ->
case model.page of
Pages.Start startModel ->
@@ -215,7 +223,7 @@ update msg model =
Refresh ->
( model, Navigation.reload )
-- TODO: Maybe popup something here?
-- TODO: Error? This would be an application error. It's pretty harmless though, probably not worth interrupting.
BlockedExternalUrl ->
( model, Cmd.none )
@@ -225,6 +233,9 @@ update msg model =
NoOp ->
( model, Cmd.none )
Batch messages ->
Util.batchUpdate messages model update
view : Model -> Browser.Document Msg
view model =
@@ -62,8 +62,14 @@ link icon text description href =
{-| Render a menu to Html.
-}
view : Shared -> String -> WlA.XOrigin -> WlA.YOrigin -> Menu msg -> Html msg
view shared anchorId xOrigin yOrigin menu =
view :
Shared
-> String
-> ( WlA.XOrigin, WlA.YOrigin )
-> ( WlA.XOrigin, WlA.YOrigin )
-> Menu msg
-> Html msg
view shared anchorId ( xAnchor, yAnchor ) ( xTransform, yTransform ) menu =
Wl.popover
(List.concat
[ [ WlA.anchor anchorId
@@ -73,7 +79,8 @@ view shared anchorId xOrigin yOrigin menu =
, HtmlA.class "menu"
, WlA.disableFocusTrap
]
, WlA.anchorOrigin xOrigin yOrigin
, WlA.anchorOrigin xAnchor yAnchor
, WlA.transformOrigin xTransform yTransform
]
)
[ Wl.popoverCard [] [ Html.ul [] (menu |> List.map (menuItem shared)) ] ]
+72 -46
View File
@@ -17,6 +17,7 @@ import Html.Events as HtmlE
import MassiveDecks.Card as Card
import MassiveDecks.Card.Model as Card
import MassiveDecks.Card.Play as Play exposing (Play)
import MassiveDecks.Components as Components
import MassiveDecks.Game.Action as Action
import MassiveDecks.Game.Action.Model as Action exposing (Action)
import MassiveDecks.Game.Messages exposing (..)
@@ -39,6 +40,7 @@ import MassiveDecks.Strings.Languages as Lang
import MassiveDecks.User as User exposing (User)
import MassiveDecks.Util as Util
import MassiveDecks.Util.Html as Html
import MassiveDecks.Util.Maybe as Maybe
import Set
import Task
import Weightless as Wl
@@ -176,6 +178,16 @@ update msg model =
SetPlayStyles playStyles ->
( { model | playStyles = playStyles }, Cmd.none )
AdvanceRound ->
case model.nextRound of
Just nextRound ->
( { model | game = { game | round = Round.P nextRound }, nextRound = Nothing }
, Cmd.none
)
Nothing ->
( model, Cmd.none )
subscriptions : Model -> Sub Global.Msg
subscriptions model =
@@ -197,7 +209,7 @@ view shared auth decks users model =
Judging.view shared auth decks round
Round.C round ->
Complete.view shared decks users round
Complete.view shared (Maybe.isJust model.nextRound) decks users round
game =
model.game
@@ -212,33 +224,49 @@ view shared auth decks users model =
call |> Card.viewFilled shared decks Card.Front [] parts
help =
case instruction of
Just i ->
Html.div [ HtmlA.class "context-help" ] [ Icon.view Icon.questionCircle, i |> Lang.html shared ]
let
id =
"context-help-button"
Nothing ->
Html.nothing
helpContent =
case instruction of
Just i ->
[ Components.iconButton
[ HtmlA.id id, Strings.WhatToDo |> Lang.title shared ]
Icon.question
, Wl.popover
(List.concat
[ WlA.anchorOrigin WlA.XCenter WlA.YBottom
, WlA.transformOrigin WlA.XRight WlA.YTop
, [ WlA.anchor id, WlA.fixed, WlA.anchorOpenEvents [ "click" ] ]
]
)
[ Wl.popoverCard [] [ i |> Lang.html shared ] ]
]
Nothing ->
[]
in
Html.div [ HtmlA.id "context-help" ] helpContent
in
Html.div [ HtmlA.id "game" ]
[ help
, Html.div [ HtmlA.class "card-area" ]
[ Html.div [ HtmlA.class "play-area" ]
[ Html.div [ HtmlA.class "cards-in-play" ] [ renderedCall ]
, viewAction shared action
]
, content
, Html.div [ HtmlA.class "scroll-top-spacer" ] []
, Html.div [ HtmlA.class "round" ]
[ renderedCall
, viewAction shared action
]
, content
, Html.div [ HtmlA.class "scroll-top-spacer" ] []
-- TODO: Hide this when at top. Waiting on native elm scroll events, as currently we'd have to ping.
, Html.div [ HtmlA.class "scroll-top" ]
[ Wl.button
[ WlA.flat
, WlA.fab
, WlA.inverted
, ScrollToTop |> lift |> HtmlE.onClick
]
[ Icon.view Icon.arrowUp ]
-- TODO: Hide this when at top. Waiting on native elm scroll events, as currently we'd have to ping constantly.
, Html.div [ HtmlA.class "scroll-top" ]
[ Wl.button
[ WlA.flat
, WlA.fab
, WlA.inverted
, ScrollToTop |> lift |> HtmlE.onClick
]
[ Icon.view Icon.arrowUp ]
]
]
@@ -248,25 +276,29 @@ viewAction shared visible =
Html.div [] (Action.actions |> List.map (Action.view shared visible))
applyGameEvent : Events.GameEvent -> Model -> ( Model, Cmd Global.Msg )
applyGameEvent gameEvent model =
applyGameEvent : Lobby.Auth -> Events.GameEvent -> Model -> ( Model, Cmd Global.Msg )
applyGameEvent auth gameEvent model =
case gameEvent of
Events.HandRedrawn redraw ->
case redraw of
Events.Player { hand } ->
-- TODO: Impl
( model, Cmd.none )
Events.HandRedrawn { player, hand } ->
let
game =
model.game
Events.Other { player } ->
--let
-- playerRecord =
-- Dict.get player model.game.players
--
-- newPlayerRecord =
-- Dict.insert player { playerRecord | score = playerRecord.score - lobby.config.rules.houseRules }
--in
-- TODO: Impl
( model, Cmd.none )
-- TODO: Error, if the rule isn't enabled, we are out of sync.
cost =
game.rules.houseRules.reboot |> Maybe.map .cost |> Maybe.withDefault 0
updatePlayer =
\id -> \p -> { p | score = p.score - cost } |> Maybe.justIf (player == id) |> Maybe.withDefault p
-- TODO: Error, if we get a hand but the event claims another player, we are out of sync.
newHand =
hand |> Maybe.andThen (Maybe.justIf (player == auth.claims.uid)) |> Maybe.withDefault model.hand
players =
game.players |> Dict.map updatePlayer
in
( { model | game = { game | players = players }, hand = newHand }, Cmd.none )
Events.PlayRevealed { id, play } ->
let
@@ -350,19 +382,13 @@ applyGameEvent gameEvent model =
Events.RoundStarted { id, czar, players, call, drawn } ->
let
oldGame =
model.game
drawnAsList =
drawn |> Maybe.withDefault []
( newRound, cmd ) =
Playing.init (Round.playing id czar players call Set.empty) Round.noPick
game =
{ oldGame | round = Round.P newRound }
in
( { model | game = game, hand = model.hand ++ drawnAsList }, cmd )
( { model | nextRound = Just newRound, hand = model.hand ++ drawnAsList }, cmd )
Events.StartRevealing { plays, drawn } ->
case model.game.round of
+22 -6
View File
@@ -13,13 +13,26 @@ import MassiveDecks.Model exposing (..)
import MassiveDecks.Pages.Lobby.Messages as Lobby
import MassiveDecks.Strings as Strings exposing (MdString)
import MassiveDecks.Strings.Languages as Lang
import MassiveDecks.Util.Html.Attributes as HtmlA
import Weightless.Attributes as WlA
actions : List Action
actions =
[ Submit, TakeBack, Judge, Like ]
[ Submit, TakeBack, Judge, Like, Advance ]
{-| Style for actions that are blocking the game from continuing until they are performed.
-}
blocking : List (Html.Attribute msg)
blocking =
[ HtmlA.class "important" ]
{-| Style for an action that doesn't block the game.
-}
normal : List (Html.Attribute msg)
normal =
[ WlA.inverted, WlA.outlined ]
view : Shared -> Maybe Action -> Action -> Html Global.Msg
@@ -28,16 +41,19 @@ view shared visible action =
{ icon, attrs, title, onClick } =
case action of
Submit ->
IconView Icon.check [ HtmlA.class "important" ] Strings.SubmitPlay Game.Submit
IconView Icon.check blocking Strings.SubmitPlay Game.Submit
TakeBack ->
IconView Icon.undo [ WlA.inverted, WlA.outlined ] Strings.TakeBackPlay Game.TakeBack
IconView Icon.undo normal Strings.TakeBackPlay Game.TakeBack
Judge ->
IconView Icon.trophy [ HtmlA.class "important" ] Strings.JudgePlay Game.Judge
IconView Icon.trophy blocking Strings.JudgePlay Game.Judge
Like ->
IconView Icon.thumbsUp [ WlA.inverted, WlA.outlined ] Strings.LikePlay Game.Like
IconView Icon.thumbsUp normal Strings.LikePlay Game.Like
Advance ->
IconView Icon.forward normal Strings.AdvanceRound Game.AdvanceRound
in
Components.floatingActionButton
(List.concat
@@ -6,3 +6,4 @@ type Action
| TakeBack
| Judge
| Like
| Advance
@@ -15,3 +15,4 @@ type Msg
| Like
| ScrollToTop
| SetPlayStyles PlayStyles
| AdvanceRound
@@ -25,6 +25,7 @@ type alias Model =
{ game : Game
, hand : List Card.Response
, playStyles : PlayStyles
, nextRound : Maybe Round.Playing
}
@@ -35,6 +36,7 @@ emptyModel game =
{ game = game
, hand = []
, playStyles = Dict.empty
, nextRound = Nothing
}
@@ -8,6 +8,7 @@ import Html.Attributes as HtmlA
import Html.Keyed as HtmlK
import MassiveDecks.Card as Card
import MassiveDecks.Card.Model as Card
import MassiveDecks.Game.Action.Model as Action
import MassiveDecks.Game.Model exposing (..)
import MassiveDecks.Game.Round as Round
import MassiveDecks.Messages as Global
@@ -20,16 +21,16 @@ import MassiveDecks.Util.Html as Html
import MassiveDecks.Util.Maybe as Maybe
view : Shared -> List Configure.Deck -> Dict User.Id User -> Round.Complete -> RoundView Global.Msg
view shared decks users round =
view : Shared -> Bool -> List Configure.Deck -> Dict User.Id User -> Round.Complete -> RoundView Global.Msg
view shared nextRoundReady decks users round =
let
winning =
round.plays |> Dict.get round.winner
in
{ instruction = Nothing
, action = Nothing
{ instruction = Strings.AdvanceRoundInstruction |> Maybe.justIf nextRoundReady
, action = Action.Advance |> Maybe.justIf nextRoundReady
, content =
HtmlK.ul [ HtmlA.class "judging plays" ]
HtmlK.ul [ HtmlA.class "complete plays cards" ]
(round.plays |> Dict.toList |> List.map (viewPlay shared decks users round.winner))
, fillCallWith = winning |> Maybe.withDefault []
}
@@ -45,7 +46,7 @@ viewPlay :
viewPlay shared decks users winner ( id, responses ) =
let
cards =
responses |> List.map (\r -> Card.view shared decks Card.Front [] (Card.R r))
responses |> List.map (\r -> ( r.details.id, Card.view shared decks Card.Front [] (Card.R r) ))
playedBy =
users |> Dict.get id |> Maybe.map .name |> Maybe.withDefault (Strings.UnknownUser |> Lang.string shared)
@@ -58,6 +59,6 @@ viewPlay shared decks users winner ( id, responses ) =
( id
, Html.li [ HtmlA.class "play-with-byline" ]
[ Html.span [ HtmlA.class "byline" ] byline
, Html.div [ HtmlA.class "play" ] cards
, HtmlK.ol [ HtmlA.class "play card-set" ] cards
]
)
@@ -7,6 +7,7 @@ import Dict exposing (Dict)
import Html exposing (Html)
import Html.Attributes as HtmlA
import Html.Events as HtmlE
import Html.Keyed as HtmlK
import MassiveDecks.Card as Card
import MassiveDecks.Card.Model as Card
import MassiveDecks.Game.Action.Model as Action
@@ -66,7 +67,7 @@ view shared auth decks model round =
( Just Action.TakeBack, Strings.WaitingForPlaysInstruction, True )
hand =
Html.div [ HtmlA.classList [ ( "hand", True ), ( "not-playing", notPlaying ) ] ]
HtmlK.ul [ HtmlA.classList [ ( "hand", True ), ( "cards", True ), ( "not-playing", notPlaying ) ] ]
(model.hand |> List.map (round.pick.cards |> viewHandCard shared decks))
backgroundPlays =
@@ -93,9 +94,10 @@ view shared auth decks model round =
{- Private -}
viewHandCard : Shared -> List Configure.Deck -> List Card.Id -> Card.Response -> Html Global.Msg
viewHandCard : Shared -> List Configure.Deck -> List Card.Id -> Card.Response -> ( String, Html Global.Msg )
viewHandCard shared decks picked response =
Card.view
( response.details.id
, Card.view
shared
decks
Card.Front
@@ -103,6 +105,7 @@ viewHandCard shared decks picked response =
, response.details.id |> Game.Pick |> lift |> HtmlE.onClick
]
(Card.R response)
)
viewBackgroundPlay : PlayStyles -> Int -> Set User.Id -> User.Id -> Html msg
@@ -22,7 +22,7 @@ type alias Details msg =
-}
view : String -> Maybe Play.Id -> List (Details msg) -> Html msg
view class picked details =
HtmlK.ul [ HtmlA.classList [ ( class, True ), ( "plays", True ) ] ] (details |> List.map (viewPlay picked))
HtmlK.ul [ HtmlA.classList [ ( class, True ), ( "cards", True ), ( "plays", True ) ] ] (details |> List.map (viewPlay picked))
@@ -32,13 +32,12 @@ view class picked details =
viewPlay : Maybe Play.Id -> Details msg -> ( String, Html msg )
viewPlay picked { id, responses, action, attrs } =
( id
, Html.li
(List.concat
[ [ HtmlA.classList [ ( "play", True ), ( "picked", picked == Just id ) ]
, action |> Maybe.map HtmlE.onClick |> Maybe.withDefault HtmlA.nothing
]
, attrs
]
)
(responses |> List.map (\card -> Html.div [ HtmlA.class "overlap" ] [ card ]))
, Html.li []
[ Html.ol
(HtmlA.classList [ ( "play", True ), ( "card-set", True ), ( "picked", picked == Just id ) ]
:: (action |> Maybe.map HtmlE.onClick |> Maybe.withDefault HtmlA.nothing)
:: attrs
)
(responses |> List.map (\card -> Html.li [] [ card ]))
]
)
+43
View File
@@ -0,0 +1,43 @@
module MassiveDecks.Icon exposing
( callCard
, massiveDecks
, minimalCardSize
, rereadGames
, responseCard
, squareCardSize
)
{-| Icons that aren't in FontAwesome that we need.
-}
import FontAwesome.Icon as Icon exposing (Icon)
rereadGames : Icon
rereadGames =
Icon "fab" "reread-games" 512 512 "M258 16c-62 0-124 24-171 71-95 94-94 246 0 339 95 93 250 93 345 0l12-12-15-14-12 12-10-10c-82 80-213 80-295 0-82-81-82-210-1-291 81-80 212-81 294-3l-38 35 129 25-1-3-26-117-39 36c-48-46-110-68-172-68zm-79 159c-46 1-70 10-70 10v133s24-9 70-9c50 0 70 20 70 20V196s-20-21-70-21zm160 0c-50 0-70 21-70 21v133s20-20 70-20c46 0 70 9 70 9V185s-24-9-70-10zM179 323c-46 1-70 10-70 10v16s24-9 70-9c50 0 81 31 81 31s30-31 80-31c46 0 70 9 70 9v-16s-24-9-70-10c-50 0-80 32-80 32s-31-32-81-32z"
massiveDecks : Icon
massiveDecks =
Icon "fab" "massive-decks" 512 512 "M273 20c-11 0-21 9-23 21l-9 88h-8L39 163c-12 2-21 15-19 27l50 283c2 12 15 21 27 19l194-34c12-3 21-15 18-27l-13-73 140 14c13 2 25-8 26-20l30-286c1-13-8-24-21-25L276 20h-3zm0 16h1l196 21c4 0 6 3 6 7l-30 286c0 4-4 7-8 6l-144-15-35-193-3-9 10-97c1-3 4-6 7-6zm47 106l-10 99 39 4c15 2 25 0 32-8 9-8 15-21 16-37 2-15-1-29-8-39-6-9-15-13-30-15zm19 20l18 1c16 2 22 14 20 35-3 22-11 32-27 31l-18-2zm-135 91l17 98-20 4-13-77-4 80-20 3-32-73 14 76-20 4-18-98 31-6 31 75 4-81z"
minimalCardSize : Icon
minimalCardSize =
Icon "fas" "minimal-card-size" 384 512 "M45 395c-21 0-39 17-39 39v39c0 21 18 39 39 39h294c21 0 39-18 39-39v-39c0-22-18-39-39-39z"
squareCardSize : Icon
squareCardSize =
Icon "fas" "square-card-size" 384 512 "M45 141c-21 0-39 17-39 39v293c0 21 18 39 39 39h294c21 0 39-18 39-39V180c0-22-18-39-39-39z"
callCard : Icon
callCard =
Icon "fas" "call-card" 384 512 "M45 10h294c16 0 29 13 29 29v434c0 16-13 29-29 29H45c-16 0-29-13-29-29V39c0-16 13-29 29-29z"
responseCard : Icon
responseCard =
Icon "fas" "response-card" 384 512 "M45 0C24 0 6 18 6 39v434c0 21 18 39 39 39h294c21 0 39-18 39-39V39c0-21-18-39-39-39zm0 20h294c11 0 19 8 19 19v434c0 11-8 19-19 19H45c-11 0-19-8-19-19V39c0-11 8-19 19-19z"
+2
View File
@@ -22,9 +22,11 @@ type Msg
| LobbyMsg Lobby.Msg
| SettingsMsg Settings.Msg
| ErrorMsg Error.Msg
| UpdateToken Lobby.Auth
| CastStatusUpdate Cast.Status
| TryCast Lobby.Auth
| Refresh
| BlockedExternalUrl
| Copy String
| NoOp
| Batch (List Msg)
@@ -97,7 +97,12 @@ settings =
(Json.maybe (Json.field "lastUsedName" Json.string))
(Json.field "recentDecks" (Json.list externalSource))
(Json.maybe (Json.field "chosenLanguage" language))
(Json.field "compactCards" Json.bool)
(Json.field "compactCards" cardSize)
cardSize : Json.Decoder Settings.CardSize
cardSize =
Json.int |> Json.andThen (\i -> i |> Settings.cardSizeFromValue |> Maybe.map Json.succeed |> Maybe.withDefault (unknownValue "card size" (String.fromInt i)))
gameCode : Json.Decoder GameCode
@@ -463,10 +468,31 @@ eventByName name =
"RoundFinished" ->
gameEvent roundFinished
"HandRedrawn" ->
gameEvent handRedrawn
"PrivilegeSet" ->
privilegeSet
_ ->
unknownValue "event" name
privilegeSet : Json.Decoder Events.Event
privilegeSet =
Json.map3 (\u -> \p -> \t -> Events.PrivilegeSet { user = u, privilege = p, token = t })
(Json.field "user" userId)
(Json.field "privilege" privilege)
(Json.maybe (Json.field "token" lobbyToken))
handRedrawn : Json.Decoder Events.GameEvent
handRedrawn =
Json.map2 (\pl -> \ha -> Events.HandRedrawn { player = pl, hand = ha })
(Json.field "player" userId)
(Json.maybe (Json.field "hand" (Json.list response)))
roundFinished : Json.Decoder Events.GameEvent
roundFinished =
Json.map2 (\wi -> \pb -> Events.RoundFinished { winner = wi, playedBy = pb })
@@ -43,7 +43,7 @@ settings s =
[ [ ( "tokens", s.tokens |> Dict.toList |> List.map (\( gc, t ) -> ( gc, Json.string t )) |> Json.object )
, ( "openUserList", Json.bool s.openUserList )
, ( "recentDecks", Json.list source s.recentDecks )
, ( "compactCards", Json.bool s.compactCards )
, ( "compactCards", s.cardSize |> cardSize )
]
, lun
, cl
@@ -52,6 +52,11 @@ settings s =
Json.object fields
cardSize : Settings.CardSize -> Json.Value
cardSize =
Settings.cardSizeToValue >> Json.int
lobbyCreation : Start.LobbyCreation -> Json.Value
lobbyCreation c =
Json.object
+98 -46
View File
@@ -40,17 +40,21 @@ import MassiveDecks.Pages.Lobby.Invite as Invite
import MassiveDecks.Pages.Lobby.Messages exposing (..)
import MassiveDecks.Pages.Lobby.Model exposing (..)
import MassiveDecks.Pages.Lobby.Route exposing (..)
import MassiveDecks.Pages.Lobby.Token as Token
import MassiveDecks.Pages.Route as Route
import MassiveDecks.Pages.Start.Route as Start
import MassiveDecks.ServerConnection as ServerConnection
import MassiveDecks.Settings as Settings
import MassiveDecks.Settings.Messages as Settings
import MassiveDecks.Settings.Model as Settings
import MassiveDecks.Strings as Strings exposing (MdString)
import MassiveDecks.Strings.Languages as Lang
import MassiveDecks.User as User exposing (User)
import MassiveDecks.Util as Util
import MassiveDecks.Util.Html as Html
import MassiveDecks.Util.Html.Attributes as HtmlA
import MassiveDecks.Util.Maybe as Maybe
import MassiveDecks.Util.Result as Result
import Weightless as Wl
import Weightless.Attributes as WlA
@@ -99,7 +103,7 @@ subscriptions model =
[ model.lobby |> Maybe.andThen .game |> Maybe.map Game.subscriptions |> Maybe.withDefault Sub.none
, model.notifications |> Animated.subscriptions |> Sub.map (NotificationMsg >> Global.LobbyMsg)
, ServerConnection.notifications
(EventReceived >> Global.LobbyMsg)
eventPreprocess
(ErrorReceived >> Global.LobbyMsg)
(Error.Json >> Error.Add >> Global.ErrorMsg)
]
@@ -149,7 +153,7 @@ update msg model =
(\game ->
Util.modelLift
(\g -> { model | lobby = Just { lobby | game = Just g } })
(Game.applyGameEvent gameEvent game)
(Game.applyGameEvent model.auth gameEvent game)
)
|> Maybe.withDefault ( model, Cmd.none )
@@ -193,6 +197,16 @@ update msg model =
in
addNotification message { model | lobby = Just { lobby | users = newUsers } }
Events.PrivilegeSet { user, privilege } ->
let
updateUser =
\id -> \u -> { u | privilege = privilege } |> Maybe.justIf (user == id) |> Maybe.withDefault u
users =
lobby.users |> Dict.map updateUser
in
( { model | lobby = Just { lobby | users = users } }, Cmd.none )
Nothing ->
case event of
Events.Sync { state, hand, play } ->
@@ -260,21 +274,15 @@ view shared model =
else
Icon.users
title =
model.lobby
|> Maybe.andThen .game
|> Maybe.map (.game >> .round >> Round.data >> .call >> .body >> Parts.viewSingleLine)
notifications =
model.notifications |> List.map (keyedViewNotification shared model.lobby)
in
[ Html.div
[ HtmlA.classList
[ ( "lobby", True )
, ( "compact-cards", shared.settings.settings.compactCards )
]
[ HtmlA.id "lobby"
, HtmlA.classList [ ( "collapsed-users", not usersShown ) ]
, shared.settings.settings.cardSize |> cardSizeToAttr
]
[ Html.div [ HtmlA.id "top-bar" ]
(Html.div [ HtmlA.id "top-bar" ]
[ Html.div [ HtmlA.class "left" ]
(List.concat
[ [ Components.iconButtonStyled
@@ -282,22 +290,22 @@ view shared model =
, Strings.ToggleUserList |> Lang.title shared
]
( [ Icon.lg ], usersIcon )
, gameMenu shared
, lobbyMenu shared model.auth
]
, castButton
]
)
, title |> Maybe.map (Html.div [ HtmlA.class "title-call" ]) |> Maybe.withDefault Html.nothing
, Html.div [] [ Settings.view shared ]
]
, model.lobby
|> Maybe.map (viewLobby shared model.configure usersShown model.auth model.selectedUser)
|> Maybe.withDefault
(Html.div [ HtmlA.class "loading" ]
[ Icon.viewStyled [ Icon.spin, Icon.fa3x ] Icon.circleNotch ]
)
, HtmlK.ol [ HtmlA.class "notifications" ] notifications
]
:: HtmlK.ol [ HtmlA.class "notifications" ] notifications
:: (model.lobby
|> Maybe.map (viewLobby shared model.configure model.auth model.selectedUser)
|> Maybe.withDefault
[ Html.div [ HtmlA.class "loading" ]
[ Icon.viewStyled [ Icon.spin, Icon.fa3x ] Icon.circleNotch ]
]
)
)
, Invite.dialog shared
model.auth.claims.gc
(model.lobby |> Maybe.andThen (.config >> .password))
@@ -309,26 +317,71 @@ view shared model =
{- Private -}
gameMenu : Shared -> Html Global.Msg
gameMenu shared =
cardSizeToAttr : Settings.CardSize -> Html.Attribute msg
cardSizeToAttr cardSize =
case cardSize of
Settings.Minimal ->
HtmlA.class "minimal-card-size"
Settings.Square ->
HtmlA.class "square-card-size"
Settings.Full ->
HtmlA.nothing
eventPreprocess : Events.Event -> Global.Msg
eventPreprocess event =
case event of
Events.PrivilegeSet { token } ->
Global.Batch
[ token
|> Maybe.map (Token.decode >> Result.unifiedMap (Error.Token >> Error.Add >> Global.ErrorMsg) Global.UpdateToken)
|> Maybe.withDefault Global.NoOp
, event |> EventReceived |> Global.LobbyMsg
]
_ ->
event |> EventReceived |> Global.LobbyMsg
lobbyMenu : Shared -> Auth -> Html Global.Msg
lobbyMenu shared auth =
let
id =
"game-menu-button"
"lobby-menu-button"
menuItems =
[ Menu.button Icon.bullhorn Strings.InvitePlayers Strings.InvitePlayers (ToggleInviteDialog |> Global.LobbyMsg |> Just)
, Menu.Separator
, Menu.button Icon.userClock Strings.SetAway Strings.SetAway Nothing
lobbyMenuItems =
[ Menu.button Icon.bullhorn Strings.InvitePlayers Strings.InvitePlayers (ToggleInviteDialog |> Global.LobbyMsg |> Just) ]
userLobbyMenuItems =
[ Menu.button Icon.userClock Strings.SetAway Strings.SetAway Nothing
, Menu.button Icon.signOutAlt Strings.LeaveGame Strings.LeaveGame Nothing
, Menu.Separator
, Menu.link Icon.info Strings.AboutTheGame Strings.AboutTheGame (Just "https://github.com/lattyware/massivedecks")
]
privilegedLobbyMenuItems =
[ Menu.button Icon.stopCircle Strings.EndGame Strings.EndGameDescription Nothing
]
mdMenuItems =
[ Menu.link Icon.info Strings.AboutTheGame Strings.AboutTheGame (Just "https://github.com/lattyware/massivedecks")
, Menu.link Icon.bug Strings.ReportError Strings.ReportError (Just "https://github.com/Lattyware/massivedecks/issues/new")
]
menuItems =
[ lobbyMenuItems |> Just
, userLobbyMenuItems |> Just
, privilegedLobbyMenuItems |> Maybe.justIf (auth.claims.pvg == User.Privileged)
, mdMenuItems |> Just
]
separatedMenuItems =
menuItems |> List.filterMap identity |> List.intersperse [ Menu.Separator ] |> List.concat
in
Html.div []
[ Components.iconButtonStyled [ HtmlA.id id, Strings.GameMenu |> Lang.title shared ]
( [ Icon.lg ], Icon.bars )
, Menu.view shared id WlA.XCenter WlA.YBottom menuItems
, Menu.view shared id ( WlA.XCenter, WlA.YBottom ) ( WlA.XLeft, WlA.YTop ) separatedMenuItems
]
@@ -447,30 +500,29 @@ username shared users id =
|> Maybe.withDefault (Strings.UnknownUser |> Lang.string shared)
viewLobby : Shared -> Configure.Model -> Bool -> Auth -> Maybe User.Id -> Lobby -> Html Global.Msg
viewLobby shared configure usersShown auth selectedUser lobby =
viewLobby : Shared -> Configure.Model -> Auth -> Maybe User.Id -> Lobby -> List (Html Global.Msg)
viewLobby shared configure auth selectedUser lobby =
let
game =
lobby.game |> Maybe.map .game
privileged =
auth.claims.pvg == User.Privileged
content =
[ viewUsers shared auth.claims.uid selectedUser lobby.users game
, Html.div [ HtmlA.id "scroll-frame" ]
[ lobby.game
|> Maybe.map (Game.view shared auth lobby.config.decks lobby.users)
|> Maybe.withDefault (Configure.view shared privileged configure auth.claims.gc lobby lobby.config)
]
]
in
Html.div [ HtmlA.classList [ ( "content-wrapper", True ), ( "collapsed-users", not usersShown ) ] ] content
[ Html.div [ HtmlA.id "lobby-content" ]
[ viewUsers shared auth.claims.uid selectedUser lobby.users game
, Html.div [ HtmlA.id "scroll-frame" ]
[ lobby.game
|> Maybe.map (Game.view shared auth lobby.config.decks lobby.users)
|> Maybe.withDefault (Configure.view shared privileged configure auth.claims.gc lobby lobby.config)
]
]
]
viewUsers : Shared -> User.Id -> Maybe User.Id -> Dict User.Id User -> Maybe Game -> Html Global.Msg
viewUsers shared player selectedUser users game =
Wl.card [ HtmlA.class "users" ]
Wl.card [ HtmlA.id "users" ]
[ Html.div [ HtmlA.class "collapsible" ]
[ users
|> Dict.toList
@@ -533,7 +585,7 @@ viewUser shared player selectedUser game ( userId, user ) =
)
, Html.span [ WlA.listItemSlot WlA.AfterItem ] score
]
, Menu.view shared id WlA.XRight WlA.YTop menuItems
, Menu.view shared id ( WlA.XRight, WlA.YCenter ) ( WlA.XLeft, WlA.YCenter ) menuItems
]
)
@@ -2,6 +2,7 @@ module MassiveDecks.Pages.Lobby.Actions exposing
( addDeck
, changeHouseRule
, judge
, redraw
, removeDeck
, reveal
, setHandSize
@@ -82,6 +83,11 @@ judge play =
action "Judge" [ ( "winner", play |> Json.string ) ]
redraw : Cmd msg
redraw =
action "Redraw" []
{- Private -}
@@ -4,7 +4,6 @@ module MassiveDecks.Pages.Lobby.Events exposing
, Event(..)
, GameEvent(..)
, PresenceState(..)
, Redraw(..)
)
import Dict exposing (Dict)
@@ -28,6 +27,7 @@ type Event
-- Not a game event because we don't need to be in a game
| GameStarted { round : Round.Playing, hand : List Card.Response }
| Game GameEvent
| PrivilegeSet { user : User.Id, privilege : User.Privilege, token : Maybe Lobby.Token }
{-| The user's intentional presence in the lobby.
@@ -53,13 +53,8 @@ type DeckChange
| Fail { reason : Source.LoadFailureReason }
type Redraw
= Player { hand : List Card.Response }
| Other { player : User.Id }
type GameEvent
= HandRedrawn Redraw
= HandRedrawn { player : User.Id, hand : Maybe (List Card.Response) }
| PlayRevealed { id : Play.Id, play : List Card.Response }
| PlaySubmitted { by : User.Id }
| PlayTakenBack { by : User.Id }
+3 -3
View File
@@ -21,6 +21,7 @@ import MassiveDecks.Card.Source.Model as Source
import MassiveDecks.Components.Form as Form
import MassiveDecks.Components.Form.Message as Message
import MassiveDecks.Error as Error
import MassiveDecks.Icon as Icon
import MassiveDecks.Messages as Global
import MassiveDecks.Model exposing (..)
import MassiveDecks.Models.MdError as MdError exposing (MdError)
@@ -43,7 +44,6 @@ import MassiveDecks.Util as Util
import MassiveDecks.Util.Html as Html
import MassiveDecks.Util.Maybe as Maybe
import MassiveDecks.Version as Version
import Reread
import Svg.Attributes as SvgA
import Weightless as Wl
import Weightless.Attributes as WlA
@@ -169,13 +169,13 @@ view shared model =
, Strings.MDProject |> Lang.title shared
, HtmlA.href "https://github.com/Lattyware/massivedecks"
]
[ Icon.viewStyled [ Strings.MDLogoDescription |> Lang.alt shared ] Reread.mdIcon ]
[ Icon.viewStyled [ Strings.MDLogoDescription |> Lang.alt shared ] Icon.massiveDecks ]
, Html.blankA
[ HtmlA.class "logo"
, Strings.DevelopedByReread |> Lang.title shared
, HtmlA.href "https://www.rereadgames.com/"
]
[ Icon.viewStyled [ Strings.RereadLogoDescription |> Lang.alt shared ] Reread.icon ]
[ Icon.viewStyled [ Strings.RereadLogoDescription |> Lang.alt shared ] Icon.rereadGames ]
]
, Html.p [ HtmlA.class "version" ]
[ Html.text "\""
@@ -4,7 +4,6 @@ module MassiveDecks.Pages.Start.LobbyBrowser.Model exposing
, UserSummary
)
import MassiveDecks.Error.Model exposing (Error)
import MassiveDecks.Pages.Lobby.GameCode as GameCode exposing (GameCode)
import MassiveDecks.Pages.Lobby.Model as Lobby
import MassiveDecks.Requests.HttpData.Model exposing (HttpData)
+1 -1
View File
@@ -67,7 +67,7 @@ checkAlive : (Request.Response () (Dict Lobby.Token Bool) -> msg) -> List Lobby.
checkAlive msg tokens =
{ method = "POST"
, headers = []
, url = url [ "games", "alive" ]
, url = url [ "alive" ]
, body = tokens |> Encoders.checkAlive |> Http.jsonBody
, expect = Request.expectResponse msg noError Decoders.tokenValidity
, timeout = Nothing
+67 -20
View File
@@ -3,6 +3,7 @@ module MassiveDecks.Settings exposing
, defaults
, init
, onJoinLobby
, onTokenUpdate
, update
, view
)
@@ -19,6 +20,7 @@ import Http
import MassiveDecks.Components as Components
import MassiveDecks.Components.Form as Form
import MassiveDecks.Components.Form.Message as Message
import MassiveDecks.Icon as Icon
import MassiveDecks.LocalStorage as LocalStorage
import MassiveDecks.Messages as Global
import MassiveDecks.Model exposing (..)
@@ -34,6 +36,7 @@ import MassiveDecks.Strings.Languages as Lang
import MassiveDecks.Strings.Languages.Model as Lang exposing (Language)
import Weightless as Wl
import Weightless.Attributes as WlA
import Weightless.Slider as Slider
init : Settings -> ( Model, Cmd Global.Msg )
@@ -63,7 +66,7 @@ defaults =
, openUserList = False
, recentDecks = []
, chosenLanguage = Nothing
, compactCards = False
, cardSize = Full
}
@@ -76,8 +79,8 @@ update msg model =
ChangeLang language ->
changeSettings (\s -> { s | chosenLanguage = language }) model
ChangeCompactCards enabled ->
changeSettings (\s -> { s | compactCards = enabled }) model
ChangeCardSize size ->
changeSettings (\s -> { s | cardSize = size }) model
ChangeOpenUserList open ->
changeSettings (\s -> { s | openUserList = open }) model
@@ -117,7 +120,7 @@ view shared =
[ Html.h3 [] [ Strings.SettingsTitle |> Lang.html shared ]
, Html.div [ HtmlA.class "body" ]
[ languageSelector shared
, compactSwitch shared
, cardSize shared
, speechSwitch shared
, notificationsSwitch shared
]
@@ -144,6 +147,22 @@ onJoinLobby auth name model =
( { model | settings = updatedSettings }, LocalStorage.store updatedSettings )
{-| Replace a token in our storage because it has been updated.
-}
onTokenUpdate : Lobby.Auth -> Model -> ( Model, Cmd msg )
onTokenUpdate auth model =
let
settings =
model.settings
updatedSettings =
{ settings
| tokens = Dict.insert (auth.claims.gc |> GameCode.toString) auth.token settings.tokens
}
in
( { model | settings = updatedSettings }, LocalStorage.store updatedSettings )
{-| Get all the tokens in settings as `Auth`s by game code.
-}
auths : Settings -> Dict String Lobby.Auth
@@ -171,8 +190,8 @@ changeSettings f model =
( { model | settings = settings }, LocalStorage.store settings )
compactSwitch : Shared -> Html Global.Msg
compactSwitch shared =
cardSize : Shared -> Html Global.Msg
cardSize shared =
let
model =
shared.settings
@@ -181,29 +200,60 @@ compactSwitch shared =
model.settings
in
Form.section shared
"compact-cards"
"card-size"
(Html.div
[ HtmlA.class "multipart" ]
[ Wl.switch
[ HtmlA.checked settings.compactCards
, HtmlE.onCheck (ChangeCompactCards >> Global.SettingsMsg)
[ Wl.slider
[ HtmlA.class "primary"
, Slider.step 1
, Slider.min 1
, Slider.max 3
, WlA.label "Card Size"
, WlA.outlined
, Slider.thumbLabel True
, String.toInt
>> Maybe.andThen cardSizeFromValue
>> Maybe.withDefault Full
>> ChangeCardSize
>> Global.SettingsMsg
|> HtmlE.onInput
, model.settings.cardSize |> cardSizeToValue |> String.fromInt |> WlA.value
]
, Html.label []
[ Icon.view Icon.searchMinus
, Html.text " "
, Strings.CompactCardsSetting |> Lang.html shared
[ Icon.viewStyled [ Slider.slot Slider.Before ] Icon.searchMinus
, [ model.settings.cardSize |> cardSizeThumb ] |> Html.span [ Slider.slot Slider.ThumbLabel ]
, Icon.viewStyled [ Slider.slot Slider.After ] Icon.searchPlus
]
]
-- [ Wl.switch
-- [ HtmlA.checked settings.compactCards
-- , HtmlE.onCheck (ChangeCompactCards >> Global.SettingsMsg)
-- ]
-- , Html.label []
-- [ Icon.view Icon.searchMinus
-- , Html.text " "
-- , Strings.CompactCardsSetting |> Lang.html shared
-- ]
-- ]
)
[ Message.info Strings.CompactCardsExplanation ]
[ Message.info Strings.CardSizeExplanation ]
cardSizeThumb : CardSize -> Html msg
cardSizeThumb size =
case size of
Minimal ->
Icon.view Icon.minimalCardSize
-- TODO: Impl
Square ->
Icon.view Icon.squareCardSize
Full ->
Icon.view Icon.callCard
speechSwitch : Shared -> Html Global.Msg
speechSwitch shared =
-- TODO: Impl
Form.section shared
"speech"
(Html.div
@@ -219,12 +269,9 @@ speechSwitch shared =
[ Message.info Strings.SpeechExplanation ]
-- TODO: Impl
notificationsSwitch : Shared -> Html Global.Msg
notificationsSwitch shared =
-- TODO: Impl
Form.section shared
"notifications"
(Html.div
@@ -2,12 +2,13 @@ module MassiveDecks.Settings.Messages exposing (Msg(..))
import Dict exposing (Dict)
import MassiveDecks.Pages.Lobby.Model as Lobby
import MassiveDecks.Settings.Model exposing (..)
import MassiveDecks.Strings.Languages.Model exposing (Language)
type Msg
= ChangeLang (Maybe Language)
| ChangeCompactCards Bool
| ChangeCardSize CardSize
| ChangeOpenUserList Bool
| ToggleOpen
| RemoveInvalid (Dict Lobby.Token Bool)
+49 -2
View File
@@ -1,4 +1,11 @@
module MassiveDecks.Settings.Model exposing (Model, Settings)
module MassiveDecks.Settings.Model exposing
( CardSize(..)
, Model
, Settings
, cardSizeFromValue
, cardSizeToValue
, cardSizes
)
import Dict exposing (Dict)
import MassiveDecks.Card.Source.Model as Source exposing (Source)
@@ -23,5 +30,45 @@ type alias Settings =
, lastUsedName : Maybe String
, recentDecks : List Source.External
, chosenLanguage : Maybe Language
, compactCards : Bool
, cardSize : CardSize
}
type CardSize
= Minimal
| Square
| Full
cardSizes : List CardSize
cardSizes =
[ Minimal, Square, Full ]
cardSizeToValue : CardSize -> Int
cardSizeToValue size =
case size of
Minimal ->
1
Square ->
2
Full ->
3
cardSizeFromValue : Int -> Maybe CardSize
cardSizeFromValue size =
case size of
1 ->
Just Minimal
2 ->
Just Square
3 ->
Just Full
_ ->
Nothing
+7 -2
View File
@@ -68,8 +68,8 @@ type MdString
| LanguageSetting -- The label for the "Language" setting.
| MissingLanguage -- A question asking if the user doesn't see the language they want.
| TranslationBeg -- A request for help translating the game.
| CompactCardsSetting -- The label for the "Compact Cards" setting.
| CompactCardsExplanation -- An explanation of what compact cards does (makes cards square).
| CardSizeSetting -- The label for the "Card Size" setting.
| CardSizeExplanation -- An explanation of what the card size does (changes the size of the card).
| SpeechSetting -- The label for the speech setting.
| SpeechExplanation -- An explanation of what the speech setting does (enables TTS on cards).
| NotificationsSetting -- The label for the notifications setting.
@@ -133,6 +133,8 @@ type MdString
| InvitePlayers -- A short term for inviting players to the game.
| SetAway -- A short term for leaving the game temporarily.
| LeaveGame -- A short term for the action of leaving the game permanently.
| EndGame -- A short term for the action of ending the game early.
| EndGameDescription -- A description of the action of ending the game early.
| KickUser -- A short term for the action of forcing a user to leave the game permanently.
| Promote -- A short term for the action of allowing a user to edit the game configuration.
-- Notifications
@@ -173,17 +175,20 @@ type MdString
| TakeBackPlay -- A description of the action of taking back a previously submitted play.
| JudgePlay -- A description of the action of choosing a play to win the round.
| LikePlay -- A description of the action of liking a play.
| AdvanceRound -- A description of the action of finishing looking at the winner and advancing to the next round.
| Playing -- A description of the stage of the round where players are playing responses into the round.
| Revealing -- A description of the stage of the round where the czar is revealing the plays.
| Judging -- A description of the stage of the round where the czar is picking a winner.
| Complete -- A description of the stage of the round where it is finished.
-- Instructions
| WhatToDo -- A description of the action of asking for help on what to do in the game at this time.
| PlayInstruction { numberOfCards : Int } -- Instruction to the player on how to play cards.
| SubmitInstruction -- Instruction to the player on how to submit their play.
| WaitingForPlaysInstruction -- Instruction to the player that they need to wait for other players to play.
| CzarsDontPlayInstruction -- Instruction to the player that as Czar they don't play into the round.
| RevealPlaysInstruction -- Instruction to reveal plays for the round.
| WaitingForCzarInstruction -- Instruction to wait for the czar to reveal plays and pick a winner.
| AdvanceRoundInstruction -- Instruction that the next round is ready and they can advance.
-- 404 Unknown
| UnknownPageTitle -- A title explaining the page the user tried to go to doesn't exist.
| GoBackHome -- The action to go back to the main page of the application.
@@ -250,11 +250,11 @@ translate mdString =
, Text "!"
]
CompactCardsSetting ->
CardSizeSetting ->
[ Text "Compact Cards" ]
CompactCardsExplanation ->
[ Text "Show cards as squarethis is useful on small screens to fit more cards on at once." ]
CardSizeExplanation ->
[ Text "Adjust how big cards arethis can be useful on small screens to scroll less." ]
SpeechSetting ->
[ Text "Text To Speech" ]
@@ -475,6 +475,12 @@ translate mdString =
LeaveGame ->
[ Text "Leave Game" ]
EndGame ->
[ Text "End Game" ]
EndGameDescription ->
[ Text "End the game early." ]
KickUser ->
[ Text "Kick" ]
@@ -609,6 +615,9 @@ translate mdString =
LikePlay ->
[ Text "Add a like to this play." ]
AdvanceRound ->
[ Text "Next round." ]
Playing ->
[ Text "Playing" ]
@@ -622,6 +631,9 @@ translate mdString =
[ Text "Finished" ]
-- Instructions
WhatToDo ->
[ Text "What should I be doing?" ]
PlayInstruction { numberOfCards } ->
[ Text "You need to choose "
, Text (asWord numberOfCards)
@@ -648,7 +660,10 @@ translate mdString =
[ Text "Click on the plays to flip them, then pick the one you think is best." ]
WaitingForCzarInstruction ->
[ Text "You ware waiting for the ", Ref Czar, Text " to reveal the plays and pick a winner for the round." ]
[ Text "You can like plays while you wait for the ", Ref Czar, Text " to reveal the plays and pick a winner for the round." ]
AdvanceRoundInstruction ->
[ Text "The next round has started, you can advance." ]
-- 404 Unknown
UnknownPageTitle ->
@@ -154,7 +154,7 @@ enhanceHtml context mdString unenhanced =
prefixed unenhanced Icon.parachuteBox
HouseRuleReboot ->
prefixed unenhanced Icon.redo
prefixed unenhanced Icon.random
HouseRuleRandoCardrissian ->
prefixed unenhanced Icon.robot
+26 -1
View File
@@ -1,5 +1,6 @@
module MassiveDecks.Util exposing
( lift
( batchUpdate
, lift
, messageLift
, modelLift
)
@@ -27,3 +28,27 @@ messageLift liftMsg ( model, subCmd ) =
modelLift : (subModel -> model) -> ( subModel, Cmd msg ) -> ( model, Cmd msg )
modelLift liftModel ( subModel, cmd ) =
( liftModel subModel, cmd )
{-| Perform the given update task for each of the messages in a row.
-}
batchUpdate : List msg -> model -> (msg -> model -> ( model, Cmd cmdMsg )) -> ( model, Cmd cmdMsg )
batchUpdate messages model update =
let
( newModel, cmdList ) =
messages |> List.foldl (batchUpdateStep update) ( model, [] )
in
( newModel, cmdList |> List.reverse |> Cmd.batch )
{- Private -}
batchUpdateStep : (msg -> model -> ( model, Cmd cmdMsg )) -> msg -> ( model, List (Cmd cmdMsg) ) -> ( model, List (Cmd cmdMsg) )
batchUpdateStep update msg ( model, cmdList ) =
let
( newModel, newCmd ) =
update msg model
in
( newModel, newCmd :: cmdList )
-13
View File
@@ -1,13 +0,0 @@
module Reread exposing (icon, mdIcon)
import FontAwesome.Icon as Icon exposing (Icon)
icon : Icon
icon =
Icon "fab" "reread" 512 512 "M258 16c-62 0-124 24-171 71-95 94-94 246 0 339 95 93 250 93 345 0l12-12-15-14-12 12-10-10c-82 80-213 80-295 0-82-81-82-210-1-291 81-80 212-81 294-3l-38 35 129 25-1-3-26-117-39 36c-48-46-110-68-172-68zm-79 159c-46 1-70 10-70 10v133s24-9 70-9c50 0 70 20 70 20V196s-20-21-70-21zm160 0c-50 0-70 21-70 21v133s20-20 70-20c46 0 70 9 70 9V185s-24-9-70-10zM179 323c-46 1-70 10-70 10v16s24-9 70-9c50 0 81 31 81 31s30-31 80-31c46 0 70 9 70 9v-16s-24-9-70-10c-50 0-80 32-80 32s-31-32-81-32z"
mdIcon : Icon
mdIcon =
Icon "fab" "massivedecks" 512 512 "M273 20c-11 0-21 9-23 21l-9 88h-8L39 163c-12 2-21 15-19 27l50 283c2 12 15 21 27 19l194-34c12-3 21-15 18-27l-13-73 140 14c13 2 25-8 26-20l30-286c1-13-8-24-21-25L276 20h-3zm0 16h1l196 21c4 0 6 3 6 7l-30 286c0 4-4 7-8 6l-144-15-35-193-3-9 10-97c1-3 4-6 7-6zm47 106l-10 99 39 4c15 2 25 0 32-8 9-8 15-21 16-37 2-15-1-29-8-39-6-9-15-13-30-15zm19 20l18 1c16 2 22 14 20 35-3 22-11 32-27 31l-18-2zm-135 91l17 98-20 4-13-77-4 80-20 3-32-73 14 76-20 4-18-98 31-6 31 75 4-81z"
+34 -28
View File
@@ -7,6 +7,7 @@ module Weightless exposing
, popover
, popoverCard
, select
, slider
, switch
, tab
, tabGroup
@@ -39,29 +40,9 @@ listItem =
Html.node "wl-list-item"
tabGroup : List (Html.Attribute msg) -> List (Html msg) -> Html msg
tabGroup =
Html.node "wl-tab-group"
tab : List (Html.Attribute msg) -> List (Html msg) -> Html msg
tab =
Html.node "wl-tab"
switch : List (Html.Attribute msg) -> Html msg
switch attrs =
Html.node "wl-switch" attrs []
textField : List (Html.Attribute msg) -> List (Html msg) -> Html msg
textField =
Html.node "wl-textfield"
textArea : List (Html.Attribute msg) -> List (Html msg) -> Html msg
textArea =
Html.node "wl-textarea"
navigationBar : List (Html.Attribute msg) -> List (Html msg) -> Html msg
navigationBar =
Html.node "wl-nav"
popover : List (Html.Attribute msg) -> List (Html msg) -> Html msg
@@ -74,16 +55,41 @@ popoverCard =
Html.node "wl-popover-card"
navigationBar : List (Html.Attribute msg) -> List (Html msg) -> Html msg
navigationBar =
Html.node "wl-nav"
select : List (Html.Attribute msg) -> List (Html msg) -> Html msg
select =
Html.node "wl-select"
slider : List (Html.Attribute msg) -> List (Html msg) -> Html msg
slider =
Html.node "wl-slider"
switch : List (Html.Attribute msg) -> Html msg
switch attrs =
Html.node "wl-switch" attrs []
tab : List (Html.Attribute msg) -> List (Html msg) -> Html msg
tab =
Html.node "wl-tab"
tabGroup : List (Html.Attribute msg) -> List (Html msg) -> Html msg
tabGroup =
Html.node "wl-tab-group"
textArea : List (Html.Attribute msg) -> List (Html msg) -> Html msg
textArea =
Html.node "wl-textarea"
textField : List (Html.Attribute msg) -> List (Html msg) -> Html msg
textField =
Html.node "wl-textfield"
tooltip : List (Html.Attribute msg) -> List (Html msg) -> Html msg
tooltip =
Html.node "wl-tooltip"
+6
View File
@@ -37,6 +37,7 @@ module Weightless.Attributes exposing
, outlined
, readonly
, selected
, step
, textFieldSlot
, transformOrigin
, type_
@@ -49,6 +50,11 @@ import Html.Attributes as HtmlA
import Json.Encode as Json
step : Int -> Html.Attribute msg
step =
numberAttr "step"
min : Int -> Html.Attribute msg
min =
numberAttr "min"
+42
View File
@@ -0,0 +1,42 @@
module Weightless.Internal exposing
( boolProp
, numberAttr
, numberProp
, presentAttribute
, stringArrayProperty
, stringAttr
)
import Html
import Html.Attributes as HtmlA
import Json.Encode as Json
presentAttribute : String -> Html.Attribute msg
presentAttribute attribute =
HtmlA.attribute attribute attribute
stringArrayProperty : String -> List String -> Html.Attribute msg
stringArrayProperty property values =
HtmlA.property property (values |> Json.list Json.string)
stringAttr : String -> String -> Html.Attribute msg
stringAttr attribute stringValue =
HtmlA.attribute attribute stringValue
numberAttr : String -> Int -> Html.Attribute msg
numberAttr attribute intValue =
intValue |> String.fromInt |> HtmlA.attribute attribute
numberProp : String -> Int -> Html.Attribute msg
numberProp property intValue =
intValue |> Json.int |> HtmlA.property property
boolProp : String -> Bool -> Html.Attribute msg
boolProp property boolValue =
boolValue |> Json.bool |> HtmlA.property property
+54
View File
@@ -0,0 +1,54 @@
module Weightless.Slider exposing
( Slot(..)
, max
, min
, slot
, step
, thumbLabel
)
import Html
import Weightless.Internal as Internal
type Slot
= Before
| After
| ThumbLabel
slot : Slot -> Html.Attribute msg
slot s =
let
textSlot =
case s of
Before ->
"before"
After ->
"after"
ThumbLabel ->
"thumb-label"
in
Internal.stringAttr "slot" textSlot
step : Int -> Html.Attribute msg
step =
Internal.numberProp "step"
min : Int -> Html.Attribute msg
min =
Internal.numberProp "min"
max : Int -> Html.Attribute msg
max =
Internal.numberProp "max"
thumbLabel : Bool -> Html.Attribute msg
thumbLabel =
Internal.boolProp "thumbLabel"
+98
View File
@@ -0,0 +1,98 @@
@import "./variables";
.minimal-card-size #game {
--card-aspect-ratio: 0;
ul.cards {
flex-direction: column;
align-items: stretch;
}
ul.card-set {
flex-direction: row;
align-items: stretch;
li {
flex-grow: 1;
}
}
:not(.background-plays) .game-card {
flex-grow: 1;
flex-shrink: 1;
width: auto;
.content {
p {
justify-content: center;
text-align: center;
}
}
.info {
display: none;
}
.aspect {
position: static;
height: auto;
width: auto;
padding: 0;
transition: none;
& > * {
position: static;
width: auto;
height: auto;
}
}
.side.back {
display: none;
.content {
text-align: center;
p {
display: none;
}
&::after {
content: "?";
}
}
}
&.face-down {
.side {
&.back {
display: block;
}
&.front {
display: none;
}
}
}
}
.round {
justify-content: stretch;
align-items: stretch;
}
.liked {
.side::after {
right: 1em;
top: 50%;
margin-top: -1.5em;
width: 3em;
height: 3em;
}
}
}
.square-card-size #game {
--card-aspect-ratio: #{$card-compact-ratio};
}
+6 -18
View File
@@ -2,12 +2,9 @@
@import "./colors";
@import "./fonts";
@import "./card/sources";
@import "./card/size";
.compact-cards .card-area {
--card-aspect-ratio: #{$card-compact-ratio};
}
.card-area {
#game {
--card-aspect-ratio: #{$card-ratio};
@include fluid-type(20rem, 64rem, 0.5rem, 0.8rem);
}
@@ -59,12 +56,14 @@
height: 1em;
flex-grow: 1;
flex-basis: 2em;
max-width: 14em;
border-bottom: 0.1em solid var(--fg);
}
}
}
}
// Creates a fixed size from an aspect ratio.
.aspect {
transition: transform 0.75s;
transform-style: preserve-3d;
@@ -73,7 +72,7 @@
width: 100%;
@include pad-to-aspect-ratio(var(--card-aspect-ratio));
& > * {
& .side {
position: absolute;
top: 0;
left: 0;
@@ -129,6 +128,7 @@
line-height: 1.4em;
}
// For custom cards.
textarea {
display: block;
vertical-align: top;
@@ -179,16 +179,4 @@
}
}
}
&.error .side {
border-color: $error;
}
}
.picked {
position: relative;
.side {
border-color: $primary;
}
}
+88 -220
View File
@@ -1,81 +1,23 @@
@import "card/variables";
@import "colors";
@import "./card/variables";
@import "./colors";
@import "~weightless/style/base/elevation";
@import "./game/judging";
@import "./game/playing";
@import "./game/plays";
@import "./pages/lobby/variables";
// The main game area.
#game {
height: 100%;
position: relative;
display: flex;
flex-direction: column;
align-items: stretch;
.card-area {
position: relative;
}
.context-help {
position: fixed;
top: 4em;
right: 0;
z-index: 1;
background-color: $secondary-background;
padding: 0.5em;
border-bottom-left-radius: 1em;
display: flex;
align-items: center;
align-self: stretch;
transition: transform 200ms ease-out;
transform: translate(calc(100% - 2em), 0);
&:hover,
&:active {
transform: translate(0, 0);
transition: transform 200ms ease-in;
}
& > svg {
margin-right: 0.5em;
}
}
.scroll-top {
pointer-events: none;
display: flex;
flex-direction: column;
justify-content: flex-end;
position: absolute;
left: 0;
top: 0;
bottom: 0;
wl-button {
pointer-events: all;
position: sticky;
bottom: 0.5em;
margin: 0.5em;
z-index: 1;
opacity: 0.5;
&:hover {
opacity: 1;
}
}
}
// This pushes the scroll-top button down below the page content.
.scroll-top-spacer {
height: 6em;
}
// A button that allows the user to perform the main action in the game for them at this moment.
.action {
position: fixed;
// Hack because the debugger covers it up.
// TODO: Remove Hack (here because the debugger covers it up).
bottom: 5rem;
//bottom: 1em;
right: 1rem;
@@ -84,179 +26,105 @@
--button-fab-size: 4rem;
font-size: 1.5rem;
}
// A style for actions that the game is waiting on - high contrast to grab attention.
.important {
--button-bg: #{$secondary};
--button-color: #{$on-secondary};
--button-bg-hover: #{$secondary-light};
--button-color-hover: #{$on-secondary-light};
--button-bg-active: #{$secondary-dark};
--button-color-acitve: #{$on-secondary-dark};
// A style for actions that the game is waiting on - high contrast to grab attention.
&.important {
--button-bg: #{$secondary};
--button-color: #{$on-secondary};
--button-bg-hover: #{$secondary-light};
--button-color-hover: #{$on-secondary-light};
--button-bg-active: #{$secondary-dark};
--button-color-acitve: #{$on-secondary-dark};
}
}
}
.play-area {
overflow: hidden;
// Contextual help for the player telling them what they need to do at this point in the game.
#context-help {
font-size: 1.2rem;
position: fixed;
top: $top-bar-height;
right: 0;
z-index: 1;
--card-padding: 0.5em;
#context-help-button {
font-size: 1.4rem;
}
wl-popover-card {
min-width: 10em;
}
}
// The button to scroll to the top of the game area.
.scroll-top {
pointer-events: none;
display: flex;
flex-direction: column;
justify-content: flex-end;
position: absolute;
left: 0;
top: 0;
bottom: 0;
wl-button {
pointer-events: all;
position: sticky;
bottom: 0.5em;
margin: 0.5em;
z-index: 1;
opacity: 0.5;
&:hover {
opacity: 1;
}
}
}
// This pushes the scroll-top button down below the page content.
.scroll-top-spacer {
height: 6em;
}
// Where the call for the round is displayed.
.round {
display: flex;
flex-direction: column;
justify-content: center;
align-items: stretch;
flex-basis: $card-width;
align-items: center;
min-width: $card-width;
padding: 2em 0;
padding: 1em 0;
}
.cards-in-play {
display: flex;
justify-content: center;
// A play or card that has been picked by the player as the target of some action.
.game-card,
ol.card-set {
&.picked {
.side {
border-color: $primary;
}
}
}
.hand {
margin-top: 0.5em;
// Display some number of cards or card sets together.
ul.cards {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
position: relative;
&.not-playing {
pointer-events: none;
.game-card .aspect::after {
content: "";
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 0.75em;
background-color: #000000;
opacity: 0.1;
}
}
}
.background-plays {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: -10;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-evenly;
.play {
position: relative;
transform: translateY(-100%);
transition: transform 500ms ease-in;
width: $card-width + $card-margin * 2;
@include pad-to-aspect-ratio(var(--card-aspect-ratio));
.game-card {
transform: rotate(0);
transition: transform 500ms ease-in;
transform-origin: center;
position: absolute;
top: 0;
right: 0;
left: 0;
}
&.played {
transform: translateY(0);
transition: transform 500ms ease-out;
.game-card {
transform: rotate(var(--rotation));
transition: transform 500ms ease-out;
}
}
}
}
.plays {
list-style: none;
padding: 0;
margin-top: 0.5em;
}
// Multiple cards that are grouped together.
ol.card-set {
margin: 0.5em;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
position: relative;
.play-with-byline {
display: flex;
flex-direction: column;
align-items: center;
}
.byline {
font-size: 2em;
text-align: center;
svg {
margin-right: 0.5em;
}
}
.play {
display: flex;
margin: 1em;
.overlap {
flex-shrink: 1;
flex-grow: 0;
flex-basis: $card-width;
overflow: visible;
z-index: 0;
&:last-of-type {
width: $card-width;
}
&:hover {
z-index: 1;
}
.game-card {
margin: 0 $card-margin;
}
}
}
}
.liked {
.side::after {
position: absolute;
bottom: 1em;
right: 1em;
width: 8em;
height: 8em;
transform: rotate(-0.125turn);
display: block;
content: "";
opacity: 0.3;
//noinspection CssInvalidFunction
background: inline-svg(
"./assets/images/certificate-thumbs-up.svg",
(
path: (
fill: $primary
)
)
);
}
list-style: none;
padding: 0;
}
+25
View File
@@ -0,0 +1,25 @@
@import "../colors";
.liked {
.side::after {
position: absolute;
bottom: 1em;
right: 1em;
width: 8em;
height: 8em;
transform: rotate(-0.125turn);
display: block;
content: "";
opacity: 0.3;
//noinspection CssInvalidFunction
background: inline-svg(
"./assets/images/certificate-thumbs-up.svg",
(
path: (
fill: $primary
)
)
);
}
}
+71
View File
@@ -0,0 +1,71 @@
@import "../card/variables";
// A hand of cards.
.hand {
margin-top: 0.5em;
position: relative;
&.not-playing {
pointer-events: none;
.game-card .aspect::after {
content: "";
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 0.75em;
background-color: #000000;
opacity: 0.1;
}
}
}
// Unknown plays for the round animated in at the top of the screen during the playing stage.
.background-plays {
--card-aspect-ratio: #{$card-ratio};
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -10;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-evenly;
.play {
position: relative;
transform: translateY(-100%);
transition: transform 500ms ease-in;
width: $card-width + $card-margin * 2;
@include pad-to-aspect-ratio(var(--card-aspect-ratio));
.game-card {
transform: rotate(0);
transition: transform 500ms ease-in;
transform-origin: center;
position: absolute;
top: 0;
right: 0;
left: 0;
}
&.played {
transform: translateY(0);
transition: transform 500ms ease-out;
.game-card {
transform: rotate(var(--rotation));
transition: transform 500ms ease-out;
}
}
}
}
+16
View File
@@ -0,0 +1,16 @@
// A play with the user's name.
.play-with-byline {
display: flex;
flex-direction: column;
align-items: center;
}
// The user's name.
.byline {
font-size: 2em;
text-align: center;
svg {
margin-right: 0.5em;
}
}
+43 -86
View File
@@ -3,42 +3,27 @@
@import "../game";
@import "./lobby/configure";
@import "./lobby/invite";
@import "./lobby/variables";
@import "~weightless/style/base/elevation";
$users-width: 15em;
@keyframes fadeIn {
from {
opacity: 0.1;
}
}
.lobby {
box-sizing: border-box;
height: 100vh;
padding-top: 4em;
overflow: hidden;
.content-wrapper {
display: flex;
flex-direction: row;
flex-wrap: wrap;
min-width: fit-content;
overflow: hidden;
height: 100%;
width: 100%;
}
#lobby {
}
#top-bar {
position: fixed;
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 10;
height: 4em;
height: $top-bar-height;
padding: 0 1em;
display: flex;
@@ -55,57 +40,30 @@ $users-width: 15em;
flex-direction: row;
flex-wrap: nowrap;
}
}
.title-call {
flex-grow: 1;
flex-shrink: 1;
overflow: hidden;
margin: 0 1em;
white-space: pre;
font-size: 1em;
text-rendering: optimizeLegibility;
font-family: "Helvetica Neue", "Nimbus Sans L", sans-serif;
font-weight: 700;
display: flex;
justify-content: center;
.slot {
display: inline-block;
flex-shrink: 0;
flex-basis: 3em;
border-bottom: 0.1em solid $call-color;
}
&::before {
content: "";
}
&::after {
content: "";
}
}
#lobby-content {
position: absolute;
left: 0;
top: $top-bar-height;
right: 0;
bottom: 0;
box-sizing: border-box;
min-width: fit-content;
}
#scroll-frame {
flex-grow: 100;
flex-shrink: 1;
flex-basis: $card-width;
max-height: 100%;
max-width: 100%;
min-width: $card-width;
position: absolute;
top: 0;
left: $users-width;
right: 0;
bottom: 0;
overflow: auto;
transition: left 0.3s;
}
.outlined-solid-icon {
path {
fill: none;
stroke: black;
stroke-width: 32px;
}
.collapsed-users #scroll-frame {
left: 0;
}
.loading {
@@ -119,23 +77,9 @@ $users-width: 15em;
justify-content: center;
}
.collapsed-users .users {
flex-grow: 0;
flex-basis: 0;
}
.context-help {
left: $users-width;
}
.collapsed-users .context-help {
left: 0;
}
.users {
#users {
font-size: 1rem;
overflow-y: auto;
overflow-x: hidden;
overflow: auto;
z-index: 2;
@@ -143,16 +87,15 @@ $users-width: 15em;
padding: 0;
flex-grow: 1;
flex-shrink: 1;
min-width: 0;
min-height: 100%;
flex-basis: $users-width;
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: $users-width;
transition: flex-basis 0.3s, flex-grow 0.3s, padding-left 0.3s,
padding-right 0.3s;
transition: width 0.3s;
--list-item-padding: 0.5em;
@@ -180,6 +123,20 @@ $users-width: 15em;
}
}
@media screen and (max-width: $users-width * 2) {
#scroll-frame {
left: 100vw;
}
#users {
width: 100vw;
}
}
.collapsed-users #users {
width: 0;
}
.cast-button {
&.connecting {
animation: fadeIn 1s infinite alternate;
@@ -4,7 +4,6 @@
.configure {
padding: 1em;
height: 100%;
overflow: auto;
wl-card {
max-width: 30em;
@@ -0,0 +1,2 @@
$top-bar-height: 4rem;
$users-width: 15rem;
+1
View File
@@ -7,6 +7,7 @@ import "weightless/nav";
import "weightless/popover";
import "weightless/popover-card";
import "weightless/select";
import "weightless/slider";
import "weightless/switch";
import "weightless/tab";
import "weightless/tab-group";
+54 -36
View File
@@ -262,12 +262,12 @@
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.10.2.tgz",
"integrity": "sha512-7449RhjE1oLFIy5E/5rT4wG5+KsfPzakJuhvpzXJ3C46lq7xywY0/Rjo9ZBcwrfbk0nRZ5xmUHkk7DZ67tSBKw==",
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.11.0.tgz",
"integrity": "sha512-mXv9ccCou89C8/4avKHuPB2WkSZyY/XcTQUXd5LFZAcLw1I3mWYVjUu6eS9Ja0QkP/ClolbcW9tb3Ov/pMdcqw==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "1.10.2",
"@typescript-eslint/experimental-utils": "1.11.0",
"eslint-utils": "^1.3.1",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^2.0.1",
@@ -275,31 +275,31 @@
}
},
"@typescript-eslint/experimental-utils": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.10.2.tgz",
"integrity": "sha512-Hf5lYcrnTH5Oc67SRrQUA7KuHErMvCf5RlZsyxXPIT6AXa8fKTyfFO6vaEnUmlz48RpbxO4f0fY3QtWkuHZNjg==",
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.11.0.tgz",
"integrity": "sha512-7LbfaqF6B8oa8cp/315zxKk8FFzosRzzhF8Kn/ZRsRsnpm7Qcu25cR/9RnAQo5utZ2KIWVgaALr+ZmcbG47ruw==",
"dev": true,
"requires": {
"@typescript-eslint/typescript-estree": "1.10.2",
"@typescript-eslint/typescript-estree": "1.11.0",
"eslint-scope": "^4.0.0"
}
},
"@typescript-eslint/parser": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.10.2.tgz",
"integrity": "sha512-xWDWPfZfV0ENU17ermIUVEVSseBBJxKfqBcRCMZ8nAjJbfA5R7NWMZmFFHYnars5MjK4fPjhu4gwQv526oZIPQ==",
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.11.0.tgz",
"integrity": "sha512-5xBExyXaxVyczrZvbRKEXvaTUFFq7gIM9BynXukXZE0zF3IQP/FxF4mPmmh3gJ9egafZFqByCpPTFm3dk4SY7Q==",
"dev": true,
"requires": {
"@types/eslint-visitor-keys": "^1.0.0",
"@typescript-eslint/experimental-utils": "1.10.2",
"@typescript-eslint/typescript-estree": "1.10.2",
"@typescript-eslint/experimental-utils": "1.11.0",
"@typescript-eslint/typescript-estree": "1.11.0",
"eslint-visitor-keys": "^1.0.0"
}
},
"@typescript-eslint/typescript-estree": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.10.2.tgz",
"integrity": "sha512-Kutjz0i69qraOsWeI8ETqYJ07tRLvD9URmdrMoF10bG8y8ucLmPtSxROvVejWvlJUGl2et/plnMiKRDW+rhEhw==",
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.11.0.tgz",
"integrity": "sha512-fquUHF5tAx1sM2OeRCC7wVxFd1iMELWMGCzOSmJ3pLzArj9+kRixdlC4d5MncuzXpjEqc6045p3KwM0o/3FuUA==",
"dev": true,
"requires": {
"lodash.unescape": "4.0.1",
@@ -871,6 +871,12 @@
"text-hex": "1.0.x"
}
},
"commander": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
"dev": true
},
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
@@ -1151,13 +1157,13 @@
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"eslint": {
"version": "5.16.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz",
"integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-6.0.1.tgz",
"integrity": "sha512-DyQRaMmORQ+JsWShYsSg4OPTjY56u1nCjAmICrE8vLWqyLKxhFXOthwMj1SA8xwfrv0CofLNVnqbfyhwCkaO0w==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"ajv": "^6.9.1",
"ajv": "^6.10.0",
"chalk": "^2.1.0",
"cross-spawn": "^6.0.5",
"debug": "^4.0.1",
@@ -1165,18 +1171,19 @@
"eslint-scope": "^4.0.3",
"eslint-utils": "^1.3.1",
"eslint-visitor-keys": "^1.0.0",
"espree": "^5.0.1",
"espree": "^6.0.0",
"esquery": "^1.0.1",
"esutils": "^2.0.2",
"file-entry-cache": "^5.0.1",
"functional-red-black-tree": "^1.0.1",
"glob": "^7.1.2",
"glob-parent": "^3.1.0",
"globals": "^11.7.0",
"ignore": "^4.0.6",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"inquirer": "^6.2.2",
"js-yaml": "^3.13.0",
"is-glob": "^4.0.0",
"js-yaml": "^3.13.1",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.3.0",
"lodash": "^4.17.11",
@@ -1184,7 +1191,6 @@
"mkdirp": "^0.5.1",
"natural-compare": "^1.4.0",
"optionator": "^0.8.2",
"path-is-inside": "^1.0.2",
"progress": "^2.0.0",
"regexpp": "^2.0.1",
"semver": "^5.5.1",
@@ -1225,9 +1231,9 @@
}
},
"eslint-config-prettier": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-5.0.0.tgz",
"integrity": "sha512-c17Aqiz5e8LEqoc/QPmYnaxQFAHTx2KlCZBPxXXjEMmNchOLnV/7j0HoPZuC+rL/tDC9bazUYOKJW9bOhftI/w==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.0.0.tgz",
"integrity": "sha512-vDrcCFE3+2ixNT5H83g28bO/uYAwibJxerXPj+E7op4qzBCsAV36QfvdAyVOoNxKAH2Os/e01T/2x++V0LPukA==",
"dev": true,
"requires": {
"get-stdin": "^6.0.0"
@@ -1265,9 +1271,9 @@
"dev": true
},
"espree": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz",
"integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-6.0.0.tgz",
"integrity": "sha512-lJvCS6YbCn3ImT3yKkPe0+tJ+mH6ljhGNjHQH9mRtiO6gjhVAOhVXW1yjnwqGwTkK3bGbye+hb00nFNmu0l/1Q==",
"dev": true,
"requires": {
"acorn": "^6.0.7",
@@ -1640,9 +1646,9 @@
}
},
"flatted": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz",
"integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
"integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
"dev": true
},
"follow-redirects": {
@@ -4404,10 +4410,22 @@
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
"integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
},
"ts-json-schema-generator": {
"version": "0.50.2",
"resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-0.50.2.tgz",
"integrity": "sha512-S6n8kHpU2y7IJvr07rA2Xn9wbKRWTs31jvgpFQnkWn4n7jWXOj/yGEzp1UN/qQOBB0r3qSZjsOMr2KzuaSWTGA==",
"dev": true,
"requires": {
"commander": "~2.20.0",
"glob": "~7.1.4",
"json-stable-stringify": "^1.0.1",
"typescript": "~3.5.2"
}
},
"ts-postgres": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/ts-postgres/-/ts-postgres-1.0.2.tgz",
"integrity": "sha512-WdCth7Q00KrcL2FwxiQq5FM2aUFa2fXqrcOssd537zb8RPa1omhtIZ6Jpi2oL2GjqXEADqar3YPQ4OUbIzjESA==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ts-postgres/-/ts-postgres-1.1.0.tgz",
"integrity": "sha512-POEX+ovAPWyvGqXwBV/xFBJ0Km7sOyfCHjxDMkFIEN3XyzLmEEwPeJM+84SH9kkcDY4Use3senRa7+kkAuDaNQ==",
"requires": {
"ts-typed-events": "^1.1.1"
}
+6 -6
View File
@@ -17,7 +17,7 @@
"start": "node dist/index.js",
"dev": "nodemon .",
"debug": "nodemon --inspect .",
"generate-json-validators": "npx typescript-json-validator src/ts/action/validation.ts --collection --noExtraProps--format=full --aliasRefs --ignoreErrors && sed -i '1s;^;/* eslint-disable */\\n;' src/ts/action/validation.validator.ts",
"generate-json-validators": "npx typescript-json-validator src/ts/action/validation.ts --collection --noExtraProps --format=full --aliasRefs --ignoreErrors && sed -i -e 's/allErrors: true/allErrors: false/g' -e 's/ChangeBase<\"\\(.*\\)\",\\(.*\\)>/ChangeBase\\1/g' -e 's/\\/\\* tslint:disable \\*\\//\\/\\* eslint-disable \\*\\//g' src/ts/action/validation.validator.ts",
"generate-secret": "npm run prestart && node dist/secret.js"
},
"files": [
@@ -40,7 +40,7 @@
"jsonwebtoken": "^8.5.1",
"moment": "^2.24.0",
"source-map-support": "^0.5.12",
"ts-postgres": "^1.0.2",
"ts-postgres": "^1.1.0",
"uuid": "^3.3.2",
"winston": "^3.2.1",
"wu": "^2.1.0"
@@ -57,10 +57,10 @@
"@types/source-map-support": "^0.5.0",
"@types/uuid": "^3.4.4",
"@types/wu": "^2.1.39",
"@typescript-eslint/eslint-plugin": "^1.10.2",
"@typescript-eslint/parser": "^1.10.2",
"eslint": "^5.12.1",
"eslint-config-prettier": "^5.0.0",
"@typescript-eslint/eslint-plugin": "^1.11.0",
"@typescript-eslint/parser": "^1.11.0",
"eslint": "^6.0.1",
"eslint-config-prettier": "^6.0.0",
"eslint-plugin-prettier": "^3.1.0",
"nodemon": "^1.19.1",
"prettier": "1.18.2",
+30 -8
View File
@@ -1,12 +1,11 @@
/* eslint-disable */
/* tslint:disable */
// generated by typescript-json-validator
import Ajv = require("ajv");
import { RegisterUser, CreateLobby, Action, CheckAlive } from "./validation";
export const ajv = new Ajv({
allErrors: false,
coerceTypes: false,
format: "fast",
format: "full",
nullable: true,
unicode: true,
uniqueItems: true,
@@ -63,6 +62,7 @@ export const Schema = {
]
},
Authenticate: {
additionalProperties: false,
defaultProperties: [],
description: "Authenticate with the game.",
properties: {
@@ -77,6 +77,7 @@ export const Schema = {
type: "object"
},
Cardcast: {
additionalProperties: false,
defaultProperties: [],
description: "A source for Cardcast.",
properties: {
@@ -94,13 +95,13 @@ export const Schema = {
Change: {
anyOf: [
{
$ref: '#/definitions/ChangeBase<"PackingHeat",PackingHeat>'
$ref: "#/definitions/ChangeBasePackingHeat"
},
{
$ref: '#/definitions/ChangeBase<"Rando",Rando>'
$ref: "#/definitions/ChangeBaseRando"
},
{
$ref: '#/definitions/ChangeBase<"Reboot",Reboot>'
$ref: "#/definitions/ChangeBaseReboot"
}
]
},
@@ -125,7 +126,8 @@ export const Schema = {
enum: ["Reboot"],
type: "string"
},
'ChangeBase<"PackingHeat",PackingHeat>': {
ChangeBasePackingHeat: {
additionalProperties: false,
defaultProperties: [],
properties: {
houseRule: {
@@ -138,7 +140,8 @@ export const Schema = {
required: ["houseRule"],
type: "object"
},
'ChangeBase<"Rando",Rando>': {
ChangeBaseRando: {
additionalProperties: false,
defaultProperties: [],
properties: {
houseRule: {
@@ -151,7 +154,8 @@ export const Schema = {
required: ["houseRule"],
type: "object"
},
'ChangeBase<"Reboot",Reboot>': {
ChangeBaseReboot: {
additionalProperties: false,
defaultProperties: [],
properties: {
houseRule: {
@@ -165,6 +169,7 @@ export const Schema = {
type: "object"
},
ChangeDecks: {
additionalProperties: false,
defaultProperties: [],
description: "Make a change to the configuration of decks for the lobby.",
properties: {
@@ -187,6 +192,7 @@ export const Schema = {
type: "object"
},
ChangeHouseRule: {
additionalProperties: false,
defaultProperties: [],
description: "Set the hand size for the lobby.",
properties: {
@@ -206,6 +212,7 @@ export const Schema = {
type: "object"
},
CheckAlive: {
additionalProperties: false,
defaultProperties: [],
description: "Previously obtained tokens to check the validity of.",
properties: {
@@ -220,6 +227,7 @@ export const Schema = {
type: "object"
},
CreateLobby: {
additionalProperties: false,
defaultProperties: [],
description: "The details needed to create a new lobby.",
properties: {
@@ -246,6 +254,7 @@ export const Schema = {
type: "string"
},
Judge: {
additionalProperties: false,
defaultProperties: [],
description: "A user declares the winning play for a round.",
properties: {
@@ -306,6 +315,7 @@ export const Schema = {
type: "string"
},
PackingHeat: {
additionalProperties: false,
defaultProperties: [],
description: 'Configuration for the "Packing Heat" house rule.',
type: "object"
@@ -319,6 +329,7 @@ export const Schema = {
type: "string"
},
Rando: {
additionalProperties: false,
defaultProperties: [],
properties: {
number: {
@@ -332,6 +343,7 @@ export const Schema = {
type: "object"
},
Reboot: {
additionalProperties: false,
defaultProperties: [],
description:
'Configuration for the "Reboot the Universe" house rule.\nThis rule allows players to draw a new hand by sacrificing a given number\nof points.',
@@ -347,6 +359,7 @@ export const Schema = {
type: "object"
},
Redraw: {
additionalProperties: false,
defaultProperties: [],
description: "A player plays a white card into a round.",
properties: {
@@ -359,6 +372,7 @@ export const Schema = {
type: "object"
},
RegisterUser: {
additionalProperties: false,
defaultProperties: [],
description: "The details to register a new user for a lobby.",
properties: {
@@ -376,6 +390,7 @@ export const Schema = {
type: "object"
},
Reveal: {
additionalProperties: false,
defaultProperties: [],
description: "A user judges the winning play for a round.",
properties: {
@@ -390,6 +405,7 @@ export const Schema = {
type: "object"
},
SetHandSize: {
additionalProperties: false,
defaultProperties: [],
description: "Set the hand size for the lobby.",
properties: {
@@ -411,6 +427,7 @@ export const Schema = {
type: "object"
},
SetPassword: {
additionalProperties: false,
defaultProperties: [],
description: "Set (or unset) the password for the lobby.",
properties: {
@@ -432,6 +449,7 @@ export const Schema = {
type: "object"
},
SetPublic: {
additionalProperties: false,
defaultProperties: [],
description: "Set (or unset) the password for the lobby.",
properties: {
@@ -451,6 +469,7 @@ export const Schema = {
type: "object"
},
SetScoreLimit: {
additionalProperties: false,
defaultProperties: [],
description: "(Un)Set the score limit for the lobby.",
properties: {
@@ -473,6 +492,7 @@ export const Schema = {
type: "object"
},
StartGame: {
additionalProperties: false,
defaultProperties: [],
description: "Start a game in the lobby if possible.",
properties: {
@@ -484,6 +504,7 @@ export const Schema = {
type: "object"
},
Submit: {
additionalProperties: false,
defaultProperties: [],
description: "A player plays a white card into a round.",
properties: {
@@ -502,6 +523,7 @@ export const Schema = {
type: "object"
},
TakeBack: {
additionalProperties: false,
defaultProperties: [],
description: "A player plays a white card into a round.",
properties: {