1
0

4 Commits 4d2a94be31 ... 8d74d8a701

Autor SHA1 Mensagem Data
  Cadel Watson 8d74d8a701 Add sorting in card explorer há 4 meses atrás
  Cadel Watson 58de406cc8 Support deleting local data há 4 meses atrás
  Cadel Watson a869a6334d Add card sorting há 4 meses atrás
  Cadel Watson a1e4bd2949 Add card explorer há 4 meses atrás
6 ficheiros alterados com 1040 adições e 18 exclusões
  1. 20 0
      js/app.js
  2. 752 13
      package-lock.json
  3. 1 0
      package.json
  4. 64 0
      src/Card.elm
  5. 24 1
      src/Database.elm
  6. 179 4
      src/Main.elm

+ 20 - 0
js/app.js

@@ -20,6 +20,20 @@ function getSetData(db, setCode, callback) {
     };
     };
 }
 }
 
 
+function deleteSetData(db, setCode, callback) {
+    const transaction = db.transaction(["sets"], "readwrite");
+    const objectStore = transaction.objectStore("sets");
+    const request = objectStore.delete(setCode);
+
+    request.onerror = (event) => {
+        alert("Database error")
+    };
+
+    request.onsuccess = (event) => {
+        callback(setCode)
+    }
+}
+
 function saveSetData(db, setData) {
 function saveSetData(db, setData) {
     const transaction = db.transaction(["sets"], "readwrite");
     const transaction = db.transaction(["sets"], "readwrite");
 
 
@@ -67,6 +81,8 @@ openDBRequest.onsuccess = (event) => {
             }
             }
         });
         });
 
 
+        console.log(app.ports);
+
         app.ports.sendDoesSetHaveLocalData.subscribe((setCode) => {
         app.ports.sendDoesSetHaveLocalData.subscribe((setCode) => {
             getSetData(database, setCode, (data) => {
             getSetData(database, setCode, (data) => {
                 app.ports.receiveDoesSetHaveLocalData.send(JSON.stringify(data));
                 app.ports.receiveDoesSetHaveLocalData.send(JSON.stringify(data));
@@ -77,6 +93,10 @@ openDBRequest.onsuccess = (event) => {
             saveSetData(database, setData);
             saveSetData(database, setData);
         })
         })
 
 
+        app.ports.sendDeleteLocalData.subscribe((setCode) => {
+            deleteSetData(database, setCode, (setCode) => app.ports.receiveDidDeleteLocalData.send(setCode))
+        })
+
     })
     })
 };
 };
 
 

Diff do ficheiro suprimidas por serem muito extensas
+ 752 - 13
package-lock.json


+ 1 - 0
package.json

@@ -15,6 +15,7 @@
   },
   },
   "dependencies": {
   "dependencies": {
     "elm-format": "^0.8.7",
     "elm-format": "^0.8.7",
+    "elm-review": "^2.13.3",
     "elm-test": "^0.19.1-revision12"
     "elm-test": "^0.19.1-revision12"
   }
   }
 }
 }

+ 64 - 0
src/Card.elm

@@ -7,15 +7,18 @@ module Card exposing
     , ManaColor(..)
     , ManaColor(..)
     , ManaCost(..)
     , ManaCost(..)
     , Power(..)
     , Power(..)
+    , Rarity(..)
     , alpa
     , alpa
     , alsa
     , alsa
     , calculatePerformanceDistributions
     , calculatePerformanceDistributions
+    , colorsSortOrder
     , gihwr
     , gihwr
     , iwd
     , iwd
     , manaCostToSymbol
     , manaCostToSymbol
     , parseManaCost
     , parseManaCost
     , parsePower
     , parsePower
     , pickRate
     , pickRate
+    , raritySortOrder
     )
     )
 
 
 import Parser as P exposing ((|.), (|=), Parser, Trailing(..))
 import Parser as P exposing ((|.), (|=), Parser, Trailing(..))
@@ -48,6 +51,14 @@ type Power
     | VariablePower
     | VariablePower
 
 
 
 
+type Rarity
+    = Common
+    | Uncommon
+    | Rare
+    | Mythic
+    | NoRarity
+
+
 type alias CardData =
 type alias CardData =
     { details : CardDetails
     { details : CardDetails
     , performance : CardPerformanceData
     , performance : CardPerformanceData
@@ -66,6 +77,7 @@ type alias CardDetails =
     , rawManaCost : String
     , rawManaCost : String
     , manaCost : Maybe (List ManaCost)
     , manaCost : Maybe (List ManaCost)
     , imageUrl : String
     , imageUrl : String
+    , rarity : Rarity
     }
     }
 
 
 
 
@@ -399,3 +411,55 @@ powerP =
         , constantPlusVariablePowerP
         , constantPlusVariablePowerP
         , constantPowerP
         , constantPowerP
         ]
         ]
