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.

Improve onboarding & empty states

+229 -142
+13 -4
src/App/Navigation/View.elm
··· 69 69 , onClickPreventDefault (RoutingMsg <| Routing.GoToUrl itemHref) 70 70 71 71 -- 72 - , if itemHref == activeHref then 73 - cssClass ActiveLink 74 - else 75 - cssClass NonActiveLink 72 + , let 73 + baseHref = 74 + itemHref 75 + |> String.dropLeft 1 76 + |> String.split "/" 77 + |> List.head 78 + |> Maybe.withDefault (String.dropLeft 1 itemHref) 79 + |> String.append "/" 80 + in 81 + if baseHref == activeHref then 82 + cssClass ActiveLink 83 + else 84 + cssClass NonActiveLink 76 85 ] 77 86 [ span 78 87 []
+22 -7
src/App/Queue/View.elm
··· 8 8 import Html.Lazy exposing (lazy, lazy2) 9 9 import Material.Icons.Av as Icons 10 10 import Material.Icons.Content as Icons 11 + import Material.Icons.Image as Icons 11 12 import Navigation.View as Navigation 12 13 import Queue.Types as Queue exposing (Item, Page(..)) 13 14 import Routing.Types ··· 19 20 -- Styles 20 21 21 22 import List.Styles exposing (Classes(..)) 22 - import Styles exposing (Classes(Button, ContentBox, InsulationContent, Intro)) 23 + import Styles exposing (Classes(..)) 23 24 24 25 25 26 -- Helpers ··· 80 81 [] 81 82 [ text "Up next" ] 82 83 , if List.isEmpty futureItems then 83 - p 84 - [ cssClass Intro ] 85 - [ text "No tracks available." ] 84 + div 85 + [ cssClass EmptyState ] 86 + [ Icons.music_note Color.black 16 87 + , div 88 + [] 89 + [ text "Nothing here yet," 90 + , br [] [] 91 + , text "add some music first." 92 + ] 93 + ] 86 94 else 87 95 Html.Keyed.node 88 96 "ul" ··· 142 150 [] 143 151 [ text "History" ] 144 152 , if List.isEmpty pastItems then 145 - p 146 - [ cssClass Intro ] 147 - [ text "No tracks have been played yet." ] 153 + div 154 + [ cssClass EmptyState ] 155 + [ Icons.music_note Color.black 16 156 + , div 157 + [] 158 + [ text "Nothing here yet," 159 + , br [] [] 160 + , text "play some music first." 161 + ] 162 + ] 148 163 else 149 164 Html.Keyed.node 150 165 "ul"
+10 -7
src/App/Settings/View.elm
··· 2 2 3 3 import Html exposing (..) 4 4 import Html.Attributes exposing (..) 5 - import Material.Icons.Action 5 + import Material.Icons.Action as Icons 6 + import Material.Icons.Image as Icons 6 7 import Navigation.View as Navigation 7 8 import Types exposing (Model, Msg(..)) 8 9 import Utils exposing (cssClass) ··· 27 28 Navigation.insideCustom 28 29 [ ( span 29 30 [] 30 - [ Material.Icons.Action.exit_to_app colorDerivatives.text 16 31 + [ Icons.exit_to_app colorDerivatives.text 16 31 32 , label [] [ text "Sign out" ] 32 33 ] 33 34 , SignOut ··· 42 43 [ h1 43 44 [] 44 45 [ text "Settings" ] 45 - , p 46 - [ cssClass Intro ] 47 - [ text """ 48 - Not much to see here yet. 49 - """ 46 + , div 47 + [ cssClass EmptyState ] 48 + [ Icons.panorama_wide_angle colorDerivatives.text 16 49 + , div 50 + [] 51 + [ text "Coming soon" 52 + ] 50 53 ] 51 54 ] 52 55 ]
+53 -27
src/App/Sources/View.elm
··· 62 62 -- Navigation 63 63 ------------------------------------ 64 64 Navigation.insideCustom 65 - [ ( span 66 - [] 67 - [ Icons.add_circle_outline colorDerivatives.text 16 68 - , label [] [ text "Add a new source" ] 69 - ] 70 - , RoutingMsg (GoToUrl "/sources/new") 71 - ) 72 - , ( span 65 + (List.concat 66 + [ -- Add 67 + [ ( span 68 + [] 69 + [ Icons.add colorDerivatives.text 16 70 + , label [] [ text "Add a new source" ] 71 + ] 72 + , RoutingMsg (GoToUrl "/sources/new") 73 + ) 74 + ] 75 + 76 + -- Other 77 + , if List.isEmpty sources then 73 78 [] 74 - [ Icons.cloud_queue colorDerivatives.text 16 75 - , label [] [ text "Process sources" ] 79 + else 80 + [ ( span 81 + [] 82 + [ Icons.sync colorDerivatives.text 16 83 + , label [] [ text "Process sources" ] 84 + ] 85 + , TopLevel.ProcessSources 86 + ) 76 87 ] 77 - , TopLevel.ProcessSources 78 - ) 79 - ] 88 + ] 89 + ) 80 90 81 91 ------------------------------------ 82 92 -- List ··· 86 96 [ h1 87 97 [] 88 98 [ text "Sources" ] 89 - , p 90 - [ cssClass Intro ] 91 - [ text """ 92 - A source is a place where your music is stored. 93 - By connecting a source, the application will scan it 94 - and keep a list of all the music in it. It will not 95 - copy anything. 96 - """ 97 - ] 99 + , if List.isEmpty sources then 100 + text "" 101 + else 102 + p 103 + [ cssClass Intro ] 104 + [ text """ 105 + A source is a place where your music is stored. 106 + By connecting a source, the application will scan it 107 + and keep a list of all the music in it. It will not 108 + copy anything. 109 + """ 110 + ] 98 111 99 112 -- Check if sources are processing 100 113 -- and if they have processing errors ··· 114 127 ) 115 128 sources 116 129 in 117 - -- Render list 118 - Html.Keyed.node 119 - "ul" 120 - [ cssClass ListWithActions ] 121 - (List.indexedMap renderSource sourcesWithContext) 130 + if List.isEmpty sources then 131 + -- No sources atm 132 + div 133 + [ cssClass EmptyState ] 134 + [ Icons.add Color.black 16 135 + , div 136 + [] 137 + [ text "No sources have been added yet," 138 + , br [] [] 139 + , text "add one to get started." 140 + ] 141 + ] 142 + else 143 + -- Render list 144 + Html.Keyed.node 145 + "ul" 146 + [ cssClass ListWithActions ] 147 + (List.indexedMap renderSource sourcesWithContext) 122 148 ] 123 149 ] 124 150
+1 -1
src/App/Tracks/View.elm
··· 191 191 192 192 msgProcessing : Html TopLevel.Msg 193 193 msgProcessing = 194 - text "Processing Tracks ..." 194 + text "Processing Tracks" 195 195 196 196 197 197 msgNoSources : Html TopLevel.Msg
+3 -1
src/App/View.elm
··· 161 161 [ cssClass Shell ] 162 162 [ -- Navigation 163 163 -- 164 - Html.Lazy.lazy authenticatedNavigation model.routing.currentPage 164 + Html.Lazy.lazy 165 + authenticatedNavigation 166 + model.routing.currentPage 165 167 166 168 -- Page 167 169 --
+127 -95
src/Css/Styles.elm
··· 49 49 | Button 50 50 | Columns 51 51 | ContentBox 52 + | EmptyState 52 53 | Important 53 54 | Insulation 54 55 | InsulationCentered ··· 106 107 , backgroundSize cover 107 108 108 109 -- For: 1.jpg 109 - --, backgroundPosition2 (pct 50) (pct 19) 110 + -- , backgroundPosition2 (pct 50) (pct 19) 110 111 -- , backgroundSize (pct 110) 111 112 -- 112 113 , backgroundRepeat noRepeat ··· 225 226 ] 226 227 227 228 ------------------------------------------------------ 228 - -- <🎃> Basic wrapper 229 - ------------------------------------------------------ 230 - , class Basic 231 - [ color (hex "#fff") 232 - , lineHeight (num 1.5) 233 - , textAlign center 234 - 235 - -- 236 - , descendants 237 - [ svg 238 - [ display inlineBlock 239 - , marginRight (gr 1) 240 - , transform (translateY (px 2)) 241 - ] 242 - ] 243 - ] 244 - 245 - ------------------------------------------------------ 246 - -- <🎃> Intro 247 - ------------------------------------------------------ 248 - , class Intro 249 - [ fontSize (Css.em 0.9) 250 - , lineHeight (num 1.6) 251 - , marginBottom (gr 6) 252 - , marginTop (gr 4) 253 - , opacity (num 0.475) 254 - 255 - -- 256 - , descendants 257 - [ a 258 - [ borderBottom3 (px 2) solid (cssColor colors.base0A) 259 - ] 260 - , svg 261 - [ marginRight (gr 1) 262 - , verticalAlign middle 263 - ] 264 - ] 265 - ] 266 - 267 - ------------------------------------------------------ 268 - -- <🎃> Important text 269 - ------------------------------------------------------ 270 - , class Important 271 - [ alignItems center 272 - , border3 273 - (px 2) 274 - solid 275 - (colors.base08 276 - |> cssColor 277 - ) 278 - , borderRadius (px 3) 279 - , color (cssColor colors.base08) 280 - , displayFlex 281 - , fontWeight (int 700) 282 - , lineHeight (int 1) 283 - , padding2 (gr 2) (gr 2) 284 - 285 - -- 286 - , descendants 287 - [ selector "svg g" 288 - [ fill currentColor ] 289 - ] 290 - ] 291 - 292 - ------------------------------------------------------ 293 - -- <🎃> Logo backdrop 294 - ------------------------------------------------------ 295 - , class LogoBackdrop 296 - [ backgroundImage (url "/images/icon.svg") 297 - , backgroundPosition2 (px -124) (pct 53.75) 298 - , backgroundRepeat noRepeat 299 - , backgroundSize cover 300 - , bottom zero 301 - , left zero 302 - , opacity (num 0.05) 303 - , position absolute 304 - , right zero 305 - , top zero 306 - ] 307 - 308 - ------------------------------------------------------ 309 - -- Buttons 229 + -- <🎃> Buttons 310 230 ------------------------------------------------------ 311 231 , (each [ class Button, button ]) 312 232 [ backgroundColor transparent ··· 341 261 ] 342 262 ] 343 263 264 + -- -- 265 + -- -- 266 + -- OTHER BASIC COMPONENTS -- 267 + -- -- 268 + -- -- 344 269 ------------------------------------------------------ 345 270 -- Authentication button 346 271 ------------------------------------------------------ ··· 379 304 ] 380 305 381 306 ------------------------------------------------------ 382 - -- Overlay 307 + -- Basic wrapper 383 308 ------------------------------------------------------ 384 - , class Overlay 385 - [ backgroundColor (rgba 0 0 0 0.25) 386 - , height (vh 100) 387 - , left zero 388 - , opacity zero 389 - , position fixed 390 - , property "pointer-events" "none" 391 - , property "transition" "opacity 1s ease" 392 - , top zero 393 - , width (vw 100) 394 - , zIndex (int 900) 309 + , class Basic 310 + [ color (hex "#fff") 311 + , lineHeight (num 1.5) 312 + , textAlign center 313 + 314 + -- 315 + , descendants 316 + [ svg 317 + [ display inlineBlock 318 + , marginRight (gr 1) 319 + , transform (translateY (px 2)) 320 + ] 321 + ] 395 322 ] 396 323 397 324 ------------------------------------------------------ ··· 409 336 , property "page-break-inside" "avoid" 410 337 ] 411 338 ] 339 + ] 340 + 341 + ------------------------------------------------------ 342 + -- Empty state 343 + ------------------------------------------------------ 344 + , class EmptyState 345 + [ color (cssColor colors.base06) 346 + , fontWeight (int 600) 347 + , left (pct 50) 348 + , lineHeight (num 1.45) 349 + , marginTop (gr 3) 350 + , position absolute 351 + , textAlign center 352 + , top (pct 50) 353 + , transform (translate2 (pct -50) (pct -50)) 354 + 355 + -- 356 + , descendants 357 + [ svg 358 + [ fontSize (basem 64) 359 + , marginBottom (gr 1) 360 + ] 361 + , selector "g" 362 + [ fill currentColor 363 + ] 364 + ] 365 + ] 366 + 367 + ------------------------------------------------------ 368 + -- Important text 369 + ------------------------------------------------------ 370 + , class Important 371 + [ alignItems center 372 + , border3 373 + (px 2) 374 + solid 375 + (colors.base08 376 + |> cssColor 377 + ) 378 + , borderRadius (px 3) 379 + , color (cssColor colors.base08) 380 + , displayFlex 381 + , fontWeight (int 700) 382 + , lineHeight (int 1) 383 + , padding2 (gr 2) (gr 2) 384 + 385 + -- 386 + , descendants 387 + [ selector "svg g" 388 + [ fill currentColor ] 389 + ] 390 + ] 391 + 392 + ------------------------------------------------------ 393 + -- Intro 394 + ------------------------------------------------------ 395 + , class Intro 396 + [ fontSize (Css.em 0.9) 397 + , lineHeight (num 1.6) 398 + , marginBottom (gr 6) 399 + , marginTop (gr 4) 400 + , opacity (num 0.475) 401 + 402 + -- 403 + , descendants 404 + [ a 405 + [ borderBottom3 (px 2) solid (cssColor colors.base0A) 406 + ] 407 + , svg 408 + [ marginRight (gr 1) 409 + , verticalAlign middle 410 + ] 411 + ] 412 + ] 413 + 414 + ------------------------------------------------------ 415 + -- Logo backdrop 416 + ------------------------------------------------------ 417 + , class LogoBackdrop 418 + [ backgroundImage (url "/images/icon.svg") 419 + , backgroundPosition2 (px -124) (pct 53.75) 420 + , backgroundRepeat noRepeat 421 + , backgroundSize cover 422 + , bottom zero 423 + , left zero 424 + , opacity (num 0.05) 425 + , position absolute 426 + , right zero 427 + , top zero 428 + ] 429 + 430 + ------------------------------------------------------ 431 + -- Overlay 432 + ------------------------------------------------------ 433 + , class Overlay 434 + [ backgroundColor (rgba 0 0 0 0.25) 435 + , height (vh 100) 436 + , left zero 437 + , opacity zero 438 + , position fixed 439 + , property "pointer-events" "none" 440 + , property "transition" "opacity 1s ease" 441 + , top zero 442 + , width (vw 100) 443 + , zIndex (int 900) 412 444 ] 413 445 ]