Include mana costs in UI
This commit is contained in:
parent
16a93b08d8
commit
c13a45fa86
8 changed files with 229 additions and 16 deletions
|
@ -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={},
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
109
www/src/Symbol.elm
Normal 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 [])
|
|
@ -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 ] <|
|
||||
|
|
Loading…
Reference in a new issue