Include mana costs in UI

This commit is contained in:
Correl Roush 2023-01-11 17:10:48 -05:00
parent 16a93b08d8
commit c13a45fa86
8 changed files with 229 additions and 16 deletions

View file

@ -224,6 +224,7 @@ async def advanced_search(
rarity=tutor.models.Rarity.from_string(row["rarity"]),
color_identity=tutor.models.Color.from_string(row["color_identity"]),
cmc=row["cmc"],
mana_cost=row["mana_cost"],
type_line=row["type_line"],
release_date=datetime.date.fromisoformat(row["release_date"]),
games=set(),
@ -373,6 +374,7 @@ async def get_decks(
'name', "oracle_latest"."name",
'color_identity', "oracle_latest"."color_identity",
'cmc', "oracle_latest"."cmc",
'mana_cost', "oracle_latest"."mana_cost",
'type_line', "oracle_latest"."type_line",
'edhrec_rank', "oracle_latest"."edhrec_rank",
'oracle_text', "oracle_latest"."oracle_text",
@ -407,6 +409,7 @@ async def get_decks(
card["color_identity"]
),
cmc=card["cmc"],
mana_cost=card["mana_cost"],
type_line=card["type_line"],
games=set(),
legalities={},
@ -441,6 +444,7 @@ async def get_deck(
'name', "oracle_latest"."name",
'color_identity', "oracle_latest"."color_identity",
'cmc', "oracle_latest"."cmc",
'mana_cost', "oracle_latest"."mana_cost",
'type_line', "oracle_latest"."type_line",
'edhrec_rank', "oracle_latest"."edhrec_rank",
'oracle_text', "oracle_latest"."oracle_text",
@ -474,6 +478,7 @@ async def get_deck(
card["color_identity"]
),
cmc=card["cmc"],
mana_cost=card["mana_cost"],
type_line=card["type_line"],
games=set(),
legalities={},

View file

@ -14,6 +14,7 @@
"elm/html": "1.0.0",
"elm/http": "2.0.0",
"elm/json": "1.1.3",
"elm/parser": "1.1.0",
"elm/regex": "1.0.0",
"elm/url": "1.0.0",
"elm-community/maybe-extra": "5.2.0",

View file

@ -6,7 +6,7 @@ import Browser.Events
import Browser.Navigation
import Card
import Color
import Dict
import Dict exposing (Dict)
import Element as E
import Element.Background as Background
import Element.Border as Border
@ -23,6 +23,7 @@ import Pages.DeckList
import Paginated
import Route
import Spinner
import Symbol
import Task
import UI
import Url
@ -37,6 +38,7 @@ type alias Model =
, viewport : UI.Dimensions
, device : E.Device
, route : Maybe Route.Route
, symbols : Dict String Symbol.Symbol
, page : Page
}
@ -48,6 +50,7 @@ type Msg
| CollectionMsg Pages.Collection.Msg
| DeckListMsg Pages.DeckList.Msg
| DeckEditorMsg Pages.DeckEditor.Msg
| GotSymbology (Result Http.Error (List Symbol.Symbol))
| SpinnerMsg Spinner.Msg
@ -70,6 +73,9 @@ init _ url key =
route =
Route.fromUrl url
symbols =
Dict.empty
initWith : (pageModel -> Page) -> (pageMsg -> Msg) -> ( pageModel, Cmd pageMsg ) -> ( Page, Cmd Msg )
initWith pageType pageMsg ( subModel, subCmd ) =
( pageType subModel, Cmd.map pageMsg subCmd )
@ -78,7 +84,7 @@ init _ url key =
case route of
Just Route.Collection ->
initWith Collection CollectionMsg <|
Pages.Collection.init key url device
Pages.Collection.init key url device symbols
Just Route.DeckList ->
initWith DeckList DeckListMsg <|
@ -86,7 +92,7 @@ init _ url key =
Just (Route.Deck deckId) ->
initWith DeckEditor DeckEditorMsg <|
Pages.DeckEditor.init key url device deckId
Pages.DeckEditor.init key url device symbols deckId
_ ->
( NotFound, Cmd.none )
@ -96,10 +102,12 @@ init _ url key =
, viewport = viewport
, device = device
, route = route
, symbols = symbols
, page = page
}
, Cmd.batch
[ UI.getViewport ViewportChanged
, getSymbology
, pageCmd
]
)
@ -128,7 +136,7 @@ update msg model =
|> updateWith Collection CollectionMsg model
( _, Just Route.Collection ) ->
Pages.Collection.init model.navigationKey url model.device
Pages.Collection.init model.navigationKey url model.device model.symbols
|> updateWith Collection CollectionMsg model
( _, Just Route.DeckList ) ->
@ -136,7 +144,7 @@ update msg model =
|> updateWith DeckList DeckListMsg model
( _, Just (Route.Deck deckId) ) ->
Pages.DeckEditor.init model.navigationKey url model.device deckId
Pages.DeckEditor.init model.navigationKey url model.device model.symbols deckId
|> updateWith DeckEditor DeckEditorMsg model
_ ->
@ -160,6 +168,32 @@ update msg model =
, Cmd.none
)
( GotSymbology (Ok symbols), _ ) ->
let
table =
symbols
|> List.map (\s -> ( s.symbol, s ))
|> Dict.fromList
( newModel, cmd ) =
case model.page of
Collection pageModel ->
Pages.Collection.update (Pages.Collection.GotSymbols table) pageModel
|> updateWith Collection CollectionMsg model
DeckEditor pageModel ->
Pages.DeckEditor.update (Pages.DeckEditor.GotSymbols table) pageModel
|> updateWith DeckEditor DeckEditorMsg model
_ ->
( model, Cmd.none )
in
( { newModel
| symbols = table
}
, cmd
)
( SpinnerMsg spinnerMsg, Collection pageModel ) ->
Pages.Collection.update (Pages.Collection.SpinnerMsg spinnerMsg) pageModel
|> updateWith Collection CollectionMsg model
@ -290,6 +324,17 @@ subscriptions model =
Sub.batch global
getSymbology : Cmd Msg
getSymbology =
Http.get
{ url = Url.Builder.crossOrigin "https://api.scryfall.com" [ "symbology" ] []
, expect =
Http.expectJson GotSymbology <|
Json.Decode.at [ "data" ] <|
Json.Decode.list Symbol.decode
}
main : Program Json.Decode.Value Model Msg
main =
Browser.application

