feat(onboarding): Adding import seed screens for old&new users

Closes #4955
Closes #5230
This commit is contained in:
Alexandra Betouni 2022-03-16 00:27:36 +02:00 committed by Iuri Matias
parent b1e5ecc115
commit a8e946f602
20 changed files with 2405 additions and 509 deletions

View File

@ -44,26 +44,9 @@ QtObject {
}
DSM.SignalTransition {
targetState: welcomeMainState
targetState: importSeedState
signal: Global.applicationWindow.navigateTo
guard: path === "Welcome"
}
}
DSM.State {
id: existingKeyState
onEntered: { onBoardingStepChanged(existingKey, ""); }
DSM.SignalTransition {
targetState: genKeyState
signal: Global.applicationWindow.navigateTo
guard: path === "GenKey"
}
DSM.SignalTransition {
targetState: appState
signal: startupModule.appStateChanged
guard: state === Constants.appState.main
guard: path === "ImportSeed"
}
DSM.SignalTransition {
@ -71,6 +54,12 @@ QtObject {
signal: Global.applicationWindow.navigateTo
guard: path === "Welcome"
}
DSM.SignalTransition {
targetState: stateLogin
signal: Global.applicationWindow.navigateTo
guard: path === "LogIn"
}
}
DSM.State {
@ -95,6 +84,24 @@ QtObject {
}
}
DSM.State {
id: importSeedState
property string seedInputState: "existingUser"
onEntered: { onBoardingStepChanged(seedPhrase, seedInputState); }
DSM.SignalTransition {
targetState: keysMainState
signal: Global.applicationWindow.navigateTo
guard: path === "KeyMain"
}
DSM.SignalTransition {
targetState: genKeyState
signal: Global.applicationWindow.navigateTo
guard: path === "GenKey"
}
}
DSM.State {
id: keycardState
onEntered: { onBoardingStepChanged(keycardFlowSelection, ""); }
@ -106,28 +113,6 @@ QtObject {
}
}
DSM.State {
id: createPasswordState
onEntered: loader.sourceComponent = createPassword
DSM.SignalTransition {
targetState: appState
signal: startupModule.appStateChanged
guard: state === Constants.appState.main
}
}
DSM.State {
id: confirmPasswordState
onEntered: loader.sourceComponent = confirmPassword
DSM.SignalTransition {
targetState: appState
signal: startupModule.appStateChanged
guard: state === Constants.appState.main
}
}
DSM.State {
id: stateLogin
onEntered: { onBoardingStepChanged(login, ""); }
@ -157,12 +142,6 @@ QtObject {
guard: path === "KeysMain"
}
DSM.SignalTransition {
targetState: existingKeyState
signal: Global.applicationWindow.navigateTo
guard: path === "ExistingKey"
}
DSM.SignalTransition {
targetState: keycardState
signal: Global.applicationWindow.navigateTo
@ -203,43 +182,51 @@ QtObject {
id: keysMain
KeysMainView {
onButtonClicked: {
OnboardingStore.accountImported = false
Global.applicationWindow.navigateTo("GenKey");
if (state === "importseed") {
importSeedState.seedInputState = "newUser";
Global.applicationWindow.navigateTo("ImportSeed");
} else {
Global.applicationWindow.navigateTo("GenKey");
}
}
onKeycardLinkClicked: {
OnboardingStore.accountImported = false
Global.applicationWindow.navigateTo("KeycardFlowSelection");
}
onSeedLinkClicked: {
Global.applicationWindow.navigateTo("ExistingKey");
importSeedState.seedInputState = "existingUser";
Global.applicationWindow.navigateTo("ImportSeed");
}
onBackClicked: {
OnboardingStore.accountImported = false
Global.applicationWindow.navigateTo("Welcome");
if (root.keysMainSetState === "connectkeys" && LoginStore.currentAccount.username !== "") {
Global.applicationWindow.navigateTo("LogIn");
} else {
Global.applicationWindow.navigateTo("Welcome");
}
}
}
}
property var existingKeyComponent: Component {
id: existingKey
ExistingKeyView {
onShowCreatePasswordView: {
Global.applicationWindow.navigateTo("GenKey");
}
onClosed: function () {
if (root.hasAccounts) {
Global.applicationWindow.navigateTo("InitialState")
property var seedPhraseInputComponent: Component {
id: seedPhrase
SeedPhraseInputView {
onExit: {
if (root.keysMainSetState === "connectkeys") {
Global.applicationWindow.navigateTo("KeyMain");
} else {
Global.applicationWindow.navigateTo("KeysMain")
root.keysMainSetState = "importseed";
Global.applicationWindow.navigateTo("KeyMain");
}
}
onSeedValidated: {
Global.applicationWindow.navigateTo("GenKey");
}
}
}
property var genKeyComponent: Component {
id: genKey
GenKeyView {
onFinished: {
onExit: {
if (LoginStore.currentAccount.username !== "") {
Global.applicationWindow.navigateTo("LogIn");
} else {
@ -268,11 +255,12 @@ QtObject {
property var loginComponent: Component {
id: login
LoginView {
onGenKeyClicked: function () {
Global.applicationWindow.navigateTo("GenKey")
onGenKeyClicked: {
Global.applicationWindow.navigateTo("GenKey");
}
onExistingKeyClicked: function () {
Global.applicationWindow.navigateTo("ExistingKey")
onAddExistingKeyClicked: {
root.keysMainSetState = "connectkeys";
Global.applicationWindow.navigateTo("KeysMain");
}
}
}

View File

@ -7,8 +7,8 @@ import utils 1.0
Page {
id: root
signal exit()
signal backClicked()
signal finished()
background: Rectangle {
color: Style.current.background

View File

@ -10,11 +10,11 @@ import shared.popups 1.0
// TODO: replace with StatusModal
ModalPopup {
property var onOpenModalClick: function () {}
id: popup
//% "Enter seed phrase"
title: qsTrId("enter-seed-phrase")
height: 200
signal openModalClicked()
StyledText {
text: "Do you want to add another existing key?"
@ -30,7 +30,7 @@ ModalPopup {
text: "Add another existing key"
onClicked : {
onOpenModalClick()
openModalClicked()
popup.close()
}
}

View File

@ -1,72 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtGraphicalEffects 1.13
import utils 1.0
import shared 1.0
import shared.panels 1.0
import shared.popups 1.0
import shared.controls 1.0
import StatusQ.Controls 0.1
// TODO: replace with StatusModal
ModalPopup {
property var onConfirmSeedClick: function () {}
id: popup
//% "Enter seed phrase"
title: qsTrId("enter-seed-phrase")
height: 400
onOpened: {
seedPhraseTextArea.textArea.text = "";
seedPhraseTextArea.textArea.forceActiveFocus(Qt.MouseFocusReason)
}
SeedPhraseTextArea {
id: seedPhraseTextArea
anchors.top: parent.top
anchors.topMargin: 20
anchors.bottomMargin: 20
width: parent.width
hideRectangle: true
textArea.anchors.leftMargin: 76
textArea.anchors.rightMargin: 76
onEnterPressed: submitBtn.clicked()
}
StyledText {
id: helpText
//% "Enter 12, 15, 18, 21 or 24 words.\nSeperate words by a single space."
text: qsTrId("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
horizontalAlignment: TextEdit.AlignHCenter
color: Style.current.secondaryText
font.pixelSize: 12
}
footer: StatusRoundButton {
id: submitBtn
anchors.bottom: parent.bottom
anchors.topMargin: Style.current.padding
anchors.right: parent.right
icon.name: "arrow-right"
icon.width: 20
icon.height: 16
enabled: seedPhraseTextArea.correctWordCount
onClicked : {
if (seedPhraseTextArea.textArea.text === "") {
return
}
if (seedPhraseTextArea.validateSeed()) {
onConfirmSeedClick(seedPhraseTextArea.textArea.text)
}
}
}
}

View File

@ -1,92 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtGraphicalEffects 1.13
import StatusQ.Controls 0.1
import utils 1.0
import shared 1.0
import shared.panels 1.0
import shared.popups 1.0
import shared.controls 1.0
import StatusQ.Controls 0.1
import StatusQ.Popups 0.1
import "../panels"1
import "../stores"
// TODO: replace with StatusModal
ModalPopup {
property int selectedIndex: 0
property var onClosed: function () {}
property var onNextClick: function () {}
id: popup
//% "Choose a chat name"
title: qsTrId("intro-wizard-title2")
height: 504
property string displayNameValidationError: ""
Input {
id: displayNameInput
placeholderText: "DisplayName"
validationError: displayNameValidationError
maxLength: 24
onTextChanged: {
let trimmedText = displayNameInput.text.trim()
if(displayNameInput.text === ""){
displayNameValidationError = qsTr("Display name is required")
} else if (!trimmedText.match(/^[a-zA-Z0-9\- ]+$/)){
displayNameValidationError = qsTr("Only letters, numbers, underscores and hyphens allowed")
} else if (trimmedText.length > 24) {
displayNameValidationError = qsTr("24 character username limit")
} else if (trimmedText.length < 5) {
displayNameValidationError = qsTr("Username must be at least 5 characters")
} else if (trimmedText.endsWith(".eth")) {
displayNameValidationError = qsTr(`Usernames ending with ".eth" are not allowed`)
} else if (trimmedText.endsWith("-eth")) {
displayNameValidationError = qsTr(`Usernames ending with "-eth" are not allowed`)
} else if (trimmedText.endsWith("_eth")) {
displayNameValidationError = qsTr(`Usernames ending with "_eth" are not allowed`)
} else if (globalUtils.isAlias(trimmedText)){
displayNameValidationError = qsTr("Sorry, the name you have chosen is not allowed, try picking another username")
} else {
displayNameValidationError = ""
}
}
}
AccountListPanel {
id: accountList
anchors.top: displayNameInput.bottom
anchors.topMargin: 100
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
interactive: false
model: OnboardingStore.onBoardingModul.accountsModel
isSelected: function (index) {
return index === selectedIndex
}
onAccountSelect: function(index) {
selectedIndex = index
}
}
footer: StatusRoundButton {
objectName: "submitButton"
id: submitBtn
enabled: displayNameInput.text.trim() !== "" && displayNameInput.text.trim().length >= 5
anchors.bottom: parent.bottom
anchors.topMargin: Style.current.padding
anchors.right: parent.right
icon.name: "arrow-right"
icon.width: 20
icon.height: 16
onClicked : {
onNextClick(selectedIndex, displayNameInput.text.trim());
popup.close()
}
}
}

View File

@ -1,36 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import utils 1.0
import StatusQ.Controls 0.1
import shared.panels 1.0
import shared.popups 1.0
// TODO: replace with StatusModal
ModalPopup {
id: popup
//% "Invalid seed phrase"
title: qsTrId("custom-seed-phrase")
height: 200
property string error: "Invalid seed phrase."
StyledText {
text: popup.error
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 15
}
footer: StatusButton {
anchors.right: parent.right
anchors.rightMargin: Style.current.smallPadding
//% "Cancel"
text: qsTrId("browsing-cancel")
anchors.bottom: parent.bottom
onClicked: {
popup.close()
}
}
}

View File

@ -1,87 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Dialogs 1.3
import utils 1.0
import StatusQ.Controls 0.1 as StatusQControls
import shared.panels 1.0
import shared.popups 1.0
import shared.status 1.0
import "../stores"
import StatusQ.Components 0.1
// TODO: replace with StatusModal
ModalPopup {
id: popup
//% "Your keys have been successfully recovered"
title: qsTrId("your-keys-have-been-successfully-recovered")
height: 400
signal buttonClicked()
StyledText {
id: info
anchors.top: parent.top
anchors.topMargin: Style.current.bigPadding
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: Style.current.bigPadding
anchors.rightMargin: Style.current.bigPadding
//% "You will have to create a new code or password to re-encrypt your keys"
text: qsTrId("recovery-success-text")
font.pixelSize: 15
color: Style.current.secondaryText
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
}
StatusSmartIdenticon {
id: identicon
anchors.top: info.bottom
anchors.topMargin: Style.current.bigPadding
anchors.horizontalCenter: parent.horizontalCenter
image.source: OnboardingStore.onboardingModuleInst.importedAccountIdenticon
image.width: 60
image.height: 60
}
StyledText {
id: username
anchors.top: identicon.bottom
anchors.topMargin: Style.current.padding
anchors.horizontalCenter: identicon.horizontalCenter
text: OnboardingStore.onboardingModuleInst.importedAccountAlias
font.weight: Font.Bold
font.pixelSize: 15
}
Address {
anchors.top: username.bottom
anchors.topMargin: Style.current.halfPadding
anchors.horizontalCenter: username.horizontalCenter
text: OnboardingStore.onboardingModuleInst.importedAccountAddress
width: 120
}
footer: Item {
width: parent.width
height: reencryptBtn.height
StatusQControls.StatusButton {
id: reencryptBtn
anchors.bottom: parent.bottom
anchors.topMargin: Style.current.padding
anchors.right: parent.right
//% "Re-encrypt your keys"
text: qsTrId("re-encrypt-key")
onClicked: {
OnboardingStore.accountImported = true
popup.buttonClicked()
}
}
}
}

View File

@ -12,8 +12,8 @@ import "../stores"
// TODO: replace with StatusModal
ModalPopup {
property var onAccountSelect: function () {}
property var onOpenModalClick: function () {}
signal accountSelected(int index)
signal openModalClicked()
id: popup
//% "Your keys"
title: qsTrId("your-keys")
@ -28,7 +28,7 @@ ModalPopup {
}
onAccountSelect: function(index) {
popup.onAccountSelect(index)
popup.accountSelected(index)
popup.close()
}
}
@ -41,7 +41,7 @@ ModalPopup {
text: qsTrId("add-another-existing-key")
onClicked : {
onOpenModalClick()
openModalClicked()
popup.close()
}
}

View File

@ -0,0 +1,19 @@
import QtQuick 2.13
ListModel {
Component.onCompleted: {
var xhr = new XMLHttpRequest();
xhr.open("GET", "english.txt");
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE) {
var words = xhr.responseText.split('\n');
for (var i = 0; i < words.length; i++) {
if (words[i] !== "") {
insert(count, {"seedWord": words[i]});
}
}
}
}
xhr.send();
}
}

View File

@ -19,7 +19,6 @@ QtObject {
property real profImgBX: 0.0
property real profImgBY: 0.0
property bool accountCreated: false
property bool accountImported: false
property bool showBeforeGetStartedPopup: true
@ -27,13 +26,11 @@ QtObject {
onboardingModuleInst.importMnemonic(mnemonic)
}
function setCurrentAccountAndDisplayName(selectedAccountIdx, displayName) {
onboardingModuleInst.setDisplayName(displayName)
onboardingModuleInst.setSelectedAccountByIndex(selectedAccountIdx)
}
function importAccountAndDisplayName(displayName) {
onboardingModuleInst.setDisplayName(displayName)
function setCurrentAccountAndDisplayName(displayName) {
onboardingModuleInst.setDisplayName(displayName);
if (!onboardingModuleInst.importedAccountPubKey) {
onboardingModuleInst.setSelectedAccountByIndex(0);
}
}
function updatedDisplayName(displayName) {
@ -68,7 +65,11 @@ QtObject {
}
function changePassword(password, newPassword) {
root.privacyModule.changePassword(password, newPassword)
root.privacyModule.changePassword(password, newPassword);
}
function validateMnemonic(text) {
root.onboardingModuleInst.validateMnemonic(text);
}
property ListModel accountsSampleData: ListModel {

File diff suppressed because it is too large Load Diff

View File

@ -121,17 +121,12 @@ OnboardingBasePage {
root.tmpPass = root.password;
} else {
submitBtn.loading = false
root.finished();
root.exit();
}
} else {
root.tmpPass = root.password;
submitBtn.loading = true
if (OnboardingStore.accountImported) {
OnboardingStore.importAccountAndDisplayName(root.displayName);
} else {
OnboardingStore.setCurrentAccountAndDisplayName(0, root.displayName);
}
OnboardingStore.setCurrentAccountAndDisplayName(root.displayName);
pause.start();
}
}
@ -183,11 +178,11 @@ OnboardingBasePage {
onAppStateChanged: {
if (state === Constants.appState.main) {
if (!!OnboardingStore.profImgUrl) {
OnboardingStore.saveImage()
OnboardingStore.saveImage();
OnboardingStore.accountCreated = true;
}
submitBtn.loading = false
root.finished()
root.exit()
}
}
}
@ -197,7 +192,7 @@ OnboardingBasePage {
onPasswordChanged: {
if (success) {
submitBtn.loading = false
root.finished();
root.exit();
}
}
}

View File

@ -41,7 +41,7 @@ OnboardingBasePage {
onClicked: {
root.newPassword = view.newPswText
root.confirmationPassword = view.confirmationPswText
root.finished()
root.exit()
}
}
}

View File

@ -1,74 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Dialogs 1.3
import utils 1.0
import "../popups"
import "../stores"
import "../shared"
Item {
property var onClosed: function () {}
signal showCreatePasswordView()
id: existingKeyView
anchors.fill: parent
Component.onCompleted: {
enterSeedPhraseModal.open()
}
Connections {
target: onboardingModule
onAccountImportError: {
if (error === Constants.existingAccountError) {
importSeedError.title = qsTr("Keys for this account already exist")
importSeedError.text = qsTr("Keys for this account already exist and can't be added again. If you've lost your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase")
} else {
importSeedError.title = qsTr("Error importing seed")
importSeedError.text = error
}
importSeedError.open()
}
onAccountImportSuccess: {
enterSeedPhraseModal.wentNext = true
enterSeedPhraseModal.close()
recoverySuccessModal.open()
}
}
MessageDialog {
id: importSeedError
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
}
EnterSeedPhraseModal {
property bool wentNext: false
id: enterSeedPhraseModal
onConfirmSeedClick: function (mnemonic) {
OnboardingStore.importMnemonic(mnemonic)
}
onClosed: function () {
if (!wentNext) {
existingKeyView.onClosed()
}
}
}
MnemonicRecoverySuccessModal {
property bool wentNext: false
id: recoverySuccessModal
onButtonClicked: {
recoverySuccessModal.wentNext = true
recoverySuccessModal.close()
showCreatePasswordView()
}
onClosed: function () {
if (!recoverySuccessModal.wentNext) {
existingKeyView.onClosed()
}
}
}
}

View File

@ -61,7 +61,7 @@ OnboardingBasePage {
newPassword: d.newPassword
confirmationPassword: d.confirmationPassword
onFinished: {
onExit: {
d.newPassword = newPassword
d.confirmationPassword = confirmationPassword
gotoKeysStack(GenKeyView.KeysStack.CONFRIM_PWD)
@ -76,7 +76,7 @@ OnboardingBasePage {
id: confirmPswView
password: d.newPassword
displayName: userDetailsPanel.displayName
onFinished: {
onExit: {
if (Qt.platform.os == "osx") {
gotoKeysStack(GenKeyView.KeysStack.TOUCH_ID);
} else {
@ -96,7 +96,7 @@ OnboardingBasePage {
if (userDetailsPanel.state === "chatkey") {
userDetailsPanel.state = "username";
} else {
root.finished();
root.exit();
}
}
}

View File

@ -23,22 +23,21 @@ Item {
state: "username"
Component.onCompleted: {
if (OnboardingStore.accountImported) {
if (!!OnboardingStore.onboardingModuleInst.importedAccountPubKey) {
root.address = OnboardingStore.onboardingModuleInst.importedAccountAddress ;
root.pubKey = OnboardingStore.onboardingModuleInst.importedAccountPubKey;
root.address = OnboardingStore.onboardingModuleInst.importedAccountAddress;
}
}
Loader {
active: !OnboardingStore.accountImported
active: !OnboardingStore.onboardingModuleInst.importedAccountPubKey
sourceComponent: ListView {
id: accountsList
model: OnboardingStore.onboardingModuleInst.accountsModel
delegate: Item {
Component.onCompleted: {
if (index === 0) {
root.pubKey = model.pubKey;
root.address = model.address;
root.pubKey = model.pubKey;
}
}
}
@ -164,6 +163,7 @@ Item {
if (OnboardingStore.accountCreated) {
OnboardingStore.updatedDisplayName(nameInput.text);
}
OnboardingStore.displayName = nameInput.text
root.displayName = nameInput.text;
root.state = "chatkey";
} else {

View File

@ -84,25 +84,25 @@ OnboardingBasePage {
}
}
StatusBaseText {
id: keycardLink
Layout.alignment: Qt.AlignHCenter
color: Theme.palette.primaryColor1
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: {
parent.font.underline = true
}
onExited: {
parent.font.underline = false
}
onClicked: {
root.keycardLinkClicked();
}
}
}
// StatusBaseText {
// id: keycardLink
// Layout.alignment: Qt.AlignHCenter
// color: Theme.palette.primaryColor1
// MouseArea {
// anchors.fill: parent
// cursorShape: Qt.PointingHandCursor
// hoverEnabled: true
// onEntered: {
// parent.font.underline = true
// }
// onExited: {
// parent.font.underline = false
// }
// onClicked: {
// root.keycardLinkClicked();
// }
// }
// }
StatusBaseText {
id: seedLink
@ -119,7 +119,11 @@ OnboardingBasePage {
parent.font.underline = false
}
onClicked: {
root.seedLinkClicked();
if (root.state === "getkeys") {
root.state = "importseed";
} else {
root.seedLinkClicked();
}
}
}
}
@ -141,15 +145,15 @@ OnboardingBasePage {
}
PropertyChanges {
target: button
text: qsTr("Scan sync code")
enabled: false
visible: false
//text: qsTr("Scan sync code")
}
PropertyChanges {
target: keycardLink
text: qsTr("Login with Keycard")
}
// PropertyChanges {
// target: keycardLink
// text: qsTr("Login with Keycard")
// }
PropertyChanges {
target: seedLink
text: qsTr("Enter a seed phrase")
@ -173,11 +177,11 @@ OnboardingBasePage {
text: qsTr("Generate new keys")
}
PropertyChanges {
target: keycardLink
text: qsTr("Generate keys for a new Keycard")
// PropertyChanges {
// target: keycardLink
// text: qsTr("Generate keys for a new Keycard")
}
// }
PropertyChanges {
target: seedLink
text: qsTr("Import a seed phrase")
@ -202,11 +206,11 @@ Only use this option if you already have a seed phrase.")
text: qsTr("Import a seed phrase")
}
PropertyChanges {
target: keycardLink
text: qsTr("Import a seed phrase into a new Keycard")
// PropertyChanges {
// target: keycardLink
// text: qsTr("Import a seed phrase into a new Keycard")
}
// }
PropertyChanges {
target: seedLink
text: ""

View File

@ -18,9 +18,9 @@ import StatusQ.Components 0.1
import utils 1.0
Item {
property var onGenKeyClicked: function () {}
property var onExistingKeyClicked: function () {}
property bool loading: false
signal genKeyClicked()
signal addExistingKeyClicked()
id: loginView
anchors.fill: parent
@ -107,19 +107,19 @@ Item {
ConfirmAddExistingKeyModal {
id: confirmAddExstingKeyModal
onOpenModalClick: function () {
onExistingKeyClicked()
onOpenModalClicked: {
addExistingKeyClicked()
}
}
SelectAnotherAccountModal {
id: selectAnotherAccountModal
onAccountSelect: function (index) {
onAccountSelected: {
LoginStore.setCurrentAccount(index)
resetLogin()
}
onOpenModalClick: function () {
onExistingKeyClicked()
onOpenModalClicked: {
addExistingKeyClicked()
}
}
@ -248,8 +248,7 @@ Item {
anchors.topMargin: 16
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
OnboardingStore.accountImported = false
onGenKeyClicked()
genKeyClicked();
}
}

View File

@ -0,0 +1,202 @@
import QtQuick 2.12
import QtGraphicalEffects 1.13
import StatusQ.Controls 0.1
import StatusQ.Popups 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import utils 1.0
import "../controls"
import "../stores"
OnboardingBasePage {
id: root
state: "existingUser"
property bool existingUser: (root.state === "existingUser")
property var mnemonicInput: []
property string mnemonicString
signal seedValidated()
Item {
implicitWidth: 731
implicitHeight: 472
anchors.centerIn: parent
StatusSwitchTabBar {
id: switchTabBar
anchors.horizontalCenter: parent.horizontalCenter
StatusSwitchTabButton {
text: qsTr("12 words")
}
StatusSwitchTabButton {
text: qsTr("18 words")
}
StatusSwitchTabButton {
text: qsTr("24 words")
}
onCurrentIndexChanged: {
root.mnemonicString = "";
root.mnemonicInput = [];
}
}
clip: true
GridView {
id: grid
width: parent.width
property var wordIndex: ["1", "5", "9", "2", "6", "10", "3", "7", "11", "4", "8", "12",
"13", "17", "21", "14", "18", "22", "15", "19", "23", "16", "20", "24"]
property var wordIndex18: ["1", "5", "9", "2", "6", "10", "3", "7", "11", "4", "8", "12",
"13", "", "14", "17", "15", "18", "16", ""]
height: (grid.count === 20 && !grid.atXBeginning) ? 144 : 244
anchors.left: parent.left
anchors.leftMargin: 12
anchors.top: switchTabBar.bottom
anchors.topMargin: ((grid.count === 20) && !grid.atXBeginning) ? 74 : 24
flow: GridView.FlowTopToBottom
cellWidth: (parent.width/4)
cellHeight: 72
interactive: false
z: 100000
model: switchTabBar.currentItem.text.substring(0,2) === "12" ? 12 :
switchTabBar.currentItem.text.substring(0,2) === "18" ? 20 : 24
delegate: StatusSeedPhraseInput {
id: seedWordInput
width: (grid.cellWidth - 24)
height: (grid.cellHeight - 28)
textEdit.input.anchors.leftMargin: 16
textEdit.input.anchors.rightMargin: 16
visible: !(((index === 13) || (index === 19)) && (grid.count === 20))
leftComponentText: (grid.count === 20) ? grid.wordIndex18[index] : grid.wordIndex[index]
inputList: BIP39_en { }
property int itemIndex: index
z: (grid.currentIndex === index) ? 150000000 : 0
onTextChanged: {
invalidSeedTxt.visible = false;
}
onDoneInsertingWord: {
root.mnemonicInput.push({"pos": leftComponentText, "seed": word.replace(/\s/g, '')});
for (var j = 0; j < mnemonicInput.length; j++) {
if (mnemonicInput[j].pos === leftComponentText && mnemonicInput[j].seed !== word) {
mnemonicInput[j].seed = word;
}
}
//remove duplicates
var valueArr = mnemonicInput.map(function(item){ return item.pos });
var isDuplicate = valueArr.some(function(item, idx){
if (valueArr.indexOf(item) !== idx) {
root.mnemonicInput.splice(idx, 1);
}
return valueArr.indexOf(item) !== idx
});
for (var i = !grid.atXBeginning ? 12 : 0; i < grid.count; i++) {
if (parseInt(grid.itemAtIndex(i).leftComponentText) === (parseInt(leftComponentText)+1)) {
grid.currentIndex = grid.itemAtIndex(i).itemIndex;
grid.itemAtIndex(i).textEdit.input.edit.forceActiveFocus();
if (grid.currentIndex === 11) {
grid.positionViewAtEnd();
if (grid.count === 20) {
grid.contentX = 1500;
}
}
}
}
submitButton.enabled = (root.mnemonicInput.length === (grid.atXBeginning ? 12 : submitButton.gridCount));
}
onEditClicked: {
grid.currentIndex = index;
grid.itemAtIndex(index).textEdit.input.edit.forceActiveFocus();
}
onKeyPressed: {
if (event.key === Qt.Key_Tab || event.key === Qt.Key_Right) {
for (var i = !grid.atXBeginning ? 12 : 0; i < grid.count; i++) {
if (parseInt(grid.itemAtIndex(i).leftComponentText) === ((parseInt(leftComponentText)+1) <= grid.count ? (parseInt(leftComponentText)+1) : grid.count)) {
grid.itemAtIndex(i).textEdit.input.edit.forceActiveFocus();
textEdit.input.tabNavItem = grid.itemAtIndex(i).textEdit.input.edit;
}
}
} else if (event.key === Qt.Key_Left) {
for (var i = !grid.atXBeginning ? 12 : 0; i < grid.count; i++) {
if (parseInt(grid.itemAtIndex(i).leftComponentText) === ((parseInt(leftComponentText)-1) >= 0 ? (parseInt(leftComponentText)-1) : 0)) {
grid.itemAtIndex(i).textEdit.input.edit.forceActiveFocus();
}
}
} else if (event.key === Qt.Key_Down) {
grid.itemAtIndex((index+1 < grid.count) ? (index+1) : (grid.count-1)).textEdit.input.edit.forceActiveFocus();
} else if (event.key === Qt.Key_Up) {
grid.itemAtIndex((index-1 >= 0) ? (index-1) : 0).textEdit.input.edit.forceActiveFocus();
}
if (event.key === Qt.Key_Delete || event.key === Qt.Key_Backspace) {
var wordIndex = mnemonicInput.findIndex(x => x.pos === leftComponentText);
if (wordIndex > -1) {
mnemonicInput.splice(wordIndex , 1);
submitButton.enabled = (root.mnemonicInput.length === (grid.atXBeginning ? 12 : submitButton.gridCount));
}
}
grid.currentIndex = index;
}
Component.onCompleted: { grid.itemAtIndex(0).textEdit.input.edit.forceActiveFocus(); }
}
}
StatusBaseText {
id: invalidSeedTxt
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: grid.bottom
anchors.topMargin: (grid.count === 20 && !grid.atXBeginning) ? 74 : 24
color: Theme.palette.dangerColor1
visible: false
text: qsTr("Invalid seed")
}
StatusButton {
id: submitButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: invalidSeedTxt.bottom
anchors.topMargin: 24
enabled: false
property int gridCount: (grid.count === 20) ? 18 : grid.count
text: root.existingUser ? qsTr("Restore Status Profile") :
((grid.count > 12) && grid.atXBeginning) ? qsTr("Next") : qsTr("Import")
onClicked: {
if ((grid.count > 12) && grid.atXBeginning) {
grid.positionViewAtEnd();
if (grid.count === 20) {
grid.contentX = 1500;
}
} else {
root.mnemonicString = "";
var sortTable = mnemonicInput.sort(function (a, b) {
return a.pos - b.pos;
});
for (var i = 0; i < mnemonicInput.length; i++) {
root.mnemonicString += sortTable[i].seed + ((i === (gridCount-1)) ? "" : " ");
}
if (Utils.isMnemonic(root.mnemonicString) && !OnboardingStore.validateMnemonic(root.mnemonicString)) {
OnboardingStore.importMnemonic(root.mnemonicString)
root.seedValidated();
root.mnemonicString = "";
root.mnemonicInput = [];
} else {
invalidSeedTxt.visible = true;
enabled = false;
}
}
}
}
}
onBackClicked: {
root.mnemonicString = "";
root.mnemonicInput = [];
if (!grid.atXBeginning) {
grid.positionViewAtBeginning();
} else {
root.exit();
}
}
}

View File

@ -21,7 +21,8 @@ var qrcExtensions = map[string]bool{
".otf": true,
".ttf": true,
".webm": true,
".qm": true,
".qm": true,
".txt": true,
}
func main() {