Add new deck source: JSON Against Humanity.

This commit is contained in:
Gareth Latty
2020-05-30 04:18:09 +01:00
parent e55267a06f
commit 9e50f08471
21 changed files with 649 additions and 21 deletions
+1 -1
View File
@@ -88,7 +88,7 @@ init flags url key =
, speech = speech
, notifications = Notifications.init
, remoteMode = remoteMode
, sources = { builtIn = Nothing, manyDecks = Nothing }
, sources = { builtIn = Nothing, manyDecks = Nothing, jsonAgainstHumanity = Nothing }
}
( page, pageCmd ) =
+9 -1
View File
@@ -21,6 +21,7 @@ import Html.Attributes as HtmlA
import MassiveDecks.Card.Source.BuiltIn as BuiltIn
import MassiveDecks.Card.Source.Custom as Player
import MassiveDecks.Card.Source.Fake as Fake
import MassiveDecks.Card.Source.JsonAgainstHumanity as JsonAgainstHumanity
import MassiveDecks.Card.Source.ManyDecks as ManyDecks
import MassiveDecks.Card.Source.Methods exposing (..)
import MassiveDecks.Card.Source.Model exposing (..)
@@ -71,6 +72,9 @@ generalMethods source =
GManyDecks ->
ManyDecks.generalMethods
GJsonAgainstHumanity ->
JsonAgainstHumanity.generalMethods
{-| Get an empty source of the given type.
-}
@@ -180,12 +184,13 @@ generalEditor shared existing currentValue update submit noOp =
enabledSources =
[ shared.sources.builtIn |> Maybe.map (\_ -> BuiltIn.generalMethods)
, shared.sources.manyDecks |> Maybe.map (\_ -> ManyDecks.generalMethods)
, shared.sources.jsonAgainstHumanity |> Maybe.map (\_ -> JsonAgainstHumanity.generalMethods)
]
toItem source =
{ id = source.id ()
, icon = source.logo ()
, primary = [ () |> source.name |> Lang.html shared ]
, primary = [ () |> source.name |> Lang.string shared |> Html.text ]
, secondary = Nothing
, meta = Nothing
}
@@ -257,3 +262,6 @@ externalMethods external =
BuiltIn id ->
BuiltIn.methods id
JsonAgainstHumanity id ->
JsonAgainstHumanity.methods id
@@ -0,0 +1,154 @@
module MassiveDecks.Card.Source.JsonAgainstHumanity exposing
( generalMethods
, methods
)
import FontAwesome.Icon as Icon
import FontAwesome.Solid as Icon
import Html as Html exposing (Html)
import Html.Attributes as HtmlA
import List.Extra as List
import MassiveDecks.Card.Source.JsonAgainstHumanity.Model exposing (..)
import MassiveDecks.Card.Source.Methods as Source
import MassiveDecks.Card.Source.Model as Source exposing (Source)
import MassiveDecks.Components.Form.Message as Message exposing (Message)
import MassiveDecks.Model exposing (..)
import MassiveDecks.Pages.Lobby.Configure.Decks.Model exposing (DeckOrError)
import MassiveDecks.Strings as Strings exposing (MdString)
import MassiveDecks.Util.Html as Html
import MassiveDecks.Util.Maybe as Maybe
import Material.Select as Select
methods : Id -> Source.ExternalMethods msg
methods givenId =
{ name = name
, logo = logo
, empty = empty
, id = id
, messages = messages
, problems = problems givenId
, defaultDetails = details givenId
, tooltip = tooltip givenId
, editor = editor givenId
, equals = equals givenId
}
generalMethods : Source.ExternalGeneralMethods msg
generalMethods =
{ name = name
, logo = logo
, empty = empty
, id = id
, messages = messages
}
{- Private -}
id : () -> Source.General
id () =
Source.GJsonAgainstHumanity
name : () -> MdString
name () =
Strings.JsonAgainstHumanity
empty : Shared -> Source.External
empty shared =
shared.sources.jsonAgainstHumanity
|> Maybe.andThen (.decks >> List.head)
|> Maybe.map .id
|> Maybe.withDefault (hardcoded "")
|> Source.JsonAgainstHumanity
equals : Id -> Source.External -> Bool
equals givenId source =
case source of
Source.JsonAgainstHumanity other ->
givenId == other
_ ->
False
messages : () -> List (Message msg)
messages () =
[ Strings.JsonAgainstHumanityAbout |> Message.info ]
problems : Id -> () -> List (Message msg)
problems givenId () =
[]
editor : Id -> Shared -> List DeckOrError -> (Source.External -> msg) -> Maybe msg -> msg -> Html msg
editor selected shared existing update _ _ =
case shared.sources.jsonAgainstHumanity of
Just { decks } ->
let
deck d =
let
matches other =
case other.source of
Source.JsonAgainstHumanity o ->
o == d.id
_ ->
False
in
{ id = d.id
, icon = Nothing
, primary = [ Html.text d.name ]
, secondary = Nothing
, meta = Icon.check |> Icon.viewIcon |> Maybe.justIf (existing |> List.any matches)
}
in
Html.span [ HtmlA.id "json-against-humanity-editor", HtmlA.class "primary" ]
[ Select.view shared
{ label = Strings.Deck
, idToString = toString
, idFromString = fromString shared.sources.jsonAgainstHumanity
, selected = Just selected
, wrap = Maybe.withDefault (hardcoded "") >> Source.JsonAgainstHumanity >> update
}
[ HtmlA.id "built-in-selector" ]
(decks |> List.map deck)
]
Nothing ->
Html.nothing
details : Id -> Shared -> Source.Details
details givenId shared =
{ name =
shared.sources.jsonAgainstHumanity
|> Maybe.andThen (.decks >> List.find (\d -> d.id == givenId))
|> Maybe.map .name
|> Maybe.withDefault (givenId |> toString)
, url = Nothing
, author = Nothing
, translator = Nothing
, language = Nothing
}
tooltip : Id -> (String -> List (Html msg) -> Html msg) -> Maybe ( String, Html msg )
tooltip givenId tooltipRender =
let
forId =
"json-against-humanity-" ++ toString givenId
in
( forId, [] |> tooltipRender forId ) |> Just
logo : () -> Maybe (Html msg)
logo () =
Icon.code |> Icon.viewIcon |> Just
@@ -0,0 +1,73 @@
module MassiveDecks.Card.Source.JsonAgainstHumanity.Model exposing
( Deck
, Id
, Info
, fromString
, hardcoded
, idDecoder
, toString
)
{-| The id for a built-in deck.
-}
import Json.Decode as Json
import List.Extra as List
type Id
= Id String
{-| Information about the source from the server.
-}
type alias Info =
{ aboutUrl : String
, decks : List Deck
}
{-| Information about a deck.
-}
type alias Deck =
{ id : Id
, name : String
}
{-| Get an id from a string.
-}
fromString : Maybe Info -> String -> Maybe Id
fromString builtInInfo stringId =
let
matching got deck =
case deck.id of
Id str ->
str == got
internal { decks } =
decks |> List.find (matching stringId) |> Maybe.map .id
in
builtInInfo |> Maybe.andThen internal
{-| Allows you to hard-code an Id for a deck. This means that the client and server could end up out of sync if the id
doesn't actually exist.
-}
hardcoded : String -> Id
hardcoded =
Id
{-| A decoder for ids.
-}
idDecoder : Json.Decoder Id
idDecoder =
Json.string |> Json.map Id
{-| Convert an Id to it's string representation.
-}
toString : Id -> String
toString (Id str) =
str
@@ -13,6 +13,7 @@ module MassiveDecks.Card.Source.Model exposing
import Json.Decode as Json
import MassiveDecks.Card.Source.BuiltIn.Model as BuiltIn
import MassiveDecks.Card.Source.JsonAgainstHumanity.Model as JsonAgainstHumanity
import MassiveDecks.Card.Source.ManyDecks.Model as ManyDecks
import MassiveDecks.Strings.Languages.Model exposing (Language)
@@ -22,6 +23,7 @@ import MassiveDecks.Strings.Languages.Model exposing (Language)
type General
= GBuiltIn
| GManyDecks
| GJsonAgainstHumanity
{-| Details on where game data came from.
@@ -46,6 +48,7 @@ sources are more limited and specific.
type External
= BuiltIn BuiltIn.Id
| ManyDecks ManyDecks.DeckCode
| JsonAgainstHumanity JsonAgainstHumanity.Id
{-| A summary of the contents of the source deck.
@@ -80,6 +83,7 @@ type LoadFailureReason
type alias Info =
{ builtIn : Maybe BuiltIn.Info
, manyDecks : Maybe ManyDecks.Info
, jsonAgainstHumanity : Maybe JsonAgainstHumanity.Info
}
@@ -94,6 +98,9 @@ generalToString source =
GManyDecks ->
"ManyDecks"
GJsonAgainstHumanity ->
"JAH"
{-| Get a general source by a string name.
-}
@@ -106,6 +113,9 @@ generalFromString sourceName =
"ManyDecks" ->
Just GManyDecks
"JAH" ->
Just GJsonAgainstHumanity
_ ->
Nothing
@@ -31,6 +31,7 @@ import MassiveDecks.Card.Model as Card exposing (Call, Response)
import MassiveDecks.Card.Parts as Parts exposing (Part, Parts)
import MassiveDecks.Card.Play as Play exposing (Play)
import MassiveDecks.Card.Source.BuiltIn.Model as BuiltIn
import MassiveDecks.Card.Source.JsonAgainstHumanity.Model as JsonAgianstHumanity
import MassiveDecks.Card.Source.ManyDecks.Model as ManyDecks
import MassiveDecks.Card.Source.Model as Source exposing (Source)
import MassiveDecks.Cast.Model as Cast
@@ -71,6 +72,7 @@ sourceInfo =
Json.succeed Source.Info
|> Json.optional "builtIn" (builtInInfo |> Json.map Just) Nothing
|> Json.optional "manyDecks" (manyDecksInfo |> Json.map Just) Nothing
|> Json.optional "jsonAgainstHumanity" (jsonAgainstHumanityInfo |> Json.map Just) Nothing
|> Json.andThen atLeastOne
@@ -86,6 +88,19 @@ manyDecksInfo =
|> Json.required "baseUrl" Json.string
jsonAgainstHumanityInfo : Json.Decoder JsonAgianstHumanity.Info
jsonAgainstHumanityInfo =
let
jsonAgainstHumanityDeck =
Json.succeed JsonAgianstHumanity.Deck
|> Json.required "id" JsonAgianstHumanity.idDecoder
|> Json.required "name" Json.string
in
Json.succeed JsonAgianstHumanity.Info
|> Json.required "aboutUrl" Json.string
|> Json.required "decks" (Json.list jsonAgainstHumanityDeck)
builtInDeck : Json.Decoder BuiltIn.Deck
builtInDeck =
Json.succeed BuiltIn.Deck
@@ -230,6 +245,9 @@ externalSourceByGeneral general =
Source.GManyDecks ->
Json.field "deckCode" (Json.string |> Json.map ManyDecks.deckCode) |> Json.map Source.ManyDecks
Source.GJsonAgainstHumanity ->
Json.field "id" (JsonAgianstHumanity.idDecoder |> Json.map Source.JsonAgainstHumanity)
tokenValidity : Json.Decoder (List Lobby.Token)
tokenValidity =
@@ -24,6 +24,7 @@ module MassiveDecks.Models.Encoders exposing
import Dict
import Json.Encode as Json
import MassiveDecks.Card.Source.BuiltIn.Model as BuiltIn
import MassiveDecks.Card.Source.JsonAgainstHumanity.Model as JsonAgainstHumanity
import MassiveDecks.Card.Source.ManyDecks.Model as ManyDecks
import MassiveDecks.Card.Source.Model as Source exposing (Source)
import MassiveDecks.Cast.Model as Cast
@@ -343,14 +344,18 @@ source s =
Source.ManyDecks deckCode ->
Json.object
[ ( "source", "ManyDecks" |> Json.string )
, ( "deckCode"
, deckCode |> ManyDecks.encode
)
, ( "deckCode", deckCode |> ManyDecks.encode )
]
Source.BuiltIn id ->
Json.object [ ( "source", "BuiltIn" |> Json.string ), ( "id", id |> BuiltIn.toString |> Json.string ) ]
Source.JsonAgainstHumanity id ->
Json.object
[ ( "source", "JAH" |> Json.string )
, ( "id", id |> JsonAgainstHumanity.toString |> Json.string )
]
language : Language -> Json.Value
language l =
+5 -1
View File
@@ -201,7 +201,11 @@ view shared model =
manyDecksAd { baseUrl } =
Html.blankA
[ HtmlA.href baseUrl, HtmlA.id "many-decks-ad", Strings.ManyDecksWhereToGet |> Lang.title shared ]
[ Html.div [] [ Icon.boxOpen |> Icon.viewIcon, Strings.ManyDecks |> Lang.html shared ] ]
[ Html.div []
[ Icon.boxOpen |> Icon.viewIcon
, Html.span [] [ Strings.ManyDecks |> Lang.string shared |> Html.text ]
]
]
in
[ Html.div [ HtmlA.class "page start" ]
[ overlay shared model.overlay
+2
View File
@@ -217,6 +217,8 @@ type MdString
| ManyDecksDeckCodeTitle -- A term referring to a deck code for Many Decks.
| ManyDecksDeckCodeShort -- A description of the problem where a deck code must be at least five characters.
| ManyDecksWhereToGet -- A description of how to get deck codes from Many Decks.
| JsonAgainstHumanity -- The name of the JSON Against Humanity source.
| JsonAgainstHumanityAbout -- A short description of the JSON Against Humanity source.
| BuiltIn -- A term referring to decks of cards that are provided by this instance of the game.
| APlayer -- A short description of a generic player in the game in the context of being the author of a card.
| DeckAlreadyAdded -- A description of the problem of the deck already being added to the game configuration.
@@ -757,7 +757,13 @@ translate mdString =
[ Text "A deck code must be at least five characters long." ]
ManyDecksWhereToGet ->
[ Text "You can create decks to play with at Many Decks." ]
[ Text "You can create and find decks to play with at ", Ref ManyDecks, Text "." ]
JsonAgainstHumanity ->
[ Text "JSON Against Humanity" ]
JsonAgainstHumanityAbout ->
[ Text "Decks provided by ", Ref JsonAgainstHumanity ]
BuiltIn ->
[ Text "Built-in" ]
@@ -786,6 +786,14 @@ translate mdString =
ManyDecksWhereToGet ->
[ Missing ]
-- TODO: Translate
JsonAgainstHumanity ->
[ Missing ]
-- TODO: Translate
JsonAgainstHumanityAbout ->
[ Missing ]
-- TODO: Translate
BuiltIn ->
[ Missing ]
@@ -773,7 +773,15 @@ translate mdString =
[ Text "Um código de deck deve ser de pelo menos cinco caracteres." ]
ManyDecksWhereToGet ->
[ Text "Você pode criar decks para jogar usando o Many Decks." ]
[ Text "Você pode criar decks para jogar usando o ", Ref ManyDecks, Text "." ]
-- TODO: Translate
JsonAgainstHumanity ->
[ Missing ]
-- TODO: Translate
JsonAgainstHumanityAbout ->
[ Missing ]
BuiltIn ->
[ Text "Embutido" ]
@@ -243,7 +243,7 @@ enhanceHtml context mdString unenhanced =
Played ->
term context PlayedDescription Icon.check unenhanced
ManyDecksWhereToGet ->
ManyDecks ->
case context.shared.sources.manyDecks of
Just { baseUrl } ->
[ Html.blankA [ HtmlA.href baseUrl ] unenhanced ]
@@ -251,6 +251,14 @@ enhanceHtml context mdString unenhanced =
Nothing ->
unenhanced
JsonAgainstHumanity ->
case context.shared.sources.jsonAgainstHumanity of
Just { aboutUrl } ->
[ Html.blankA [ HtmlA.href aboutUrl ] unenhanced ]
Nothing ->
unenhanced
_ ->
unenhanced
+2
View File
@@ -182,6 +182,8 @@
left: -0.8em;
top: -3em;
z-index: 11;
display: flex;
flex-direction: column-reverse;
align-items: center;