From 846dc646e1edb6acf6da2146c7ed3154d8e7ab9d Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Fri, 12 Jun 2020 16:47:44 -0400 Subject: [PATCH] feat: add design to enter a seed key --- ui/imports/Theme.qml | 1 + ui/main.qml | 37 +-- ui/nim-status-client.pro | 3 + ui/onboarding/CreatePasswordModal.qml | 136 ++++++++++ ui/onboarding/EnterSeedPhraseModal.qml | 83 ++++++ ui/onboarding/ExistingKey.qml | 237 ++---------------- ui/onboarding/Login.qml | 32 ++- .../Login/ConfirmAddExistingKeyModal.qml | 37 +++ .../Login/SelectAnotherAccountModal.qml | 7 +- ui/onboarding/Login/qmldir | 1 + ui/onboarding/qmldir | 3 + 11 files changed, 335 insertions(+), 242 deletions(-) create mode 100644 ui/onboarding/CreatePasswordModal.qml create mode 100644 ui/onboarding/EnterSeedPhraseModal.qml create mode 100644 ui/onboarding/Login/ConfirmAddExistingKeyModal.qml diff --git a/ui/imports/Theme.qml b/ui/imports/Theme.qml index 10e76bd847..a75328fab6 100644 --- a/ui/imports/Theme.qml +++ b/ui/imports/Theme.qml @@ -16,6 +16,7 @@ QtObject { readonly property color darkBlueBtn: "#5a70dd" readonly property color red: "#FF2D55" + readonly property int xlPadding: 32 readonly property int bigPadding: 24 readonly property int padding: 16 readonly property int smallPadding: 10 diff --git a/ui/main.qml b/ui/main.qml index 7a6f21fee3..ce3f1d1cb8 100644 --- a/ui/main.qml +++ b/ui/main.qml @@ -57,12 +57,6 @@ ApplicationWindow { id: keysMainState onEntered: loader.sourceComponent = keysMain - DSM.SignalTransition { - targetState: existingKeyState - signal: applicationWindow.navigateTo - guard: path === "ExistingKey" - } - DSM.SignalTransition { targetState: genKeyState signal: applicationWindow.navigateTo @@ -90,12 +84,6 @@ ApplicationWindow { signal: onboardingModel.loginResponseChanged guard: !error } - - DSM.SignalTransition { - targetState: existingKeyState - signal: applicationWindow.navigateTo - guard: path === "ExistingKey" - } } DSM.State { @@ -115,6 +103,18 @@ ApplicationWindow { } } + DSM.SignalTransition { + targetState: loginModel.rowCount() ? stateLogin : stateIntro + signal: applicationWindow.navigateTo + guard: path === "InitialState" + } + + DSM.SignalTransition { + targetState: existingKeyState + signal: applicationWindow.navigateTo + guard: path === "ExistingKey" + } + DSM.FinalState { id: onboardingDoneState } @@ -158,7 +158,11 @@ ApplicationWindow { Component { id: existingKey - ExistingKey {} + ExistingKey { + onClosed: function () { + applicationWindow.navigateTo("InitialState") + } + } } Component { @@ -171,7 +175,12 @@ ApplicationWindow { Component { id: login Login { - btnGenKey.onClicked: applicationWindow.navigateTo("GenKey") + onGenKeyClicked: function () { + applicationWindow.navigateTo("GenKey") + } + onExistingKeyClicked: function () { + applicationWindow.navigateTo("ExistingKey") + } } } } diff --git a/ui/nim-status-client.pro b/ui/nim-status-client.pro index 3f9e64f281..00255ae0ed 100644 --- a/ui/nim-status-client.pro +++ b/ui/nim-status-client.pro @@ -136,6 +136,8 @@ DISTFILES += \ app/img/walletActive.svg \ app/qmldir \ imports/qmldir \ + onboarding/CreatePasswordModal.qml \ + onboarding/EnterSeedPhraseModal.qml \ onboarding/ExistingKey.qml \ onboarding/GenKey.qml \ onboarding/Intro.qml \ @@ -144,6 +146,7 @@ DISTFILES += \ onboarding/Login/AccountList.qml \ onboarding/Login/AccountSelection.qml \ onboarding/Login/AddressView.qml \ + onboarding/Login/ConfirmAddExistingKeyModal.qml \ onboarding/Login/SelectAnotherAccountModal.qml \ onboarding/Login/qmldir \ onboarding/Login/samples/AccountsData.qml \ diff --git a/ui/onboarding/CreatePasswordModal.qml b/ui/onboarding/CreatePasswordModal.qml new file mode 100644 index 0000000000..586c0a2128 --- /dev/null +++ b/ui/onboarding/CreatePasswordModal.qml @@ -0,0 +1,136 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 +import QtQuick.Dialogs 1.3 +import "../imports" +import "../shared" + +ModalPopup { + property bool loading: false + id: popup + title: qsTr("Create a password") + height: 500 + + onOpened: { + firstPasswordField.text = ""; + firstPasswordField.forceActiveFocus(Qt.MouseFocusReason) + } + + Input { + id: firstPasswordField + anchors.rightMargin: 56 + anchors.leftMargin: 56 + anchors.top: parent.top + anchors.topMargin: 88 + placeholderText: qsTr("New password...") + textField.echoMode: TextInput.Password + } + + Input { + id: repeatPasswordField + anchors.rightMargin: 0 + anchors.leftMargin: 0 + anchors.right: firstPasswordField.right + anchors.left: firstPasswordField.left + anchors.top: firstPasswordField.bottom + anchors.topMargin: Theme.xlPadding + placeholderText: qsTr("Confirm password…") + textField.echoMode: TextInput.Password + Keys.onReturnPressed: { + submitBtn.clicked() + } + } + + Text { + text: qsTr("At least 6 characters. You will use this password to unlock status on this device & sign transactions.") + wrapMode: Text.WordWrap + anchors.right: parent.right + anchors.rightMargin: Theme.xlPadding + anchors.left: parent.left + anchors.leftMargin: Theme.xlPadding + horizontalAlignment: Text.AlignHCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 0 + color: Theme.darkGrey + font.pixelSize: 12 + } + + footer: StyledButton { + id: submitBtn + anchors.bottom: parent.bottom + anchors.topMargin: Theme.padding + anchors.right: parent.right + anchors.rightMargin: Theme.padding + label: loading ? qsTr("Logging in...") : qsTr("Create password") + + disabled: firstPasswordField.text === "" || repeatPasswordField.text === "" || loading + + MessageDialog { + id: importError + title: qsTr("Error importing account") + text: qsTr("An error occurred while importing your account: ") + icon: StandardIcon.Critical + standardButtons: StandardButton.Ok + onVisibilityChanged: { + loading = false + } + } + + MessageDialog { + id: importLoginError + title: qsTr("Login failed") + text: qsTr("Login failed. Please re-enter your password and try again.") + icon: StandardIcon.Critical + standardButtons: StandardButton.Ok + onVisibilityChanged: { + loading = false + } + } + + MessageDialog { + id: passwordsDontMatchError + title: qsTr("Error") + text: qsTr("Passwords don't match") + icon: StandardIcon.Warning + standardButtons: StandardButton.Ok + onAccepted: { + repeatPasswordField.clear() + repeatPasswordField.forceActiveFocus(Qt.MouseFocusReason) + } + } + + Connections { + target: onboardingModel + ignoreUnknownSignals: true + onLoginResponseChanged: { + if (error) { + loading = false + importLoginError.open() + } + } + } + + onClicked : { + if (firstPasswordField.text === "" || repeatPasswordField.text === "") { + return + } + if (repeatPasswordField.text !== firstPasswordField.text) { + return passwordsDontMatchError.open() + } + // TODO this doesn't seem to work because the function freezes the view + loading = true + const result = onboardingModel.storeDerivedAndLogin(repeatPasswordField.text); + const error = JSON.parse(result).error + if (error) { + importError.text += error + return importError.open() + } + } + } +} + +/*##^## +Designer { + D{i:0;formeditorColor:"#ffffff";height:500;width:400} +} +##^##*/ diff --git a/ui/onboarding/EnterSeedPhraseModal.qml b/ui/onboarding/EnterSeedPhraseModal.qml new file mode 100644 index 0000000000..81320dd760 --- /dev/null +++ b/ui/onboarding/EnterSeedPhraseModal.qml @@ -0,0 +1,83 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 +import "../imports" +import "../shared" + +ModalPopup { + property var onConfirmSeedClick: function () {} + id: popup + title: qsTr("Add key") + height: 400 + + onOpened: { + mnemonicTextField.text = ""; + mnemonicTextField.forceActiveFocus(Qt.MouseFocusReason) + } + + TextArea { + id: mnemonicTextField + height: 44 + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 15 + placeholderText: "Enter your seed phrase here..." + anchors.left: parent.left + anchors.leftMargin: 76 + anchors.right: parent.right + anchors.rightMargin: 76 + anchors.verticalCenter: parent.verticalCenter + + Keys.onReturnPressed: { + submitBtn.clicked() + } + } + + Text { + text: qsTr("Enter 12, 15, 18, 21 or 24 words.\nSeperate words by a single space.") + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 0 + color: Theme.darkGrey + font.pixelSize: 12 + } + + footer: Button { + id: submitBtn + anchors.bottom: parent.bottom + anchors.topMargin: Theme.padding + anchors.right: parent.right + anchors.rightMargin: Theme.padding + width: 44 + height: 44 + background: Rectangle { + radius: 50 + color: Theme.lightBlue + } + + Image { + sourceSize.height: 15 + sourceSize.width: 20 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + source: "../app/img/leave_chat.svg" + rotation: 180 + fillMode: Image.PreserveAspectFit + } + + onClicked : { + if (mnemonicTextField.text === "") { + return + } + + onConfirmSeedClick(mnemonicTextField.text) + popup.close() + } + } +} + +/*##^## +Designer { + D{i:0;formeditorColor:"#ffffff";height:500;width:400} +} +##^##*/ diff --git a/ui/onboarding/ExistingKey.qml b/ui/onboarding/ExistingKey.qml index c5b2a82b23..70c299d27e 100644 --- a/ui/onboarding/ExistingKey.qml +++ b/ui/onboarding/ExistingKey.qml @@ -5,233 +5,36 @@ import QtQuick.Window 2.11 import QtQuick.Dialogs 1.3 import "../shared" import "../imports" +import "." -SwipeView { - id: swipeView +Item { + property var onClosed: function () {} + id: existingKeyView anchors.fill: parent - currentIndex: 0 - onCurrentItemChanged: { - currentItem.txtPassword.textField.focus = true; + Component.onCompleted: { + enterSeedPhraseModal.open() } - Item { - id: wizardStep1 - property Item txtPassword: txtMnemonic - width: 620 - height: 427 - - Text { - id: title - text: "Enter mnemonic" - font.pointSize: 36 - anchors.top: parent.top - anchors.topMargin: 20 - anchors.horizontalCenter: parent.horizontalCenter + EnterSeedPhraseModal { + property bool wentNext: false + id: enterSeedPhraseModal + onConfirmSeedClick: function (mnemonic) { + wentNext = true + onboardingModel.importMnemonic(mnemonic) + createPasswordModal.open() } - - Input { - id: txtMnemonic - anchors.verticalCenter: parent.verticalCenter - anchors.rightMargin: Theme.padding - anchors.leftMargin: Theme.padding - anchors.left: parent.left - anchors.right: parent.right - placeholderText: "Enter 12, 15, 21 or 24 words. Separate words by a single space." - - Keys.onReturnPressed: { - btnImport.clicked() - } - } - - StyledButton { - id: btnImport - label: "Next" - anchors.bottom: parent.bottom - anchors.bottomMargin: Theme.padding - anchors.horizontalCenter: parent.horizontalCenter - onClicked: { - onboardingModel.importMnemonic(txtMnemonic.textField.text); - swipeView.incrementCurrentIndex(); + onClosed: function () { + if (!wentNext) { + existingKeyView.onClosed() } } } - Item { - id: wizardStep2 - property Item txtPassword: txtPassword - - Text { - id: step2Title - text: "Enter password" - font.pointSize: 36 - anchors.top: parent.top - anchors.topMargin: 20 - anchors.horizontalCenter: parent.horizontalCenter - } - - Row { - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: step2Title.bottom - anchors.topMargin: 30 - Column { - Image { - source: onboardingModel.currentAccount.identicon - } - } - Column { - Text { - text: onboardingModel.currentAccount.username - } - Text { - text: onboardingModel.currentAccount.address - width: 160 - elide: Text.ElideMiddle - } - - } - } - - Input { - id: txtPassword - anchors.verticalCenter: parent.verticalCenter - anchors.rightMargin: Theme.padding - anchors.leftMargin: Theme.padding - anchors.left: parent.left - anchors.right: parent.right - placeholderText: "Enter password" - - Component.onCompleted: { - this.textField.echoMode = TextInput.Password - } - Keys.onReturnPressed: { - btnNext.clicked() - } - } - - StyledButton { - id: btnNext - label: "Next" - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - anchors.bottomMargin: 20 - onClicked: { - swipeView.incrementCurrentIndex(); - } - } - } - - Item { - id: wizardStep3 - property Item txtPassword: txtConfirmPassword - - Text { - id: step3Title - text: "Confirm password" - font.pointSize: 36 - anchors.top: parent.top - anchors.topMargin: 20 - anchors.horizontalCenter: parent.horizontalCenter - } - - Row { - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: step3Title.bottom - anchors.topMargin: 30 - Column { - Image { - source: onboardingModel.currentAccount.identicon - } - } - Column { - Text { - text: onboardingModel.currentAccount.username - } - Text { - text: onboardingModel.currentAccount.address - width: 160 - elide: Text.ElideMiddle - } - - } - } - - Input { - id: txtConfirmPassword - anchors.verticalCenter: parent.verticalCenter - anchors.rightMargin: Theme.padding - anchors.leftMargin: Theme.padding - anchors.left: parent.left - anchors.right: parent.right - placeholderText: "Confirm entered password" - - Component.onCompleted: { - this.textField.echoMode = TextInput.Password - } - Keys.onReturnPressed: { - btnFinish.clicked() - } - } - - MessageDialog { - id: importError - title: "Error importing account" - text: "An error occurred while importing your account: " - icon: StandardIcon.Critical - standardButtons: StandardButton.Ok - onAccepted: { - swipeView.currentIndex = 0 - } - } - - MessageDialog { - id: importLoginError - title: "Login failed" - text: "Login failed. Please re-enter your password and try again." - icon: StandardIcon.Critical - standardButtons: StandardButton.Ok - } - - MessageDialog { - id: passwordsDontMatchError - title: "Error" - text: "Passwords don't match" - icon: StandardIcon.Warning - standardButtons: StandardButton.Ok - onAccepted: { - txtConfirmPassword.clear(); - swipeView.currentIndex = 1; - txtPassword.focus = true; - } - } - - Connections { - target: onboardingModel - ignoreUnknownSignals: true - onLoginResponseChanged: { - if(error){ - importLoginError.open() - } - } - } - - StyledButton { - id: btnFinish - label: "Finish" - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - anchors.bottomMargin: 20 - onClicked: { - if (txtConfirmPassword.textField.text != txtPassword.textField.text) { - return passwordsDontMatchError.open(); - } - const result = onboardingModel.storeDerivedAndLogin(txtConfirmPassword.textField.text); - const error = JSON.parse(result).error; - if (error) { - importError.text += error; - importError.open(); - } - } + CreatePasswordModal { + id: createPasswordModal + onClosed: function () { + existingKeyView.onClosed() } } } diff --git a/ui/onboarding/Login.qml b/ui/onboarding/Login.qml index 41a4db3e01..dbc92c55c6 100644 --- a/ui/onboarding/Login.qml +++ b/ui/onboarding/Login.qml @@ -9,7 +9,8 @@ import "../imports" import "./Login" Item { - property alias btnGenKey: genrateKeysLink + property var onGenKeyClicked: function () {} + property var onExistingKeyClicked: function () {} property bool loading: false id: loginView @@ -43,11 +44,21 @@ Item { anchors.horizontalCenter: parent.horizontalCenter } + ConfirmAddExistingKeyModal { + id: confirmAddExstingKeyModal + onOpenModalClick: function () { + onExistingKeyClicked() + } + } + SelectAnotherAccountModal { id: selectAnotherAccountModal onAccountSelect: function (index) { loginModel.setCurrentAccount(index) } + onOpenModalClick: function () { + confirmAddExstingKeyModal.open() + } } Rectangle { @@ -88,9 +99,11 @@ Item { changeAccountBtn.isHovered = false } onClicked: { - selectAnotherAccountModal.open() - - // TODO add popup for when there are no other accounts + if (loginModel.rowCount() > 1) { + selectAnotherAccountModal.open() + } else { + onExistingKeyClicked() + } } } } @@ -182,16 +195,19 @@ Item { } MouseArea { - id: genrateKeysLink - width: genrateKeysLinkText.width - height: genrateKeysLinkText.height + id: generateKeysLink + width: generateKeysLinkText.width + height: generateKeysLinkText.height cursorShape: Qt.PointingHandCursor anchors.top: txtPassword.bottom anchors.topMargin: 26 anchors.horizontalCenter: parent.horizontalCenter + onClicked: { + onGenKeyClicked() + } Text { - id: genrateKeysLinkText + id: generateKeysLinkText color: Theme.blue text: qsTr("Generate new keys") font.pixelSize: 13 diff --git a/ui/onboarding/Login/ConfirmAddExistingKeyModal.qml b/ui/onboarding/Login/ConfirmAddExistingKeyModal.qml new file mode 100644 index 0000000000..69b9f018f1 --- /dev/null +++ b/ui/onboarding/Login/ConfirmAddExistingKeyModal.qml @@ -0,0 +1,37 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 +import "../../imports" +import "../../shared" + +ModalPopup { + property var onOpenModalClick: function () {} + id: popup + title: qsTr("Enter seed phrase") + height: 200 + + Text { + text: "Do you want to add another existing key?" + anchors.left: parent.left + anchors.top: parent.top + } + + footer: StyledButton { + anchors.bottom: parent.bottom + anchors.topMargin: Theme.padding + anchors.right: parent.right + anchors.rightMargin: Theme.padding + label: "Add another existing key" + + onClicked : { + onOpenModalClick() + popup.close() + } + } +} + +/*##^## +Designer { + D{i:0;formeditorColor:"#ffffff";height:500;width:400} +} +##^##*/ diff --git a/ui/onboarding/Login/SelectAnotherAccountModal.qml b/ui/onboarding/Login/SelectAnotherAccountModal.qml index ff476a15c5..4d62deda5e 100644 --- a/ui/onboarding/Login/SelectAnotherAccountModal.qml +++ b/ui/onboarding/Login/SelectAnotherAccountModal.qml @@ -6,6 +6,7 @@ import "../../shared" ModalPopup { property var onAccountSelect: function () {} + property var onOpenModalClick: function () {} id: popup title: qsTr("Your accounts") @@ -20,16 +21,16 @@ ModalPopup { } } - footer: StyledButton { - anchors.top: parent.top + anchors.bottom: parent.bottom anchors.topMargin: Theme.padding anchors.right: parent.right anchors.rightMargin: Theme.padding label: "Add another existing key" onClicked : { - console.log('Open other popup for seed') + onOpenModalClick() + popup.close() } } } diff --git a/ui/onboarding/Login/qmldir b/ui/onboarding/Login/qmldir index 7174342c17..39a86bf64c 100644 --- a/ui/onboarding/Login/qmldir +++ b/ui/onboarding/Login/qmldir @@ -2,3 +2,4 @@ AccountSelection 1.0 AccountSelection.qml AccountList 1.0 AccountList.qml AddressView 1.0 AddressView.qml SelectAnotherAccountModal 1.0 SelectAnotherAccountModal.qml +ConfirmAddExistingKeyModal 1.0 ConfirmAddExistingKeyModal.qml diff --git a/ui/onboarding/qmldir b/ui/onboarding/qmldir index 85740f890c..1e290c8987 100644 --- a/ui/onboarding/qmldir +++ b/ui/onboarding/qmldir @@ -5,3 +5,6 @@ KeysMain 1.0 KeysMain.qml Login 1.0 Login.qml OnboardingMain 1.0 OnboardingMain.qml Slide 1.0 Slide.qml +EnterSeedPhraseModal 1.0 EnterSeedPhraseModal.qml +CreatePasswordModal 1.0 CreatePasswordModal.qml +