Add deck initial editor page with card listing
This commit is contained in:
parent
32e213a16d
commit
d51554f435
8 changed files with 345 additions and 12 deletions
|
@ -339,7 +339,7 @@ async def store_deck_card(
|
||||||
) -> None:
|
) -> None:
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO "deck_cards" ("deck_id", "oracle_id", "quantity")
|
INSERT INTO "deck_list" ("deck_id", "oracle_id", "quantity")
|
||||||
VALUES (%(deck_id)s, %(oracle_id)s, %(quantity)s)
|
VALUES (%(deck_id)s, %(oracle_id)s, %(quantity)s)
|
||||||
""",
|
""",
|
||||||
{"deck_id": deck_id, "oracle_id": str(oracle_id), "quantity": quantity},
|
{"deck_id": deck_id, "oracle_id": str(oracle_id), "quantity": quantity},
|
||||||
|
@ -352,19 +352,117 @@ async def get_decks(
|
||||||
db.row_factory = psycopg.rows.dict_row
|
db.row_factory = psycopg.rows.dict_row
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
SELECT * FROM "decks"
|
SELECT "decks"."deck_id"
|
||||||
|
, "decks"."name"
|
||||||
|
, JSON_STRIP_NULLS(JSON_AGG(JSON_BUILD_OBJECT(
|
||||||
|
'oracle_id', "deck_list"."oracle_id",
|
||||||
|
'name', "oracle"."name",
|
||||||
|
'color_identity', "oracle"."color_identity",
|
||||||
|
'cmc', "oracle"."cmc",
|
||||||
|
'type_line', "oracle"."type_line",
|
||||||
|
'edhrec_rank', "oracle"."edhrec_rank",
|
||||||
|
'oracle_text', "oracle"."oracle_text",
|
||||||
|
'games', "oracle"."games",
|
||||||
|
'legalities', "oracle"."legalities",
|
||||||
|
'quantity', "deck_list"."quantity"
|
||||||
|
))) AS "cards"
|
||||||
|
FROM "decks"
|
||||||
|
LEFT JOIN "deck_list" USING ("deck_id")
|
||||||
|
LEFT JOIN "oracle" USING ("oracle_id")
|
||||||
|
GROUP BY "decks"."deck_id"
|
||||||
|
, "decks"."name"
|
||||||
ORDER BY "decks"."deck_id"
|
ORDER BY "decks"."deck_id"
|
||||||
LIMIT %(limit)s OFFSET %(offset)s
|
LIMIT %(limit)s OFFSET %(offset)s
|
||||||
""",
|
""",
|
||||||
{"limit": limit, "offset": offset},
|
{"limit": limit, "offset": offset},
|
||||||
)
|
)
|
||||||
rows = await db.fetchall()
|
rows = await db.fetchall()
|
||||||
return [
|
return [
|
||||||
tutor.models.Deck(deck_id=row["deck_id"], name=row["name"], cards=[])
|
tutor.models.Deck(
|
||||||
|
deck_id=row["deck_id"],
|
||||||
|
name=row["name"],
|
||||||
|
cards=[
|
||||||
|
tutor.models.DeckCard(
|
||||||
|
card=tutor.models.OracleCard(
|
||||||
|
oracle_id=card["oracle_id"],
|
||||||
|
name=card["name"],
|
||||||
|
color_identity=tutor.models.Color.from_string(
|
||||||
|
card["color_identity"]
|
||||||
|
),
|
||||||
|
cmc=card["cmc"],
|
||||||
|
type_line=card["type_line"],
|
||||||
|
games=set(),
|
||||||
|
legalities={},
|
||||||
|
edhrec_rank=card.get("edhrec_rank"),
|
||||||
|
oracle_text=card.get("oracle_text"),
|
||||||
|
),
|
||||||
|
quantity=card["quantity"],
|
||||||
|
)
|
||||||
|
for card in row["cards"]
|
||||||
|
if card and card.get("oracle_id")
|
||||||
|
],
|
||||||
|
)
|
||||||
for row in rows
|
for row in rows
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
async def get_deck(
|
||||||
|
db: psycopg.Cursor, deck_id: int
|
||||||
|
) -> typing.Optional[tutor.models.Deck]:
|
||||||
|
db.row_factory = psycopg.rows.dict_row
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
SELECT "decks"."deck_id"
|
||||||
|
, "decks"."name"
|
||||||
|
, JSON_STRIP_NULLS(JSON_AGG(JSON_BUILD_OBJECT(
|
||||||
|
'oracle_id', "deck_list"."oracle_id",
|
||||||
|
'name', "oracle"."name",
|
||||||
|
'color_identity', "oracle"."color_identity",
|
||||||
|
'cmc', "oracle"."cmc",
|
||||||
|
'type_line', "oracle"."type_line",
|
||||||
|
'edhrec_rank', "oracle"."edhrec_rank",
|
||||||
|
'oracle_text', "oracle"."oracle_text",
|
||||||
|
'games', "oracle"."games",
|
||||||
|
'legalities', "oracle"."legalities",
|
||||||
|
'quantity', "deck_list"."quantity"
|
||||||
|
))) AS "cards"
|
||||||
|
FROM "decks"
|
||||||
|
LEFT JOIN "deck_list" USING ("deck_id")
|
||||||
|
LEFT JOIN "oracle" USING ("oracle_id")
|
||||||
|
WHERE "decks"."deck_id" = %(deck_id)s
|
||||||
|
GROUP BY "decks"."deck_id"
|
||||||
|
, "decks"."name"
|
||||||
|
""",
|
||||||
|
{"deck_id": deck_id},
|
||||||
|
)
|
||||||
|
row = await db.fetchone()
|
||||||
|
if row:
|
||||||
|
return tutor.models.Deck(
|
||||||
|
deck_id=row["deck_id"],
|
||||||
|
name=row["name"],
|
||||||
|
cards=[
|
||||||
|
tutor.models.DeckCard(
|
||||||
|
card=tutor.models.OracleCard(
|
||||||
|
oracle_id=card["oracle_id"],
|
||||||
|
name=card["name"],
|
||||||
|
color_identity=tutor.models.Color.from_string(
|
||||||
|
card["color_identity"]
|
||||||
|
),
|
||||||
|
cmc=card["cmc"],
|
||||||
|
type_line=card["type_line"],
|
||||||
|
games=set(),
|
||||||
|
legalities={},
|
||||||
|
edhrec_rank=card.get("edhrec_rank"),
|
||||||
|
oracle_text=card.get("oracle_text"),
|
||||||
|
),
|
||||||
|
quantity=card["quantity"],
|
||||||
|
)
|
||||||
|
for card in row["cards"]
|
||||||
|
if card and card.get("oracle_id")
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def store_var(db: psycopg.Cursor, key: str, value: str) -> None:
|
async def store_var(db: psycopg.Cursor, key: str, value: str) -> None:
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -183,7 +183,20 @@ class DecksHandler(RequestHandler):
|
||||||
{
|
{
|
||||||
"deck_id": deck.deck_id,
|
"deck_id": deck.deck_id,
|
||||||
"name": deck.name,
|
"name": deck.name,
|
||||||
"cards": [],
|
"cards": [
|
||||||
|
{
|
||||||
|
"quantity": card.quantity,
|
||||||
|
"card": {
|
||||||
|
"oracle_id": str(card.card.oracle_id),
|
||||||
|
"name": card.card.name,
|
||||||
|
"color_identity": tutor.models.Color.to_string(
|
||||||
|
card.card.color_identity
|
||||||
|
),
|
||||||
|
"oracle_text": card.card.oracle_text,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for card in deck.cards
|
||||||
|
],
|
||||||
}
|
}
|
||||||
for deck in decks
|
for deck in decks
|
||||||
]
|
]
|
||||||
|
@ -194,6 +207,42 @@ class DecksHandler(RequestHandler):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class DeckHandler(RequestHandler):
|
||||||
|
async def get(self, deck_id) -> None:
|
||||||
|
self.set_header("Content-Type", "application/json")
|
||||||
|
self.set_header("Access-Control-Allow-Origin", "*")
|
||||||
|
async with self.application.pool.connection() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
deck = await tutor.database.get_deck(cursor, deck_id)
|
||||||
|
if not deck:
|
||||||
|
raise tornado.web.HTTPError(404)
|
||||||
|
self.write(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"deck_id": deck.deck_id,
|
||||||
|
"name": deck.name,
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"quantity": card.quantity,
|
||||||
|
"card": {
|
||||||
|
"oracle_id": str(card.card.oracle_id),
|
||||||
|
"name": card.card.name,
|
||||||
|
"color_identity": tutor.models.Color.to_string(
|
||||||
|
card.card.color_identity
|
||||||
|
),
|
||||||
|
"oracle_text": card.card.oracle_text,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for card in deck.cards
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
async def put(self, deck_id) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
class TemplateHandler(RequestHandler):
|
class TemplateHandler(RequestHandler):
|
||||||
def initialize(
|
def initialize(
|
||||||
self,
|
self,
|
||||||
|
@ -214,7 +263,14 @@ class StaticFileHandler(tornado.web.StaticFileHandler):
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_absolute_path(cls, root: str, path: str) -> str:
|
def get_absolute_path(cls, root: str, path: str) -> str:
|
||||||
# Rewrite paths to load the index
|
# Rewrite paths to load the index
|
||||||
if path in ("collection", "decks", "decks/new123123"):
|
if any(
|
||||||
|
re.match(pattern, path)
|
||||||
|
for pattern in [
|
||||||
|
r"^collection$",
|
||||||
|
r"^decks$",
|
||||||
|
r"^decks/.*",
|
||||||
|
]
|
||||||
|
):
|
||||||
path = "index.html"
|
path = "index.html"
|
||||||
return tornado.web.StaticFileHandler.get_absolute_path(root, path)
|
return tornado.web.StaticFileHandler.get_absolute_path(root, path)
|
||||||
|
|
||||||
|
@ -243,12 +299,13 @@ class Application(tornado.web.Application):
|
||||||
(r"/api/search", SearchHandler),
|
(r"/api/search", SearchHandler),
|
||||||
(r"/api/collection", CollectionHandler),
|
(r"/api/collection", CollectionHandler),
|
||||||
(r"/api/decks", DecksHandler),
|
(r"/api/decks", DecksHandler),
|
||||||
|
(r"/api/decks/([1-9][0-9]*)", DeckHandler),
|
||||||
]
|
]
|
||||||
if static_path := settings.get("static"):
|
if static_path := settings.get("static"):
|
||||||
paths.extend(
|
paths.extend(
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
fr"/(.*)",
|
rf"/(.*)",
|
||||||
StaticFileHandler,
|
StaticFileHandler,
|
||||||
{"path": static_path, "default_filename": "index.html"},
|
{"path": static_path, "default_filename": "index.html"},
|
||||||
),
|
),
|
||||||
|
|
|
@ -18,6 +18,7 @@ import Http
|
||||||
import Json.Decode
|
import Json.Decode
|
||||||
import Maybe.Extra
|
import Maybe.Extra
|
||||||
import Pages.Collection
|
import Pages.Collection
|
||||||
|
import Pages.DeckEditor
|
||||||
import Pages.DeckList
|
import Pages.DeckList
|
||||||
import Paginated
|
import Paginated
|
||||||
import Route
|
import Route
|
||||||
|
@ -46,6 +47,7 @@ type Msg
|
||||||
| LinkClicked Browser.UrlRequest
|
| LinkClicked Browser.UrlRequest
|
||||||
| CollectionMsg Pages.Collection.Msg
|
| CollectionMsg Pages.Collection.Msg
|
||||||
| DeckListMsg Pages.DeckList.Msg
|
| DeckListMsg Pages.DeckList.Msg
|
||||||
|
| DeckEditorMsg Pages.DeckEditor.Msg
|
||||||
| SpinnerMsg Spinner.Msg
|
| SpinnerMsg Spinner.Msg
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,6 +55,7 @@ type Page
|
||||||
= NotFound
|
= NotFound
|
||||||
| Collection Pages.Collection.Model
|
| Collection Pages.Collection.Model
|
||||||
| DeckList Pages.DeckList.Model
|
| DeckList Pages.DeckList.Model
|
||||||
|
| DeckEditor Pages.DeckEditor.Model
|
||||||
|
|
||||||
|
|
||||||
init : Json.Decode.Value -> Url.Url -> Browser.Navigation.Key -> ( Model, Cmd Msg )
|
init : Json.Decode.Value -> Url.Url -> Browser.Navigation.Key -> ( Model, Cmd Msg )
|
||||||
|
@ -81,6 +84,10 @@ init _ url key =
|
||||||
initWith DeckList DeckListMsg <|
|
initWith DeckList DeckListMsg <|
|
||||||
Pages.DeckList.init key url device
|
Pages.DeckList.init key url device
|
||||||
|
|
||||||
|
Just (Route.Deck deckId) ->
|
||||||
|
initWith DeckEditor DeckEditorMsg <|
|
||||||
|
Pages.DeckEditor.init key url device deckId
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
( NotFound, Cmd.none )
|
( NotFound, Cmd.none )
|
||||||
in
|
in
|
||||||
|
@ -128,6 +135,10 @@ update msg model =
|
||||||
Pages.DeckList.init model.navigationKey url model.device
|
Pages.DeckList.init model.navigationKey url model.device
|
||||||
|> updateWith DeckList DeckListMsg model
|
|> updateWith DeckList DeckListMsg model
|
||||||
|
|
||||||
|
( _, Just (Route.Deck deckId) ) ->
|
||||||
|
Pages.DeckEditor.init model.navigationKey url model.device deckId
|
||||||
|
|> updateWith DeckEditor DeckEditorMsg model
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
( { model | page = NotFound }, Cmd.none )
|
( { model | page = NotFound }, Cmd.none )
|
||||||
in
|
in
|
||||||
|
@ -161,6 +172,10 @@ update msg model =
|
||||||
Pages.DeckList.update pageMsg pageModel
|
Pages.DeckList.update pageMsg pageModel
|
||||||
|> updateWith DeckList DeckListMsg model
|
|> updateWith DeckList DeckListMsg model
|
||||||
|
|
||||||
|
( DeckEditorMsg pageMsg, DeckEditor pageModel ) ->
|
||||||
|
Pages.DeckEditor.update pageMsg pageModel
|
||||||
|
|> updateWith DeckEditor DeckEditorMsg model
|
||||||
|
|
||||||
( _, _ ) ->
|
( _, _ ) ->
|
||||||
( model, Cmd.none )
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
@ -210,6 +225,9 @@ view model =
|
||||||
DeckList pageModel ->
|
DeckList pageModel ->
|
||||||
E.map DeckListMsg <| Pages.DeckList.view pageModel
|
E.map DeckListMsg <| Pages.DeckList.view pageModel
|
||||||
|
|
||||||
|
DeckEditor pageModel ->
|
||||||
|
E.map DeckEditorMsg <| Pages.DeckEditor.view pageModel
|
||||||
|
|
||||||
NotFound ->
|
NotFound ->
|
||||||
E.column
|
E.column
|
||||||
[ E.width E.fill
|
[ E.width E.fill
|
||||||
|
@ -265,6 +283,9 @@ subscriptions model =
|
||||||
DeckList pageModel ->
|
DeckList pageModel ->
|
||||||
Sub.batch (Sub.map DeckListMsg (Pages.DeckList.subscriptions pageModel) :: global)
|
Sub.batch (Sub.map DeckListMsg (Pages.DeckList.subscriptions pageModel) :: global)
|
||||||
|
|
||||||
|
DeckEditor pageModel ->
|
||||||
|
Sub.batch (Sub.map DeckEditorMsg (Pages.DeckEditor.subscriptions pageModel) :: global)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
Sub.batch global
|
Sub.batch global
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,13 @@ type alias Prices =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Oracle =
|
||||||
|
{ oracleId : String
|
||||||
|
, name : String
|
||||||
|
, oracleText : String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
type alias Card =
|
type alias Card =
|
||||||
{ scryfallId : String
|
{ scryfallId : String
|
||||||
, name : String
|
, name : String
|
||||||
|
@ -25,6 +32,17 @@ type alias Card =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
decodeOracle : Json.Decode.Decoder Oracle
|
||||||
|
decodeOracle =
|
||||||
|
Json.Decode.succeed Oracle
|
||||||
|
|> JDP.required "oracle_id" Json.Decode.string
|
||||||
|
|> JDP.required "name" Json.Decode.string
|
||||||
|
|> JDP.required "oracle_text"
|
||||||
|
(Json.Decode.nullable Json.Decode.string
|
||||||
|
|> Json.Decode.map (Maybe.withDefault "")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
decode : Json.Decode.Decoder Card
|
decode : Json.Decode.Decoder Card
|
||||||
decode =
|
decode =
|
||||||
Json.Decode.succeed Card
|
Json.Decode.succeed Card
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
module Deck exposing (..)
|
module Deck exposing (..)
|
||||||
|
|
||||||
|
import Card
|
||||||
import Json.Decode
|
import Json.Decode
|
||||||
import Json.Decode.Pipeline as JDP
|
import Json.Decode.Pipeline as JDP
|
||||||
|
|
||||||
|
@ -7,6 +8,13 @@ import Json.Decode.Pipeline as JDP
|
||||||
type alias Deck =
|
type alias Deck =
|
||||||
{ id : Int
|
{ id : Int
|
||||||
, name : String
|
, name : String
|
||||||
|
, cards : List Card
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Card =
|
||||||
|
{ oracle : Card.Oracle
|
||||||
|
, quantity : Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,3 +23,11 @@ decode =
|
||||||
Json.Decode.succeed Deck
|
Json.Decode.succeed Deck
|
||||||
|> JDP.required "deck_id" Json.Decode.int
|
|> JDP.required "deck_id" Json.Decode.int
|
||||||
|> JDP.required "name" Json.Decode.string
|
|> JDP.required "name" Json.Decode.string
|
||||||
|
|> JDP.required "cards" (Json.Decode.list decodeCard)
|
||||||
|
|
||||||
|
|
||||||
|
decodeCard : Json.Decode.Decoder Card
|
||||||
|
decodeCard =
|
||||||
|
Json.Decode.succeed Card
|
||||||
|
|> JDP.required "card" Card.decodeOracle
|
||||||
|
|> JDP.required "quantity" Json.Decode.int
|
||||||
|
|
|
@ -1,8 +1,125 @@
|
||||||
module DeckEditor exposing (..)
|
module Pages.DeckEditor exposing (..)
|
||||||
|
|
||||||
import Card
|
import Browser
|
||||||
|
import Browser.Events
|
||||||
|
import Browser.Navigation
|
||||||
|
import Deck
|
||||||
|
import Element as E
|
||||||
|
import Element.Background as Background
|
||||||
|
import Element.Events as Events
|
||||||
|
import Element.Font as Font
|
||||||
|
import Http
|
||||||
|
import Paginated
|
||||||
|
import Route
|
||||||
|
import Spinner
|
||||||
|
import UI
|
||||||
|
import Url
|
||||||
|
import Url.Builder
|
||||||
|
|
||||||
|
|
||||||
type alias Deck =
|
type alias Model =
|
||||||
{ cards : List Card.Card
|
{ navigationKey : Browser.Navigation.Key
|
||||||
|
, url : Url.Url
|
||||||
|
, device : E.Device
|
||||||
|
, spinner : Spinner.Model
|
||||||
|
, deck : Deck
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= ViewportChanged UI.Dimensions
|
||||||
|
| SpinnerMsg Spinner.Msg
|
||||||
|
| GotDeck (Result Http.Error Deck.Deck)
|
||||||
|
|
||||||
|
|
||||||
|
type Deck
|
||||||
|
= Ready Deck.Deck
|
||||||
|
| Loading
|
||||||
|
| NotFound
|
||||||
|
| Failed
|
||||||
|
|
||||||
|
|
||||||
|
init : Browser.Navigation.Key -> Url.Url -> E.Device -> Int -> ( Model, Cmd Msg )
|
||||||
|
init key url device deckId =
|
||||||
|
( { navigationKey = key
|
||||||
|
, url = url
|
||||||
|
, device = device
|
||||||
|
, spinner = Spinner.init
|
||||||
|
, deck = Loading
|
||||||
|
}
|
||||||
|
, getDeck deckId
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
ViewportChanged viewport ->
|
||||||
|
( { model
|
||||||
|
| device = E.classifyDevice viewport
|
||||||
|
}
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
SpinnerMsg msg_ ->
|
||||||
|
( { model | spinner = Spinner.update msg_ model.spinner }, Cmd.none )
|
||||||
|
|
||||||
|
GotDeck (Ok deck) ->
|
||||||
|
( { model | deck = Ready deck }, Cmd.none )
|
||||||
|
|
||||||
|
GotDeck (Err _) ->
|
||||||
|
( { model | deck = Failed }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> E.Element Msg
|
||||||
|
view model =
|
||||||
|
case model.deck of
|
||||||
|
Failed ->
|
||||||
|
E.none
|
||||||
|
|
||||||
|
NotFound ->
|
||||||
|
E.none
|
||||||
|
|
||||||
|
Loading ->
|
||||||
|
E.el [ E.height E.fill, E.centerX ] <|
|
||||||
|
E.html <|
|
||||||
|
Spinner.view UI.manaSpinner
|
||||||
|
model.spinner
|
||||||
|
|
||||||
|
Ready deck ->
|
||||||
|
let
|
||||||
|
cardRow card =
|
||||||
|
E.row
|
||||||
|
[ E.width E.fill
|
||||||
|
, E.spacing 10
|
||||||
|
, E.padding 3
|
||||||
|
]
|
||||||
|
[ E.column [ E.centerY, E.height E.fill, E.width E.fill, E.clipX ]
|
||||||
|
[ UI.title card.oracle.name
|
||||||
|
, UI.subtitle ("x" ++ String.fromInt card.quantity)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
in
|
||||||
|
E.column [ E.width E.fill, E.centerX, E.spacing 5 ] <|
|
||||||
|
[ E.paragraph [ Font.heavy, Font.size 24, Font.center ] [ UI.title deck.name ] ]
|
||||||
|
++ List.map cardRow deck.cards
|
||||||
|
|
||||||
|
|
||||||
|
subscriptions : Model -> Sub Msg
|
||||||
|
subscriptions model =
|
||||||
|
Sub.batch
|
||||||
|
[ Browser.Events.onResize
|
||||||
|
(\w h -> ViewportChanged { width = w, height = h })
|
||||||
|
, Sub.map SpinnerMsg Spinner.subscription
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
getDeck : Int -> Cmd Msg
|
||||||
|
getDeck deckId =
|
||||||
|
Http.get
|
||||||
|
{ url =
|
||||||
|
Url.Builder.absolute
|
||||||
|
[ "api", "decks", String.fromInt deckId ]
|
||||||
|
[]
|
||||||
|
, expect = Http.expectJson GotDeck Deck.decode
|
||||||
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ view model =
|
||||||
E.row
|
E.row
|
||||||
[ E.width E.fill
|
[ E.width E.fill
|
||||||
, E.padding 10
|
, E.padding 10
|
||||||
, E.spacing 10
|
, E.spacing 3
|
||||||
, E.pointer
|
, E.pointer
|
||||||
, E.mouseOver [ Background.color UI.colors.hover ]
|
, E.mouseOver [ Background.color UI.colors.hover ]
|
||||||
, Events.onClick <| DeckClicked deck.id
|
, Events.onClick <| DeckClicked deck.id
|
||||||
|
|
|
@ -4,6 +4,7 @@ module UI exposing
|
||||||
, getViewport
|
, getViewport
|
||||||
, isMobile
|
, isMobile
|
||||||
, manaSpinner
|
, manaSpinner
|
||||||
|
, subtitle
|
||||||
, text
|
, text
|
||||||
, title
|
, title
|
||||||
)
|
)
|
||||||
|
@ -140,3 +141,8 @@ text string =
|
||||||
title : String -> E.Element msg
|
title : String -> E.Element msg
|
||||||
title string =
|
title string =
|
||||||
E.el [ Font.color colors.title ] <| E.text string
|
E.el [ Font.color colors.title ] <| E.text string
|
||||||
|
|
||||||
|
|
||||||
|
subtitle : String -> E.Element msg
|
||||||
|
subtitle string =
|
||||||
|
E.el [ Font.size 16, Font.italic, Font.color colors.subtitle ] <| E.text string
|
||||||
|
|
Loading…
Reference in a new issue