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.

Services and tracks state

+162 -102
+22 -97
src/Applications/UI.elm
··· 71 71 import UI.Reply as Reply exposing (Reply(..)) 72 72 import UI.Reply.Translate as Reply 73 73 import UI.Routing.State as Routing 74 + import UI.Services.State as Services 74 75 import UI.Settings as Settings 75 76 import UI.Settings.Page 76 77 import UI.Sources as Sources ··· 193 194 return model 194 195 195 196 Reply reply -> 196 - translateReply reply model 197 + Reply.translate reply model 197 198 198 199 -- 199 200 Audio a -> ··· 208 209 AuthenticationBootFailure err -> 209 210 model 210 211 |> showNotification (Notifications.error err) 211 - |> andThen (translateReply LoadDefaultBackdrop) 212 + |> andThen (Reply.translate LoadDefaultBackdrop) 212 213 213 214 MissingSecretKey json -> 214 215 "There seems to be existing data that's encrypted, I will need the passphrase (ie. encryption key) to continue." 215 216 |> Notifications.error 216 217 |> showNotificationWithModel model 217 - |> andThen (translateReply <| Reply.LoadDefaultBackdrop) 218 - |> andThen (translateReply <| Reply.ToggleLoadingScreen Off) 218 + |> andThen (Reply.translate <| Reply.LoadDefaultBackdrop) 219 + |> andThen (Reply.translate <| Reply.ToggleLoadingScreen Off) 219 220 220 221 NotAuthenticated -> 221 222 -- This is the message we get when the app initially ··· 259 260 ----------------------------------------- 260 261 AlfredMsg sub -> 261 262 Return3.wieldNested 262 - translateReply 263 + Reply.translate 263 264 { mapCmd = AlfredMsg 264 265 , mapModel = \child -> { model | alfred = child } 265 266 , update = Alfred.update ··· 270 271 271 272 AuthenticationMsg sub -> 272 273 Return3.wieldNested 273 - translateReply 274 + Reply.translate 274 275 { mapCmd = AuthenticationMsg 275 276 , mapModel = \child -> { model | authentication = child } 276 277 , update = Authentication.update ··· 281 282 282 283 BackdropMsg sub -> 283 284 Return3.wieldNested 284 - translateReply 285 + Reply.translate 285 286 { mapCmd = BackdropMsg 286 287 , mapModel = \child -> { model | backdrop = child } 287 288 , update = Backdrop.update ··· 292 293 293 294 EqualizerMsg sub -> 294 295 Return3.wieldNested 295 - translateReply 296 + Reply.translate 296 297 { mapCmd = EqualizerMsg 297 298 , mapModel = \child -> { model | equalizer = child } 298 299 , update = Equalizer.update ··· 303 304 304 305 PlaylistsMsg sub -> 305 306 Return3.wieldNested 306 - translateReply 307 + Reply.translate 307 308 { mapCmd = PlaylistsMsg 308 309 , mapModel = \child -> { model | playlists = child } 309 310 , update = Playlists.update ··· 314 315 315 316 QueueMsg sub -> 316 317 Return3.wieldNested 317 - translateReply 318 + Reply.translate 318 319 { mapCmd = QueueMsg 319 320 , mapModel = \child -> { model | queue = child } 320 321 , update = Queue.update ··· 325 326 326 327 SourcesMsg sub -> 327 328 Return3.wieldNested 328 - translateReply 329 + Reply.translate 329 330 { mapCmd = SourcesMsg 330 331 , mapModel = \child -> { model | sources = child } 331 332 , update = Sources.update ··· 336 337 337 338 TracksMsg sub -> 338 339 Return3.wieldNested 339 - translateReply 340 + Reply.translate 340 341 { mapCmd = TracksMsg 341 342 , mapModel = \child -> { model | tracks = child } 342 343 , update = Tracks.update ··· 363 364 ----------------------------------------- 364 365 -- Services 365 366 ----------------------------------------- 366 - GotLastFmSession (Ok sessionKey) -> 367 - { model | lastFm = LastFm.gotSessionKey sessionKey model.lastFm } 368 - |> showNotification 369 - (Notifications.success "Connected successfully with Last.fm") 370 - |> andThen 371 - (translateReply SaveSettings) 372 - 373 - GotLastFmSession (Err _) -> 374 - showNotification 375 - (Notifications.stickyError "Could not connect with Last.fm") 376 - { model | lastFm = LastFm.failedToAuthenticate model.lastFm } 377 - 378 - Scrobble { duration, timestamp, trackId } -> 379 - case model.tracks.nowPlaying of 380 - Just ( _, track ) -> 381 - if trackId == track.id then 382 - ( model 383 - , LastFm.scrobble model.lastFm 384 - { duration = duration 385 - , msg = Bypass 386 - , timestamp = timestamp 387 - , track = track 388 - } 389 - ) 367 + GotLastFmSession a -> 368 + Services.gotLastFmSession a model 390 369 391 - else 392 - return model 393 - 394 - Nothing -> 395 - return model 370 + Scrobble a -> 371 + Services.scrobble a model 396 372 397 373 ----------------------------------------- 398 374 -- Tracks 399 375 ----------------------------------------- 400 376 DownloadTracksFinished -> 401 - case model.downloading of 402 - Just { notificationId } -> 403 - { id = notificationId } 404 - |> DismissNotification 405 - |> Reply.translateWithModel { model | downloading = Nothing } 377 + Tracks.downloadTracksFinished model 406 378 407 - Nothing -> 408 - return model 379 + FailedToStoreTracksInCache a -> 380 + Tracks.failedToStoreTracksInCache a model 409 381 410 - FailedToStoreTracksInCache trackIds -> 411 - model 412 - |> Lens.modify Tracks.lens 413 - (\m -> { m | cachingInProgress = List.without trackIds m.cachingInProgress }) 414 - |> showNotification 415 - (Notifications.error "Failed to store track in cache") 416 - 417 - FinishedStoringTracksInCache trackIds -> 418 - model 419 - |> Lens.modify Tracks.lens 420 - (\t -> 421 - { t 422 - | cached = t.cached ++ trackIds 423 - , cachingInProgress = List.without trackIds t.cachingInProgress 424 - } 425 - ) 426 - |> (\m -> 427 - -- When a context menu of a track is open, 428 - -- it should be "rerendered" in case 429 - -- the track is no longer being downloaded. 430 - case m.contextMenu of 431 - Just contextMenu -> 432 - let 433 - isTrackContextMenu = 434 - ContextMenu.anyItem 435 - (.label >> (==) "Downloading ...") 436 - contextMenu 437 - 438 - coordinates = 439 - ContextMenu.coordinates contextMenu 440 - in 441 - if isTrackContextMenu then 442 - m.tracks.collection.harvested 443 - |> List.pickIndexes m.tracks.selectedTrackIndexes 444 - |> ShowTracksContextMenu coordinates { alt = False } 445 - |> Reply.translateWithModel m 446 - 447 - else 448 - return m 449 - 450 - Nothing -> 451 - return m 452 - ) 453 - |> andThen (update <| TracksMsg Tracks.Harvest) 454 - |> andThen (translateReply SaveEnclosedUserData) 382 + FinishedStoringTracksInCache a -> 383 + Tracks.finishedStoringTracksInCache a model 455 384 456 385 ----------------------------------------- 457 386 -- User ··· 467 396 468 397 LoadHypaethralUserData a -> 469 398 User.loadHypaethralUserData a model 470 - 471 - 472 - translateReply = 473 - Reply.translate 474 399 475 400 476 401
+53
src/Applications/UI/Services/State.elm
··· 1 + module UI.Services.State exposing (..) 2 + 3 + import Http 4 + import LastFm 5 + import Management 6 + import Monocle.Lens as Lens 7 + import Notifications 8 + import Return exposing (andThen, return) 9 + import Return.Ext as Return exposing (communicate) 10 + import UI.Common.State as Common exposing (showNotification) 11 + import UI.Reply exposing (Reply(..)) 12 + import UI.Types as UI exposing (Manager, Msg(..)) 13 + 14 + 15 + 16 + -- 📣 17 + 18 + 19 + gotLastFmSession : Result Http.Error String -> UI.Manager 20 + gotLastFmSession result model = 21 + case result of 22 + Err _ -> 23 + showNotification 24 + (Notifications.stickyError "Could not connect with Last.fm") 25 + { model | lastFm = LastFm.failedToAuthenticate model.lastFm } 26 + 27 + Ok sessionKey -> 28 + { model | lastFm = LastFm.gotSessionKey sessionKey model.lastFm } 29 + |> showNotification 30 + (Notifications.success "Connected successfully with Last.fm") 31 + |> andThen 32 + (Return.performance <| Reply SaveSettings) 33 + 34 + 35 + scrobble : { duration : Int, timestamp : Int, trackId : String } -> UI.Manager 36 + scrobble { duration, timestamp, trackId } model = 37 + case model.tracks.nowPlaying of 38 + Just ( _, track ) -> 39 + if trackId == track.id then 40 + ( model 41 + , LastFm.scrobble model.lastFm 42 + { duration = duration 43 + , msg = Bypass 44 + , timestamp = timestamp 45 + , track = track 46 + } 47 + ) 48 + 49 + else 50 + Return.singleton model 51 + 52 + Nothing -> 53 + Return.singleton model
+79 -1
src/Applications/UI/Tracks/State.elm
··· 1 1 module UI.Tracks.State exposing (..) 2 2 3 + import ContextMenu 4 + import Http 5 + import LastFm 6 + import List.Ext as List 7 + import List.Extra as List 8 + import Management 3 9 import Monocle.Lens as Lens exposing (Lens) 10 + import Notifications 11 + import Return exposing (andThen, return) 12 + import Return.Ext as Return exposing (communicate) 13 + import UI.Common.State as Common exposing (showNotification) 14 + import UI.Reply as Reply exposing (Reply(..)) 15 + import UI.Tracks as Tracks 16 + import UI.Types as UI exposing (Manager, Msg(..)) 4 17 5 18 6 19 ··· 14 27 15 28 16 29 17 - -- 🔱 30 + -- 📣 31 + 32 + 33 + downloadTracksFinished : UI.Manager 34 + downloadTracksFinished model = 35 + case model.downloading of 36 + Just { notificationId } -> 37 + { id = notificationId } 38 + |> DismissNotification 39 + |> Reply 40 + |> Return.performanceF { model | downloading = Nothing } 41 + 42 + Nothing -> 43 + Return.singleton model 44 + 45 + 46 + failedToStoreTracksInCache : List String -> UI.Manager 47 + failedToStoreTracksInCache trackIds model = 48 + model 49 + |> Lens.modify lens 50 + (\m -> { m | cachingInProgress = List.without trackIds m.cachingInProgress }) 51 + |> showNotification 52 + (Notifications.error "Failed to store track in cache") 53 + 54 + 55 + finishedStoringTracksInCache : List String -> UI.Manager 56 + finishedStoringTracksInCache trackIds model = 57 + model 58 + |> Lens.modify lens 59 + (\t -> 60 + { t 61 + | cached = t.cached ++ trackIds 62 + , cachingInProgress = List.without trackIds t.cachingInProgress 63 + } 64 + ) 65 + |> (\m -> 66 + -- When a context menu of a track is open, 67 + -- it should be "rerendered" in case 68 + -- the track is no longer being downloaded. 69 + case m.contextMenu of 70 + Just contextMenu -> 71 + let 72 + isTrackContextMenu = 73 + ContextMenu.anyItem 74 + (.label >> (==) "Downloading ...") 75 + contextMenu 76 + 77 + coordinates = 78 + ContextMenu.coordinates contextMenu 79 + in 80 + if isTrackContextMenu then 81 + m.tracks.collection.harvested 82 + |> List.pickIndexes m.tracks.selectedTrackIndexes 83 + |> ShowTracksContextMenu coordinates { alt = False } 84 + |> Reply 85 + |> Return.performanceF m 86 + 87 + else 88 + Return.singleton m 89 + 90 + Nothing -> 91 + Return.singleton m 92 + ) 93 + -- TODO: Make sync 94 + |> Return.effect_ (\_ -> Return.task <| TracksMsg Tracks.Harvest) 95 + |> Return.effect_ (\_ -> Return.task <| Reply SaveEnclosedUserData)
+8 -4
src/Library/Return/Ext.elm
··· 16 16 -} 17 17 performance : msg -> model -> ( model, Cmd msg ) 18 18 performance msg model = 19 - msg 20 - |> Task.succeed 21 - |> Task.perform identity 22 - |> Tuple.pair model 19 + ( model, task msg ) 23 20 24 21 25 22 performanceF : model -> msg -> ( model, Cmd msg ) 26 23 performanceF model msg = 27 24 performance msg model 25 + 26 + 27 + task : msg -> Cmd msg 28 + task msg = 29 + msg 30 + |> Task.succeed 31 + |> Task.perform identity