+
+
+raritySortOrder : Rarity -> Int
+raritySortOrder r =
+    case r of
+        Mythic ->
+            0
+
+        Rare ->
+            1
+
+        Uncommon ->
+            2
+
+        Common ->
+            3
+
+        NoRarity ->
+            4
+
+
+colorsSortOrder : List ManaColor -> Int
+colorsSortOrder cs =
+    case cs of
+        -- Colorless
+        [] ->
+            5
+
+        -- Monocolor
+        [ x ] ->
+            case x of
+                Red ->
+                    0
+
+                Green ->
+                    1
+
+                Blue ->
+                    2
+
+                Black ->
+                    3
+
+                White ->
+                    4
+
+                Colorless ->
+                    5
+
+        -- Multicolor
+        _ ->
+            6

+ 24 - 1
src/Database.elm

@@ -1,6 +1,6 @@
 module Database exposing (Database, decode, decoder, encode, fromCardData, get, getAll)
 module Database exposing (Database, decode, decoder, encode, fromCardData, get, getAll)
 
 
-import Card exposing (CardData, CardDetails, CardPerformanceData, CardType(..), ManaColor(..), Power(..), parseManaCost, parsePower)
+import Card exposing (CardData, CardDetails, CardPerformanceData, CardType(..), ManaColor(..), Power(..), Rarity(..), parseManaCost, parsePower)
 import Dict exposing (Dict)
 import Dict exposing (Dict)
 import Json.Decode as Decode exposing (Decoder, decodeString)
 import Json.Decode as Decode exposing (Decoder, decodeString)
 import Json.Decode.Pipeline exposing (custom, optional, required)
 import Json.Decode.Pipeline exposing (custom, optional, required)
@@ -278,6 +278,7 @@ decodeCardDetails =
                         )
                         )
                 )
                 )
             |> required "image_uris" (Decode.field "large" Decode.string)
             |> required "image_uris" (Decode.field "large" Decode.string)
+            |> optional "rarity" decodeRarity NoRarity
         )
         )
 
 
 
 
@@ -391,3 +392,25 @@ decodeCardType =
                 else
                 else
                     Other
                     Other
             )
             )
+
+
+decodeRarity : Decoder Rarity
+decodeRarity =
+    Decode.string
+        |> Decode.map
+            (\r ->
+                if String.contains "mythic" r then
+                    Mythic
+
+                else if String.contains "rare" r then
+                    Rare
+
+                else if String.contains "uncommon" r then
+                    Uncommon
+
+                else if String.contains "common" r then
+                    Common
+
+                else
+                    NoRarity
+            )

+ 179 - 4
src/Main.elm

@@ -2,7 +2,7 @@ port module Main exposing (..)
 
 
 import API
 import API
 import Browser exposing (Document)
 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 as C
 import Chart.Attributes as CA
 import Chart.Attributes as CA
 import Components.Button as Button
 import Components.Button as Button
@@ -37,6 +37,7 @@ main =
 type Model
 type Model
     = ChooseSet ChooseSetModel
     = ChooseSet ChooseSetModel
     | Ready ReadyModel
     | Ready ReadyModel
+    | CardExplorer CardExplorerModel
     | Error ErrorModel
     | Error ErrorModel
 
 
 
 
@@ -53,6 +54,12 @@ type FocusStat
     | FocusIWD
     | FocusIWD
 
 
 
 
+type SortOrder
+    = SortOrderDefault
+    | SortOrderRarity
+    | SortOrderName
+
+
 type alias ToolboxAccordion =
 type alias ToolboxAccordion =
     { cmc : Bool, signals : Bool, signalsDelta : Bool, deckList : Bool }
     { cmc : Bool, signals : Bool, signalsDelta : Bool, deckList : Bool }
 
 
@@ -62,6 +69,7 @@ type SetLoadStatus
     | NoLocalData
     | NoLocalData
     | HasLocalData Database.Database
     | HasLocalData Database.Database
     | FetchingRemoteData
     | FetchingRemoteData
+    | DeletingLocalData
 
 
 
 
 type alias ChooseSetModel =
 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 =
 type alias ErrorModel =
     { error : String
     { error : String
     }
     }
@@ -117,6 +134,7 @@ type Msg
     | Highlight String
     | Highlight String
     | FlipHighlightedCard
     | FlipHighlightedCard
     | SetFocusStat FocusStat
     | SetFocusStat FocusStat
+    | SetSortOrder SortOrder
     | SetDeckProgress DeckProgress
     | SetDeckProgress DeckProgress
     | ToggleCMCChart
     | ToggleCMCChart
     | ToggleSignalsChart
     | ToggleSignalsChart
@@ -126,8 +144,11 @@ type Msg
     | IOGotSets (Result String (List String))
     | IOGotSets (Result String (List String))
     | IOFetchSetData String
     | IOFetchSetData String
     | IOGotSetData (Result String ( String, Maybe Database.Database ))
     | IOGotSetData (Result String ( String, Maybe Database.Database ))
+    | IODeleteSetData String -- Delete by set code
+    | IOGotDeleteSetData String -- Successful deletion by set code
     | PortReceiveDoesSetHaveLocalData String
     | PortReceiveDoesSetHaveLocalData String
     | StartDraft String Database.Database
     | StartDraft String Database.Database
