A music player that connects to your cloud/distributed storage.
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Implement directory playlists

+502 -33
+58 -3
src/Applications/UI.elm
··· 53 53 import UI.Navigation as Navigation 54 54 import UI.Notifications 55 55 import UI.Page as Page 56 + import UI.Playlists as Playlists 57 + import UI.Playlists.Directory 56 58 import UI.Ports as Ports 57 59 import UI.Queue as Queue 58 60 import UI.Queue.Common ··· 123 125 , authentication = Authentication.initialModel url 124 126 , backdrop = Backdrop.initialModel 125 127 , equalizer = Equalizer.initialModel 128 + , playlists = Playlists.initialModel 126 129 , queue = Queue.initialModel 127 130 , sources = Sources.initialModel 128 131 , tracks = Tracks.initialModel ··· 319 322 , msg = sub 320 323 } 321 324 325 + PlaylistsMsg sub -> 326 + Return3.wieldNested 327 + translateReply 328 + { mapCmd = PlaylistsMsg 329 + , mapModel = \child -> { model | playlists = child } 330 + , update = Playlists.update 331 + } 332 + { model = model.playlists 333 + , msg = sub 334 + } 335 + 322 336 QueueMsg sub -> 323 337 Return3.wieldNested 324 338 translateReply ··· 586 600 UI.Notifications.show (Notifications.stickyWarning string) model 587 601 588 602 ----------------------------------------- 603 + -- Playlists 604 + ----------------------------------------- 605 + ActivatePlaylist playlist -> 606 + update (TracksMsg <| Tracks.SelectPlaylist playlist) model 607 + 608 + DeactivatePlaylist -> 609 + update (TracksMsg <| Tracks.DeselectPlaylist) model 610 + 611 + GenerateDirectoryPlaylists -> 612 + let 613 + nonDirectoryPlaylists = 614 + List.filter 615 + (.autoGenerated >> (==) False) 616 + model.playlists.collection 617 + 618 + directoryPlaylists = 619 + UI.Playlists.Directory.generate 620 + model.sources.collection 621 + model.tracks.collection.untouched 622 + 623 + playlists = 624 + model.playlists 625 + in 626 + [ nonDirectoryPlaylists 627 + , directoryPlaylists 628 + ] 629 + |> List.concat 630 + |> (\c -> { playlists | collection = c }) 631 + |> (\p -> { model | playlists = p }) 632 + |> return 633 + 634 + ----------------------------------------- 589 635 -- Queue 590 636 ----------------------------------------- 591 637 ActiveQueueItemChanged maybeQueueItem -> ··· 931 977 932 978 ( False, _ ) -> 933 979 model.authentication 934 - |> Authentication.view 980 + |> Lazy.lazy Authentication.view 935 981 |> Html.map AuthenticationMsg 936 982 |> List.singleton 937 983 |> content ··· 961 1007 -------- 962 1008 , case model.page of 963 1009 Page.Equalizer -> 964 - Html.map EqualizerMsg (Equalizer.view model.equalizer) 1010 + model.equalizer 1011 + |> Lazy.lazy Equalizer.view 1012 + |> Html.map EqualizerMsg 965 1013 966 1014 Page.Index -> 967 1015 nothing 968 1016 1017 + Page.Playlists subPage -> 1018 + model.playlists 1019 + |> Lazy.lazy3 Playlists.view subPage model.tracks.selectedPlaylist 1020 + |> Html.map PlaylistsMsg 1021 + 969 1022 Page.Queue subPage -> 970 - Html.map QueueMsg (Queue.view subPage model.queue) 1023 + model.queue 1024 + |> Lazy.lazy2 Queue.view subPage 1025 + |> Html.map QueueMsg 971 1026 972 1027 Page.Settings subPage -> 973 1028 Settings.view subPage model
+3
src/Applications/UI/Core.elm
··· 17 17 import UI.Backdrop as Backdrop 18 18 import UI.Equalizer as Equalizer 19 19 import UI.Page exposing (Page) 20 + import UI.Playlists as Playlists 20 21 import UI.Queue.Core as Queue 21 22 import UI.Sources as Sources 22 23 import UI.Tracks.Core as Tracks ··· 64 65 , backdrop : Backdrop.Model 65 66 , equalizer : Equalizer.Model 66 67 , queue : Queue.Model 68 + , playlists : Playlists.Model 67 69 , sources : Sources.Model 68 70 , tracks : Tracks.Model 69 71 } ··· 113 115 | AuthenticationMsg Authentication.Msg 114 116 | BackdropMsg Backdrop.Msg 115 117 | EqualizerMsg Equalizer.Msg 118 + | PlaylistsMsg Playlists.Msg 116 119 | QueueMsg Queue.Msg 117 120 | SourcesMsg Sources.Msg 118 121 | TracksMsg Tracks.Msg
+1 -1
src/Applications/UI/Kit.elm
··· 1 - module UI.Kit exposing (ButtonType(..), button, buttonFocus, buttonLink, buttonWithColor, buttonWithOptions, canister, centeredContent, checkbox, colorKit, colors, defaultFontFamilies, defaultFontStyles, h1, h2, h3, headerFontFamilies, inlineIcon, inputFocus, insulationWidth, intro, label, link, logoBackdrop, navFocus, receptacle, select, textArea, textButton, textField, textFieldAlt, textFocus, vessel) 1 + module UI.Kit exposing (ButtonType(..), button, buttonFocus, buttonLink, buttonWithColor, buttonWithOptions, canister, centeredContent, checkbox, colorKit, colors, defaultFontFamilies, defaultFontStyles, h1, h2, h3, headerFontFamilies, headerFontStyles, inlineIcon, inputFocus, insulationWidth, intro, label, link, logoBackdrop, navFocus, receptacle, select, textArea, textButton, textField, textFieldAlt, textFocus, vessel) 2 2 3 3 import Chunky exposing (..) 4 4 import Color
+22 -5
src/Applications/UI/List.elm
··· 6 6 import Color.Ext as Color 7 7 import Conditional exposing (..) 8 8 import Css exposing (px, solid) 9 - import Html.Events.Extra.Mouse as Mouse exposing (onClick) 9 + import Html.Events.Extra.Mouse as Mouse exposing (onWithOptions) 10 10 import Html.Styled as Html exposing (Html, fromUnstyled) 11 11 import Html.Styled.Attributes as Attributes exposing (css, style, title) 12 + import Html.Styled.Events exposing (onClick) 12 13 import Material.Icons exposing (Coloring(..)) 13 14 import Material.Icons.Action as Icons 14 15 import Maybe.Extra as Maybe ··· 33 34 type alias Item msg = 34 35 { label : Html msg 35 36 , actions : List (Action msg) 37 + , msg : Maybe msg 36 38 } 37 39 38 40 ··· 57 59 58 60 59 61 item : Variant Int msg -> Int -> Item msg -> Html msg 60 - item variant idx { label, actions } = 62 + item variant idx { label, actions, msg } = 61 63 let 62 64 dragHandleColoring = 63 65 actions ··· 68 70 brick 69 71 (case variant of 70 72 Normal -> 71 - [ css (itemStyles { dragTarget = False }) ] 73 + List.append 74 + [ css (itemStyles { dragTarget = False }) ] 75 + (case msg of 76 + Just m -> 77 + [ onClick m ] 78 + 79 + Nothing -> 80 + [] 81 + ) 72 82 73 83 Draggable env -> 74 84 List.concat ··· 81 91 , T.fw6 82 92 , T.items_center 83 93 , T.pv3 94 + 95 + -- 96 + , ifThenElse (Maybe.isJust msg) T.pointer "" 84 97 ] 85 98 [ -- Label 86 99 -------- ··· 110 123 brick 111 124 (case action.msg of 112 125 Just msg -> 113 - [ Attributes.fromUnstyled (onClick msg) 114 - , title action.title 126 + [ title action.title 127 + 128 + -- 129 + , msg 130 + |> onWithOptions "click" { stopPropagation = True, preventDefault = True } 131 + |> Attributes.fromUnstyled 115 132 ] 116 133 117 134 Nothing ->
+16
src/Applications/UI/Page.elm
··· 1 1 module UI.Page exposing (Page(..), fromUrl, sameBase, sources, toString) 2 2 3 3 import Sources exposing (Service(..)) 4 + import UI.Playlists.Page as Playlists 4 5 import UI.Queue.Page as Queue 5 6 import UI.Settings.Page as Settings 6 7 import UI.Sources.Page as Sources ··· 17 18 = Equalizer 18 19 | Index 19 20 | Queue Queue.Page 21 + | Playlists Playlists.Page 20 22 | Settings Settings.Page 21 23 | Sources Sources.Page 22 24 ··· 43 45 44 46 Index -> 45 47 "/" 48 + 49 + ----------------------------------------- 50 + -- Playlists 51 + ----------------------------------------- 52 + Playlists Playlists.Index -> 53 + "/playlists" 46 54 47 55 ----------------------------------------- 48 56 -- Queue ··· 86 94 sameBase : Page -> Page -> Bool 87 95 sameBase a b = 88 96 case ( a, b ) of 97 + ( Playlists _, Playlists _ ) -> 98 + True 99 + 89 100 ( Queue _, Queue _ ) -> 90 101 True 91 102 ··· 130 141 131 142 -- 132 143 , map Equalizer (s "equalizer") 144 + 145 + ----------------------------------------- 146 + -- Playlists 147 + ----------------------------------------- 148 + , map (Playlists Playlists.Index) (s "playlists") 133 149 134 150 ----------------------------------------- 135 151 -- Queue
+173
src/Applications/UI/Playlists.elm
··· 1 + module UI.Playlists exposing (Model, Msg(..), initialModel, update, view) 2 + 3 + import Chunky exposing (..) 4 + import Color 5 + import Color.Ext as Color 6 + import Css 7 + import Html.Styled exposing (Html, text) 8 + import Html.Styled.Attributes exposing (css, style) 9 + import Material.Icons exposing (Coloring(..)) 10 + import Material.Icons.Content as Icons 11 + import Material.Icons.File as Icons 12 + import Material.Icons.Navigation as Icons 13 + import Playlists exposing (..) 14 + import Return3 exposing (..) 15 + import Tachyons.Classes as T 16 + import UI.Kit 17 + import UI.List 18 + import UI.Navigation exposing (..) 19 + import UI.Page 20 + import UI.Playlists.Page exposing (Page(..)) 21 + import UI.Reply exposing (Reply(..)) 22 + 23 + 24 + 25 + -- 🌳 26 + 27 + 28 + type alias Model = 29 + { collection : List Playlist } 30 + 31 + 32 + initialModel : Model 33 + initialModel = 34 + { collection = [] } 35 + 36 + 37 + 38 + -- 📣 39 + 40 + 41 + type Msg 42 + = Activate Playlist 43 + | Bypass 44 + | Deactivate 45 + 46 + 47 + update : Msg -> Model -> Return Model Msg Reply 48 + update msg model = 49 + case msg of 50 + Activate playlist -> 51 + returnRepliesWithModel 52 + model 53 + [ ActivatePlaylist playlist 54 + , GoToPage UI.Page.Index 55 + ] 56 + 57 + Bypass -> 58 + return model 59 + 60 + Deactivate -> 61 + returnReplyWithModel 62 + model 63 + DeactivatePlaylist 64 + 65 + 66 + 67 + -- 🗺 68 + 69 + 70 + view : Page -> Maybe Playlist -> Model -> Html Msg 71 + view page selectedPlaylist model = 72 + UI.Kit.receptacle 73 + (case page of 74 + Index -> 75 + index selectedPlaylist model 76 + ) 77 + 78 + 79 + 80 + -- INDEX 81 + 82 + 83 + index : Maybe Playlist -> Model -> List (Html Msg) 84 + index selectedPlaylist model = 85 + let 86 + selectedPlaylistName = 87 + Maybe.map .name selectedPlaylist 88 + in 89 + [ ----------------------------------------- 90 + -- Navigation 91 + ----------------------------------------- 92 + UI.Navigation.local 93 + [ ( Icon Icons.arrow_back 94 + , Label "Back to list" Hidden 95 + , NavigateToPage UI.Page.Index 96 + ) 97 + , ( Icon Icons.add 98 + , Label "Create a new playlist" Shown 99 + -- TODO 100 + , PerformMsg Bypass 101 + ) 102 + ] 103 + 104 + ----------------------------------------- 105 + -- Content 106 + ----------------------------------------- 107 + , UI.Kit.canister 108 + [ UI.Kit.h1 "Playlists" 109 + 110 + -- Intro 111 + -------- 112 + , intro 113 + 114 + -- Directory Playlists 115 + ---------------------- 116 + , category "Autogenerated Directory Playlists" 117 + , model.collection 118 + |> List.filter .autoGenerated 119 + |> List.map 120 + (\p -> 121 + if selectedPlaylistName == Just p.name then 122 + { label = 123 + brick 124 + [ style "color" (Color.toCssString UI.Kit.colorKit.accent) ] 125 + [] 126 + [ text p.name ] 127 + , actions = 128 + [ { color = Color UI.Kit.colorKit.accent 129 + , icon = Icons.check 130 + , msg = Nothing 131 + , title = "Selected playlist" 132 + } 133 + ] 134 + , msg = Just Deactivate 135 + } 136 + 137 + else 138 + { label = text p.name 139 + , actions = [] 140 + , msg = Just (Activate p) 141 + } 142 + ) 143 + |> UI.List.view UI.List.Normal 144 + ] 145 + ] 146 + 147 + 148 + intro : Html Msg 149 + intro = 150 + [ text "Playlists are not tied to the sources of its tracks." 151 + , lineBreak 152 + , text "Same goes for favourites." 153 + ] 154 + |> raw 155 + |> UI.Kit.intro 156 + 157 + 158 + category : String -> Html Msg 159 + category cat = 160 + brick 161 + [ css categoryStyles ] 162 + [ T.f7, T.mb3, T.mt4, T.truncate, T.ttu ] 163 + [ UI.Kit.inlineIcon Icons.folder 164 + , inline [ T.fw7, T.ml2 ] [ text cat ] 165 + ] 166 + 167 + 168 + categoryStyles : List Css.Style 169 + categoryStyles = 170 + [ Css.color (Color.toElmCssColor UI.Kit.colorKit.base06) 171 + , Css.fontFamilies UI.Kit.headerFontFamilies 172 + , Css.fontSize (Css.px 11) 173 + ]
+85
src/Applications/UI/Playlists/Directory.elm
··· 1 + module UI.Playlists.Directory exposing (generate) 2 + 3 + import Dict exposing (Dict) 4 + import List.Extra as List 5 + import Playlists exposing (..) 6 + import Set exposing (Set) 7 + import Sources exposing (Source) 8 + import String.Ext as String 9 + import Tracks exposing (Track) 10 + 11 + 12 + 13 + -- 🔱 14 + 15 + 16 + generate : List Source -> List Track -> List Playlist 17 + generate sources tracks = 18 + let 19 + sourceDirectories = 20 + List.foldl 21 + (\s -> 22 + if s.directoryPlaylists then 23 + s.data 24 + |> Dict.get "directoryPath" 25 + |> Maybe.map fixPath 26 + |> Maybe.withDefault "" 27 + |> Dict.insert s.id 28 + 29 + else 30 + identity 31 + ) 32 + Dict.empty 33 + sources 34 + 35 + playlistNames = 36 + List.foldr 37 + (reducer sourceDirectories) 38 + Set.empty 39 + tracks 40 + in 41 + playlistNames 42 + |> Set.toList 43 + |> List.map 44 + (\n -> 45 + { autoGenerated = True 46 + , name = n 47 + , tracks = [] 48 + } 49 + ) 50 + 51 + 52 + fixPath : String -> String 53 + fixPath string = 54 + string 55 + |> String.chopStart "/" 56 + |> (\s -> 57 + if String.isEmpty s || String.endsWith "/" s then 58 + s 59 + 60 + else 61 + s ++ "/" 62 + ) 63 + 64 + 65 + 66 + -- ㊙️ 67 + 68 + 69 + reducer : Dict String String -> Track -> Set String -> Set String 70 + reducer sourceDirectories track = 71 + case Dict.get track.sourceId sourceDirectories of 72 + Just prefix -> 73 + let 74 + path = 75 + String.dropLeft (String.length prefix) track.path 76 + in 77 + case String.split "/" path of 78 + a :: _ :: _ -> 79 + Set.insert a 80 + 81 + _ -> 82 + identity 83 + 84 + Nothing -> 85 + identity
+7
src/Applications/UI/Playlists/Page.elm
··· 1 + module UI.Playlists.Page exposing (Page(..)) 2 + 3 + -- 🌳 4 + 5 + 6 + type Page 7 + = Index
+2
src/Applications/UI/Queue.elm
··· 489 489 , title = ifThenElse item.manualEntry "Remove" "Ignore" 490 490 } 491 491 ] 492 + , msg = Nothing 492 493 } 493 494 494 495 ··· 567 568 , text (track.tags.artist ++ " - " ++ track.tags.title) 568 569 ] 569 570 , actions = [] 571 + , msg = Nothing 570 572 }
+7
src/Applications/UI/Reply.elm
··· 3 3 import Authentication 4 4 import Common exposing (Switch(..)) 5 5 import Coordinates exposing (Coordinates) 6 + import Playlists exposing (Playlist) 6 7 import Queue 7 8 import Sources exposing (Source) 8 9 import Tracks exposing (IdentifiedTrack) ··· 33 34 | ShowErrorNotificationWithCode String String 34 35 | ShowSuccessNotification String 35 36 | ShowWarningNotification String 37 + ----------------------------------------- 38 + -- Playlists 39 + ----------------------------------------- 40 + | ActivatePlaylist Playlist 41 + | DeactivatePlaylist 42 + | GenerateDirectoryPlaylists 36 43 ----------------------------------------- 37 44 -- Queue 38 45 -----------------------------------------
+1
src/Applications/UI/Sources.elm
··· 308 308 (\source -> 309 309 { label = Html.text (Dict.fetch "name" "" source.data) 310 310 , actions = sourceActions model.processingError source 311 + , msg = Nothing 311 312 } 312 313 ) 313 314 |> UI.List.view UI.List.Normal
+34 -18
src/Applications/UI/Tracks.elm
··· 35 35 import UI.Kit 36 36 import UI.Navigation exposing (..) 37 37 import UI.Page exposing (Page) 38 + import UI.Playlists.Page 38 39 import UI.Ports 39 40 import UI.Queue.Page 40 41 import UI.Reply exposing (Reply(..)) ··· 59 60 , scene = List 60 61 , searchResults = Nothing 61 62 , searchTerm = Nothing 63 + , selectedPlaylist = Nothing 62 64 , sortBy = Artist 63 65 , sortDirection = Asc 64 66 } ··· 237 239 |> returnReplyWithModel model 238 240 239 241 ----------------------------------------- 242 + -- Playlists 243 + ----------------------------------------- 244 + DeselectPlaylist -> 245 + reviseCollection arrange 246 + { model | selectedPlaylist = Nothing } 247 + 248 + SelectPlaylist playlist -> 249 + reviseCollection arrange 250 + { model | selectedPlaylist = Just playlist } 251 + 252 + ----------------------------------------- 240 253 -- Search 241 254 ----------------------------------------- 242 255 ClearSearch -> ··· 297 310 , hideDuplicates = model.hideDuplicates 298 311 , nowPlaying = model.nowPlaying 299 312 , searchResults = model.searchResults 313 + , selectedPlaylist = model.selectedPlaylist 300 314 , sortBy = model.sortBy 301 315 , sortDirection = model.sortDirection 302 316 } ··· 310 324 modelWithNewCollection = 311 325 { model | collection = newCollection } 312 326 313 - oldHarvest = 314 - List.map (Tuple.second >> .id) model.collection.harvested 327 + collectionChanged = 328 + Collection.tracksChanged 329 + model.collection.untouched 330 + newCollection.untouched 315 331 316 - newHarvest = 317 - List.map (Tuple.second >> .id) newCollection.harvested 332 + harvestChanged = 333 + if collectionChanged then 334 + True 335 + 336 + else 337 + Collection.harvestChanged 338 + model.collection.harvested 339 + newCollection.harvested 318 340 in 319 341 ( modelWithNewCollection 320 342 ---------- 321 343 -- Command 322 344 ---------- 323 - , if oldHarvest /= newHarvest then 345 + , if harvestChanged then 324 346 case model.scene of 325 347 List -> 326 348 UI.Tracks.Scene.List.scrollToTop ··· 330 352 -------- 331 353 -- Reply 332 354 -------- 333 - , Maybe.values 334 - [ if model.collection.untouched /= newCollection.untouched then 335 - Just SaveTracks 336 - 337 - else 338 - Nothing 355 + , if collectionChanged then 356 + [ GenerateDirectoryPlaylists, ResetQueue, SaveTracks ] 339 357 340 - -- 341 - , if oldHarvest /= newHarvest then 342 - Just ResetQueue 358 + else if harvestChanged then 359 + [ ResetQueue ] 343 360 344 - else 345 - Nothing 346 - ] 361 + else 362 + [] 347 363 ) 348 364 349 365 ··· 528 544 tabindex_ 529 545 [ ( Icon Icons.waves 530 546 , Label "Playlists" Hidden 531 - , PerformMsg Bypass 547 + , NavigateToPage (UI.Page.Playlists UI.Playlists.Page.Index) 532 548 ) 533 549 , ( Icon Icons.event_seat 534 550 , Label "Queue" Hidden
+7
src/Applications/UI/Tracks/Core.elm
··· 3 3 import Html.Events.Extra.Mouse as Mouse 4 4 import InfiniteList 5 5 import Json.Encode as Json 6 + import Playlists exposing (Playlist) 6 7 import Tracks exposing (..) 7 8 import UI.Reply exposing (Reply) 8 9 ··· 23 24 , scene : Scene 24 25 , searchResults : Maybe (List String) 25 26 , searchTerm : Maybe String 27 + , selectedPlaylist : Maybe Playlist 26 28 , sortBy : SortBy 27 29 , sortDirection : SortDirection 28 30 } ··· 62 64 ----------------------------------------- 63 65 | ShowTrackMenu IdentifiedTrack Mouse.Event 64 66 | ShowViewMenu (Maybe Grouping) Mouse.Event 67 + ----------------------------------------- 68 + -- Playlists 69 + ----------------------------------------- 70 + | DeselectPlaylist 71 + | SelectPlaylist Playlist 65 72 ----------------------------------------- 66 73 -- Search 67 74 -----------------------------------------
+1
src/Applications/UI/UserData.elm
··· 125 125 |> Tracks.identify 126 126 |> Tracks.resolveParcel adjustedModel 127 127 |> andThen (Tracks.update Tracks.Search) 128 + |> addReply UI.GenerateDirectoryPlaylists 128 129 |> addReplyIfNecessary 129 130 130 131
+4
src/Library/Dict/Ext.elm
··· 3 3 import Dict exposing (Dict) 4 4 5 5 6 + 7 + -- 🔱 8 + 9 + 6 10 unionFlipped : Dict comparable v -> Dict comparable v -> Dict comparable v 7 11 unionFlipped a b = 8 12 Dict.union b a
+2
src/Library/String/Ext.elm
··· 1 1 module String.Ext exposing (chopEnd, chopStart) 2 2 3 + -- 🔱 4 + 3 5 4 6 chopEnd : String -> String -> String 5 7 chopEnd needle str =
+2
src/Library/Tracks.elm
··· 1 1 module Tracks exposing (Collection, CollectionDependencies, Favourite, Grouping(..), IdentifiedTrack, Identifiers, Parcel, SortBy(..), SortDirection(..), Tags, Track, emptyCollection, emptyIdentifiedTrack, emptyIdentifiers, emptyTags, emptyTrack, isNowPlaying, makeTrack, missingId) 2 2 3 3 import Base64 4 + import Playlists exposing (Playlist) 4 5 import String.Ext as String 5 6 import Time 6 7 import Time.Ext as Time ··· 91 92 , grouping : Maybe Grouping 92 93 , hideDuplicates : Bool 93 94 , nowPlaying : Maybe IdentifiedTrack 95 + , selectedPlaylist : Maybe Playlist 94 96 , searchResults : Maybe (List String) 95 97 , sortBy : SortBy 96 98 , sortDirection : SortDirection
+39 -1
src/Library/Tracks/Collection.elm
··· 1 - module Tracks.Collection exposing (add, arrange, harvest, identify, map, removeByPaths, removeBySourceId) 1 + module Tracks.Collection exposing (add, arrange, harvest, harvestChanged, identify, map, removeByPaths, removeBySourceId, tracksChanged) 2 2 3 3 import List.Extra as List 4 4 import Tracks exposing (..) ··· 74 74 ( deps 75 75 , { emptyCollection | untouched = List.filter (.sourceId >> (/=) sourceId) untouched } 76 76 ) 77 + 78 + 79 + 80 + -- ⚗️ 81 + 82 + 83 + tracksChanged : List Track -> List Track -> Bool 84 + tracksChanged listA listB = 85 + case ( listA, listB ) of 86 + ( [], [] ) -> 87 + False 88 + 89 + ( a :: restA, b :: restB ) -> 90 + if a.id /= b.id then 91 + True 92 + 93 + else 94 + tracksChanged restA restB 95 + 96 + _ -> 97 + True 98 + 99 + 100 + harvestChanged : List IdentifiedTrack -> List IdentifiedTrack -> Bool 101 + harvestChanged listA listB = 102 + case ( listA, listB ) of 103 + ( [], [] ) -> 104 + False 105 + 106 + ( ( _, a ) :: restA, ( _, b ) :: restB ) -> 107 + if a.id /= b.id then 108 + True 109 + 110 + else 111 + harvestChanged restA restB 112 + 113 + _ -> 114 + True
+17 -4
src/Library/Tracks/Collection/Internal/Arrange.elm
··· 103 103 104 104 105 105 groupByDirectory : CollectionDependencies -> Collection -> Collection 106 - groupByDirectory = 107 - groupBy { reversed = False } groupByDirectoryFolder 106 + groupByDirectory deps = 107 + groupBy { reversed = False } (groupByDirectoryFolder deps) deps 108 108 109 109 110 - groupByDirectoryFolder : IdentifiedTrack -> Dict String (List IdentifiedTrack) -> Dict String (List IdentifiedTrack) 111 - groupByDirectoryFolder ( i, t ) = 110 + groupByDirectoryFolder : CollectionDependencies -> IdentifiedTrack -> Dict String (List IdentifiedTrack) -> Dict String (List IdentifiedTrack) 111 + groupByDirectoryFolder deps ( i, t ) = 112 112 -- TODO: 113 113 -- When directory playlists are added, and if one is active, 114 114 -- remove the first (ie. root) directory. 115 115 let 116 + prefix = 117 + case deps.selectedPlaylist of 118 + Just playlist -> 119 + if playlist.autoGenerated then 120 + playlist.name ++ "/" 121 + 122 + else 123 + "" 124 + 125 + _ -> 126 + "" 127 + 116 128 directory = 117 129 t.path 130 + |> String.dropLeft (String.length prefix) 118 131 |> String.split "/" 119 132 |> List.init 120 133 |> Maybe.map (String.join " / ")
+21 -1
src/Library/Tracks/Collection/Internal/Harvest.elm
··· 34 34 35 35 else 36 36 Tuple.first >> .isMissing >> (==) False 37 + 38 + -- Playlist 39 + ----------- 40 + , case deps.selectedPlaylist of 41 + Just playlist -> 42 + case playlist.autoGenerated of 43 + True -> 44 + \( _, t ) -> 45 + t.path 46 + |> String.split "/" 47 + |> List.head 48 + |> Maybe.withDefault "" 49 + |> (==) playlist.name 50 + 51 + False -> 52 + \( i, _ ) -> 53 + Maybe.isJust i.indexInPlaylist 54 + 55 + Nothing -> 56 + always True 37 57 ] 38 58 39 59 theFilter x = 40 60 List.foldl 41 61 (\filter bool -> 42 - if bool == True then 62 + if True == bool then 43 63 filter x 44 64 45 65 else