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.

Interface state

+488 -275
+43 -258
src/Applications/UI.elm
··· 4 4 import Alien 5 5 import Browser 6 6 import Browser.Dom 7 - import Browser.Events 8 7 import Browser.Navigation as Nav 9 8 import Chunky exposing (..) 10 9 import Common exposing (Switch(..)) ··· 25 24 import Html.Lazy as Lazy 26 25 import Json.Decode 27 26 import Json.Encode 28 - import Keyboard 29 27 import LastFm 30 28 import List.Ext as List 31 29 import List.Extra as List 32 30 import Maybe.Extra as Maybe 33 31 import Monocle.Lens as Lens 34 - import Notifications exposing (Notification) 32 + import Notifications 35 33 import Playlists.Encoding as Playlists 36 34 import Process 37 35 import Queue ··· 53 51 import UI.Authentication as Authentication 54 52 import UI.Authentication.ContextMenu as Authentication 55 53 import UI.Backdrop as Backdrop 54 + import UI.Common.State exposing (showNotification, showNotificationWithModel) 56 55 import UI.Console 57 56 import UI.ContextMenu 58 57 import UI.Demo as Demo 59 - import UI.DnD as DnD 60 58 import UI.Equalizer as Equalizer 59 + import UI.Interface.State as Interface 60 + import UI.Interface.Types as Interface 61 61 import UI.Navigation as Navigation 62 62 import UI.Notifications 63 63 import UI.Page as Page ··· 79 79 import UI.Tracks as Tracks 80 80 import UI.Tracks.ContextMenu as Tracks 81 81 import UI.Tracks.Scene.List 82 - import UI.Types exposing (..) 82 + import UI.Types as UI exposing (..) 83 83 import Url exposing (Protocol(..), Url) 84 84 import Url.Ext as Url 85 85 import User.Layer exposing (..) ··· 171 171 Cmd.none 172 172 ) 173 173 |> addCommand 174 - (Task.perform SetCurrentTime Time.now) 174 + (Task.perform 175 + (Interface.SetCurrentTime >> Interface) 176 + Time.now 177 + ) 175 178 176 179 177 180 ··· 188 191 translateReply reply model 189 192 190 193 -- 191 - Blur -> 192 - return { model | focusedOnInput = False } 193 - 194 - Debounce debouncerMsg -> 195 - Return3.wieldNested 196 - update 197 - { mapCmd = Debounce 198 - , mapModel = \child -> { model | debounce = child } 199 - , update = \m -> Debouncer.update m >> Return3.fromDebouncer 200 - } 201 - { model = model.debounce 202 - , msg = debouncerMsg 203 - } 204 - 205 194 DownloadTracksFinished -> 206 195 case model.downloading of 207 196 Just { notificationId } -> ··· 212 201 Nothing -> 213 202 return model 214 203 215 - FocusedOnInput -> 216 - return { model | focusedOnInput = True } 217 - 218 - HideOverlay -> 219 - hideOverlay model 220 - 221 - KeyboardMsg subMsg -> 222 - (\m -> 223 - let 224 - skip = 225 - return m 226 - 227 - authenticated = 228 - case model.authentication of 229 - Authentication.Authenticated _ -> 230 - True 231 - 232 - _ -> 233 - False 234 - in 235 - if m.focusedOnInput || not authenticated then 236 - -- Stop here if using input or not authenticated 237 - skip 238 - 239 - else 240 - case m.pressedKeys of 241 - [ Keyboard.Escape ] -> 242 - hideOverlay m 243 - 244 - [ Keyboard.ArrowLeft ] -> 245 - translateReply RewindQueue m 246 - 247 - [ Keyboard.ArrowRight ] -> 248 - translateReply ShiftQueue m 249 - 250 - [ Keyboard.ArrowUp ] -> 251 - translateReply (Seek <| (m.audio.position - 10) / m.audio.duration) m 252 - 253 - [ Keyboard.ArrowDown ] -> 254 - translateReply (Seek <| (m.audio.position + 10) / m.audio.duration) m 255 - 256 - [ Keyboard.Character "N" ] -> 257 - translateReply ScrollToNowPlaying m 258 - 259 - [ Keyboard.Character "P" ] -> 260 - translateReply TogglePlayPause m 261 - 262 - [ Keyboard.Character "R" ] -> 263 - translateReply ToggleRepeat m 264 - 265 - [ Keyboard.Character "S" ] -> 266 - translateReply ToggleShuffle m 267 - 268 - _ -> 269 - skip 270 - ) 271 - { model | pressedKeys = Keyboard.update subMsg model.pressedKeys } 272 - 273 204 LoadEnclosedUserData json -> 274 205 model 275 206 |> importEnclosed json ··· 318 249 return m 319 250 ) 320 251 321 - PreferredColorSchemaChanged { dark } -> 322 - return { model | darkMode = dark } 323 - 324 - RemoveQueueSelection -> 325 - let 326 - queue = 327 - model.queue 328 - in 329 - ( { model 330 - | queue = { queue | selection = Nothing } 331 - } 332 - , Cmd.none 333 - ) 334 - 335 - RemoveTrackSelection -> 336 - let 337 - tracks = 338 - model.tracks 339 - in 340 - ( { model 341 - | tracks = { tracks | selectedTrackIndexes = [] } 342 - } 343 - , Cmd.none 344 - ) 345 - 346 - ResizedWindow ( width, height ) -> 347 - ( { model 348 - | contextMenu = Nothing 349 - , viewport = { height = toFloat height, width = toFloat width } 350 - } 351 - -- 352 - , Cmd.none 353 - ) 354 - 355 - SetCurrentTime time -> 356 - let 357 - sources = 358 - model.sources 359 - in 360 - ( { model 361 - | currentTime = time 362 - , sources = { sources | currentTime = time } 363 - } 364 - -- 365 - , Cmd.none 366 - ) 367 - 368 - SetIsOnline False -> 369 - -- The app went offline, cache everything 370 - -- (if caching is supported). 371 - ( { model | isOnline = False } 372 - , case model.authentication of 373 - Authentication.Authenticated (Dropbox _) -> 374 - Ports.toBrain (Alien.trigger Alien.SyncHypaethralData) 375 - 376 - Authentication.Authenticated (RemoteStorage _) -> 377 - Ports.toBrain (Alien.trigger Alien.SyncHypaethralData) 378 - 379 - _ -> 380 - Cmd.none 381 - ) 382 - 383 - SetIsOnline True -> 384 - -- We're caching the user's data in the browser while offline. 385 - -- If we're back online again, sync all the user's data. 386 - (case model.authentication of 387 - Authentication.Authenticated (Dropbox _) -> 388 - syncHypaethralData 389 - 390 - Authentication.Authenticated (RemoteStorage _) -> 391 - syncHypaethralData 392 - 393 - _ -> 394 - return 395 - ) 396 - { model | isOnline = True } 397 - 398 - SetIsTouchDevice bool -> 399 - return { model | isTouchDevice = bool } 400 - 401 - ShowNotification notification -> 402 - showNotification notification model 403 - 404 - StoppedDragging -> 405 - let 406 - notDragging = 407 - { model | isDragging = False } 408 - in 409 - -- Depending on where we stopped dragging something, 410 - -- do the appropriate thing. 411 - case model.page of 412 - Page.Queue _ -> 413 - DnD.stoppedDragging 414 - |> Queue.DragMsg 415 - |> QueueMsg 416 - |> updateWithModel notDragging 417 - 418 - Page.Index -> 419 - case model.tracks.scene of 420 - Tracks.List -> 421 - DnD.stoppedDragging 422 - |> UI.Tracks.Scene.List.DragAndDropMsg 423 - |> Tracks.ListSceneMsg 424 - |> TracksMsg 425 - |> updateWithModel notDragging 426 - 427 - _ -> 428 - return notDragging 429 - 430 - UI.Types.ToggleLoadingScreen On -> 431 - return { model | isLoading = True } 432 - 433 - UI.Types.ToggleLoadingScreen Off -> 434 - return { model | isLoading = False } 435 - 436 252 ----------------------------------------- 437 253 -- Authentication 438 254 ----------------------------------------- ··· 786 602 ----------------------------------------- 787 603 Audio a -> 788 604 Audio.update a model 605 + 606 + Interface a -> 607 + Interface.update a model 789 608 790 609 791 610 updateTracksModel : (Tracks.Model -> Tracks.Model) -> Model -> Model ··· 1629 1448 -- 📣 ░░ FUNCTIONS 1630 1449 1631 1450 1632 - hideOverlay : Model -> Return Model Msg 1633 - hideOverlay model = 1634 - ( { model 1635 - | alfred = { instance = Nothing } 1636 - , confirmation = Nothing 1637 - , contextMenu = Nothing 1638 - } 1639 - -- 1640 - , Cmd.none 1641 - ) 1642 - 1643 - 1644 1451 loadSourceForForm : Model -> String -> ( Model, Cmd Msg ) 1645 1452 loadSourceForForm model sourceId = 1646 1453 let ··· 1709 1516 hypaethralBit.list 1710 1517 1711 1518 1712 - showNotification : Notification Reply -> Model -> Return Model Msg 1713 - showNotification notification model = 1714 - model.notifications 1715 - |> UI.Notifications.show notification 1716 - |> mapModel (\n -> { model | isLoading = False, notifications = n }) 1717 - |> mapCommand Reply 1718 - 1719 - 1720 - showNotificationWithModel : Model -> Notification Reply -> Return Model Msg 1721 - showNotificationWithModel model notification = 1722 - showNotification notification model 1723 - 1724 - 1725 - syncHypaethralData : Model -> Return Model Msg 1726 - syncHypaethralData model = 1727 - "Syncing" 1728 - |> Notifications.warning 1729 - |> showNotificationWithModel model 1730 - |> addCommand (Ports.toBrain <| Alien.trigger Alien.SyncHypaethralData) 1731 - 1732 - 1733 1519 1734 1520 -- 📰 1735 1521 ··· 1763 1549 (Sub.map AlfredMsg <| Alfred.subscriptions model.alfred) 1764 1550 Sub.none 1765 1551 1766 - -- Resize 1767 - --------- 1768 - , Browser.Events.onResize 1769 - (\w h -> 1770 - ( w, h ) 1771 - |> ResizedWindow 1772 - |> Debouncer.provideInput 1773 - |> Debounce 1774 - ) 1775 - 1776 1552 -- 1777 1553 , Ports.downloadTracksFinished (\_ -> DownloadTracksFinished) 1778 - , Ports.indicateTouchDevice (\_ -> SetIsTouchDevice True) 1779 - , Ports.preferredColorSchemaChanged PreferredColorSchemaChanged 1780 1554 , Ports.scrobble Scrobble 1781 1555 , Ports.setAverageBackgroundColor (Backdrop.BackgroundColor >> BackdropMsg) 1782 - , Ports.setIsOnline SetIsOnline 1783 - , Ports.showErrorNotification (Notifications.error >> ShowNotification) 1784 - , Ports.showStickyErrorNotification (Notifications.stickyError >> ShowNotification) 1785 1556 1786 1557 -- 1787 - , Sub.map KeyboardMsg Keyboard.subscriptions 1788 - , Time.every (60 * 1000) SetCurrentTime 1558 + , Interface.subscriptions model 1789 1559 ] 1790 1560 1791 1561 ··· 1827 1597 SourcesMsg Sources.FinishedProcessing 1828 1598 1829 1599 Just Alien.HideLoadingScreen -> 1830 - UI.Types.ToggleLoadingScreen Off 1600 + Interface (Interface.ToggleLoadingScreen Off) 1831 1601 1832 1602 Just Alien.ImportLegacyData -> 1833 - ShowNotification (Notifications.success "Imported data successfully!") 1603 + "Imported data successfully!" 1604 + |> Notifications.success 1605 + |> Interface.ShowNotification 1606 + |> Interface 1834 1607 1835 1608 Just Alien.LoadEnclosedUserData -> 1836 1609 LoadEnclosedUserData event.data ··· 1869 1642 err 1870 1643 |> Json.Decode.errorToString 1871 1644 |> Notifications.error 1872 - |> ShowNotification 1645 + |> Interface.ShowNotification 1646 + |> Interface 1873 1647 1874 1648 Just Alien.UpdateSourceData -> 1875 1649 SourcesMsg (Sources.UpdateSourceData event.data) ··· 1911 1685 FailedToStoreTracksInCache trackIds 1912 1686 1913 1687 Err _ -> 1914 - ShowNotification (Notifications.error err) 1688 + err 1689 + |> Notifications.error 1690 + |> Interface.ShowNotification 1691 + |> Interface 1915 1692 1916 1693 _ -> 1917 - ShowNotification (Notifications.error err) 1694 + err 1695 + |> Notifications.error 1696 + |> Interface.ShowNotification 1697 + |> Interface 1918 1698 1919 1699 1920 1700 ··· 1932 1712 body model = 1933 1713 section 1934 1714 (if Maybe.isJust model.contextMenu || Maybe.isJust model.alfred.instance then 1935 - [ on "tap" (Json.Decode.succeed HideOverlay) ] 1715 + [ on "tap" (interfaceEventHandler Interface.HideOverlay) ] 1936 1716 1937 1717 else if Maybe.isJust model.equalizer.activeKnob then 1938 1718 [ Pointer.onMove (EqualizerMsg << Equalizer.AdjustKnob) ··· 1942 1722 1943 1723 else if model.isDragging then 1944 1724 [ class C.dragging_something 1945 - , on "mouseup" (Json.Decode.succeed StoppedDragging) 1946 - , on "touchcancel" (Json.Decode.succeed StoppedDragging) 1947 - , on "touchend" (Json.Decode.succeed StoppedDragging) 1725 + , on "mouseup" (interfaceEventHandler Interface.StoppedDragging) 1726 + , on "touchcancel" (interfaceEventHandler Interface.StoppedDragging) 1727 + , on "touchend" (interfaceEventHandler Interface.StoppedDragging) 1948 1728 ] 1949 1729 1950 1730 else if Maybe.isJust model.queue.selection then 1951 - [ on "tap" (Json.Decode.succeed RemoveQueueSelection) ] 1731 + [ on "tap" (interfaceEventHandler Interface.RemoveQueueSelection) ] 1952 1732 1953 1733 else if not (List.isEmpty model.tracks.selectedTrackIndexes) then 1954 - [ on "tap" (Json.Decode.succeed RemoveTrackSelection) ] 1734 + [ on "tap" (interfaceEventHandler Interface.RemoveTrackSelection) ] 1955 1735 1956 1736 else 1957 1737 [] ··· 2114 1894 content : { justifyCenter : Bool, scrolling : Bool } -> List (Html Msg) -> Html Msg 2115 1895 content { justifyCenter, scrolling } nodes = 2116 1896 brick 2117 - [ on "focusout" (Json.Decode.succeed Blur) 1897 + [ on "focusout" (interfaceEventHandler Interface.Blur) 2118 1898 , on "focusin" inputFocusDecoder 2119 1899 , style "height" "calc(var(--vh, 1vh) * 100)" 2120 1900 ] ··· 2154 1934 (\targetTagName -> 2155 1935 case targetTagName of 2156 1936 "INPUT" -> 2157 - Json.Decode.succeed FocusedOnInput 1937 + interfaceEventHandler Interface.FocusedOnInput 2158 1938 2159 1939 "TEXTAREA" -> 2160 - Json.Decode.succeed FocusedOnInput 1940 + interfaceEventHandler Interface.FocusedOnInput 2161 1941 2162 1942 _ -> 2163 1943 Json.Decode.fail "NOT_INPUT" 2164 1944 ) 1945 + 1946 + 1947 + interfaceEventHandler : Interface.Msg -> Json.Decode.Decoder UI.Msg 1948 + interfaceEventHandler = 1949 + Interface >> Json.Decode.succeed 2165 1950 2166 1951 2167 1952 loadingAnimation : Html msg ··· 2176 1961 Maybe.isJust maybeAlfred || Maybe.isJust maybeContextMenu 2177 1962 in 2178 1963 brick 2179 - [ onClick HideOverlay ] 1964 + [ onClick (Interface Interface.HideOverlay) ] 2180 1965 [ C.inset_0 2181 1966 , C.bg_black 2182 1967 , C.fixed
+34
src/Applications/UI/Common/State.elm
··· 1 + module UI.Common.State exposing (..) 2 + 3 + import Monocle.Lens as Lens exposing (Lens) 4 + import Notifications exposing (Notification) 5 + import Return 6 + import UI.Notifications 7 + import UI.Reply exposing (Reply) 8 + import UI.Types as UI 9 + 10 + 11 + 12 + -- 📣 13 + 14 + 15 + showNotification : Notification Reply -> UI.Manager 16 + showNotification notification model = 17 + model.notifications 18 + |> UI.Notifications.show notification 19 + |> Return.map (\n -> { model | isLoading = False, notifications = n }) 20 + |> Return.mapCmd UI.Reply 21 + 22 + 23 + showNotificationWithModel : UI.Model -> Notification Reply -> ( UI.Model, Cmd UI.Msg ) 24 + showNotificationWithModel model notification = 25 + showNotification notification model 26 + 27 + 28 + 29 + -- 🛠 30 + 31 + 32 + modifySingleton : Lens a b -> (b -> b) -> a -> ( a, Cmd msg ) 33 + modifySingleton lens modifier = 34 + Lens.modify lens modifier >> Return.singleton
+333
src/Applications/UI/Interface/State.elm
··· 1 + module UI.Interface.State exposing (..) 2 + 3 + import Alien 4 + import Browser.Events 5 + import Common exposing (Switch(..)) 6 + import Debouncer.Basic as Debouncer 7 + import Keyboard 8 + import Management 9 + import Maybe.Extra as Maybe 10 + import Monocle.Lens as Lens 11 + import Notifications 12 + import Return exposing (return) 13 + import Return.Ext as Return exposing (communicate) 14 + import Time 15 + import UI.Authentication as Authentication 16 + import UI.Common.State as Common exposing (modifySingleton) 17 + import UI.DnD as DnD 18 + import UI.Interface.Types as Interface exposing (Msg(..)) 19 + import UI.Page as Page 20 + import UI.Ports as Ports 21 + import UI.Queue as Queue 22 + import UI.Queue.State as Queue 23 + import UI.Reply as Reply 24 + import UI.Sources.State as Sources 25 + import UI.Tracks as Tracks 26 + import UI.Tracks.Scene.List 27 + import UI.Tracks.State as Tracks 28 + import UI.Types as UI exposing (..) 29 + import User.Layer exposing (..) 30 + 31 + 32 + 33 + -- 📣 34 + 35 + 36 + update : Interface.Msg -> Manager 37 + update msg = 38 + case msg of 39 + Blur -> 40 + blur 41 + 42 + Debounce a -> 43 + debounce a 44 + 45 + FocusedOnInput -> 46 + focusedOnInput 47 + 48 + HideOverlay -> 49 + hideOverlay 50 + 51 + KeyboardMsg a -> 52 + keyboardMsg a 53 + 54 + PreferredColorSchemaChanged a -> 55 + preferredColorSchemaChanged a 56 + 57 + RemoveQueueSelection -> 58 + removeQueueSelection 59 + 60 + RemoveTrackSelection -> 61 + removeTrackSelection 62 + 63 + ResizedWindow a -> 64 + resizedWindow a 65 + 66 + SetCurrentTime a -> 67 + setCurrentTime a 68 + 69 + SetIsOnline a -> 70 + setIsOnline a 71 + 72 + SetIsTouchDevice a -> 73 + setIsTouchDevice a 74 + 75 + ShowNotification a -> 76 + Common.showNotification a 77 + 78 + StoppedDragging -> 79 + stoppedDragging 80 + 81 + ToggleLoadingScreen a -> 82 + toggleLoadingScreen a 83 + 84 + 85 + 86 + -- 📰 87 + 88 + 89 + subscriptions : UI.Model -> Sub UI.Msg 90 + subscriptions _ = 91 + Sub.map UI.Interface <| 92 + Sub.batch 93 + [ Ports.indicateTouchDevice (\_ -> SetIsTouchDevice True) 94 + , Ports.preferredColorSchemaChanged PreferredColorSchemaChanged 95 + , Ports.setIsOnline SetIsOnline 96 + , Ports.showErrorNotification (Notifications.error >> ShowNotification) 97 + , Ports.showStickyErrorNotification (Notifications.stickyError >> ShowNotification) 98 + 99 + -- Resize 100 + --------- 101 + , Browser.Events.onResize 102 + (\w h -> 103 + ( w, h ) 104 + |> ResizedWindow 105 + |> Debouncer.provideInput 106 + |> Debounce 107 + ) 108 + 109 + -- 110 + , Sub.map KeyboardMsg Keyboard.subscriptions 111 + , Time.every (60 * 1000) SetCurrentTime 112 + ] 113 + 114 + 115 + 116 + -- 🔱 117 + 118 + 119 + blur : UI.Manager 120 + blur model = 121 + Return.singleton { model | focusedOnInput = False } 122 + 123 + 124 + debounce : Debouncer.Msg Interface.Msg -> UI.Manager 125 + debounce debouncerMsg model = 126 + let 127 + ( subModel, subCmd, emittedMsg ) = 128 + Debouncer.update debouncerMsg model.debounce 129 + 130 + mappedCmd = 131 + Cmd.map (Debounce >> UI.Interface) subCmd 132 + 133 + updatedModel = 134 + { model | debounce = subModel } 135 + in 136 + case emittedMsg of 137 + Just emitted -> 138 + updatedModel 139 + |> update emitted 140 + |> Return.command mappedCmd 141 + 142 + Nothing -> 143 + return updatedModel mappedCmd 144 + 145 + 146 + focusedOnInput : UI.Manager 147 + focusedOnInput model = 148 + Return.singleton { model | focusedOnInput = True } 149 + 150 + 151 + hideOverlay : UI.Manager 152 + hideOverlay model = 153 + Return.singleton 154 + { model 155 + | alfred = { instance = Nothing } 156 + , confirmation = Nothing 157 + , contextMenu = Nothing 158 + } 159 + 160 + 161 + keyboardMsg : Keyboard.Msg -> UI.Manager 162 + keyboardMsg msg model = 163 + (\m -> 164 + let 165 + skip = 166 + Return.singleton m 167 + 168 + authenticated = 169 + case model.authentication of 170 + Authentication.Authenticated _ -> 171 + True 172 + 173 + _ -> 174 + False 175 + in 176 + if m.focusedOnInput || not authenticated then 177 + -- Stop here if using input or not authenticated 178 + skip 179 + 180 + else 181 + case m.pressedKeys of 182 + [ Keyboard.Escape ] -> 183 + hideOverlay m 184 + 185 + [ Keyboard.ArrowLeft ] -> 186 + Return.performance (Reply Reply.RewindQueue) m 187 + 188 + [ Keyboard.ArrowRight ] -> 189 + Return.performance (Reply Reply.ShiftQueue) m 190 + 191 + [ Keyboard.ArrowUp ] -> 192 + Return.performance (Reply (Reply.Seek <| (m.audio.position - 10) / m.audio.duration)) m 193 + 194 + [ Keyboard.ArrowDown ] -> 195 + Return.performance (Reply (Reply.Seek <| (m.audio.position + 10) / m.audio.duration)) m 196 + 197 + [ Keyboard.Character "N" ] -> 198 + Return.performance (Reply Reply.ScrollToNowPlaying) m 199 + 200 + [ Keyboard.Character "P" ] -> 201 + Return.performance (Reply Reply.TogglePlayPause) m 202 + 203 + [ Keyboard.Character "R" ] -> 204 + Return.performance (Reply Reply.ToggleRepeat) m 205 + 206 + [ Keyboard.Character "S" ] -> 207 + Return.performance (Reply Reply.ToggleShuffle) m 208 + 209 + _ -> 210 + skip 211 + ) 212 + { model | pressedKeys = Keyboard.update msg model.pressedKeys } 213 + 214 + 215 + preferredColorSchemaChanged : { dark : Bool } -> UI.Manager 216 + preferredColorSchemaChanged { dark } model = 217 + Return.singleton { model | darkMode = dark } 218 + 219 + 220 + removeQueueSelection : UI.Manager 221 + removeQueueSelection = 222 + modifySingleton Queue.lens (\q -> { q | selection = Nothing }) 223 + 224 + 225 + removeTrackSelection : UI.Manager 226 + removeTrackSelection = 227 + modifySingleton Tracks.lens (\t -> { t | selectedTrackIndexes = [] }) 228 + 229 + 230 + resizedWindow : ( Int, Int ) -> UI.Manager 231 + resizedWindow ( width, height ) model = 232 + Return.singleton 233 + { model 234 + | contextMenu = Nothing 235 + , viewport = { height = toFloat height, width = toFloat width } 236 + } 237 + 238 + 239 + setIsOnline : Bool -> UI.Manager 240 + setIsOnline bool model = 241 + if bool then 242 + -- We're caching the user's data in the browser while offline. 243 + -- If we're back online again, sync all the user's data. 244 + (case model.authentication of 245 + Authentication.Authenticated (Dropbox _) -> 246 + syncHypaethralData 247 + 248 + Authentication.Authenticated (RemoteStorage _) -> 249 + syncHypaethralData 250 + 251 + _ -> 252 + Return.singleton 253 + ) 254 + { model | isOnline = True } 255 + 256 + else 257 + -- The app went offline, cache everything 258 + -- (if caching is supported). 259 + ( { model | isOnline = False } 260 + , case model.authentication of 261 + Authentication.Authenticated (Dropbox _) -> 262 + Ports.toBrain (Alien.trigger Alien.SyncHypaethralData) 263 + 264 + Authentication.Authenticated (RemoteStorage _) -> 265 + Ports.toBrain (Alien.trigger Alien.SyncHypaethralData) 266 + 267 + _ -> 268 + Cmd.none 269 + ) 270 + 271 + 272 + setCurrentTime : Time.Posix -> UI.Manager 273 + setCurrentTime time model = 274 + model 275 + |> (\m -> { m | currentTime = time }) 276 + |> Lens.modify Sources.lens (\s -> { s | currentTime = time }) 277 + |> Return.singleton 278 + 279 + 280 + setIsTouchDevice : Bool -> UI.Manager 281 + setIsTouchDevice bool model = 282 + Return.singleton { model | isTouchDevice = bool } 283 + 284 + 285 + stoppedDragging : UI.Manager 286 + stoppedDragging model = 287 + let 288 + notDragging = 289 + { model | isDragging = False } 290 + in 291 + -- Depending on where we stopped dragging something, 292 + -- do the appropriate thing. 293 + case model.page of 294 + Page.Queue _ -> 295 + -- TODO! 296 + DnD.stoppedDragging 297 + |> Queue.DragMsg 298 + |> QueueMsg 299 + |> Return.performanceF notDragging 300 + 301 + Page.Index -> 302 + case model.tracks.scene of 303 + Tracks.List -> 304 + -- TODO! 305 + DnD.stoppedDragging 306 + |> UI.Tracks.Scene.List.DragAndDropMsg 307 + |> Tracks.ListSceneMsg 308 + |> TracksMsg 309 + |> Return.performanceF notDragging 310 + 311 + _ -> 312 + Return.singleton notDragging 313 + 314 + 315 + toggleLoadingScreen : Switch -> UI.Manager 316 + toggleLoadingScreen switch model = 317 + case switch of 318 + On -> 319 + Return.singleton { model | isLoading = True } 320 + 321 + Off -> 322 + Return.singleton { model | isLoading = False } 323 + 324 + 325 + 326 + -- ⚗️ 327 + 328 + 329 + syncHypaethralData : UI.Manager 330 + syncHypaethralData model = 331 + model 332 + |> Common.showNotification (Notifications.warning "Syncing") 333 + |> Return.command (Ports.toBrain <| Alien.trigger Alien.SyncHypaethralData)
+30
src/Applications/UI/Interface/Types.elm
··· 1 + module UI.Interface.Types exposing (..) 2 + 3 + import Common exposing (Switch) 4 + import Debouncer.Basic as Debouncer exposing (Debouncer) 5 + import Keyboard 6 + import Notifications exposing (Notification) 7 + import Time 8 + import UI.Reply exposing (Reply) 9 + 10 + 11 + 12 + -- 📣 13 + 14 + 15 + type Msg 16 + = Blur 17 + | Debounce (Debouncer.Msg Msg) 18 + | FocusedOnInput 19 + | HideOverlay 20 + | KeyboardMsg Keyboard.Msg 21 + | PreferredColorSchemaChanged { dark : Bool } 22 + | RemoveQueueSelection 23 + | RemoveTrackSelection 24 + | ResizedWindow ( Int, Int ) 25 + | ShowNotification (Notification Reply) 26 + | SetCurrentTime Time.Posix 27 + | SetIsOnline Bool 28 + | SetIsTouchDevice Bool 29 + | StoppedDragging 30 + | ToggleLoadingScreen Switch
+13
src/Applications/UI/Queue/State.elm
··· 1 + module UI.Queue.State exposing (..) 2 + 3 + import Monocle.Lens as Lens exposing (Lens) 4 + 5 + 6 + 7 + -- 🌳 8 + 9 + 10 + lens = 11 + { get = .queue 12 + , set = \queue ui -> { ui | queue = queue } 13 + }
+13
src/Applications/UI/Sources/State.elm
··· 1 + module UI.Sources.State exposing (..) 2 + 3 + import Monocle.Lens as Lens exposing (Lens) 4 + 5 + 6 + 7 + -- 🌳 8 + 9 + 10 + lens = 11 + { get = .sources 12 + , set = \sources ui -> { ui | sources = sources } 13 + }
+13
src/Applications/UI/Tracks/State.elm
··· 1 + module UI.Tracks.State exposing (..) 2 + 3 + import Monocle.Lens as Lens exposing (Lens) 4 + 5 + 6 + 7 + -- 🌳 8 + 9 + 10 + lens = 11 + { get = .tracks 12 + , set = \tracks ui -> { ui | tracks = tracks } 13 + }
+4 -17
src/Applications/UI/Types.elm
··· 13 13 import Coordinates exposing (Viewport) 14 14 import Css exposing (url) 15 15 import Css.Classes as C 16 - import Debouncer.Basic as Debouncer exposing (Debouncer) 16 + import Debouncer.Basic exposing (Debouncer) 17 17 import Dict exposing (Dict) 18 18 import Dict.Ext as Dict 19 19 import File exposing (File) ··· 59 59 import UI.Demo as Demo 60 60 import UI.DnD as DnD 61 61 import UI.Equalizer as Equalizer 62 + import UI.Interface.Types as Interface 62 63 import UI.Navigation as Navigation 63 64 import UI.Notifications 64 65 import UI.Page as Page exposing (Page) ··· 108 109 , confirmation : Maybe String 109 110 , currentTime : Time.Posix 110 111 , darkMode : Bool 111 - , debounce : Debouncer Msg Msg 112 + , debounce : Debouncer Interface.Msg Interface.Msg 112 113 , downloading : Maybe { notificationId : Int } 113 114 , focusedOnInput : Bool 114 115 , isDragging : Bool ··· 152 153 = Bypass 153 154 | Reply Reply 154 155 -- 155 - | Blur 156 - | Debounce (Debouncer.Msg Msg) 157 156 | DownloadTracksFinished 158 - | FocusedOnInput 159 - | HideOverlay 160 - | KeyboardMsg Keyboard.Msg 161 157 | LoadEnclosedUserData Json.Decode.Value 162 158 | LoadHypaethralUserData Json.Decode.Value 163 - | PreferredColorSchemaChanged { dark : Bool } 164 - | RemoveQueueSelection 165 - | RemoveTrackSelection 166 - | ResizedWindow ( Int, Int ) 167 - | ShowNotification (Notification Reply) 168 - | SetCurrentTime Time.Posix 169 - | SetIsOnline Bool 170 - | SetIsTouchDevice Bool 171 - | StoppedDragging 172 - | ToggleLoadingScreen Switch 173 159 ----------------------------------------- 174 160 -- Authentication 175 161 ----------------------------------------- ··· 217 203 -- TARGET => Pieces 218 204 ----------------------------------------- 219 205 | Audio Audio.Msg 206 + | Interface Interface.Msg 220 207 221 208 222 209 type alias Organizer model =
+5
src/Library/Return/Ext.elm
··· 20 20 |> Task.succeed 21 21 |> Task.perform identity 22 22 |> Tuple.pair model 23 + 24 + 25 + performanceF : model -> msg -> ( model, Cmd msg ) 26 + performanceF model msg = 27 + performance msg model