From 014ac6560986fec0db1f440ae974c7833b49ef96 Mon Sep 17 00:00:00 2001 From: Christopher Bertels Date: Sat, 17 Mar 2018 12:48:49 +0100 Subject: [PATCH] WIP: Key export / import Related to #13 --- src/Daemon.elm | 20 ++++++++++++ src/MainScreen.elm | 20 ++++++++++++ src/Model.elm | 3 ++ src/Ports.elm | 8 +++++ src/SettingsDialog/View.elm | 12 +++++++ src/SetupWizard.elm | 42 ++++--------------------- src/WizardDialog/View.elm | 45 +++++++++++++++++++++++++++ static/MainScreen.scss | 62 +++++-------------------------------- static/WizardDialog.scss | 48 ++++++++++++++++++++++++++++ static/ports.js | 11 +++++++ 10 files changed, 180 insertions(+), 91 deletions(-) create mode 100644 src/WizardDialog/View.elm diff --git a/src/Daemon.elm b/src/Daemon.elm index a54a81f..7db410c 100644 --- a/src/Daemon.elm +++ b/src/Daemon.elm @@ -5,6 +5,7 @@ module Daemon , addVaultUser , attemptDelayed , deleteVault + , exportUserKey , exportVault , getConfig , getFlyingVault @@ -74,6 +75,7 @@ type ApiPath | Login | LoginCheck | Logout + | ExportUserKey type ApiStreamPath @@ -422,6 +424,21 @@ exportVault vaultId path { config } = |> Cmd.map (ExportedVault vaultId) +exportUserKey : String -> Model -> Cmd Msg +exportUserKey path { config } = + let + json = + Json.Encode.object [ ( "path", Json.Encode.string path ) ] + in + config + |> apiRequest + Post + ExportUserKey + (Json json) + exportStatusResponseDecoder + |> Cmd.map ExportedUserKey + + type alias Path = String @@ -507,6 +524,9 @@ apiPath apiPath = Logout -> "auth/logout" + ExportUserKey -> + "user_key_export" + {-| Converts `RequestMethod` into `String`. diff --git a/src/MainScreen.elm b/src/MainScreen.elm index f92cb9d..c76a1b0 100644 --- a/src/MainScreen.elm +++ b/src/MainScreen.elm @@ -70,6 +70,7 @@ subscriptions model = , Time.every Time.second (Date.fromTime >> SetTime) , Time.every model.config.updateInterval (\_ -> UpdateStats) , Ports.getEmailCompletionList EmailCompletionList + , Ports.selectedUserKeyExportFile SelectedUserKeyExportFile ] _ -> @@ -366,6 +367,25 @@ update msg model = , Cmd.none ) + OpenUserKeyExportDialog -> + ( model + , Ports.openUserKeyExportFileDialog "Export Key to file" + ) + + SelectedUserKeyExportFile filePath -> + let + _ = + Debug.log "SelectedUserKeyExportFile" filePath + in + ( model + , Daemon.exportUserKey filePath model + ) + + ExportedUserKey _ -> + ( model + , Cmd.none + ) + setSetupWizardEmail : String -> Model -> Model setSetupWizardEmail email ({ setupWizard, loginDialog } as model) = diff --git a/src/Model.elm b/src/Model.elm index 26c0ccd..1c5d528 100644 --- a/src/Model.elm +++ b/src/Model.elm @@ -136,6 +136,9 @@ type Msg | SendPasswordResetLink | SetupWizardEmail String | SetupWizardPassword String + | OpenUserKeyExportDialog + | SelectedUserKeyExportFile String + | ExportedUserKey (WebData ExportStatusResponse) diff --git a/src/Ports.elm b/src/Ports.elm index 68ca4fc..0a69408 100644 --- a/src/Ports.elm +++ b/src/Ports.elm @@ -4,7 +4,9 @@ port module Ports , focusOn , getEmailCompletionList , openPasswordResetInBrowser + , openUserKeyExportFileDialog , openVaultFolder + , selectedUserKeyExportFile , updateEmailCompletionList ) @@ -25,3 +27,9 @@ port getEmailCompletionList : (List String -> msg) -> Sub msg port openPasswordResetInBrowser : () -> Cmd msg + + +port openUserKeyExportFileDialog : String -> Cmd msg + + +port selectedUserKeyExportFile : (String -> msg) -> Sub msg diff --git a/src/SettingsDialog/View.elm b/src/SettingsDialog/View.elm index 56eaab7..0ce4aa2 100644 --- a/src/SettingsDialog/View.elm +++ b/src/SettingsDialog/View.elm @@ -53,6 +53,10 @@ contents model = , languageButton German model , languageButton English model , separator + , div [ class "InfoLabel" ] + [ text "Key export / import" ] + , keyExportButton model + , separator , div [ class "InfoLabel" ] [ text <| dialogText T.AccountOptions model ] , changePasswordButton model @@ -61,6 +65,14 @@ contents model = ] +keyExportButton : HasSettingsDialog a -> Html Model.Msg +keyExportButton model = + button [ class "Button" ] + { onClick = Model.OpenUserKeyExportDialog + , label = "Export Key" + } + + changePasswordButton : HasSettingsDialog a -> Html Model.Msg changePasswordButton model = button [ class "Button" ] diff --git a/src/SetupWizard.elm b/src/SetupWizard.elm index 6bff3e9..22aacde 100644 --- a/src/SetupWizard.elm +++ b/src/SetupWizard.elm @@ -8,6 +8,12 @@ import Language exposing (Language(..)) import Model import Util exposing (ButtonSettings, Position(..), button) import WizardDialog.Model exposing (..) +import WizardDialog.View + exposing + ( infoText + , infoTextWithHeader + , infoTextWithHeaders + ) steps : List (StepConfig Model.Model Model.Msg) @@ -41,42 +47,6 @@ viewSettings state model = WizardDialog.Model.viewSettings steps state model -infoTextLine : String -> Html msg -infoTextLine line = - span [] - [ text line ] - - -infoText : List (Html.Attribute msg) -> List String -> Html msg -infoText attrs lines = - div [ class "InfoText" ] - [ div attrs - (List.map infoTextLine lines) - ] - - -infoTextWithHeader : List (Html.Attribute msg) -> String -> List String -> Html msg -infoTextWithHeader attrs header lines = - div [ class "InfoText" ] - [ div attrs <| - span [ class "Header" ] - [ text header ] - :: List.map infoTextLine lines - ] - - -infoTextWithHeaders : List (Html.Attribute msg) -> String -> String -> List String -> Html msg -infoTextWithHeaders attrs header subHeader lines = - div [ class "InfoText" ] - [ div attrs <| - span [ class "Header" ] - [ text header ] - :: span [ class "SubHeader" ] - [ text subHeader ] - :: List.map infoTextLine lines - ] - - -- STEPS diff --git a/src/WizardDialog/View.elm b/src/WizardDialog/View.elm new file mode 100644 index 0000000..cf7c970 --- /dev/null +++ b/src/WizardDialog/View.elm @@ -0,0 +1,45 @@ +module WizardDialog.View + exposing + ( infoText + , infoTextWithHeader + , infoTextWithHeaders + ) + +import Html exposing (Html, div, span, text) +import Html.Attributes exposing (class) + + +infoTextLine : String -> Html msg +infoTextLine line = + span [] + [ text line ] + + +infoText : List (Html.Attribute msg) -> List String -> Html msg +infoText attrs lines = + div [ class "InfoText" ] + [ div attrs + (List.map infoTextLine lines) + ] + + +infoTextWithHeader : List (Html.Attribute msg) -> String -> List String -> Html msg +infoTextWithHeader attrs header lines = + div [ class "InfoText" ] + [ div attrs <| + span [ class "Header" ] + [ text header ] + :: List.map infoTextLine lines + ] + + +infoTextWithHeaders : List (Html.Attribute msg) -> String -> String -> List String -> Html msg +infoTextWithHeaders attrs header subHeader lines = + div [ class "InfoText" ] + [ div attrs <| + span [ class "Header" ] + [ text header ] + :: span [ class "SubHeader" ] + [ text subHeader ] + :: List.map infoTextLine lines + ] diff --git a/static/MainScreen.scss b/static/MainScreen.scss index e284b6a..157d1ef 100644 --- a/static/MainScreen.scss +++ b/static/MainScreen.scss @@ -50,7 +50,7 @@ li { .MainScreen { width: 70%; height: 100%; - -webkit-transition: width 0.5s; + transition: width 0.5s; } .MainScreen-Container { @@ -167,65 +167,17 @@ li { height: 300px; overflow-y: scroll; - .InfoText { - line-height: ($font-size-regular + $padding-tiny); - + .TermsOfService { span { - line-height: ($font-size-regular + $padding-tiny); - display: block; - } - - .Header { - font-weight: bold; - font-size: $font-size-header; - margin-bottom: $padding-tiny; - } - - .SubHeader { - font-weight: bold; - margin-bottom: $padding-tiny; - } - - .TermsOfService { - span { - margin-top: $padding-tiny; - text-align: justify; - } + margin-top: $padding-tiny; + text-align: justify; } } - .Options { - display: block; - max-width: 50%; - margin-top: $padding-medium; - + .ForgotPasswordButton { button { - margin-top: $padding-small; - margin-right: $padding-small; - } - - .InputLabel { - margin-top: $padding-small; - font-weight: bold; - } - - input { - margin-top: $padding-tiny; - font-size: $font-size-regular; - height: $font-size-small; - width: 200px; - padding-top: $padding-tiny; - padding-bottom: $padding-tiny; - padding-left: $padding-small; - padding-right: $padding-small; - border: $border-radius-input solid #3AE2E2 !important; - } - - .ForgotPasswordButton { - button { - margin-top: $padding-medium; - margin-bottom: $padding-small; - } + margin-top: $padding-medium; + margin-bottom: $padding-small; } } } diff --git a/static/WizardDialog.scss b/static/WizardDialog.scss index cf8a1f8..dfe8381 100644 --- a/static/WizardDialog.scss +++ b/static/WizardDialog.scss @@ -49,4 +49,52 @@ .Button-Close { float: right; } + + .InfoText { + line-height: ($font-size-regular + $padding-tiny); + + span { + line-height: ($font-size-regular + $padding-tiny); + display: block; + } + + .Header { + font-weight: bold; + font-size: $font-size-header; + margin-bottom: $padding-tiny; + } + + .SubHeader { + font-weight: bold; + margin-bottom: $padding-tiny; + } + } + + .Options { + display: block; + max-width: 50%; + margin-top: $padding-medium; + + button { + margin-top: $padding-small; + margin-right: $padding-small; + } + + .InputLabel { + margin-top: $padding-small; + font-weight: bold; + } + + input { + margin-top: $padding-tiny; + font-size: $font-size-regular; + height: $font-size-small; + width: 200px; + padding-top: $padding-tiny; + padding-bottom: $padding-tiny; + padding-left: $padding-small; + padding-right: $padding-small; + border: $border-radius-input solid #3AE2E2 !important; + } + } } diff --git a/static/ports.js b/static/ports.js index d763087..9c18fb7 100644 --- a/static/ports.js +++ b/static/ports.js @@ -138,6 +138,16 @@ const openPasswordResetInBrowser = () => { } } +const openUserKeyExportFileDialog = (buttonLabel) => { + Electron.remote.dialog.showSaveDialog({ + title: "Select file to export your key to", + buttonLabel: buttonLabel, + filters: [{ name: "User Key Export Archive", extensions: ["zip"] }] + }, (file) => { + elmApp.ports.selectedUserKeyExportFile.send(file); + }); +} + var setupElmApp = function (daemonApiToken) { elmApp = Elm.Main.embed(mainContainer, { apiAuthToken: daemonApiToken, @@ -155,6 +165,7 @@ var setupElmApp = function (daemonApiToken) { elmApp.ports.addEmailToCompletionList.subscribe(addEmailToCompletionList) elmApp.ports.updateEmailCompletionList.subscribe(updateEmailCompletionList) elmApp.ports.openPasswordResetInBrowser.subscribe(openPasswordResetInBrowser) + elmApp.ports.openUserKeyExportFileDialog.subscribe(openUserKeyExportFileDialog) } readAuthToken(setupElmApp)