View file

@ -16,6 +16,7 @@ type alias Prices =
type alias Oracle =
{ oracleId : String
, name : String
, manaCost : String
, typeLine : String
, oracleText : String
}
@ -26,6 +27,7 @@ type alias Card =
, name : String
, setCode : String
, rarity : String
, manaCost : String
, typeLine : String
, oracleText : String
, prices : Prices
@ -44,6 +46,10 @@ decodeOracle =
Json.Decode.succeed Oracle
|> JDP.required "oracle_id" Json.Decode.string
|> JDP.required "name" Json.Decode.string
|> JDP.required "mana_cost"
(Json.Decode.nullable Json.Decode.string
|> Json.Decode.map (Maybe.withDefault "")
)
|> JDP.required "type_line" Json.Decode.string
|> JDP.required "oracle_text"
(Json.Decode.nullable Json.Decode.string
@ -58,6 +64,10 @@ decode =
|> JDP.required "name" Json.Decode.string
|> JDP.required "set_code" Json.Decode.string
|> JDP.required "rarity" Json.Decode.string
|> JDP.required "mana_cost"
(Json.Decode.nullable Json.Decode.string
|> Json.Decode.map (Maybe.withDefault "")
)
|> JDP.required "type_line" Json.Decode.string
|> JDP.required "oracle_text"
(Json.Decode.nullable Json.Decode.string

View file

@ -20,6 +20,7 @@ import Json.Decode.Pipeline as JDP
import Maybe.Extra
import Paginated
import Spinner
import Symbol
import Task
import UI
import Url
@ -59,6 +60,7 @@ type alias Model =
, cardPage : CardPage
, activeCard : Maybe Card.Copy
, collectionStatistics : Maybe Statistics
, symbols : Symbol.Table
}
@ -74,6 +76,7 @@ type Msg
| FoundCards (Result Http.Error (Paginated.Page Card.Copy))
| ShowCardDetails Card.Copy
| ClearCardDetails
| GotSymbols Symbol.Table
type CriteriaMsg
@ -179,8 +182,8 @@ criteriaFromUrl url =
|> Maybe.withDefault emptyCriteria
init : Browser.Navigation.Key -> Url.Url -> E.Device -> ( Model, Cmd Msg )
init key url device =
init : Browser.Navigation.Key -> Url.Url -> E.Device -> Symbol.Table -> ( Model, Cmd Msg )
init key url device symbols =
let
criteria =
criteriaFromUrl url
@ -193,6 +196,7 @@ init key url device =
, cardPage = Loading Paginated.empty
, activeCard = Nothing
, collectionStatistics = Nothing
, symbols = symbols
}
, Cmd.batch
[ UI.getViewport ViewportChanged
@ -287,6 +291,9 @@ update msg model =
ClearCardDetails ->
( { model | activeCard = Nothing }, Cmd.none )
GotSymbols symbols ->
( { model | symbols = symbols }, Cmd.none )
searchBar : Model -> E.Element Msg
searchBar model =
@ -480,7 +487,7 @@ viewCardBrowser model =
[]
)
in
UI.cardRow { foil = copy.foil, subtitle = copy.collection } cardAttrs copy.card
UI.cardRow { foil = copy.foil, subtitle = copy.collection } cardAttrs model.symbols copy.card
in
E.column attrs
[ E.row

View file

@ -12,6 +12,7 @@ import Http
import Paginated
import Route
import Spinner
import Symbol
import UI
import Url
import Url.Builder
@ -22,6 +23,7 @@ type alias Model =
, url : Url.Url
, device : E.Device
, spinner : Spinner.Model
, symbols : Symbol.Table
, deck : Deck
}
@ -30,6 +32,7 @@ type Msg
= ViewportChanged UI.Dimensions
| SpinnerMsg Spinner.Msg
| GotDeck (Result Http.Error Deck.Deck)
| GotSymbols Symbol.Table
type Deck
@ -85,12 +88,13 @@ groups cards =
]
init : Browser.Navigation.Key -> Url.Url -> E.Device -> Int -> ( Model, Cmd Msg )
init key url device deckId =
init : Browser.Navigation.Key -> Url.Url -> E.Device -> Symbol.Table -> Int -> ( Model, Cmd Msg )
init key url device symbols deckId =
( { navigationKey = key
, url = url
, device = device
, spinner = Spinner.init
, symbols = symbols
, deck = Loading
}
, getDeck deckId
@ -116,9 +120,12 @@ update msg model =
GotDeck (Err _) ->
( { model | deck = Failed }, Cmd.none )
GotSymbols symbols ->
( { model | symbols = symbols }, Cmd.none )
viewDeck : Deck.Deck -> E.Element Msg
viewDeck deck =
viewDeck : Symbol.Table -> Deck.Deck -> E.Element Msg
viewDeck symbols deck =
let
viewGroup group =
E.column [ E.spacing 10 ]
@ -131,6 +138,7 @@ viewDeck deck =
, subtitle = "x" ++ String.fromInt dc.quantity
}
[ E.width <| E.px 400, E.clipX, Background.color UI.colors.background ]
symbols
dc.card
)
group.cards
@ -159,7 +167,7 @@ view model =
]
[ case model.deck of
Ready deck ->
viewDeck deck
viewDeck model.symbols deck
Failed ->
E.none

109
www/src/Symbol.elm Normal file
View file

@ -0,0 +1,109 @@
module Symbol exposing (Symbol, Table, Text, decode, text)
import Dict exposing (Dict)
import Element as E
import Element.Background as Background
import Element.Border as Border
import Element.Font as Font
import Json.Decode
import Json.Decode.Pipeline as JDP
import Parser as P exposing ((|.), (|=))
type alias Symbol =
{ symbol : String
, svgURI : String
, english : String
}
type alias Table =
Dict String Symbol
type Text
= Text String
| Inline Symbol
decode : Json.Decode.Decoder Symbol
decode =
Json.Decode.succeed Symbol
|> JDP.required "symbol" Json.Decode.string
|> JDP.required "svg_uri" Json.Decode.string
|> JDP.required "english" Json.Decode.string
symbolTextParser : Dict String Symbol -> P.Parser (List Text)
symbolTextParser available =
let
lookupSymbol : String -> Text
lookupSymbol string =
Dict.get string available
|> Maybe.map Inline
|> Maybe.withDefault (Text "")
textParser : P.Parser Text
textParser =
P.chompUntilEndOr "{"
|> P.getChompedString
|> P.map Text
symbolStringParser : P.Parser String
symbolStringParser =
P.getChompedString <|
P.succeed ()
|. P.symbol "{"
|. P.chompWhile (\c -> c /= '}')
|. P.symbol "}"
symbolParser : P.Parser Text
symbolParser =
symbolStringParser
|> P.map lookupSymbol
in
P.loop []
(\reversedParts ->
P.oneOf
[ P.end
|> P.map (\_ -> P.Done (List.reverse reversedParts))
, symbolParser
|> P.map (\part -> P.Loop (part :: reversedParts))
, textParser
|> P.map (\part -> P.Loop (part :: reversedParts))
]
)
toElement : Int -> Text -> E.Element msg
toElement size symbolText =
let
padding =
size // 6
fontSize =
size - padding
symbol : Symbol -> E.Element msg
symbol s =
E.image
[ E.width <| E.px size
, E.height <| E.px size
]
{ src = s.svgURI
, description = s.english
}
in
case symbolText of
Text s ->
E.el [ Font.size size, E.height <| E.px size ] <| E.text s
Inline s ->
symbol s
text : Dict String Symbol -> Int -> String -> E.Element msg
text available size string =
E.row [ E.spacing (size // 10) ] <|
List.map (toElement size) <|
(P.run (symbolTextParser available) string |> Result.withDefault [])

View file

@ -21,6 +21,7 @@ import Element.Border as Border
import Element.Font as Font
import Maybe.Extra
import Spinner
import Symbol
import Task
import Url.Builder
@ -86,6 +87,32 @@ colors =
, rare = rare
, uncommon = uncommon
, common = common
, mana =
{ white =
{ fg = E.rgb255 249 250 244
, bg = E.rgb255 248 231 185
}
, black =
{ fg = E.rgb255 21 11 0
, bg = E.rgb255 166 159 157
}
, blue =
{ fg = E.rgb255 14 104 171
, bg = E.rgb255 179 206 234
}
, green =
{ fg = E.rgb255 0 114 62
, bg = E.rgb255 196 211 202
}
, red =
{ fg = E.rgb255 211 32 42
, bg = E.rgb255 235 159 130
}
, generic =
{ fg = E.rgb255 190 190 190
, bg = E.rgb255 250 250 250
}
}
}
@ -173,8 +200,8 @@ priceBadge { currency, amount } =
]
cardRow : { foil : Bool, subtitle : String } -> List (E.Attribute msg) -> Card.Card -> E.Element msg
cardRow options attributes card =
cardRow : { foil : Bool, subtitle : String } -> List (E.Attribute msg) -> Symbol.Table -> Card.Card -> E.Element msg
cardRow options attributes symbols card =
let
badge color foil string =
E.el
@ -265,7 +292,8 @@ cardRow options attributes card =
, description = card.name
}
, E.column [ E.centerY, E.height E.fill, E.width E.fill, E.clipX ]
[ E.el [ Font.color colors.title ] <| E.text card.name
[ Symbol.text symbols 12 card.manaCost
, E.el [ Font.color colors.title ] <| E.text card.name
, E.el [ Font.size 16, Font.italic, Font.color colors.subtitle ] <| E.text options.subtitle
]
, E.column [ E.alignRight, E.height E.fill ] <|