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.

Merge GroupedList and List scenes

+142 -520
+1
src/Applications/UI/ContextMenu.elm
··· 77 77 [ onClick msg ] 78 78 [ T.bb 79 79 , T.pa3 80 + , T.pr4 80 81 , T.pointer 81 82 , T.truncate 82 83
+2 -12
src/Applications/UI/Tracks.elm
··· 39 39 import UI.Queue.Page 40 40 import UI.Reply exposing (Reply(..)) 41 41 import UI.Tracks.Core exposing (..) 42 - import UI.Tracks.Scene.GroupedList 43 42 import UI.Tracks.Scene.List 44 43 45 44 ··· 53 52 , enabledSourceIds = [] 54 53 , favourites = [] 55 54 , favouritesOnly = False 56 - , grouping = Just AddedOnGroups 55 + , grouping = Nothing 57 56 , hideDuplicates = False 58 57 , infiniteList = InfiniteList.init 59 58 , nowPlaying = Nothing 60 - , scene = GroupedList 59 + , scene = List 61 60 , searchResults = Nothing 62 61 , searchTerm = Nothing 63 62 , sortBy = Artist ··· 115 114 ) 116 115 |> Maybe.map 117 116 (case model.scene of 118 - GroupedList -> 119 - UI.Tracks.Scene.GroupedList.scrollToNowPlaying 120 - 121 117 List -> 122 118 UI.Tracks.Scene.List.scrollToNowPlaying 123 119 ) ··· 310 306 ---------- 311 307 , if oldHarvest /= newHarvest then 312 308 case model.scene of 313 - GroupedList -> 314 - UI.Tracks.Scene.GroupedList.scrollToTop 315 - 316 309 List -> 317 310 UI.Tracks.Scene.List.scrollToTop 318 311 ··· 397 390 398 391 else 399 392 case core.tracks.scene of 400 - GroupedList -> 401 - UI.Tracks.Scene.GroupedList.view { height = core.viewport.height } core.tracks 402 - 403 393 List -> 404 394 UI.Tracks.Scene.List.view { height = core.viewport.height } core.tracks 405 395 ]
+1 -2
src/Applications/UI/Tracks/Core.elm
··· 68 68 69 69 70 70 type Scene 71 - = GroupedList 72 - | List 71 + = List
-417
src/Applications/UI/Tracks/Scene/GroupedList.elm
··· 1 - module UI.Tracks.Scene.GroupedList exposing (scrollToNowPlaying, scrollToTop, view) 2 - 3 - import Browser.Dom as Dom 4 - import Chunky exposing (..) 5 - import Classes as C 6 - import Color 7 - import Color.Ext as Color 8 - import Conditional exposing (ifThenElse) 9 - import Css 10 - import Dict 11 - import Html as UnstyledHtml 12 - import Html.Attributes as UnstyledHtmlAttributes 13 - import Html.Events.Extra.Mouse exposing (onWithOptions) 14 - import Html.Styled as Html exposing (Html, text) 15 - import Html.Styled.Attributes exposing (css, fromUnstyled, id) 16 - import Html.Styled.Events exposing (onClick, onDoubleClick) 17 - import Html.Styled.Lazy 18 - import InfiniteList 19 - import List.Extra as List 20 - import Material.Icons exposing (Coloring(..)) 21 - import Material.Icons.Maps as Icons 22 - import Material.Icons.Navigation as Icons 23 - import Tachyons.Classes as T 24 - import Task 25 - import Time 26 - import Time.Ext as Time 27 - import Tracks exposing (..) 28 - import UI.Kit 29 - import UI.Reply 30 - import UI.Tracks.Core exposing (..) 31 - 32 - 33 - 34 - -- 🗺 35 - 36 - 37 - type alias Necessities = 38 - { height : Float 39 - } 40 - 41 - 42 - view : Necessities -> Model -> Html Msg 43 - view necessities model = 44 - let 45 - { infiniteList } = 46 - model 47 - in 48 - brick 49 - [ fromUnstyled (InfiniteList.onScroll InfiniteListMsg) 50 - , id containerId 51 - ] 52 - [ T.flex_grow_1 53 - , T.vh_25 54 - , T.overflow_x_hidden 55 - , T.overflow_y_scroll 56 - ] 57 - [ Html.Styled.Lazy.lazy2 header model.sortBy model.sortDirection 58 - , Html.fromUnstyled 59 - (InfiniteList.view 60 - (infiniteListConfig necessities model) 61 - infiniteList 62 - model.collection.harvested 63 - ) 64 - ] 65 - 66 - 67 - containerId : String 68 - containerId = 69 - "diffuse__track-list" 70 - 71 - 72 - scrollToNowPlaying : IdentifiedTrack -> Cmd Msg 73 - scrollToNowPlaying ( identifiers, track ) = 74 - (22 - rowHeight / 2 + 5 + toFloat identifiers.indexInList * rowHeight) 75 - |> Dom.setViewportOf containerId 0 76 - |> Task.attempt (always Bypass) 77 - 78 - 79 - scrollToTop : Cmd Msg 80 - scrollToTop = 81 - Task.attempt (always Bypass) (Dom.setViewportOf containerId 0 0) 82 - 83 - 84 - 85 - -- HEADER 86 - 87 - 88 - header : SortBy -> SortDirection -> Html Msg 89 - header sortBy sortDirection = 90 - let 91 - sortIcon = 92 - (if sortDirection == Desc then 93 - Icons.expand_less 94 - 95 - else 96 - Icons.expand_more 97 - ) 98 - 15 99 - Inherit 100 - 101 - sortIconHtml = 102 - Html.fromUnstyled sortIcon 103 - 104 - maybeSortIcon s = 105 - ifThenElse (sortBy == s) (Just sortIconHtml) Nothing 106 - in 107 - brick 108 - [ css headerStyles ] 109 - [ T.bg_white, T.flex, T.fw6, T.relative, T.z_5 ] 110 - [ headerColumn "" 4.5 First Nothing Bypass 111 - , headerColumn "Title" 37.5 Between (maybeSortIcon Title) (SortBy Title) 112 - , headerColumn "Artist" 29.0 Between (maybeSortIcon Artist) (SortBy Artist) 113 - , headerColumn "Album" 29.0 Last (maybeSortIcon Album) (SortBy Album) 114 - ] 115 - 116 - 117 - headerStyles : List Css.Style 118 - headerStyles = 119 - [ Css.borderBottom3 (Css.px 1) Css.solid (Color.toElmCssColor UI.Kit.colors.subtleBorder) 120 - , Css.color (Color.toElmCssColor headerTextColor) 121 - , Css.fontSize (Css.px 11) 122 - ] 123 - 124 - 125 - headerTextColor : Color.Color 126 - headerTextColor = 127 - Color.rgb255 207 207 207 128 - 129 - 130 - 131 - -- HEADER COLUMN 132 - 133 - 134 - type Pos 135 - = First 136 - | Between 137 - | Last 138 - 139 - 140 - headerColumn : 141 - String 142 - -> Float 143 - -> Pos 144 - -> Maybe (Html msg) 145 - -> msg 146 - -> Html msg 147 - headerColumn text_ width pos maybeSortIcon msg = 148 - brick 149 - [ onClick msg 150 - , css 151 - [ Css.borderLeft3 152 - (Css.px <| ifThenElse (pos /= First) 1 0) 153 - Css.solid 154 - (Color.toElmCssColor UI.Kit.colors.subtleBorder) 155 - , Css.width (Css.pct width) 156 - ] 157 - ] 158 - [ T.lh_title 159 - , T.pv1 160 - , T.relative 161 - 162 - -- 163 - , ifThenElse (pos == First) T.pl3 T.pl2 164 - , ifThenElse (pos == Last) T.pr3 T.pr2 165 - , ifThenElse (pos == First) "" T.pointer 166 - ] 167 - [ brick 168 - [ css [ Css.top (Css.px 1) ] ] 169 - [ T.relative ] 170 - [ text text_ ] 171 - , case maybeSortIcon of 172 - Just sortIcon -> 173 - brick 174 - [ css sortIconStyles ] 175 - [ T.absolute, T.mr1, T.right_0 ] 176 - [ sortIcon ] 177 - 178 - Nothing -> 179 - nothing 180 - ] 181 - 182 - 183 - sortIconStyles : List Css.Style 184 - sortIconStyles = 185 - [ Css.fontSize (Css.px 0) 186 - , Css.lineHeight (Css.px 0) 187 - , Css.top (Css.pct 50) 188 - , Css.transform (Css.translateY <| Css.pct -50) 189 - ] 190 - 191 - 192 - 193 - -- INFINITE LIST 194 - 195 - 196 - infiniteListConfig : Necessities -> Model -> InfiniteList.Config IdentifiedTrack Msg 197 - infiniteListConfig necessities model = 198 - InfiniteList.withCustomContainer 199 - infiniteListContainer 200 - (InfiniteList.config 201 - { itemView = itemView model 202 - , itemHeight = InfiniteList.withConstantHeight (round rowHeight) 203 - , containerHeight = round necessities.height 204 - } 205 - ) 206 - 207 - 208 - infiniteListContainer : 209 - List ( String, String ) 210 - -> List (UnstyledHtml.Html msg) 211 - -> UnstyledHtml.Html msg 212 - infiniteListContainer styles children = 213 - UnstyledHtml.div 214 - (List.map (\( k, v ) -> UnstyledHtmlAttributes.style k v) styles) 215 - [ (Html.toUnstyled << rawy) <| 216 - brick 217 - [ css listStyles ] 218 - [ T.f6 219 - , T.list 220 - , T.ma0 221 - , T.ph0 222 - , T.pv1 223 - ] 224 - (List.map Html.fromUnstyled children) 225 - ] 226 - 227 - 228 - listStyles : List Css.Style 229 - listStyles = 230 - [ Css.fontSize (Css.px 12.5) ] 231 - 232 - 233 - itemView : Model -> Int -> Int -> IdentifiedTrack -> UnstyledHtml.Html Msg 234 - itemView { favouritesOnly } _ idx ( identifiers, track ) = 235 - let 236 - groupName = 237 - identifiers.group 238 - |> Maybe.map .name 239 - |> Maybe.withDefault "Unknown" 240 - 241 - shouldRenderGroup = 242 - identifiers.group 243 - |> Maybe.map (.index >> (==) 0) 244 - |> Maybe.withDefault False 245 - in 246 - Html.toUnstyled <| 247 - chunk 248 - [] 249 - [ if shouldRenderGroup then 250 - brick 251 - [ css groupStyles ] 252 - [ T.f7 253 - , T.fw6 254 - , T.lh_copy 255 - , T.mb3 256 - , T.mh3 257 - , T.mt4 258 - , T.tracked 259 - ] 260 - [ inline 261 - [ T.dib, T.v_mid, C.lh_0 ] 262 - [ Html.fromUnstyled (Icons.terrain 16 Inherit) ] 263 - , inline 264 - [ T.dib, T.pl2, T.v_mid ] 265 - (if String.contains "1970" groupName then 266 - [ text "AGES AGO" ] 267 - 268 - else 269 - [ text groupName ] 270 - ) 271 - ] 272 - 273 - else 274 - nothing 275 - 276 - -- 277 - , brick 278 - [ css (rowStyles idx identifiers) 279 - 280 - -- Play 281 - ------- 282 - , [ UI.Reply.PlayTrack ( identifiers, track ) ] 283 - |> Reply 284 - |> onDoubleClick 285 - 286 - -- Context Menu 287 - --------------- 288 - , ( identifiers, track ) 289 - |> ShowTrackMenu 290 - |> onWithOptions 291 - "contextmenu" 292 - { stopPropagation = True 293 - , preventDefault = True 294 - } 295 - |> Html.Styled.Attributes.fromUnstyled 296 - ] 297 - [ T.flex 298 - , T.items_center 299 - 300 - -- 301 - , ifThenElse identifiers.isMissing "" T.pointer 302 - , ifThenElse identifiers.isSelected T.fw6 "" 303 - ] 304 - [ favouriteColumn favouritesOnly identifiers 305 - , otherColumn 37.5 False track.tags.title 306 - , otherColumn 29.0 False track.tags.artist 307 - , otherColumn 29.0 True track.tags.album 308 - ] 309 - ] 310 - 311 - 312 - groupStyles : List Css.Style 313 - groupStyles = 314 - [ Css.color (Color.toElmCssColor UI.Kit.colorKit.base04) 315 - , Css.fontFamilies UI.Kit.headerFontFamilies 316 - , Css.fontSize (Css.px 10.5) 317 - ] 318 - 319 - 320 - 321 - -- ROWS 322 - 323 - 324 - rowHeight : Float 325 - rowHeight = 326 - 35 327 - 328 - 329 - rowStyles : Int -> Identifiers -> List Css.Style 330 - rowStyles idx { isMissing, isNowPlaying } = 331 - let 332 - bgColor = 333 - if isNowPlaying then 334 - Color.toElmCssColor UI.Kit.colorKit.base0D 335 - 336 - else if modBy 2 idx == 1 then 337 - Css.rgb 252 252 252 338 - 339 - else 340 - Css.rgb 255 255 255 341 - 342 - color = 343 - if isNowPlaying then 344 - Css.rgb 255 255 255 345 - 346 - else if isMissing then 347 - Color.toElmCssColor UI.Kit.colorKit.base04 348 - 349 - else 350 - Color.toElmCssColor UI.Kit.colors.text 351 - in 352 - [ Css.backgroundColor bgColor 353 - , Css.color color 354 - , Css.height (Css.px rowHeight) 355 - ] 356 - 357 - 358 - 359 - -- COLUMNS 360 - 361 - 362 - favouriteColumn : Bool -> Identifiers -> Html Msg 363 - favouriteColumn favouritesOnly identifiers = 364 - brick 365 - [ css (favouriteColumnStyles favouritesOnly identifiers) 366 - , onClick (ToggleFavourite identifiers.indexInList) 367 - ] 368 - [ T.pl3 ] 369 - [ if identifiers.isFavourite then 370 - text "t" 371 - 372 - else 373 - text "f" 374 - ] 375 - 376 - 377 - favouriteColumnStyles : Bool -> Identifiers -> List Css.Style 378 - favouriteColumnStyles favouritesOnly { isFavourite, isNowPlaying, isSelected } = 379 - let 380 - color = 381 - if isSelected then 382 - Color.toElmCssColor UI.Kit.colors.selection 383 - 384 - else if isNowPlaying && isFavourite then 385 - Css.rgb 255 255 255 386 - 387 - else if isNowPlaying then 388 - Css.rgba 255 255 255 0.4 389 - 390 - else if favouritesOnly || not isFavourite then 391 - Css.rgb 222 222 222 392 - 393 - else 394 - Color.toElmCssColor UI.Kit.colorKit.base08 395 - in 396 - [ Css.color color 397 - , Css.fontFamilies [ "or-favourites" ] 398 - , Css.width (Css.pct 4.5) 399 - ] 400 - 401 - 402 - otherColumn : Float -> Bool -> String -> Html msg 403 - otherColumn width isLast text_ = 404 - brick 405 - [ css (otherColumnStyles width) ] 406 - [ T.pl2 407 - , T.truncate 408 - 409 - -- 410 - , ifThenElse isLast T.pr3 T.pr2 411 - ] 412 - [ text text_ ] 413 - 414 - 415 - otherColumnStyles : Float -> List Css.Style 416 - otherColumnStyles columnWidth = 417 - [ Css.width (Css.pct columnWidth) ]
+138 -89
src/Applications/UI/Tracks/Scene/List.elm
··· 2 2 3 3 import Browser.Dom as Dom 4 4 import Chunky exposing (..) 5 + import Classes as C 5 6 import Color 6 7 import Color.Ext as Color 7 8 import Conditional exposing (ifThenElse) ··· 15 16 import Html.Styled.Lazy 16 17 import InfiniteList 17 18 import Material.Icons exposing (Coloring(..)) 19 + import Material.Icons.Maps as Icons 18 20 import Material.Icons.Navigation as Icons 19 21 import Tachyons.Classes as T 20 22 import Task ··· 184 186 185 187 186 188 189 + -- INFINITE LIST 190 + 191 + 192 + infiniteListConfig : Necessities -> Model -> InfiniteList.Config IdentifiedTrack Msg 193 + infiniteListConfig necessities model = 194 + InfiniteList.withCustomContainer 195 + infiniteListContainer 196 + (InfiniteList.config 197 + { itemView = itemView model 198 + , itemHeight = InfiniteList.withConstantHeight (round rowHeight) 199 + , containerHeight = round necessities.height 200 + } 201 + ) 202 + 203 + 204 + infiniteListContainer : 205 + List ( String, String ) 206 + -> List (UnstyledHtml.Html msg) 207 + -> UnstyledHtml.Html msg 208 + infiniteListContainer styles children = 209 + UnstyledHtml.div 210 + (List.map (\( k, v ) -> UnstyledHtmlAttributes.style k v) styles) 211 + [ (Html.toUnstyled << rawy) <| 212 + brick 213 + [ css listStyles ] 214 + [ T.f6 215 + , T.pv1 216 + ] 217 + (List.map Html.fromUnstyled children) 218 + ] 219 + 220 + 221 + listStyles : List Css.Style 222 + listStyles = 223 + [ Css.fontSize (Css.px 12.5) 224 + ] 225 + 226 + 227 + itemView : Model -> Int -> Int -> IdentifiedTrack -> UnstyledHtml.Html Msg 228 + itemView { favouritesOnly } _ idx ( identifiers, track ) = 229 + let 230 + shouldRenderGroup = 231 + identifiers.group 232 + |> Maybe.map (.index >> (==) 0) 233 + |> Maybe.withDefault False 234 + in 235 + Html.toUnstyled <| 236 + Html.div 237 + [] 238 + [ if shouldRenderGroup then 239 + groupNode identifiers 240 + 241 + else 242 + nothing 243 + 244 + -- 245 + , brick 246 + [ css (rowStyles idx identifiers) 247 + 248 + -- Play 249 + ------- 250 + , [ UI.Reply.PlayTrack ( identifiers, track ) ] 251 + |> Reply 252 + |> onDoubleClick 253 + 254 + -- Context Menu 255 + --------------- 256 + , ( identifiers, track ) 257 + |> ShowTrackMenu 258 + |> onWithOptions 259 + "contextmenu" 260 + { stopPropagation = True 261 + , preventDefault = True 262 + } 263 + |> Html.Styled.Attributes.fromUnstyled 264 + ] 265 + [ T.flex 266 + , T.items_center 267 + 268 + -- 269 + , ifThenElse identifiers.isMissing "" T.pointer 270 + , ifThenElse identifiers.isSelected T.fw6 "" 271 + ] 272 + [ favouriteColumn favouritesOnly identifiers 273 + , otherColumn 37.5 False track.tags.title 274 + , otherColumn 29.0 False track.tags.artist 275 + , otherColumn 29.0 True track.tags.album 276 + ] 277 + ] 278 + 279 + 280 + 187 281 -- ROWS 188 282 189 283 284 + groupNode : Identifiers -> Html Msg 285 + groupNode identifiers = 286 + let 287 + groupName = 288 + identifiers.group 289 + |> Maybe.map .name 290 + |> Maybe.withDefault "Unknown" 291 + in 292 + brick 293 + [ css groupStyles ] 294 + [ T.f7 295 + , T.fw6 296 + , T.lh_copy 297 + , T.mb3 298 + , T.mh3 299 + , T.mt4 300 + , T.tracked 301 + ] 302 + [ inline 303 + [ T.dib, T.v_mid, C.lh_0 ] 304 + [ Html.fromUnstyled (Icons.terrain 16 Inherit) ] 305 + , inline 306 + [ T.dib, T.pl2, T.v_mid ] 307 + (if String.contains "1970" groupName then 308 + [ text "AGES AGO" ] 309 + 310 + else 311 + [ text groupName ] 312 + ) 313 + ] 314 + 315 + 316 + groupStyles : List Css.Style 317 + groupStyles = 318 + [ Css.color (Color.toElmCssColor UI.Kit.colorKit.base04) 319 + , Css.fontFamilies UI.Kit.headerFontFamilies 320 + , Css.fontSize (Css.px 10.5) 321 + ] 322 + 323 + 190 324 rowHeight : Float 191 325 rowHeight = 192 326 35 ··· 217 351 in 218 352 [ Css.backgroundColor bgColor 219 353 , Css.color color 354 + , Css.height (Css.px rowHeight) 220 355 ] 221 356 222 357 ··· 230 365 [ css (favouriteColumnStyles favouritesOnly identifiers) 231 366 , onClick (ToggleFavourite identifiers.indexInList) 232 367 ] 233 - [ T.dtc, T.pl3, T.v_mid ] 368 + [ T.pl3 ] 234 369 [ if identifiers.isFavourite then 235 370 text "t" 236 371 ··· 260 395 in 261 396 [ Css.color color 262 397 , Css.fontFamilies [ "or-favourites" ] 263 - , Css.height (Css.px rowHeight) 264 398 , Css.width (Css.pct 4.5) 265 399 ] 266 400 ··· 269 403 otherColumn width isLast text_ = 270 404 brick 271 405 [ css (otherColumnStyles width) ] 272 - [ T.dtc 273 - , T.pl2 406 + [ T.pl2 274 407 , T.truncate 275 - , T.v_mid 276 408 277 409 -- 278 410 , ifThenElse isLast T.pr3 T.pr2 ··· 282 414 283 415 otherColumnStyles : Float -> List Css.Style 284 416 otherColumnStyles columnWidth = 285 - [ Css.height (Css.px rowHeight) 286 - , Css.width (Css.pct columnWidth) 417 + [ Css.width (Css.pct columnWidth) 287 418 ] 288 - 289 - 290 - 291 - -- INFINITE LIST 292 - 293 - 294 - infiniteListConfig : Necessities -> Model -> InfiniteList.Config IdentifiedTrack Msg 295 - infiniteListConfig necessities model = 296 - InfiniteList.withCustomContainer 297 - infiniteListContainer 298 - (InfiniteList.config 299 - { itemView = itemView model 300 - , itemHeight = InfiniteList.withConstantHeight (round rowHeight) 301 - , containerHeight = round necessities.height 302 - } 303 - ) 304 - 305 - 306 - infiniteListContainer : 307 - List ( String, String ) 308 - -> List (UnstyledHtml.Html msg) 309 - -> UnstyledHtml.Html msg 310 - infiniteListContainer styles children = 311 - UnstyledHtml.div 312 - (List.map (\( k, v ) -> UnstyledHtmlAttributes.style k v) styles) 313 - [ (Html.toUnstyled << rawy) <| 314 - slab 315 - Html.ol 316 - [ css listStyles ] 317 - [ T.dt 318 - , T.dt__fixed 319 - , T.f6 320 - , T.list 321 - , T.ma0 322 - , T.ph0 323 - , T.pv1 324 - ] 325 - (List.map Html.fromUnstyled children) 326 - ] 327 - 328 - 329 - listStyles : List Css.Style 330 - listStyles = 331 - [ Css.fontSize (Css.px 12.5) 332 - ] 333 - 334 - 335 - itemView : Model -> Int -> Int -> IdentifiedTrack -> UnstyledHtml.Html Msg 336 - itemView { favouritesOnly } _ idx ( identifiers, track ) = 337 - Html.toUnstyled <| 338 - slab 339 - Html.li 340 - [ css (rowStyles idx identifiers) 341 - 342 - -- Play 343 - ------- 344 - , [ UI.Reply.PlayTrack ( identifiers, track ) ] 345 - |> Reply 346 - |> onDoubleClick 347 - 348 - -- Context Menu 349 - --------------- 350 - , ( identifiers, track ) 351 - |> ShowTrackMenu 352 - |> onWithOptions 353 - "contextmenu" 354 - { stopPropagation = True 355 - , preventDefault = True 356 - } 357 - |> Html.Styled.Attributes.fromUnstyled 358 - ] 359 - [ T.dt_row 360 - 361 - -- 362 - , ifThenElse identifiers.isMissing "" T.pointer 363 - , ifThenElse identifiers.isSelected T.fw6 "" 364 - ] 365 - [ favouriteColumn favouritesOnly identifiers 366 - , otherColumn 37.5 False track.tags.title 367 - , otherColumn 29.0 False track.tags.artist 368 - , otherColumn 29.0 True track.tags.album 369 - ]