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

Configure Feed

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

Grouping

+198 -101
+3
src/Applications/UI.elm
··· 564 564 ShowTracksContextMenu coordinates tracks -> 565 565 return { model | contextMenu = Just (Tracks.trackMenu tracks coordinates) } 566 566 567 + ShowTracksViewMenu coordinates -> 568 + return { model | contextMenu = Just (Tracks.viewMenu coordinates) } 569 + 567 570 ----------------------------------------- 568 571 -- Notifications 569 572 -----------------------------------------
+5 -4
src/Applications/UI/Authentication/ContextMenu.elm
··· 16 16 moreOptionsMenu : Coordinates -> ContextMenu Msg 17 17 moreOptionsMenu = 18 18 ContextMenu 19 - [ ( \_ _ -> Svg.map never UI.Svg.Elements.ipfsLogo 20 - , "IPFS" 21 - , AuthenticationMsg (Authentication.ShowNewEncryptionKeyScreen Authentication.Ipfs) 22 - ) 19 + [ Item 20 + ( \_ _ -> Svg.map never UI.Svg.Elements.ipfsLogo 21 + , "IPFS" 22 + , AuthenticationMsg (Authentication.ShowNewEncryptionKeyScreen Authentication.Ipfs) 23 + ) 23 24 24 25 -- , ( \_ _ -> Svg.map never UI.Svg.Elements.solidLogo 25 26 -- , "Solid"
+9 -1
src/Applications/UI/ContextMenu.elm
··· 51 51 List.length items - 1 52 52 in 53 53 List.indexedMap 54 - (itemView lastIndex) 54 + (\idx item -> 55 + case item of 56 + Item i -> 57 + itemView lastIndex idx i 58 + 59 + Divider -> 60 + -- TODO 61 + nothing 62 + ) 55 63 items 56 64 ) 57 65
+1
src/Applications/UI/Reply.elm
··· 24 24 | ShowMoreAuthenticationOptions Coordinates 25 25 | ShowSourceContextMenu Coordinates Source 26 26 | ShowTracksContextMenu Coordinates (List IdentifiedTrack) 27 + | ShowTracksViewMenu Coordinates 27 28 ----------------------------------------- 28 29 -- Notifications 29 30 -----------------------------------------
+15 -12
src/Applications/UI/Sources/ContextMenu.elm
··· 20 20 sourceMenu : Source -> Coordinates -> ContextMenu Msg 21 21 sourceMenu source = 22 22 ContextMenu 23 - [ ( ifThenElse source.directoryPlaylists Icons.folder Icons.folder_open 24 - , ifThenElse source.directoryPlaylists "Disable Directory Playlists" "Enable Directory Playlists" 25 - , SourcesMsg (Sources.ToggleDirectoryPlaylists { sourceId = source.id }) 26 - ) 27 - , ( Icons.edit 28 - , "Edit source" 29 - , source.id 23 + [ Item 24 + ( ifThenElse source.directoryPlaylists Icons.folder Icons.folder_open 25 + , ifThenElse source.directoryPlaylists "Disable Directory Playlists" "Enable Directory Playlists" 26 + , SourcesMsg (Sources.ToggleDirectoryPlaylists { sourceId = source.id }) 27 + ) 28 + , Item 29 + ( Icons.edit 30 + , "Edit source" 31 + , source.id 30 32 |> UI.Sources.Page.Edit 31 33 |> UI.Page.Sources 32 34 |> ChangeUrlUsingPage 33 - ) 34 - , ( Icons.delete 35 - , "Remove source" 36 - , SourcesMsg (Sources.RemoveFromCollection { sourceId = source.id }) 37 - ) 35 + ) 36 + , Item 37 + ( Icons.delete 38 + , "Remove source" 39 + , SourcesMsg (Sources.RemoveFromCollection { sourceId = source.id }) 40 + ) 38 41 ]
+20 -4
src/Applications/UI/Tracks.elm
··· 7 7 import Common exposing (Switch(..)) 8 8 import Coordinates 9 9 import Css 10 + import Html.Events.Extra.Mouse as Mouse 10 11 import Html.Styled as Html exposing (Html, text) 11 - import Html.Styled.Attributes exposing (css, placeholder, tabindex, title, value) 12 + import Html.Styled.Attributes exposing (css, fromUnstyled, placeholder, tabindex, title, value) 12 13 import Html.Styled.Events exposing (onBlur, onClick, onInput) 13 14 import Html.Styled.Ext exposing (onEnterKey) 14 15 import Html.Styled.Lazy exposing (..) ··· 22 23 import Material.Icons.Content as Icons 23 24 import Material.Icons.Editor as Icons 24 25 import Material.Icons.Image as Icons 26 + import Material.Icons.Navigation as Icons 25 27 import Maybe.Extra as Maybe 26 28 import Return3 as Return exposing (..) 27 29 import Tachyons.Classes as T ··· 51 53 , enabledSourceIds = [] 52 54 , favourites = [] 53 55 , favouritesOnly = False 56 + , grouping = Just AddedOnGroups 54 57 , hideDuplicates = False 55 58 , infiniteList = InfiniteList.init 56 59 , nowPlaying = Nothing 57 - , scene = List 60 + , scene = GroupedList 58 61 , searchResults = Nothing 59 62 , searchTerm = Nothing 60 63 , sortBy = Artist ··· 78 81 Reply replies -> 79 82 returnRepliesWithModel model replies 80 83 81 - ShowContextMenu track mouseEvent -> 84 + ShowTrackMenu track mouseEvent -> 82 85 [ track ] 83 86 |> ShowTracksContextMenu (Coordinates.fromTuple mouseEvent.clientPos) 84 87 |> returnReplyWithModel model 85 88 89 + ShowViewMenu mouseEvent -> 90 + mouseEvent.clientPos 91 + |> Coordinates.fromTuple 92 + |> ShowTracksViewMenu 93 + |> returnReplyWithModel model 94 + 86 95 ScrollToNowPlaying -> 87 96 let 88 97 -- The index identifier might be out-of-date, ··· 273 282 ( { enabledSourceIds = model.enabledSourceIds 274 283 , favourites = model.favourites 275 284 , favouritesOnly = model.favouritesOnly 285 + , grouping = model.grouping 276 286 , hideDuplicates = model.hideDuplicates 277 287 , nowPlaying = model.nowPlaying 278 288 , searchResults = model.searchResults ··· 496 506 ] 497 507 498 508 -- 3 499 - , nothing 509 + , brick 510 + [ css searchActionIconStyle 511 + , fromUnstyled (Mouse.onClick ShowViewMenu) 512 + , title "View settings" 513 + ] 514 + [ T.pointer ] 515 + [ Html.fromUnstyled (Icons.more_vert 16 searchIconColoring) ] 500 516 ] 501 517 ] 502 518 , -----------------------------------------
+24 -10
src/Applications/UI/Tracks/ContextMenu.elm
··· 1 - module UI.Tracks.ContextMenu exposing (trackMenu) 1 + module UI.Tracks.ContextMenu exposing (trackMenu, viewMenu) 2 2 3 3 import ContextMenu exposing (..) 4 4 import Coordinates exposing (Coordinates) 5 5 import Material.Icons.Action as Icons 6 + import Material.Icons.Maps as Icons 6 7 import Tracks exposing (IdentifiedTrack) 7 8 import UI.Core exposing (Msg(..)) 8 9 import UI.Queue.Core as Queue ··· 17 18 ContextMenu (queueActions tracks) 18 19 19 20 21 + viewMenu : Coordinates -> ContextMenu Msg 22 + viewMenu = 23 + ContextMenu 24 + [ Item 25 + ( Icons.terrain 26 + , "Group by added-to-collection date" 27 + , Bypass 28 + ) 29 + ] 30 + 31 + 20 32 21 33 -- ACTIONS 22 34 23 35 24 - queueActions : List IdentifiedTrack -> ContextMenuItems Msg 36 + queueActions : List IdentifiedTrack -> List (ContextMenu.Item Msg) 25 37 queueActions identifiedTracks = 26 - [ ( Icons.event_seat 27 - , "Play next" 28 - , QueueMsg (Queue.InjectFirst { showNotification = True } identifiedTracks) 29 - ) 30 - , ( Icons.event_seat 31 - , "Add to queue" 32 - , QueueMsg (Queue.InjectLast { showNotification = True } identifiedTracks) 33 - ) 38 + [ Item 39 + ( Icons.event_seat 40 + , "Play next" 41 + , QueueMsg (Queue.InjectFirst { showNotification = True } identifiedTracks) 42 + ) 43 + , Item 44 + ( Icons.event_seat 45 + , "Add to queue" 46 + , QueueMsg (Queue.InjectLast { showNotification = True } identifiedTracks) 47 + ) 34 48 ]
+3 -1
src/Applications/UI/Tracks/Core.elm
··· 16 16 , enabledSourceIds : List String 17 17 , favourites : List Favourite 18 18 , favouritesOnly : Bool 19 + , grouping : Maybe Grouping 19 20 , hideDuplicates : Bool 20 21 , infiniteList : InfiniteList.Model 21 22 , nowPlaying : Maybe IdentifiedTrack ··· 38 39 | ScrollToNowPlaying 39 40 | SetEnabledSourceIds (List String) 40 41 | SetNowPlaying (Maybe IdentifiedTrack) 41 - | ShowContextMenu IdentifiedTrack Mouse.Event 42 + | ShowTrackMenu IdentifiedTrack Mouse.Event 43 + | ShowViewMenu Mouse.Event 42 44 | SortBy SortBy 43 45 | ToggleHideDuplicates 44 46 -----------------------------------------
+16 -58
src/Applications/UI/Tracks/Scene/GroupedList.elm
··· 39 39 } 40 40 41 41 42 - type alias GroupedIdentifiedTrack = 43 - { date : ( Int, Time.Month ) 44 - , identifiedTrack : IdentifiedTrack 45 - , isFirst : Bool 46 - } 47 - 48 - 49 42 view : Necessities -> Model -> Html Msg 50 43 view necessities model = 51 44 let 52 45 { infiniteList } = 53 46 model 54 - 55 - groupedTracksDictionary = 56 - List.foldl 57 - (\( i, t ) -> 58 - let 59 - ( year, month ) = 60 - ( Time.toYear Time.utc t.insertedAt 61 - , Time.toMonth Time.utc t.insertedAt 62 - ) 63 - 64 - item = 65 - { date = ( year, month ) 66 - , identifiedTrack = ( i, t ) 67 - , isFirst = False 68 - } 69 - in 70 - Dict.update 71 - (year * 1000 + Time.monthNumber month) 72 - (\maybeList -> 73 - case maybeList of 74 - Just list -> 75 - Just (item :: list) 76 - 77 - Nothing -> 78 - Just [ { item | isFirst = True } ] 79 - ) 80 - ) 81 - Dict.empty 82 - model.collection.harvested 83 - 84 - groupedTracks = 85 - groupedTracksDictionary 86 - |> Dict.values 87 - |> List.reverse 88 - |> List.map List.reverse 89 - |> List.concat 90 47 in 91 48 brick 92 49 [ fromUnstyled (InfiniteList.onScroll InfiniteListMsg) ··· 102 59 (InfiniteList.view 103 60 (infiniteListConfig necessities model) 104 61 infiniteList 105 - groupedTracks 62 + model.collection.harvested 106 63 ) 107 64 ] 108 65 ··· 236 193 -- INFINITE LIST 237 194 238 195 239 - infiniteListConfig : Necessities -> Model -> InfiniteList.Config GroupedIdentifiedTrack Msg 196 + infiniteListConfig : Necessities -> Model -> InfiniteList.Config IdentifiedTrack Msg 240 197 infiniteListConfig necessities model = 241 198 InfiniteList.withCustomContainer 242 199 infiniteListContainer ··· 273 230 [ Css.fontSize (Css.px 12.5) ] 274 231 275 232 276 - itemView : Model -> Int -> Int -> GroupedIdentifiedTrack -> UnstyledHtml.Html Msg 277 - itemView { favouritesOnly } _ idx { date, identifiedTrack, isFirst } = 233 + itemView : Model -> Int -> Int -> IdentifiedTrack -> UnstyledHtml.Html Msg 234 + itemView { favouritesOnly } _ idx ( identifiers, track ) = 278 235 let 279 - ( year, month ) = 280 - date 236 + groupName = 237 + identifiers.group 238 + |> Maybe.map .name 239 + |> Maybe.withDefault "Unknown" 281 240 282 - ( identifiers, track ) = 283 - identifiedTrack 241 + shouldRenderGroup = 242 + identifiers.group 243 + |> Maybe.map (.index >> (==) 0) 244 + |> Maybe.withDefault False 284 245 in 285 246 Html.toUnstyled <| 286 247 chunk 287 248 [] 288 - [ if isFirst then 249 + [ if shouldRenderGroup then 289 250 brick 290 251 [ css groupStyles ] 291 252 [ T.f7 ··· 301 262 [ Html.fromUnstyled (Icons.terrain 16 Inherit) ] 302 263 , inline 303 264 [ T.dib, T.pl2, T.v_mid ] 304 - (if year == 1970 then 265 + (if String.contains "1970" groupName then 305 266 [ text "AGES AGO" ] 306 267 307 268 else 308 - [ text (Time.monthNumber month |> String.fromInt |> String.padLeft 2 '0') 309 - , text " / " 310 - , text (String.fromInt year) 311 - ] 269 + [ text groupName ] 312 270 ) 313 271 ] 314 272 ··· 328 286 -- Context Menu 329 287 --------------- 330 288 , ( identifiers, track ) 331 - |> ShowContextMenu 289 + |> ShowTrackMenu 332 290 |> onWithOptions 333 291 "contextmenu" 334 292 { stopPropagation = True
+1 -1
src/Applications/UI/Tracks/Scene/List.elm
··· 348 348 -- Context Menu 349 349 --------------- 350 350 , ( identifiers, track ) 351 - |> ShowContextMenu 351 + |> ShowTrackMenu 352 352 |> onWithOptions 353 353 "contextmenu" 354 354 { stopPropagation = True
+5 -4
src/Library/ContextMenu.elm
··· 1 - module ContextMenu exposing (ContextMenu(..), ContextMenuItems) 1 + module ContextMenu exposing (ContextMenu(..), Item(..)) 2 2 3 3 import Color exposing (Color) 4 4 import Coordinates exposing (Coordinates) ··· 11 11 12 12 13 13 type ContextMenu msg 14 - = ContextMenu (ContextMenuItems msg) Coordinates 14 + = ContextMenu (List (Item msg)) Coordinates 15 15 16 16 17 - type alias ContextMenuItems msg = 18 - List ( Int -> Coloring -> Svg msg, String, msg ) 17 + type Item msg 18 + = Item ( Int -> Coloring -> Svg msg, String, msg ) 19 + | Divider
+12 -2
src/Library/Tracks.elm
··· 1 - module Tracks exposing (Collection, CollectionDependencies, Favourite, IdentifiedTrack, Identifiers, Parcel, SortBy(..), SortDirection(..), Tags, Track, emptyCollection, emptyIdentifiedTrack, emptyTags, emptyTrack, isNowPlaying, makeTrack, missingId) 1 + module Tracks exposing (Collection, CollectionDependencies, Favourite, Grouping(..), IdentifiedTrack, Identifiers, Parcel, SortBy(..), SortDirection(..), Tags, Track, emptyCollection, emptyIdentifiedTrack, emptyTags, emptyTrack, isNowPlaying, makeTrack, missingId) 2 2 3 3 import Base64 4 4 import String.Ext as String ··· 60 60 , isSelected : Bool 61 61 62 62 -- 63 + , group : Maybe { name : String, index : Int } 63 64 , indexInList : Int 64 65 , indexInPlaylist : Maybe Int 65 66 } ··· 87 88 { enabledSourceIds : List String 88 89 , favourites : List Favourite 89 90 , favouritesOnly : Bool 91 + , grouping : Maybe Grouping 90 92 , hideDuplicates : Bool 91 93 , nowPlaying : Maybe IdentifiedTrack 92 94 , searchResults : Maybe (List String) ··· 100 102 101 103 102 104 103 - -- SORTING 105 + -- GROUPING & SORTING 106 + 107 + 108 + type Grouping 109 + = AddedOnGroups 110 + | TrackYearGroups 104 111 105 112 106 113 type SortBy ··· 148 155 , isMissing = False 149 156 , isNowPlaying = False 150 157 , isSelected = False 158 + 159 + -- 160 + , group = Nothing 151 161 , indexInList = 0 152 162 , indexInPlaylist = Nothing 153 163 }
+84 -4
src/Library/Tracks/Collection/Internal/Arrange.elm
··· 1 1 module Tracks.Collection.Internal.Arrange exposing (arrange) 2 2 3 + import Dict 4 + import Time 5 + import Time.Ext as Time 3 6 import Tracks exposing (..) 4 7 import Tracks.Sorting as Sorting 5 8 ··· 10 13 11 14 arrange : Parcel -> Parcel 12 15 arrange ( deps, collection ) = 13 - collection.identified 14 - |> Sorting.sort deps.sortBy deps.sortDirection 15 - |> (\x -> { collection | arranged = x }) 16 - |> (\x -> ( deps, x )) 16 + case deps.grouping of 17 + Just AddedOnGroups -> 18 + ( deps, groupByInsertedAt deps collection ) 19 + 20 + Just TrackYearGroups -> 21 + -- TODO 22 + collection.identified 23 + |> Sorting.sort deps.sortBy deps.sortDirection 24 + |> (\x -> { collection | arranged = x }) 25 + |> (\x -> ( deps, x )) 26 + 27 + Nothing -> 28 + collection.identified 29 + |> Sorting.sort deps.sortBy deps.sortDirection 30 + |> (\x -> { collection | arranged = x }) 31 + |> (\x -> ( deps, x )) 32 + 33 + 34 + 35 + -- GROUPING 36 + 37 + 38 + groupByInsertedAt : CollectionDependencies -> Collection -> Collection 39 + groupByInsertedAt deps collection = 40 + let 41 + groupedTracksDictionary = 42 + List.foldl 43 + (\( i, t ) -> 44 + let 45 + ( year, month ) = 46 + ( Time.toYear Time.utc t.insertedAt 47 + , Time.toMonth Time.utc t.insertedAt 48 + ) 49 + 50 + item = 51 + ( { i 52 + | group = 53 + Just 54 + { name = insertedAtGroupName year month 55 + , index = 0 56 + } 57 + } 58 + , t 59 + ) 60 + in 61 + Dict.update 62 + (year * 1000 + Time.monthNumber month) 63 + (\maybeList -> 64 + case maybeList of 65 + Just list -> 66 + Just (item :: list) 67 + 68 + Nothing -> 69 + Just [ item ] 70 + ) 71 + ) 72 + Dict.empty 73 + collection.identified 74 + in 75 + groupedTracksDictionary 76 + |> Dict.values 77 + |> List.reverse 78 + |> List.map (Sorting.sort deps.sortBy deps.sortDirection >> List.indexedMap setIndexInGroup) 79 + |> List.concat 80 + |> (\arranged -> { collection | arranged = arranged }) 81 + 82 + 83 + insertedAtGroupName : Int -> Time.Month -> String 84 + insertedAtGroupName year month = 85 + month 86 + |> Time.monthNumber 87 + |> String.fromInt 88 + |> String.padLeft 2 '0' 89 + |> (\m -> m ++ " / " ++ String.fromInt year) 90 + 91 + 92 + setIndexInGroup : Int -> IdentifiedTrack -> IdentifiedTrack 93 + setIndexInGroup idx ( i, t ) = 94 + ( { i | group = Maybe.map (\g -> { g | index = idx }) i.group } 95 + , t 96 + )