+    | OpenCardExplorer String Database.Database
 
 
 
 
 startDraft : ChooseSetModel -> Database.Database -> ( Model, Cmd Msg )
 startDraft : ChooseSetModel -> Database.Database -> ( Model, Cmd Msg )
@@ -157,6 +178,19 @@ startDraft initFlags database =
             ( Error { error = "Error decoding draft data: " ++ err }, Cmd.none )
             ( 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 -> ( Model, Cmd Msg )
 update msg model =
 update msg model =
     case model of
     case model of
@@ -165,6 +199,9 @@ update msg model =
                 StartDraft setCode database ->
                 StartDraft setCode database ->
                     startDraft mdl database
                     startDraft mdl database
 
 
+                OpenCardExplorer setCode database ->
+                    openCardExplorer mdl database
+
                 IOGotSets (Ok remoteData) ->
                 IOGotSets (Ok remoteData) ->
                     -- Merge any new sets into the local sets
                     -- Merge any new sets into the local sets
                     let
                     let
@@ -226,6 +263,16 @@ update msg model =
                     , API.getSetData setCode IOGotSetData
                     , 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 )) ->
                 IOGotSetData (Ok ( setCode, Just database )) ->
                     ( ChooseSet { mdl | sets = Dict.insert setCode (HasLocalData database) mdl.sets }
                     ( ChooseSet { mdl | sets = Dict.insert setCode (HasLocalData database) mdl.sets }
                     , sendSaveLocalData (Database.encode ( setCode, database ))
                     , sendSaveLocalData (Database.encode ( setCode, database ))
@@ -260,6 +307,9 @@ update msg model =
                 SetFocusStat stat ->
                 SetFocusStat stat ->
                     ( Ready { mdl | focusStat = stat }, Cmd.none )
                     ( Ready { mdl | focusStat = stat }, Cmd.none )
 
 
+                SetSortOrder _ ->
+                    ( Ready mdl, Cmd.none )
+
                 SetDeckProgress progress ->
                 SetDeckProgress progress ->
                     ( Ready { mdl | deckProgress = progress }, Cmd.none )
                     ( Ready { mdl | deckProgress = progress }, Cmd.none )
 
 
@@ -303,12 +353,38 @@ update msg model =
                 IOFetchSetData _ ->
                 IOFetchSetData _ ->
                     ( Ready mdl, Cmd.none )
                     ( Ready mdl, Cmd.none )
 
 
+                IODeleteSetData setCode ->
+                    ( Ready mdl, Cmd.none )
+
+                IOGotDeleteSetData setCode ->
+                    ( Ready mdl, Cmd.none )
+
                 PortReceiveDoesSetHaveLocalData _ ->
                 PortReceiveDoesSetHaveLocalData _ ->
                     ( Ready mdl, Cmd.none )
                     ( Ready mdl, Cmd.none )
 
 
                 StartDraft _ _ ->
                 StartDraft _ _ ->
                     ( Ready mdl, Cmd.none )
                     ( 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 ->
             ( Error mdl, Cmd.none )
             ( Error mdl, Cmd.none )
 
 
@@ -324,6 +400,9 @@ view model =
             Ready mdl ->
             Ready mdl ->
                 viewReady mdl
                 viewReady mdl
 
 
+            CardExplorer mdl ->
+                viewCardExplorer mdl
+
             Error mdl ->
             Error mdl ->
                 viewError mdl
                 viewError mdl
         ]
         ]
@@ -342,11 +421,18 @@ viewChooseSet model =
                 CheckingLocalData ->
                 CheckingLocalData ->
                     text "Loading..."
                     text "Loading..."
 
 
+                DeletingLocalData ->
+                    text "Deleting..."
+
                 NoLocalData ->
                 NoLocalData ->
                     Button.make "Download" (IOFetchSetData setCode) |> Button.view
                     Button.make "Download" (IOFetchSetData setCode) |> Button.view
 
 
                 HasLocalData database ->
                 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
     in
     div [ class "w-full h-full bg-slate-100 flex justify-center items-center" ]
     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" ]
         [ 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 : ReadyModel -> Html Msg
 viewSidebar model =
 viewSidebar model =
     div [ class "col-span-2 p-6 bg-slate-600" ]
     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 =
 viewHighlightedCard model =
     div [ class "col-span-2 shadow-xl bg-slate-600 p-6" ]
     div [ class "col-span-2 shadow-xl bg-slate-600 p-6" ]
         [ case model.highlighted of
         [ case model.highlighted of
@@ -877,7 +1043,10 @@ viewKeyedCard model wasChosen { name, frontImage, backImage } =
 
 
 subscriptions : Model -> Sub Msg
 subscriptions : Model -> Sub Msg
 subscriptions _ =
 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 sendSaveLocalData : Encode.Value -> Cmd msg
+
+
+port sendDeleteLocalData : Encode.Value -> Cmd msg
+
+
+port receiveDidDeleteLocalData : (String -> msg) -> Sub msg

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff