|
|
@@ -2,7 +2,7 @@ port module Main exposing (..)
|
|
|
|
|
|
import API
|
|
|
import Browser exposing (Document)
|
|
|
-import Card exposing (CardData, CardPerformanceDistributions, calculatePerformanceDistributions, manaCostToSymbol)
|
|
|
+import Card exposing (CardData, CardPerformanceDistributions, CardType(..), calculatePerformanceDistributions, colorsSortOrder, manaCostToSymbol, raritySortOrder)
|
|
|
import Chart as C
|
|
|
import Chart.Attributes as CA
|
|
|
import Components.Button as Button
|
|
|
@@ -37,6 +37,7 @@ main =
|
|
|
type Model
|
|
|
= ChooseSet ChooseSetModel
|
|
|
| Ready ReadyModel
|
|
|
+ | CardExplorer CardExplorerModel
|
|
|
| Error ErrorModel
|
|
|
|
|
|
|
|
|
@@ -53,6 +54,12 @@ type FocusStat
|
|
|
| FocusIWD
|
|
|
|
|
|
|
|
|
+type SortOrder
|
|
|
+ = SortOrderDefault
|
|
|
+ | SortOrderRarity
|
|
|
+ | SortOrderName
|
|
|
+
|
|
|
+
|
|
|
type alias ToolboxAccordion =
|
|
|
{ cmc : Bool, signals : Bool, signalsDelta : Bool, deckList : Bool }
|
|
|
|
|
|
@@ -62,6 +69,7 @@ type SetLoadStatus
|
|
|
| NoLocalData
|
|
|
| HasLocalData Database.Database
|
|
|
| FetchingRemoteData
|
|
|
+ | DeletingLocalData
|
|
|
|
|
|
|
|
|
type alias ChooseSetModel =
|
|
|
@@ -83,6 +91,15 @@ type alias ReadyModel =
|
|
|
}
|
|
|
|
|
|
|
|
|
+type alias CardExplorerModel =
|
|
|
+ { database : Database.Database
|
|
|
+ , highlighted : Maybe CardData
|
|
|
+ , flipHighlighted : Bool
|
|
|
+ , performanceDistributions : CardPerformanceDistributions
|
|
|
+ , sortOrder : SortOrder
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
type alias ErrorModel =
|
|
|
{ error : String
|
|
|
}
|
|
|
@@ -117,6 +134,7 @@ type Msg
|
|
|
| Highlight String
|
|
|
| FlipHighlightedCard
|
|
|
| SetFocusStat FocusStat
|
|
|
+ | SetSortOrder SortOrder
|
|
|
| SetDeckProgress DeckProgress
|
|
|
| ToggleCMCChart
|
|
|
| ToggleSignalsChart
|
|
|
@@ -126,8 +144,11 @@ type Msg
|
|
|
| IOGotSets (Result String (List String))
|
|
|
| IOFetchSetData String
|
|
|
| IOGotSetData (Result String ( String, Maybe Database.Database ))
|
|
|
+ | IODeleteSetData String -- Delete by set code
|
|
|
+ | IOGotDeleteSetData String -- Successful deletion by set code
|
|
|
| PortReceiveDoesSetHaveLocalData String
|
|
|
| StartDraft String Database.Database
|
|
|
+ | OpenCardExplorer String Database.Database
|
|
|
|
|
|
|
|
|
startDraft : ChooseSetModel -> Database.Database -> ( Model, Cmd Msg )
|
|
|
@@ -157,6 +178,19 @@ startDraft initFlags database =
|
|
|
( Error { error = "Error decoding draft data: " ++ err }, Cmd.none )
|
|
|
|
|
|
|
|
|
+openCardExplorer : ChooseSetModel -> Database.Database -> ( Model, Cmd Msg )
|
|
|
+openCardExplorer initFlags database =
|
|
|
+ ( CardExplorer
|
|
|
+ { database = database
|
|
|
+ , performanceDistributions = calculatePerformanceDistributions (Database.getAll database)
|
|
|
+ , highlighted = Nothing
|
|
|
+ , flipHighlighted = False
|
|
|
+ , sortOrder = SortOrderDefault
|
|
|
+ }
|
|
|
+ , Cmd.none
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
|
|
update msg model =
|
|
|
case model of
|
|
|
@@ -165,6 +199,9 @@ update msg model =
|
|
|
StartDraft setCode database ->
|
|
|
startDraft mdl database
|
|
|
|
|
|
+ OpenCardExplorer setCode database ->
|
|
|
+ openCardExplorer mdl database
|
|
|
+
|
|
|
IOGotSets (Ok remoteData) ->
|
|
|
-- Merge any new sets into the local sets
|
|
|
let
|
|
|
@@ -226,6 +263,16 @@ update msg model =
|
|
|
, API.getSetData setCode IOGotSetData
|
|
|
)
|
|
|
|
|
|
+ IODeleteSetData setCode ->
|
|
|
+ ( ChooseSet { mdl | sets = Dict.insert setCode DeletingLocalData mdl.sets }
|
|
|
+ , sendDeleteLocalData (Encode.string setCode)
|
|
|
+ )
|
|
|
+
|
|
|
+ IOGotDeleteSetData setCode ->
|
|
|
+ ( ChooseSet { mdl | sets = Dict.insert setCode NoLocalData mdl.sets }
|
|
|
+ , Cmd.none
|
|
|
+ )
|
|
|
+
|
|
|
IOGotSetData (Ok ( setCode, Just database )) ->
|
|
|
( ChooseSet { mdl | sets = Dict.insert setCode (HasLocalData database) mdl.sets }
|
|
|
, sendSaveLocalData (Database.encode ( setCode, database ))
|
|
|
@@ -260,6 +307,9 @@ update msg model =
|
|
|
SetFocusStat stat ->
|
|
|
( Ready { mdl | focusStat = stat }, Cmd.none )
|
|
|
|
|
|
+ SetSortOrder _ ->
|
|
|
+ ( Ready mdl, Cmd.none )
|
|
|
+
|
|
|
SetDeckProgress progress ->
|
|
|
( Ready { mdl | deckProgress = progress }, Cmd.none )
|
|
|
|
|
|
@@ -303,12 +353,38 @@ update msg model =
|
|
|
IOFetchSetData _ ->
|
|
|
( Ready mdl, Cmd.none )
|
|
|
|
|
|
+ IODeleteSetData setCode ->
|
|
|
+ ( Ready mdl, Cmd.none )
|
|
|
+
|
|
|
+ IOGotDeleteSetData setCode ->
|
|
|
+ ( Ready mdl, Cmd.none )
|
|
|
+
|
|
|
PortReceiveDoesSetHaveLocalData _ ->
|
|
|
( Ready mdl, Cmd.none )
|
|
|
|
|
|
StartDraft _ _ ->
|
|
|
( Ready mdl, Cmd.none )
|
|
|
|
|
|
+ OpenCardExplorer _ _ ->
|
|
|
+ ( Ready mdl, Cmd.none )
|
|
|
+
|
|
|
+ CardExplorer mdl ->
|
|
|
+ case msg of
|
|
|
+ Highlight card ->
|
|
|
+ ( CardExplorer
|
|
|
+ { mdl
|
|
|
+ | highlighted = Database.get card mdl.database
|
|
|
+ , flipHighlighted = False
|
|
|
+ }
|
|
|
+ , Cmd.none
|
|
|
+ )
|
|
|
+
|
|
|
+ SetSortOrder sortOrder ->
|
|
|
+ ( CardExplorer { mdl | sortOrder = sortOrder }, Cmd.none )
|
|
|
+
|
|
|
+ _ ->
|
|
|
+ ( CardExplorer mdl, Cmd.none )
|
|
|
+
|
|
|
Error mdl ->
|
|
|
( Error mdl, Cmd.none )
|
|
|
|
|
|
@@ -324,6 +400,9 @@ view model =
|
|
|
Ready mdl ->
|
|
|
viewReady mdl
|
|
|
|
|
|
+ CardExplorer mdl ->
|
|
|
+ viewCardExplorer mdl
|
|
|
+
|
|
|
Error mdl ->
|
|
|
viewError mdl
|
|
|
]
|
|
|
@@ -342,11 +421,18 @@ viewChooseSet model =
|
|
|
CheckingLocalData ->
|
|
|
text "Loading..."
|
|
|
|
|
|
+ DeletingLocalData ->
|
|
|
+ text "Deleting..."
|
|
|
+
|
|
|
NoLocalData ->
|
|
|
Button.make "Download" (IOFetchSetData setCode) |> Button.view
|
|
|
|
|
|
HasLocalData database ->
|
|
|
- Button.make "Open" (StartDraft setCode database) |> Button.view
|
|
|
+ div [ class "flex gap-2" ]
|
|
|
+ [ Button.make "Explore" (OpenCardExplorer setCode database) |> Button.view
|
|
|
+ , Button.make "View draft" (StartDraft setCode database) |> Button.view
|
|
|
+ , Button.make "Delete" (IODeleteSetData setCode) |> Button.view
|
|
|
+ ]
|
|
|
in
|
|
|
div [ class "w-full h-full bg-slate-100 flex justify-center items-center" ]
|
|
|
[ div [ class "max-w-2xl max-h-2xl bg-slate-500 rounded-lg p-6 shadow-xl" ]
|
|
|
@@ -378,6 +464,14 @@ viewReady model =
|
|
|
]
|
|
|
|
|
|
|
|
|
+viewCardExplorer : CardExplorerModel -> Html Msg
|
|
|
+viewCardExplorer model =
|
|
|
+ div [ class "grid grid-cols-12 gap-6 h-full bg-slate-100" ]
|
|
|
+ [ viewAllCards model
|
|
|
+ , viewHighlightedCard model
|
|
|
+ ]
|
|
|
+
|
|
|
+
|
|
|
viewSidebar : ReadyModel -> Html Msg
|
|
|
viewSidebar model =
|
|
|
div [ class "col-span-2 p-6 bg-slate-600" ]
|
|
|
@@ -697,7 +791,79 @@ viewDraft model =
|
|
|
]
|
|
|
|
|
|
|
|
|
-viewHighlightedCard : ReadyModel -> Html Msg
|
|
|
+viewAllCards : CardExplorerModel -> Html Msg
|
|
|
+viewAllCards model =
|
|
|
+ let
|
|
|
+ allCards : List CardData
|
|
|
+ allCards =
|
|
|
+ Database.getAll model.database
|
|
|
+ |> (case model.sortOrder of
|
|
|
+ SortOrderDefault ->
|
|
|
+ List.sortBy
|
|
|
+ (\c ->
|
|
|
+ ( if
|
|
|
+ c.details.cardType
|
|
|
+ /= Land
|
|
|
+ then
|
|
|
+ 0
|
|
|
+
|
|
|
+ else
|
|
|
+ 1
|
|
|
+ , colorsSortOrder
|
|
|
+ c.details.colors
|
|
|
+ , raritySortOrder c.details.rarity
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+ SortOrderRarity ->
|
|
|
+ List.sortBy (\c -> raritySortOrder c.details.rarity)
|
|
|
+
|
|
|
+ SortOrderName ->
|
|
|
+ List.sortBy (\c -> c.details.name)
|
|
|
+ )
|
|
|
+
|
|
|
+ viewCard : CardData -> ( String, Html Msg )
|
|
|
+ viewCard card =
|
|
|
+ ( card.details.name
|
|
|
+ , div
|
|
|
+ [ classList
|
|
|
+ [ ( "relative border-4 hover:border-4 hover:border-yellow-500", True )
|
|
|
+ ]
|
|
|
+ , Events.onMouseEnter (Highlight card.details.name)
|
|
|
+ ]
|
|
|
+ [ img [ src card.details.imageUrl, alt card.details.name ] []
|
|
|
+ ]
|
|
|
+ )
|
|
|
+
|
|
|
+ viewSortOrderButton name sortOrder =
|
|
|
+ button
|
|
|
+ [ onClick (SetSortOrder sortOrder)
|
|
|
+ , classList
|
|
|
+ [ ( "rounded text-white font-medium p-2 shadow mr-2 mb-2", True )
|
|
|
+ , ( "bg-slate-300 text-slate-900 shadow-inset", model.sortOrder == sortOrder )
|
|
|
+ , ( "bg-slate-900", model.sortOrder /= sortOrder )
|
|
|
+ ]
|
|
|
+ ]
|
|
|
+ [ text name
|
|
|
+ ]
|
|
|
+ in
|
|
|
+ div [ class "col-span-10 shadow-xl bg-slate-600 p-6 h-full overflow-y-scroll" ]
|
|
|
+ [ div []
|
|
|
+ [ viewSortOrderButton "Sort: default" SortOrderDefault
|
|
|
+ , viewSortOrderButton "Sort: rarity" SortOrderRarity
|
|
|
+ , viewSortOrderButton "Sort: name" SortOrderName
|
|
|
+ ]
|
|
|
+ , -- Making this a keyed node forces Elm to recreate each card when the card name changes,
|
|
|
+ -- so the old image doesn't hang around during loading of the new card image
|
|
|
+ div [ class "flex flex-col space-between col-span-8 " ]
|
|
|
+ [ Keyed.node "div"
|
|
|
+ [ class "flex-grow grid grid-cols-8 auto-rows-min gap-6 p-6" ]
|
|
|
+ (List.map viewCard allCards)
|
|
|
+ ]
|
|
|
+ ]
|
|
|
+
|
|
|
+
|
|
|
+viewHighlightedCard : { a | highlighted : Maybe CardData, flipHighlighted : Bool, performanceDistributions : CardPerformanceDistributions } -> Html Msg
|
|
|
viewHighlightedCard model =
|
|
|
div [ class "col-span-2 shadow-xl bg-slate-600 p-6" ]
|
|
|
[ case model.highlighted of
|
|
|
@@ -877,7 +1043,10 @@ viewKeyedCard model wasChosen { name, frontImage, backImage } =
|
|
|
|
|
|
subscriptions : Model -> Sub Msg
|
|
|
subscriptions _ =
|
|
|
- receiveDoesSetHaveLocalData PortReceiveDoesSetHaveLocalData
|
|
|
+ Sub.batch
|
|
|
+ [ receiveDoesSetHaveLocalData PortReceiveDoesSetHaveLocalData
|
|
|
+ , receiveDidDeleteLocalData IOGotDeleteSetData
|
|
|
+ ]
|
|
|
|
|
|
|
|
|
|
|
|
@@ -891,3 +1060,9 @@ port receiveDoesSetHaveLocalData : (String -> msg) -> Sub msg
|
|
|
|
|
|
|
|
|
port sendSaveLocalData : Encode.Value -> Cmd msg
|
|
|
+
|
|
|
+
|
|
|
+port sendDeleteLocalData : Encode.Value -> Cmd msg
|
|
|
+
|
|
|
+
|
|
|
+port receiveDidDeleteLocalData : (String -> msg) -> Sub msg
|