diff --git a/src/Applications/UI/Tracks.elm b/src/Applications/UI/Tracks.elm index 35fa9392..7528dbcf 100644 --- a/src/Applications/UI/Tracks.elm +++ b/src/Applications/UI/Tracks.elm @@ -19,8 +19,7 @@ import Material.Icons.Content as Icons import Material.Icons.Editor as Icons import Maybe.Extra as Maybe import Replying exposing (R3D3) -import Return2 -import Return3 +import Return3 as R3 import Tachyons.Classes as T import Tracks exposing (..) import Tracks.Collection exposing (..) @@ -62,20 +61,36 @@ update : Msg -> Model -> R3D3 Model Msg Reply update msg model = case msg of Bypass -> - Return3.withNothing model + R3.withNothing model Play identifiedTrack -> ( model, Cmd.none, Just [ PlayTrack identifiedTrack ] ) InfiniteListMsg infiniteList -> - Return3.withNothing { model | infiniteList = infiniteList } + R3.withNothing { model | infiniteList = infiniteList } SetEnabledSourceIds sourceIds -> - Return3.withNothing { model | enabledSourceIds = sourceIds } + R3.withNothing { model | enabledSourceIds = sourceIds } SetNowPlaying maybeIdentifiedTrack -> reviseCollection harvest { model | nowPlaying = maybeIdentifiedTrack } + SortBy property -> + let + sortDir = + if model.sortBy /= property then + Asc + + else if model.sortDirection == Asc then + Desc + + else + Asc + in + { model | sortBy = property, sortDirection = sortDir } + |> reviseCollection arrange + |> R3.mapReply (Maybe.map <| (::) SaveEnclosedUserData) + ----------------------------------------- -- Collection ----------------------------------------- @@ -83,16 +98,13 @@ update msg model = -- > Add tracks to the collection. -- Add json -> - let - tracks = - json - |> Json.decodeValue (Json.list Encoding.trackDecoder) - |> Result.withDefault [] - in - model - |> makeParcel - |> add tracks - |> resolveParcel model + reviseCollection + (json + |> Json.decodeValue (Json.list Encoding.trackDecoder) + |> Result.withDefault [] + |> add + ) + model -- # Remove -- > Remove tracks from the collection. @@ -110,16 +122,14 @@ update msg model = |> Json.decodeValue decoder |> Result.withDefault ( [], missingId ) in - model - |> makeParcel - |> removeByPaths sourceId paths - |> resolveParcel model + reviseCollection + (removeByPaths sourceId paths) + model RemoveBySourceId sourceId -> - model - |> makeParcel - |> removeBySourceId sourceId - |> resolveParcel model + reviseCollection + (removeBySourceId sourceId) + model ----------------------------------------- -- Favourites @@ -149,17 +159,19 @@ update msg model = reviseCollection harvest { model | searchResults = Nothing } ( Nothing, Nothing ) -> - Return3.withNothing model + R3.withNothing model SetSearchResults json -> - json - |> Json.decodeValue (Json.list Json.string) - |> Result.withDefault [] - |> (\results -> { model | searchResults = Just results }) - |> reviseCollection harvest + reviseCollection + harvest + (json + |> Json.decodeValue (Json.list Json.string) + |> Result.withDefault [] + |> (\results -> { model | searchResults = Just results }) + ) SetSearchTerm term -> - Return3.withNothing + R3.withNothing (case String.trim term of "" -> { model | searchTerm = Nothing } diff --git a/src/Applications/UI/Tracks/Core.elm b/src/Applications/UI/Tracks/Core.elm index 01df1e39..b6e903af 100644 --- a/src/Applications/UI/Tracks/Core.elm +++ b/src/Applications/UI/Tracks/Core.elm @@ -34,6 +34,7 @@ type Msg | Play IdentifiedTrack | SetEnabledSourceIds (List String) | SetNowPlaying (Maybe IdentifiedTrack) + | SortBy SortBy ----------------------------------------- -- Collection ----------------------------------------- diff --git a/src/Applications/UI/Tracks/Scene/List.elm b/src/Applications/UI/Tracks/Scene/List.elm index 79e1c25c..a8b7e075 100644 --- a/src/Applications/UI/Tracks/Scene/List.elm +++ b/src/Applications/UI/Tracks/Scene/List.elm @@ -9,7 +9,7 @@ import Html as UnstyledHtml import Html.Attributes as UnstyledHtmlAttributes import Html.Styled as Html exposing (Html, text) import Html.Styled.Attributes exposing (css, fromUnstyled) -import Html.Styled.Events exposing (onDoubleClick) +import Html.Styled.Events exposing (onClick, onDoubleClick) import Html.Styled.Lazy import InfiniteList import Material.Icons.Navigation as Icons @@ -80,10 +80,10 @@ header sortBy sortDirection = brick [ css headerStyles ] [ T.bg_white, T.flex, T.fw6, T.relative, T.z_5 ] - [ headerColumn "" 4.5 First Nothing - , headerColumn "Title" 37.5 Between (maybeSortIcon Title) - , headerColumn "Artist" 29.0 Between (maybeSortIcon Artist) - , headerColumn "Album" 29.0 Last (maybeSortIcon Album) + [ headerColumn "" 4.5 First Nothing Bypass + , headerColumn "Title" 37.5 Between (maybeSortIcon Title) (SortBy Title) + , headerColumn "Artist" 29.0 Between (maybeSortIcon Artist) (SortBy Artist) + , headerColumn "Album" 29.0 Last (maybeSortIcon Album) (SortBy Album) ] @@ -115,8 +115,9 @@ headerColumn : -> Float -> Pos -> Maybe (Html msg) + -> msg -> Html msg -headerColumn text_ width pos maybeSortIcon = +headerColumn text_ width pos maybeSortIcon msg = let paddingLeft = ifThenElse (pos == First) T.pl2 T.pl1 @@ -125,7 +126,8 @@ headerColumn text_ width pos maybeSortIcon = ifThenElse (pos == Last) T.pr2 T.pr1 in brick - [ css + [ onClick msg + , css [ Css.borderLeft3 (Css.px <| ifThenElse (pos /= First) 1 0) Css.solid diff --git a/src/Applications/UI/UserData.elm b/src/Applications/UI/UserData.elm index 06413b6e..1abd8d51 100644 --- a/src/Applications/UI/UserData.elm +++ b/src/Applications/UI/UserData.elm @@ -107,13 +107,15 @@ exportEnclosed model = { backgroundImage = model.backdrop.chosen , repeat = model.queue.repeat , shuffle = model.queue.shuffle + , sortBy = model.tracks.sortBy + , sortDirection = model.tracks.sortDirection } importEnclosed : Json.Value -> UI.Core.Model -> R3D3 UI.Core.Model UI.Core.Msg UI.Reply importEnclosed value model = let - { backdrop, queue } = + { backdrop, queue, tracks } = model in case decodeEnclosed value of @@ -124,11 +126,15 @@ importEnclosed value model = newQueue = { queue | repeat = data.repeat, shuffle = data.shuffle } + + newTracks = + { tracks | sortBy = data.sortBy, sortDirection = data.sortDirection } in R3.withNothing { model | backdrop = newBackDrop , queue = newQueue + , tracks = newTracks } Err err -> diff --git a/src/Library/Authentication.elm b/src/Library/Authentication.elm index 075790d3..be99dbe6 100644 --- a/src/Library/Authentication.elm +++ b/src/Library/Authentication.elm @@ -24,6 +24,8 @@ type alias EnclosedUserData = { backgroundImage : Maybe String , repeat : Bool , shuffle : Bool + , sortBy : Tracks.SortBy + , sortDirection : Tracks.SortDirection } @@ -35,15 +37,17 @@ type alias HypaethralUserData = --- 🔱 +-- 🔱 ░░ METHOD -emptyHypaethralUserData : HypaethralUserData -emptyHypaethralUserData = - { favourites = [] - , sources = [] - , tracks = [] - } +decodeMethod : Json.Value -> Maybe Method +decodeMethod = + Json.decodeValue (Json.map methodFromString Json.string) >> Result.toMaybe >> Maybe.join + + +encodeMethod : Method -> Json.Value +encodeMethod = + methodToString >> Json.Encode.string methodToString : Method -> String @@ -70,7 +74,7 @@ methodFromString string = --- 🔱 ░░ DECODING & ENCODING +-- 🔱 ░░ ENCLOSED decodeEnclosed : Json.Value -> Result Json.Error EnclosedUserData @@ -78,33 +82,44 @@ decodeEnclosed = Json.decodeValue enclosedDecoder -decodeHypaethral : Json.Value -> Result Json.Error HypaethralUserData -decodeHypaethral = - Json.decodeValue hypaethralDecoder - - -decodeMethod : Json.Value -> Maybe Method -decodeMethod = - Json.decodeValue (Json.map methodFromString Json.string) >> Result.toMaybe >> Maybe.join - - enclosedDecoder : Json.Decoder EnclosedUserData enclosedDecoder = Json.succeed EnclosedUserData |> required "backgroundImage" (Json.maybe Json.string) |> optional "repeat" Json.bool False |> optional "shuffle" Json.bool False + |> optional "sortBy" Tracks.sortByDecoder Tracks.Artist + |> optional "sortDirection" Tracks.sortDirectionDecoder Tracks.Asc encodeEnclosed : EnclosedUserData -> Json.Value -encodeEnclosed { backgroundImage, repeat, shuffle } = +encodeEnclosed { backgroundImage, repeat, shuffle, sortBy, sortDirection } = Json.Encode.object [ ( "backgroundImage", Json.Encode.string (Maybe.withDefault "" backgroundImage) ) , ( "repeat", Json.Encode.bool repeat ) , ( "shuffle", Json.Encode.bool shuffle ) + , ( "sortBy", Tracks.encodeSortBy sortBy ) + , ( "sortDirection", Tracks.encodeSortDirection sortDirection ) ] + +-- 🔱 ░░ HYPAETHRAL + + +decodeHypaethral : Json.Value -> Result Json.Error HypaethralUserData +decodeHypaethral = + Json.decodeValue hypaethralDecoder + + +emptyHypaethralUserData : HypaethralUserData +emptyHypaethralUserData = + { favourites = [] + , sources = [] + , tracks = [] + } + + encodeHypaethral : HypaethralUserData -> Json.Value encodeHypaethral { favourites, sources, tracks } = Json.Encode.object @@ -114,11 +129,6 @@ encodeHypaethral { favourites, sources, tracks } = ] -encodeMethod : Method -> Json.Value -encodeMethod = - methodToString >> Json.Encode.string - - hypaethralDecoder : Json.Decoder HypaethralUserData hypaethralDecoder = Json.succeed HypaethralUserData diff --git a/src/Library/Tracks/Encoding.elm b/src/Library/Tracks/Encoding.elm index cfc216eb..0bbb7e4a 100644 --- a/src/Library/Tracks/Encoding.elm +++ b/src/Library/Tracks/Encoding.elm @@ -1,4 +1,4 @@ -module Tracks.Encoding exposing (decodeFavourite, decodeTrack, encodeFavourite, encodeMaybe, encodeTags, encodeTrack, favouriteDecoder, tagsDecoder, trackDecoder) +module Tracks.Encoding exposing (decodeFavourite, decodeTrack, encodeFavourite, encodeMaybe, encodeSortBy, encodeSortDirection, encodeTags, encodeTrack, favouriteDecoder, sortByDecoder, sortDirectionDecoder, tagsDecoder, trackDecoder) import Json.Decode as Decode import Json.Encode as Encode @@ -9,6 +9,40 @@ import Tracks exposing (..) -- ENCODE +encodeFavourite : Favourite -> Encode.Value +encodeFavourite fav = + Encode.object + [ ( "artist", Encode.string fav.artist ) + , ( "title", Encode.string fav.title ) + ] + + +encodeSortBy : SortBy -> Encode.Value +encodeSortBy v = + case v of + Artist -> + Encode.string "ARTIST" + + Album -> + Encode.string "ALBUM" + + PlaylistIndex -> + Encode.string "PLAYLIST_INDEX" + + Title -> + Encode.string "TITLE" + + +encodeSortDirection : SortDirection -> Encode.Value +encodeSortDirection v = + case v of + Asc -> + Encode.string "ASC" + + Desc -> + Encode.string "DESC" + + encodeTrack : Track -> Encode.Value encodeTrack track = Encode.object @@ -19,14 +53,6 @@ encodeTrack track = ] -encodeFavourite : Favourite -> Encode.Value -encodeFavourite fav = - Encode.object - [ ( "artist", Encode.string fav.artist ) - , ( "title", Encode.string fav.title ) - ] - - encodeTags : Tags -> Encode.Value encodeTags tags = Encode.object @@ -56,25 +82,63 @@ encodeMaybe maybe encoder = -- DECODE +decodeFavourite : Decode.Value -> Maybe Favourite +decodeFavourite = + Decode.decodeValue favouriteDecoder + >> Result.toMaybe + + decodeTrack : Decode.Value -> Maybe Track decodeTrack = Decode.decodeValue trackDecoder >> Result.toMaybe -decodeFavourite : Decode.Value -> Maybe Favourite -decodeFavourite = - Decode.decodeValue favouriteDecoder - >> Result.toMaybe +favouriteDecoder : Decode.Decoder Favourite +favouriteDecoder = + Decode.map2 Favourite + (Decode.field "artist" Decode.string) + (Decode.field "title" Decode.string) -trackDecoder : Decode.Decoder Track -trackDecoder = - Decode.map4 Track - (Decode.field "id" Decode.string) - (Decode.field "path" Decode.string) - (Decode.field "sourceId" Decode.string) - (Decode.field "tags" tagsDecoder) +sortByDecoder : Decode.Decoder SortBy +sortByDecoder = + Decode.andThen + (\string -> + case string of + "ARTIST" -> + Decode.succeed Artist + + "ALBUM" -> + Decode.succeed Album + + "PLAYLIST_INDEX" -> + Decode.succeed PlaylistIndex + + "TITLE" -> + Decode.succeed Title + + _ -> + Decode.fail "Invalid SortBy" + ) + Decode.string + + +sortDirectionDecoder : Decode.Decoder SortDirection +sortDirectionDecoder = + Decode.andThen + (\string -> + case string of + "ASC" -> + Decode.succeed Asc + + "DESC" -> + Decode.succeed Desc + + _ -> + Decode.fail "Invalid SortDirection" + ) + Decode.string tagsDecoder : Decode.Decoder Tags @@ -90,8 +154,10 @@ tagsDecoder = (Decode.maybe <| Decode.field "year" Decode.int) -favouriteDecoder : Decode.Decoder Favourite -favouriteDecoder = - Decode.map2 Favourite - (Decode.field "artist" Decode.string) - (Decode.field "title" Decode.string) +trackDecoder : Decode.Decoder Track +trackDecoder = + Decode.map4 Track + (Decode.field "id" Decode.string) + (Decode.field "path" Decode.string) + (Decode.field "sourceId" Decode.string) + (Decode.field "tags" tagsDecoder)