···2222import UI.Adjunct as Adjunct
2323import UI.Alfred.State as Alfred
2424import UI.Audio.State as Audio
2525-import UI.Authentication.State as Authentication
2626-import UI.Authentication.Types as Authentication
2725import UI.Backdrop as Backdrop
2826import UI.Common.State as Common
2927import UI.DnD as DnD
···4038import UI.Sources.Form
4139import UI.Sources.State as Sources
4240import UI.Sources.Types as Sources
4141+import UI.Syncing.State as Syncing
4242+import UI.Syncing.Types as Syncing
4343import UI.Tracks.State as Tracks
4444import UI.Tracks.Types as Tracks
4545import UI.Types exposing (..)
4646-import UI.User.State as User
4746import UI.User.State.Export as User
4847import UI.User.State.Import as User
4948import UI.View exposing (view)
5049import Url exposing (Url)
5150import Url.Ext as Url
5151+import User.Layer as User
525253535454···104104 , isTouchDevice = False
105105 , isUpgrading = flags.upgrade
106106 , lastFm = LastFm.initialModel
107107- , migratingData = False
108107 , navKey = key
109108 , page = page
110109 , pressedKeys = []
···217216 -----------------------------------------
218217 -- 🦉 Nested
219218 -----------------------------------------
220220- , authentication = Authentication.initialModel url
219219+ , syncing = Syncing.initialModel url
221220 }
222221 |> Routing.transition
223222 page
224223 |> Return.command
225224 (url
226226- |> Authentication.initialCommand
227227- |> Cmd.map AuthenticationMsg
225225+ |> Syncing.initialCommand
226226+ |> Cmd.map SyncingMsg
228227 )
229228 |> Return.command
230229 (if Maybe.isNothing maybePage then
···472471 -----------------------------------------
473472 Export ->
474473 User.export
475475-476476- GotWebnativeResponse a ->
477477- User.gotWebnativeResponse a
478474479475 ImportFile a ->
480476 User.importFile a
···482478 ImportJson a ->
483479 User.importJson a
484480485485- ImportLegacyData ->
486486- User.importLegacyData
487487-488481 InsertDemo ->
489482 User.insertDemo
490483···493486494487 LoadHypaethralUserData a ->
495488 User.loadHypaethralUserData a
496496-497497- MigrateHypaethralUserData ->
498498- User.migrateHypaethralUserData
499489500490 RequestImport ->
501491 User.requestImport
···512502 -----------------------------------------
513503 -- 🦉 Nested
514504 -----------------------------------------
515515- AuthenticationMsg a ->
516516- Authentication.update a
505505+ SyncingMsg a ->
506506+ Syncing.update a
517507518508 QueueMsg a ->
519509 Queue.update a
···608598 -----------------------------------------
609599 -- 📭 Other
610600 -----------------------------------------
601601+ , Ports.collectedFissionCapabilities (\_ -> SyncingMsg <| Syncing.ActivateSync <| User.Fission {})
611602 , Ports.installedNewServiceWorker (\_ -> InstalledServiceWorker)
612603 , Ports.installingNewServiceWorker (\_ -> InstallingServiceWorker)
613604 , Ports.refreshedAccessToken (Alien.broadcast Alien.RefreshedAccessToken >> RedirectToBrain)
614605 , Ports.setIsOnline SetIsOnline
615615- , Ports.webnativeResponse GotWebnativeResponse
616606 , Sub.map KeyboardMsg Keyboard.subscriptions
617607 , Time.every (60 * 1000) SetCurrentTime
618608 ]
···641631 Alien.AddTracks ->
642632 TracksMsg (Tracks.Add data)
643633644644- Alien.AuthMethod ->
645645- AuthenticationMsg (Authentication.SignedIn data)
646646-647634 Alien.FinishedProcessingSource ->
648635 SourcesMsg (Sources.FinishedProcessingSource data)
649636···656643 Alien.HideLoadingScreen ->
657644 ToggleLoadingScreen Off
658645659659- Alien.ImportLegacyData ->
660660- ShowNotification (Notifications.success "Imported data successfully!")
661661-662646 Alien.LoadEnclosedUserData ->
663647 LoadEnclosedUserData data
664648665649 Alien.LoadHypaethralUserData ->
666650 LoadHypaethralUserData data
667667-668668- Alien.MissingSecretKey ->
669669- AuthenticationMsg (Authentication.MissingSecretKey data)
670670-671671- Alien.NotAuthenticated ->
672672- AuthenticationMsg Authentication.NotAuthenticated
673651674652 Alien.ReloadTracks ->
675653 TracksMsg (Tracks.Reload data)
···686664 Alien.SearchTracks ->
687665 TracksMsg (Tracks.SetSearchResults data)
688666667667+ Alien.StartedSyncing ->
668668+ SyncingMsg (Syncing.StartedSyncing data)
669669+689670 Alien.StoreTracksInCache ->
690671 TracksMsg (Tracks.StoredInCache data Nothing)
672672+673673+ Alien.SyncMethod ->
674674+ SyncingMsg (Syncing.GotSyncMethod data)
691675692676 Alien.UpdateSourceData ->
693677 SourcesMsg (Sources.UpdateSourceData data)
···699683translateAlienError : Alien.Tag -> Json.Value -> String -> Msg
700684translateAlienError tag data err =
701685 case tag of
702702- Alien.AuthAnonymous ->
703703- AuthenticationMsg (Authentication.BootFailure err)
704704-705705- Alien.AuthDropbox ->
706706- AuthenticationMsg (Authentication.BootFailure err)
707707-708708- Alien.AuthIpfs ->
709709- AuthenticationMsg (Authentication.BootFailure err)
710710-711711- Alien.AuthRemoteStorage ->
712712- AuthenticationMsg (Authentication.BootFailure err)
713713-714686 Alien.StoreTracksInCache ->
715687 TracksMsg (Tracks.StoredInCache data <| Just err)
716688717689 _ ->
718718- ShowNotification (Notifications.stickyError err)
690690+ if String.startsWith "There seems to be existing data that's encrypted, I will need the passphrase" err then
691691+ SyncingMsg (Syncing.NeedEncryptionKey { error = err })
692692+693693+ else
694694+ ShowNotification (Notifications.stickyError err)
+1-5
src/Applications/UI/Adjunct.elm
···55import Return
66import UI.Alfred.State as Alfred
77import UI.Audio.State as Audio
88-import UI.Authentication.Common as Authentication
98import UI.Commands.State as Commands
109import UI.Common.State as Common
1110import UI.Interface.State exposing (hideOverlay)
···3029 let
3130 skip =
3231 Return.singleton m
3333-3434- authenticated =
3535- Authentication.isAuthenticated model.authentication
3632 in
3737- if not authenticated || (m.focusedOnInput && Maybe.isNothing model.alfred) then
3333+ if m.focusedOnInput && Maybe.isNothing model.alfred then
3834 case m.pressedKeys of
3935 [ Keyboard.Escape ] ->
4036 hideOverlay m
-43
src/Applications/UI/Authentication/Common.elm
···11-module UI.Authentication.Common exposing (..)
22-33-import UI.Authentication.Types exposing (..)
44-import User.Layer exposing (Method)
55-66-77-88--- 🛠
99-1010-1111-isAuthenticated : State -> Bool
1212-isAuthenticated state =
1313- case state of
1414- Authenticated _ ->
1515- True
1616-1717- _ ->
1818- False
1919-2020-2121-extractMethod : State -> Maybe Method
2222-extractMethod state =
2323- case state of
2424- Authenticated method ->
2525- Just method
2626-2727- Authenticating ->
2828- Nothing
2929-3030- InputScreen method _ ->
3131- Just method
3232-3333- NewEncryptionKeyScreen method _ ->
3434- Just method
3535-3636- UpdateEncryptionKeyScreen method _ ->
3737- Just method
3838-3939- Unauthenticated ->
4040- Nothing
4141-4242- Welcome ->
4343- Nothing
···5656 , title = "Import data (⚠️ will override current data)"
5757 , value = Command UI.RequestImport
5858 }
5959- , { icon = Just (Icons.save 16)
6060- , title = "Migrate user data to different storage"
6161- , value = Command UI.MigrateHypaethralUserData
6262- }
6359 ]
64606561
+16
src/Applications/UI/Common/State.elm
···1313import UI.Notifications
1414import UI.Page as Page exposing (Page)
1515import UI.Playlists.Directory
1616+import UI.Syncing.Types as Syncing
1617import UI.Tracks.Scene.Covers
1718import UI.Tracks.Scene.List
1819import UI.Types as UI exposing (Manager, Msg)
2020+import User.Layer exposing (Method)
192120222123···8991showNotificationWithModel : UI.Model -> Notification Msg -> ( UI.Model, Cmd UI.Msg )
9092showNotificationWithModel model notification =
9193 showNotification notification model
9494+9595+9696+showSyncingNotification : Method -> Manager
9797+showSyncingNotification method model =
9898+ let
9999+ notification =
100100+ Notifications.stickyCasual "Syncing user data ..."
101101+102102+ syncing =
103103+ Syncing.Syncing { method = method, notificationId = Notifications.id notification }
104104+ in
105105+ showNotification
106106+ notification
107107+ { model | syncing = syncing }
921089310994110toggleLoadingScreen : Switch -> Manager
···11-module UI.User.State exposing (..)
22-33-import Return
44-import UI.Authentication.State as Authentication
55-import UI.Types exposing (..)
66-import User.Layer as User
77-import Webnative exposing (Artifact(..), DecodedResponse(..))
88-import Webnative.Tag as Tag
99-1010-1111-1212--- 🔱
1313-1414-1515-gotWebnativeResponse : Webnative.Response -> Manager
1616-gotWebnativeResponse response model =
1717- case Webnative.decodeResponse Tag.fromString response of
1818- Webnative (Initialisation state) ->
1919- if Webnative.isAuthenticated state then
2020- Authentication.signIn
2121- (User.Fission { initialised = False })
2222- model
2323-2424- else
2525- Return.singleton model
2626-2727- _ ->
2828- Return.singleton model
2929-3030-3131-migrateHypaethralUserData : Manager
3232-migrateHypaethralUserData model =
3333- Authentication.signOut { model | migratingData = True }
+4
src/Applications/UI/User/State/Export.elm
···88import Return exposing (return)
99import Settings exposing (Settings)
1010import Sources.Encoding as Sources
1111+import Time
1112import Tracks.Encoding as Tracks
1213import UI.Ports as Ports
1314import UI.Types exposing (..)
···2526 , settings = Just (gatherSettings model)
2627 , sources = model.sources
2728 , tracks = model.tracks.untouched
2929+3030+ --
3131+ , modifiedAt = Just model.currentTime
2832 }
2933 |> encodeHypaethralData
3034 |> Json.Encode.encode 2
+1-26
src/Applications/UI/User/State/Import.elm
···11module UI.User.State.Import exposing (..)
2233-import Alien
43import File exposing (File)
54import File.Select
65import Json.Decode
···6564 |> saveAllHypaethralData
666567666868-importLegacyData : Manager
6969-importLegacyData model =
7070- Alien.ImportLegacyData
7171- |> Alien.trigger
7272- |> Ports.toBrain
7373- |> return model
7474- |> andThen
7575- ("""
7676- I'll try to import data from Diffuse version one.
7777- If this was successful, you'll get a notification.
7878- """
7979- |> Notifications.casual
8080- |> Common.showNotification
8181- )
8282-8383-8467insertDemo : Manager
8568insertDemo model =
8669 model
···11699 """
117100 Thank you for using Diffuse V1!
118101 If you want to import your old data,
119119- please go to the [import page](#/settings/import-export).
102102+ please go to the [import page](#/settings/data).
120103 """
121104 |> Notifications.stickySuccess
122105 |> Common.showNotificationWithModel m
···126109 )
127110 |> andThen
128111 Sources.addSourcesFromUrl
129129- |> andThen
130130- (\m ->
131131- if m.processAutomatically then
132132- Sources.process m
133133-134134- else
135135- Return.singleton m
136136- )
137112138113139114requestImport : Manager
···11+module Syncing.Services.Dropbox.Token exposing (..)
22+33+import Time
44+55+66+isExpired : { currentTime : Time.Posix, expiresAt : Int } -> Bool
77+isExpired { currentTime, expiresAt } =
88+ let
99+ currentTimeInSeconds =
1010+ Time.posixToMillis currentTime // 1000
1111+1212+ currentTimeWithOffset =
1313+ -- We add 60 seconds here because we only get the current time every minute,
1414+ -- so there's always the chance the "current time" is 1-60 seconds behind.
1515+ currentTimeInSeconds + 60
1616+ in
1717+ -- If the access token is expired
1818+ currentTimeWithOffset >= expiresAt
+11-1
src/Library/Task/Extra.elm
···11-module Task.Extra exposing (do, doDelayed)
11+module Task.Extra exposing (do, doDelayed, fromResult)
2233import Process
44import Task
···1616doDelayed : Float -> msg -> Cmd msg
1717doDelayed delay msg =
1818 Task.perform (always msg) (Process.sleep delay)
1919+2020+2121+fromResult : Result error value -> Task.Task error value
2222+fromResult result =
2323+ case result of
2424+ Ok v ->
2525+ Task.succeed v
2626+2727+ Err e ->
2828+ Task.fail e