diff --git a/Justfile b/Justfile index 555ef4d1..bddf1be4 100644 --- a/Justfile +++ b/Justfile @@ -77,7 +77,7 @@ default: dev @elm: echo "> Compiling Elm application" elm make {{SRC_DIR}}/Applications/Brain.elm --output {{BUILD_DIR}}/brain.elm.js - elm make {{SRC_DIR}}/Applications/UI.elm --output {{BUILD_DIR}}/ui.elm.js + elm make {{SRC_DIR}}/Applications/UI.elm --output {{BUILD_DIR}}/ui.elm.js --debug @elm-prod: diff --git a/src/Applications/UI/Adjunct.elm b/src/Applications/UI/Adjunct.elm index 7bab990f..57435279 100644 --- a/src/Applications/UI/Adjunct.elm +++ b/src/Applications/UI/Adjunct.elm @@ -6,7 +6,7 @@ import Return import Return.Ext as Return import UI.Alfred.State as Alfred import UI.Audio.State as Audio -import UI.Authentication.Types as Authentication +import UI.Authentication.Common as Authentication import UI.Common.State as Common import UI.Interface.State exposing (hideOverlay) import UI.Page as Page @@ -32,12 +32,7 @@ keyboardInput msg model = Return.singleton m authenticated = - case model.authentication of - Authentication.Authenticated _ -> - True - - _ -> - False + Authentication.isAuthenticated model.authentication in if not authenticated || (m.focusedOnInput && Maybe.isNothing model.alfred) then case m.pressedKeys of diff --git a/src/Applications/UI/Authentication/Common.elm b/src/Applications/UI/Authentication/Common.elm index 25400b33..6dab592a 100644 --- a/src/Applications/UI/Authentication/Common.elm +++ b/src/Applications/UI/Authentication/Common.elm @@ -8,6 +8,16 @@ import User.Layer exposing (Method) -- 🛠 +isAuthenticated : State -> Bool +isAuthenticated state = + case state of + Authenticated _ -> + True + + _ -> + False + + extractMethod : State -> Maybe Method extractMethod state = case state of diff --git a/src/Applications/UI/Authentication/State.elm b/src/Applications/UI/Authentication/State.elm index 0435c311..6bfbdd49 100644 --- a/src/Applications/UI/Authentication/State.elm +++ b/src/Applications/UI/Authentication/State.elm @@ -29,6 +29,7 @@ import UI.Authentication.Types as Authentication exposing (..) import UI.Backdrop as Backdrop import UI.Common.State as Common exposing (showNotification, showNotificationWithModel) import UI.Ports as Ports +import UI.Sources.Query import UI.Sources.State as Sources import UI.Types as UI exposing (..) import UI.User.State.Import as User @@ -283,21 +284,28 @@ notAuthenticated : Manager notAuthenticated model = -- This is the message we get when the app initially -- finds out we're not authenticated. - andThen - Backdrop.setDefault - (if model.isUpgrading then - """ - Thank you for using Diffuse V1! - If you want to import your old data, - please pick the storage method you used before and - go to the [import page](#/settings/import-export). - """ - |> Notifications.stickySuccess - |> showNotificationWithModel { model | isUpgrading = False } + (if model.isUpgrading then + """ + Thank you for using Diffuse V1! + If you want to import your old data, + please pick the storage method you used before and + go to the [import page](#/settings/import-export). + """ + |> Notifications.stickySuccess + |> showNotificationWithModel { model | isUpgrading = False } + + else + Return.singleton model + ) + |> andThen Backdrop.setDefault + -- When the user wants to create a source (by passing the info through the url) + -- and the user isn't signed in yet, sign in using the "Local" method. + |> (if UI.Sources.Query.requestedAddition model.url then + andThen (signIn Local) - else - Return.singleton model - ) + else + identity + ) remoteStorageWebfinger : RemoteStorage.Attributes -> Result Http.Error String -> Manager diff --git a/src/Applications/UI/Sources/Query.elm b/src/Applications/UI/Sources/Query.elm new file mode 100644 index 00000000..00f8993c --- /dev/null +++ b/src/Applications/UI/Sources/Query.elm @@ -0,0 +1,68 @@ +module UI.Sources.Query exposing (..) + +import Dict +import Json.Decode as Decode +import Sources exposing (Source) +import Url exposing (Url) +import Url.Parser as Url +import Url.Parser.Query as Query + + +requestedAddition : Url -> Bool +requestedAddition url = + case Url.parse (urlParser identity) url of + Nothing -> + False + + Just [] -> + False + + Just (_ :: _) -> + True + + +sourcesFromUrl : Url -> List Source +sourcesFromUrl url = + url + |> Url.parse (urlParser <| List.filterMap fromUrl) + |> Maybe.withDefault [] + + + +-- 🔬 + + +fromUrl : String -> Maybe Source +fromUrl json = + json + |> Decode.decodeString sourceParser + |> Result.toMaybe + + +urlParser individualParser = + individualParser + |> Query.custom "source" + |> Url.query + + +sourceParser : Decode.Decoder Source +sourceParser = + Decode.andThen + (\{ service, data } -> + if Dict.member "name" data then + Decode.succeed + { id = "FILL_IN_LATER" + , data = data + , directoryPlaylists = True + , enabled = True + , service = service + } + + else + Decode.fail "Missing `name` in `data` dictionary" + ) + <| + Decode.map2 + (\s d -> { service = s, data = d }) + (Decode.field "kind" Sources.serviceDecoder) + (Decode.field "data" <| Decode.dict Decode.string) diff --git a/src/Applications/UI/Sources/State.elm b/src/Applications/UI/Sources/State.elm index 6da30d93..c27e17ca 100644 --- a/src/Applications/UI/Sources/State.elm +++ b/src/Applications/UI/Sources/State.elm @@ -26,6 +26,7 @@ import UI.Ports as Ports import UI.Sources.ContextMenu as Sources import UI.Sources.Form as Form import UI.Sources.Page as Sources +import UI.Sources.Query import UI.Sources.Types exposing (..) import UI.Tracks.State as Tracks import UI.Types as UI exposing (Manager, Model) @@ -150,6 +151,24 @@ update msg = -- 🔱 +addSourcesFromUrl : Manager +addSourcesFromUrl model = + case UI.Sources.Query.sourcesFromUrl model.url of + [] -> + Return.singleton model + + sources -> + sources + |> List.foldl + (\s -> andThen <| addToCollection s) + (Return.singleton model) + |> Return.command + (Nav.replaceUrl + model.navKey + (model.url.path ++ Page.toString model.page) + ) + + finishedProcessing : Manager finishedProcessing model = (case model.processingNotificationId of diff --git a/src/Applications/UI/User/State/Import.elm b/src/Applications/UI/User/State/Import.elm index 9c1563f7..20e762a2 100644 --- a/src/Applications/UI/User/State/Import.elm +++ b/src/Applications/UI/User/State/Import.elm @@ -125,6 +125,8 @@ loadHypaethralUserData json model = else Return.singleton m ) + |> andThen + Sources.addSourcesFromUrl |> andThen (\m -> if m.processAutomatically then diff --git a/src/Library/Sources.elm b/src/Library/Sources.elm index 70f8a4cb..530df661 100644 --- a/src/Library/Sources.elm +++ b/src/Library/Sources.elm @@ -1,7 +1,8 @@ -module Sources exposing (Property, Service(..), Source, SourceData, enabledSourceIds, setProperId) +module Sources exposing (..) import Conditional exposing (..) import Dict exposing (Dict) +import Json.Decode import Time @@ -49,6 +50,36 @@ type Service | WebDav +serviceDictionary : Dict String Service +serviceDictionary = + Dict.fromList + [ ( "amazons3", AmazonS3 ) + , ( "amazon_s3", AmazonS3 ) + , ( "azureblob", AzureBlob ) + , ( "azure_blob", AzureBlob ) + , ( "azurefile", AzureFile ) + , ( "azure_file", AzureFile ) + , ( "btfs", Btfs ) + , ( "dropbox", Dropbox ) + , ( "google", Google ) + , ( "ipfs", Ipfs ) + , ( "webdav", WebDav ) + , ( "web_dav", WebDav ) + ] + + +serviceDecoder : Json.Decode.Decoder Service +serviceDecoder = + Json.Decode.andThen + (\string -> + serviceDictionary + |> Dict.get string + |> Maybe.map Json.Decode.succeed + |> Maybe.withDefault (Json.Decode.fail "Invalid source kind") + ) + Json.Decode.string + + --- 🔱