feat(@desktop/Wallet): Add acccounts Modal as per new design
fixes #5073
This commit is contained in:
parent
b732ad6e5c
commit
2852d70731
|
@ -1 +1 @@
|
||||||
Subproject commit b2ac2794bbee350539e20d95f1e3adf626e8feb7
|
Subproject commit 46dfd594dfa889448fbeefc9a011bea38042f41e
|
|
@ -25,7 +25,7 @@ Rectangle {
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
height: sendBtn.height
|
height: parent.height
|
||||||
spacing: Style.current.padding
|
spacing: Style.current.padding
|
||||||
|
|
||||||
StatusFlatButton {
|
StatusFlatButton {
|
||||||
|
|
|
@ -0,0 +1,224 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
import QtQuick.Dialogs 1.3
|
||||||
|
import QtQuick.Layouts 1.14
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Controls.Validators 0.1
|
||||||
|
import StatusQ.Popups 0.1
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
|
||||||
|
import shared.controls 1.0
|
||||||
|
|
||||||
|
import "../stores"
|
||||||
|
import "../views"
|
||||||
|
|
||||||
|
StatusModal {
|
||||||
|
id: popup
|
||||||
|
|
||||||
|
property int marginBetweenInputs: 38
|
||||||
|
property string passwordValidationError: ""
|
||||||
|
property bool loading: false
|
||||||
|
property var emojiPopup: null
|
||||||
|
|
||||||
|
signal afterAddAccount()
|
||||||
|
|
||||||
|
//% "Generate an account"
|
||||||
|
header.title: qsTrId("generate-a-new-account")
|
||||||
|
|
||||||
|
function validate() {
|
||||||
|
if (passwordInput.text === "") {
|
||||||
|
//% "You need to enter a password"
|
||||||
|
passwordValidationError = qsTrId("you-need-to-enter-a-password")
|
||||||
|
} else if (passwordInput.text.length < 6) {
|
||||||
|
//% "Password needs to be 6 characters or more"
|
||||||
|
passwordValidationError = qsTrId("password-needs-to-be-6-characters-or-more")
|
||||||
|
} else {
|
||||||
|
passwordValidationError = ""
|
||||||
|
}
|
||||||
|
return passwordValidationError === "" && accountNameInput.valid
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpened: {
|
||||||
|
passwordValidationError = "";
|
||||||
|
passwordInput.text = "";
|
||||||
|
accountNameInput.text = "";
|
||||||
|
accountNameInput.reset()
|
||||||
|
accountNameInput.input.icon.emoji = StatusQUtils.Emoji.getRandomEmoji()
|
||||||
|
colorSelectionGrid.selectedColorIndex = Math.floor(Math.random() * colorSelectionGrid.model.length)
|
||||||
|
advancedSelection.expanded = false
|
||||||
|
advancedSelection.reset()
|
||||||
|
passwordInput.forceActiveFocus(Qt.MouseFocusReason)
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
enabled: popup.opened
|
||||||
|
target: emojiPopup
|
||||||
|
onEmojiSelected: function (emojiText, atCursor) {
|
||||||
|
accountNameInput.input.icon.emoji = emojiText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: ScrollView {
|
||||||
|
width: popup.width
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
topPadding: Style.current.halfPadding
|
||||||
|
bottomPadding: Style.current.halfPadding
|
||||||
|
height: 400
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Column {
|
||||||
|
property alias accountNameInput: accountNameInput
|
||||||
|
width: popup.width
|
||||||
|
spacing: Style.current.halfPadding
|
||||||
|
topPadding: 20
|
||||||
|
|
||||||
|
// To-Do Password hidden option not supported in StatusQ StatusBaseInput
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: passwordInput.height
|
||||||
|
Input {
|
||||||
|
id: passwordInput
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Style.current.padding
|
||||||
|
anchors.rightMargin: Style.current.padding
|
||||||
|
|
||||||
|
//% "Enter your password…"
|
||||||
|
placeholderText: qsTrId("enter-your-password…")
|
||||||
|
//% "Password"
|
||||||
|
label: qsTrId("password")
|
||||||
|
textField.echoMode: TextInput.Password
|
||||||
|
validationError: popup.passwordValidationError
|
||||||
|
inputLabel.font.pixelSize: 15
|
||||||
|
inputLabel.font.weight: Font.Normal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusInput {
|
||||||
|
id: accountNameInput
|
||||||
|
//% "Enter an account name..."
|
||||||
|
input.placeholderText: qsTrId("enter-an-account-name...")
|
||||||
|
//% "Account name"
|
||||||
|
label: qsTrId("account-name")
|
||||||
|
input.isIconSelectable: true
|
||||||
|
input.icon.color: colorSelectionGrid.selectedColor ? colorSelectionGrid.selectedColor : Theme.palette.directColor1
|
||||||
|
onIconClicked: {
|
||||||
|
popup.emojiPopup.open()
|
||||||
|
popup.emojiPopup.x = popup.x + accountNameInput.x + Style.current.padding
|
||||||
|
popup.emojiPopup.y = popup.y + contentItem.y + accountNameInput.y + accountNameInput.height + Style.current.halfPadding
|
||||||
|
}
|
||||||
|
validators: [
|
||||||
|
StatusMinLengthValidator {
|
||||||
|
//% "You need to enter an account name"
|
||||||
|
errorMessage: qsTrId("you-need-to-enter-an-account-name")
|
||||||
|
minLength: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusColorSelectorGrid {
|
||||||
|
id: colorSelectionGrid
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
//% "color"
|
||||||
|
titleText: qsTr("color").toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusExpandableItem {
|
||||||
|
id: advancedSelection
|
||||||
|
|
||||||
|
property bool isValid: true
|
||||||
|
|
||||||
|
function validate() {
|
||||||
|
if(expandableItem) {
|
||||||
|
return expandableItem.validate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
if(expandableItem) {
|
||||||
|
return expandableItem.reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
//% "Advanced"
|
||||||
|
primaryText: qsTr("Advanced")
|
||||||
|
type: StatusExpandableItem.Type.Tertiary
|
||||||
|
expandable: true
|
||||||
|
expandableComponent: AdvancedAddAccountView {
|
||||||
|
width: parent.width
|
||||||
|
Layout.margins: Style.current.padding
|
||||||
|
Component.onCompleted: advancedSelection.isValid = Qt.binding(function(){return isValid})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rightButtons: [
|
||||||
|
StatusButton {
|
||||||
|
id: nextButton
|
||||||
|
text: loading ?
|
||||||
|
//% "Loading..."
|
||||||
|
qsTrId("loading") :
|
||||||
|
//% "Add account"
|
||||||
|
qsTrId("add-account")
|
||||||
|
|
||||||
|
enabled: !loading && passwordInput.text !== "" && accountNameInput.text !== "" && advancedSelection.isValid
|
||||||
|
|
||||||
|
MessageDialog {
|
||||||
|
id: accountError
|
||||||
|
title: "Adding the account failed"
|
||||||
|
icon: StandardIcon.Critical
|
||||||
|
standardButtons: StandardButton.Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked : {
|
||||||
|
// TODO the loaidng doesn't work because the function freezes th eview. Might need to use threads
|
||||||
|
loading = true
|
||||||
|
if (!validate() || !advancedSelection.validate()) {
|
||||||
|
Global.playErrorSound();
|
||||||
|
return loading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
var errMessage = ""
|
||||||
|
if(advancedSelection.expandableItem) {
|
||||||
|
switch(advancedSelection.expandableItem.addAccountType) {
|
||||||
|
case AdvancedAddAccountView.AddAccountType.GenerateNew:
|
||||||
|
errMessage = RootStore.generateNewAccount(passwordInput.text, accountNameInput.text, colorSelectionGrid.selectedColor, accountNameInput.input.icon.emoji)
|
||||||
|
break
|
||||||
|
case AdvancedAddAccountView.AddAccountType.ImportSeedPhrase:
|
||||||
|
errMessage = RootStore.addAccountsFromSeed(advancedSelection.expandableItem.mnemonicText, passwordInput.text, accountNameInput.text, colorSelectionGrid.selectedColor, accountNameInput.input.icon.emoji)
|
||||||
|
break
|
||||||
|
case AdvancedAddAccountView.AddAccountType.ImportPrivateKey:
|
||||||
|
errMessage = RootStore.addAccountsFromPrivateKey(advancedSelection.expandableItem.privateKey, passwordInput.text, accountNameInput.text, colorSelectionGrid.selectedColor, accountNameInput.input.icon.emoji)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errMessage = RootStore.generateNewAccount(passwordInput.text, accountNameInput.text, colorSelectionGrid.selectedColor, accountNameInput.input.icon.emoji)
|
||||||
|
}
|
||||||
|
|
||||||
|
loading = false
|
||||||
|
if (errMessage) {
|
||||||
|
Global.playErrorSound();
|
||||||
|
if (Utils.isInvalidPasswordMessage(errMessage)) {
|
||||||
|
//% "Wrong password"
|
||||||
|
popup.passwordValidationError = qsTrId("wrong-password")
|
||||||
|
} else {
|
||||||
|
accountError.text = errMessage;
|
||||||
|
accountError.open();
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
popup.afterAddAccount();
|
||||||
|
popup.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,211 +0,0 @@
|
||||||
import QtQuick 2.13
|
|
||||||
import QtQuick.Controls 2.13
|
|
||||||
import QtQuick.Dialogs 1.3
|
|
||||||
|
|
||||||
import utils 1.0
|
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
|
||||||
import StatusQ.Core.Theme 0.1
|
|
||||||
import StatusQ.Controls 0.1
|
|
||||||
import StatusQ.Popups 0.1
|
|
||||||
import StatusQ.Controls.Validators 0.1
|
|
||||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
|
||||||
|
|
||||||
import shared.panels 1.0
|
|
||||||
import shared.controls 1.0
|
|
||||||
|
|
||||||
import "../stores"
|
|
||||||
|
|
||||||
StatusModal {
|
|
||||||
id: popup
|
|
||||||
|
|
||||||
property int marginBetweenInputs: 38
|
|
||||||
property string passwordValidationError: ""
|
|
||||||
property string privateKeyValidationError: ""
|
|
||||||
property bool loading: false
|
|
||||||
property var emojiPopup: null
|
|
||||||
|
|
||||||
signal afterAddAccount()
|
|
||||||
|
|
||||||
function validate() {
|
|
||||||
if (passwordInput.text === "") {
|
|
||||||
//% "You need to enter a password"
|
|
||||||
passwordValidationError = qsTrId("you-need-to-enter-a-password")
|
|
||||||
} else if (passwordInput.text.length < 6) {
|
|
||||||
//% "Password needs to be 6 characters or more"
|
|
||||||
passwordValidationError = qsTrId("password-needs-to-be-6-characters-or-more")
|
|
||||||
} else {
|
|
||||||
passwordValidationError = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accountPKeyInput.text === "") {
|
|
||||||
//% "You need to enter a private key"
|
|
||||||
privateKeyValidationError = qsTrId("you-need-to-enter-a-private-key")
|
|
||||||
} else if (!Utils.isPrivateKey(accountPKeyInput.text)) {
|
|
||||||
//% "Enter a valid private key (64 characters hexadecimal string)"
|
|
||||||
privateKeyValidationError = qsTrId("enter-a-valid-private-key-(64-characters-hexadecimal-string)")
|
|
||||||
} else {
|
|
||||||
privateKeyValidationError = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return passwordValidationError === "" && privateKeyValidationError === "" && accountNameInput.valid
|
|
||||||
}
|
|
||||||
|
|
||||||
//% "Add account from private key"
|
|
||||||
header.title: qsTrId("add-private-key-account")
|
|
||||||
|
|
||||||
onOpened: {
|
|
||||||
passwordInput.text = ""
|
|
||||||
accountPKeyInput.text = ""
|
|
||||||
accountNameInput.reset()
|
|
||||||
accountNameInput.text = ""
|
|
||||||
accountNameInput.input.icon.emoji = StatusQUtils.Emoji.getRandomEmoji()
|
|
||||||
passwordValidationError = ""
|
|
||||||
privateKeyValidationError = ""
|
|
||||||
accountColorInput.selectedColorIndex = Math.floor(Math.random() * accountColorInput.model.length)
|
|
||||||
passwordInput.forceActiveFocus(Qt.MouseFocusReason)
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
enabled: popup.opened
|
|
||||||
target: emojiPopup
|
|
||||||
onEmojiSelected: function (emojiText, atCursor) {
|
|
||||||
popup.contentItem.accountNameInput.input.icon.emoji = emojiText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Column {
|
|
||||||
property alias accountNameInput: accountNameInput
|
|
||||||
|
|
||||||
width: popup.width
|
|
||||||
spacing: 8
|
|
||||||
topPadding: 20
|
|
||||||
|
|
||||||
Column {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Style.current.xlPadding
|
|
||||||
// To-Do Password hidden option not supported in StatusQ StatusBaseInput
|
|
||||||
Input {
|
|
||||||
id: passwordInput
|
|
||||||
anchors.leftMargin: Style.current.padding
|
|
||||||
anchors.rightMargin: Style.current.padding
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
//% "Enter your password…"
|
|
||||||
placeholderText: qsTrId("enter-your-password…")
|
|
||||||
//% "Password"
|
|
||||||
label: qsTrId("password")
|
|
||||||
textField.echoMode: TextInput.Password
|
|
||||||
validationError: popup.passwordValidationError
|
|
||||||
inputLabel.font.pixelSize: 15
|
|
||||||
inputLabel.font.weight: Font.Normal
|
|
||||||
}
|
|
||||||
// To-Do use StatusInput
|
|
||||||
StyledTextArea {
|
|
||||||
id: accountPKeyInput
|
|
||||||
customHeight: 88
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.leftMargin: Style.current.padding
|
|
||||||
anchors.rightMargin: Style.current.padding
|
|
||||||
|
|
||||||
validationError: popup.privateKeyValidationError
|
|
||||||
//% "Private key"
|
|
||||||
label: qsTrId("private-key")
|
|
||||||
textField.wrapMode: Text.WordWrap
|
|
||||||
textField.horizontalAlignment: TextEdit.AlignHCenter
|
|
||||||
textField.verticalAlignment: TextEdit.AlignVCenter
|
|
||||||
textField.font.weight: Font.DemiBold
|
|
||||||
//% "Paste the contents of your private key"
|
|
||||||
placeholderText: qsTrId("paste-the-contents-of-your-private-key")
|
|
||||||
textField.placeholderTextColor: Style.current.secondaryText
|
|
||||||
textField.selectByKeyboard: true
|
|
||||||
textField.selectionColor: Style.current.secondaryBackground
|
|
||||||
textField.selectedTextColor: Style.current.secondaryText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusInput {
|
|
||||||
id: accountNameInput
|
|
||||||
//% "Enter an account name..."
|
|
||||||
input.placeholderText: qsTrId("enter-an-account-name...")
|
|
||||||
//% "Account name"
|
|
||||||
label: qsTrId("account-name")
|
|
||||||
input.isIconSelectable: true
|
|
||||||
input.icon.color: accountColorInput.selectedColor ? accountColorInput.selectedColor : Theme.palette.directColor1
|
|
||||||
onIconClicked: {
|
|
||||||
popup.emojiPopup.open()
|
|
||||||
popup.emojiPopup.x = popup.x + Style.current.padding
|
|
||||||
popup.emojiPopup.y = popup.y + contentItem.y + accountNameInput.y + accountNameInput.height + Style.current.halfPadding
|
|
||||||
}
|
|
||||||
validators: [
|
|
||||||
StatusMinLengthValidator {
|
|
||||||
//% "You need to enter an account name"
|
|
||||||
errorMessage: qsTrId("you-need-to-enter-an-account-name")
|
|
||||||
minLength: 1
|
|
||||||
},
|
|
||||||
StatusRegularExpressionValidator {
|
|
||||||
regularExpression: /^[^<>]+$/
|
|
||||||
errorMessage: qsTr("This is not a valid account name")
|
|
||||||
}
|
|
||||||
]
|
|
||||||
charLimit: 40
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusColorSelectorGrid {
|
|
||||||
id: accountColorInput
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
//% "color"
|
|
||||||
titleText: qsTr("color").toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rightButtons: [
|
|
||||||
StatusButton {
|
|
||||||
text: loading ?
|
|
||||||
//% "Loading..."
|
|
||||||
qsTrId("loading") :
|
|
||||||
//% "Add account"
|
|
||||||
qsTrId("add-account")
|
|
||||||
|
|
||||||
enabled: !loading && passwordInput.text !== "" && accountNameInput.text !== "" && accountNameInput.valid && accountPKeyInput.text !== ""
|
|
||||||
|
|
||||||
MessageDialog {
|
|
||||||
id: accountError
|
|
||||||
title: "Adding the account failed"
|
|
||||||
icon: StandardIcon.Critical
|
|
||||||
standardButtons: StandardButton.Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked : {
|
|
||||||
// TODO the loaidng doesn't work because the function freezes th eview. Might need to use threads
|
|
||||||
loading = true
|
|
||||||
if (!validate()) {
|
|
||||||
return loading = false
|
|
||||||
}
|
|
||||||
|
|
||||||
const errMessage = RootStore.addAccountsFromPrivateKey(accountPKeyInput.text, passwordInput.text, accountNameInput.text, accountColorInput.selectedColor, accountNameInput.input.icon.emoji)
|
|
||||||
|
|
||||||
loading = false
|
|
||||||
if (errMessage) {
|
|
||||||
Global.playErrorSound();
|
|
||||||
if (Utils.isInvalidPasswordMessage(errMessage)) {
|
|
||||||
//% "Wrong password"
|
|
||||||
popup.passwordValidationError = qsTrId("wrong-password")
|
|
||||||
} else {
|
|
||||||
accountError.text = errMessage
|
|
||||||
accountError.open()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
popup.afterAddAccount()
|
|
||||||
popup.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,200 +0,0 @@
|
||||||
import QtQuick 2.13
|
|
||||||
import QtQuick.Controls 2.13
|
|
||||||
import QtQuick.Dialogs 1.3
|
|
||||||
|
|
||||||
import utils 1.0
|
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
|
||||||
import StatusQ.Core.Theme 0.1
|
|
||||||
import StatusQ.Controls 0.1
|
|
||||||
import StatusQ.Popups 0.1
|
|
||||||
import StatusQ.Controls.Validators 0.1
|
|
||||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
|
||||||
|
|
||||||
import shared.controls 1.0
|
|
||||||
|
|
||||||
import "../stores"
|
|
||||||
|
|
||||||
StatusModal {
|
|
||||||
id: popup
|
|
||||||
|
|
||||||
property string passwordValidationError: ""
|
|
||||||
property string seedValidationError: ""
|
|
||||||
property bool loading: false
|
|
||||||
property var emojiPopup: null
|
|
||||||
|
|
||||||
signal afterAddAccount()
|
|
||||||
|
|
||||||
function reset() {
|
|
||||||
passwordInput.text = ""
|
|
||||||
accountNameInput.text = ""
|
|
||||||
seedPhraseTextArea.textArea.text = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
function validate() {
|
|
||||||
if (passwordInput.text === "") {
|
|
||||||
//% "You need to enter a password"
|
|
||||||
passwordValidationError = qsTrId("you-need-to-enter-a-password")
|
|
||||||
} else if (passwordInput.text.length < 6) {
|
|
||||||
//% "Password needs to be 6 characters or more"
|
|
||||||
passwordValidationError = qsTrId("password-needs-to-be-6-characters-or-more")
|
|
||||||
} else {
|
|
||||||
passwordValidationError = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if (seedPhraseTextArea.textArea.text === "") {
|
|
||||||
//% "You need to enter a seed phrase"
|
|
||||||
seedValidationError = qsTrId("you-need-to-enter-a-seed-phrase")
|
|
||||||
} else if (!Utils.isMnemonic(seedPhraseTextArea.textArea.text)) {
|
|
||||||
//% "Enter a valid mnemonic"
|
|
||||||
seedValidationError = qsTrId("enter-a-valid-mnemonic")
|
|
||||||
} else {
|
|
||||||
seedValidationError = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return passwordValidationError === "" && seedValidationError === "" && accountNameInput.valid
|
|
||||||
}
|
|
||||||
|
|
||||||
//% "Add account with a seed phrase"
|
|
||||||
header.title: qsTrId("add-seed-account")
|
|
||||||
|
|
||||||
onOpened: {
|
|
||||||
seedPhraseTextArea.textArea.text = ""
|
|
||||||
passwordInput.text = ""
|
|
||||||
accountNameInput.text = ""
|
|
||||||
accountNameInput.reset()
|
|
||||||
accountNameInput.input.icon.emoji = StatusQUtils.Emoji.getRandomEmoji()
|
|
||||||
passwordValidationError = ""
|
|
||||||
seedValidationError = ""
|
|
||||||
accountColorInput.selectedColorIndex = Math.floor(Math.random() * accountColorInput.model.length)
|
|
||||||
passwordInput.forceActiveFocus(Qt.MouseFocusReason)
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
enabled: popup.opened
|
|
||||||
target: emojiPopup
|
|
||||||
onEmojiSelected: function (emojiText, atCursor) {
|
|
||||||
popup.contentItem.accountNameInput.input.icon.emoji = emojiText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Column {
|
|
||||||
property alias accountNameInput: accountNameInput
|
|
||||||
|
|
||||||
width: popup.width
|
|
||||||
spacing: 8
|
|
||||||
topPadding: 20
|
|
||||||
|
|
||||||
Column {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Style.current.xlPadding
|
|
||||||
// To-Do Password hidden option not supported in StatusQ StatusBaseInput
|
|
||||||
Input {
|
|
||||||
id: passwordInput
|
|
||||||
anchors.leftMargin: Style.current.padding
|
|
||||||
anchors.rightMargin: Style.current.padding
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
//% "Enter your password…"
|
|
||||||
placeholderText: qsTrId("enter-your-password…")
|
|
||||||
//% "Password"
|
|
||||||
label: qsTrId("password")
|
|
||||||
textField.echoMode: TextInput.Password
|
|
||||||
validationError: popup.passwordValidationError
|
|
||||||
inputLabel.font.pixelSize: 15
|
|
||||||
inputLabel.font.weight: Font.Normal
|
|
||||||
}
|
|
||||||
// To-Do use StatusInput
|
|
||||||
SeedPhraseTextArea {
|
|
||||||
id: seedPhraseTextArea
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Style.current.padding
|
|
||||||
width: parent.width - 2*Style.current.padding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusInput {
|
|
||||||
id: accountNameInput
|
|
||||||
//% "Enter an account name..."
|
|
||||||
input.placeholderText: qsTrId("enter-an-account-name...")
|
|
||||||
//% "Account name"
|
|
||||||
label: qsTrId("account-name")
|
|
||||||
input.isIconSelectable: true
|
|
||||||
input.icon.color: accountColorInput.selectedColor ? accountColorInput.selectedColor : Theme.palette.directColor1
|
|
||||||
onIconClicked: {
|
|
||||||
popup.emojiPopup.open()
|
|
||||||
popup.emojiPopup.x = popup.x + Style.current.padding
|
|
||||||
popup.emojiPopup.y = popup.y + contentItem.y + accountNameInput.y + accountNameInput.height + Style.current.halfPadding
|
|
||||||
}
|
|
||||||
validators: [
|
|
||||||
StatusMinLengthValidator {
|
|
||||||
//% "You need to enter an account name"
|
|
||||||
errorMessage: qsTrId("you-need-to-enter-an-account-name")
|
|
||||||
minLength: 1
|
|
||||||
},
|
|
||||||
StatusRegularExpressionValidator {
|
|
||||||
regularExpression: /^[^<>]+$/
|
|
||||||
errorMessage: qsTr("This is not a valid account name")
|
|
||||||
}
|
|
||||||
]
|
|
||||||
charLimit: 40
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusColorSelectorGrid {
|
|
||||||
id: accountColorInput
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
//% "color"
|
|
||||||
titleText: qsTr("color").toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rightButtons: [
|
|
||||||
StatusButton {
|
|
||||||
text: loading ?
|
|
||||||
//% "Loading..."
|
|
||||||
qsTrId("loading") :
|
|
||||||
//% "Add account"
|
|
||||||
qsTrId("add-account")
|
|
||||||
|
|
||||||
enabled: !loading && passwordInput.text !== "" && accountNameInput.text !== "" && accountNameInput.valid && seedPhraseTextArea.correctWordCount
|
|
||||||
|
|
||||||
MessageDialog {
|
|
||||||
id: accountError
|
|
||||||
title: "Adding the account failed"
|
|
||||||
icon: StandardIcon.Critical
|
|
||||||
standardButtons: StandardButton.Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked : {
|
|
||||||
// TODO the loading doesn't work because the function freezes the view. Might need to use threads
|
|
||||||
loading = true
|
|
||||||
if (!validate() || !seedPhraseTextArea.validateSeed()) {
|
|
||||||
Global.playErrorSound();
|
|
||||||
return loading = false
|
|
||||||
}
|
|
||||||
|
|
||||||
const errMessage = RootStore.addAccountsFromSeed(seedPhraseTextArea.textArea.text, passwordInput.text, accountNameInput.text, accountColorInput.selectedColor, accountNameInput.input.icon.emoji)
|
|
||||||
loading = false
|
|
||||||
if (errMessage) {
|
|
||||||
Global.playErrorSound();
|
|
||||||
if (Utils.isInvalidPasswordMessage(errMessage)) {
|
|
||||||
//% "Wrong password"
|
|
||||||
popup.passwordValidationError = qsTrId("wrong-password")
|
|
||||||
} else {
|
|
||||||
accountError.text = errMessage
|
|
||||||
accountError.open()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
popup.afterAddAccount()
|
|
||||||
popup.reset()
|
|
||||||
popup.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
import QtQuick 2.13
|
|
||||||
import QtQuick.Controls 2.13
|
|
||||||
|
|
||||||
import shared 1.0
|
|
||||||
import shared.popups 1.0
|
|
||||||
|
|
||||||
import utils 1.0
|
|
||||||
|
|
||||||
// TODO: replace with StatusPopupMenu
|
|
||||||
PopupMenu {
|
|
||||||
id: newAccountMenu
|
|
||||||
width: 260
|
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
|
||||||
|
|
||||||
signal generateNewAccountTriggered();
|
|
||||||
signal addWatchAccountTriggered();
|
|
||||||
signal enterSeedPhraseTriggered();
|
|
||||||
signal enterPrivateKeyTriggered();
|
|
||||||
|
|
||||||
Action {
|
|
||||||
//% "Generate an account"
|
|
||||||
text: qsTrId("generate-a-new-account")
|
|
||||||
icon.source: Style.svg("generate_account")
|
|
||||||
icon.width: 19
|
|
||||||
icon.height: 19
|
|
||||||
onTriggered: {
|
|
||||||
newAccountMenu.generateNewAccountTriggered();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Action {
|
|
||||||
//% "Add a watch-only address"
|
|
||||||
text: qsTrId("add-a-watch-account")
|
|
||||||
icon.source: Style.svg("eye")
|
|
||||||
icon.width: 19
|
|
||||||
icon.height: 19
|
|
||||||
onTriggered: {
|
|
||||||
newAccountMenu.addWatchAccountTriggered();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Action {
|
|
||||||
//% "Enter a seed phrase"
|
|
||||||
text: qsTrId("enter-a-seed-phrase")
|
|
||||||
icon.source: Style.svg("enter_seed_phrase")
|
|
||||||
icon.width: 19
|
|
||||||
icon.height: 19
|
|
||||||
onTriggered: {
|
|
||||||
newAccountMenu.enterSeedPhraseTriggered();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Action {
|
|
||||||
//% "Enter a private key"
|
|
||||||
text: qsTrId("enter-a-private-key")
|
|
||||||
icon.source: Style.svg("enter_private_key")
|
|
||||||
icon.width: 19
|
|
||||||
icon.height: 19
|
|
||||||
onTriggered: {
|
|
||||||
newAccountMenu.enterPrivateKeyTriggered();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,150 +0,0 @@
|
||||||
import QtQuick 2.13
|
|
||||||
import QtQuick.Controls 2.13
|
|
||||||
import QtQuick.Dialogs 1.3
|
|
||||||
|
|
||||||
import utils 1.0
|
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
|
||||||
import StatusQ.Core.Theme 0.1
|
|
||||||
import StatusQ.Popups 0.1
|
|
||||||
import StatusQ.Controls 0.1
|
|
||||||
import StatusQ.Controls.Validators 0.1
|
|
||||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
|
||||||
|
|
||||||
import shared.controls 1.0
|
|
||||||
|
|
||||||
import "../stores"
|
|
||||||
|
|
||||||
StatusModal {
|
|
||||||
id: popup
|
|
||||||
|
|
||||||
property bool loading: false
|
|
||||||
property var emojiPopup: null
|
|
||||||
|
|
||||||
signal afterAddAccount()
|
|
||||||
|
|
||||||
//% "Add a watch-only account"
|
|
||||||
header.title: qsTrId("add-watch-account")
|
|
||||||
|
|
||||||
onOpened: {
|
|
||||||
addressInput.text = ""
|
|
||||||
addressInput.reset()
|
|
||||||
accountNameInput.text = ""
|
|
||||||
accountNameInput.reset()
|
|
||||||
accountNameInput.input.icon.emoji = StatusQUtils.Emoji.getRandomEmoji()
|
|
||||||
accountColorInput.selectedColorIndex = Math.floor(Math.random() * accountColorInput.model.length)
|
|
||||||
addressInput.forceActiveFocus(Qt.MouseFocusReason)
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
enabled: popup.opened
|
|
||||||
target: emojiPopup
|
|
||||||
onEmojiSelected: function (emojiText, atCursor) {
|
|
||||||
popup.contentItem.accountNameInput.input.icon.emoji = emojiText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Column {
|
|
||||||
property alias accountNameInput: accountNameInput
|
|
||||||
|
|
||||||
width: popup.width
|
|
||||||
spacing: 8
|
|
||||||
topPadding: 20
|
|
||||||
|
|
||||||
StatusInput {
|
|
||||||
id: addressInput
|
|
||||||
// TODO add QR code reader for the address
|
|
||||||
//% "Enter address..."
|
|
||||||
input.placeholderText: qsTrId("enter-address...")
|
|
||||||
//% "Account address"
|
|
||||||
label: qsTrId("wallet-key-title")
|
|
||||||
validators: [
|
|
||||||
StatusAddressValidator {
|
|
||||||
//% "This needs to be a valid address (starting with 0x)"
|
|
||||||
errorMessage: qsTrId("this-needs-to-be-a-valid-address-(starting-with-0x)")
|
|
||||||
},
|
|
||||||
StatusMinLengthValidator {
|
|
||||||
//% "You need to enter an address"
|
|
||||||
errorMessage: qsTrId("you-need-to-enter-an-address")
|
|
||||||
minLength: 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusInput {
|
|
||||||
id: accountNameInput
|
|
||||||
//% "Enter an account name..."
|
|
||||||
input.placeholderText: qsTrId("enter-an-account-name...")
|
|
||||||
//% "Account name"
|
|
||||||
label: qsTrId("account-name")
|
|
||||||
input.isIconSelectable: true
|
|
||||||
input.icon.color: accountColorInput.selectedColor ? accountColorInput.selectedColor : Theme.palette.directColor1
|
|
||||||
onIconClicked: {
|
|
||||||
popup.emojiPopup.open()
|
|
||||||
popup.emojiPopup.x = popup.x + Style.current.padding
|
|
||||||
popup.emojiPopup.y = popup.y + contentItem.y + accountNameInput.y + accountNameInput.height + Style.current.halfPadding
|
|
||||||
}
|
|
||||||
validators: [
|
|
||||||
StatusMinLengthValidator {
|
|
||||||
//% "You need to enter an account name"
|
|
||||||
errorMessage: qsTrId("you-need-to-enter-an-account-name")
|
|
||||||
minLength: 1
|
|
||||||
},
|
|
||||||
StatusRegularExpressionValidator {
|
|
||||||
regularExpression: /^[^<>]+$/
|
|
||||||
errorMessage: qsTr("This is not a valid account name")
|
|
||||||
}
|
|
||||||
]
|
|
||||||
charLimit: 40
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusColorSelectorGrid {
|
|
||||||
id: accountColorInput
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
//% "color"
|
|
||||||
titleText: qsTr("color").toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rightButtons: [
|
|
||||||
StatusButton {
|
|
||||||
text: loading ?
|
|
||||||
//% "Loading..."
|
|
||||||
qsTrId("loading") :
|
|
||||||
//% "Add account"
|
|
||||||
qsTrId("add-account")
|
|
||||||
|
|
||||||
enabled: !loading && addressInput.text !== "" && accountNameInput.text !== "" && accountNameInput.valid
|
|
||||||
|
|
||||||
MessageDialog {
|
|
||||||
id: accountError
|
|
||||||
title: "Adding the account failed"
|
|
||||||
icon: StandardIcon.Critical
|
|
||||||
standardButtons: StandardButton.Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked : {
|
|
||||||
// TODO the loaidng doesn't work because the function freezes th eview. Might need to use threads
|
|
||||||
loading = true
|
|
||||||
if (!addressInput.valid || !accountNameInput.valid) {
|
|
||||||
Global.playErrorSound();
|
|
||||||
return loading = false
|
|
||||||
}
|
|
||||||
const error = RootStore.addWatchOnlyAccount(addressInput.text, accountNameInput.text, accountColorInput.selectedColor, accountNameInput.input.icon.emoji);
|
|
||||||
loading = false
|
|
||||||
if (error) {
|
|
||||||
Global.playErrorSound();
|
|
||||||
accountError.text = error
|
|
||||||
return accountError.open()
|
|
||||||
}
|
|
||||||
popup.afterAddAccount()
|
|
||||||
popup.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,173 +0,0 @@
|
||||||
import QtQuick 2.13
|
|
||||||
import QtQuick.Controls 2.13
|
|
||||||
import QtQuick.Dialogs 1.3
|
|
||||||
|
|
||||||
import utils 1.0
|
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
|
||||||
import StatusQ.Core.Theme 0.1
|
|
||||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
|
||||||
import StatusQ.Controls 0.1
|
|
||||||
import StatusQ.Controls.Validators 0.1
|
|
||||||
import StatusQ.Popups 0.1
|
|
||||||
|
|
||||||
import shared.controls 1.0
|
|
||||||
|
|
||||||
import "../stores"
|
|
||||||
|
|
||||||
StatusModal {
|
|
||||||
id: popup
|
|
||||||
|
|
||||||
property int marginBetweenInputs: 38
|
|
||||||
property string passwordValidationError: ""
|
|
||||||
property bool loading: false
|
|
||||||
property var emojiPopup: null
|
|
||||||
|
|
||||||
signal afterAddAccount()
|
|
||||||
|
|
||||||
//% "Generate an account"
|
|
||||||
header.title: qsTrId("generate-a-new-account")
|
|
||||||
|
|
||||||
function validate() {
|
|
||||||
if (passwordInput.text === "") {
|
|
||||||
//% "You need to enter a password"
|
|
||||||
passwordValidationError = qsTrId("you-need-to-enter-a-password")
|
|
||||||
} else if (passwordInput.text.length < 6) {
|
|
||||||
//% "Password needs to be 6 characters or more"
|
|
||||||
passwordValidationError = qsTrId("password-needs-to-be-6-characters-or-more")
|
|
||||||
} else {
|
|
||||||
passwordValidationError = ""
|
|
||||||
}
|
|
||||||
return passwordValidationError === "" && accountNameInput.valid
|
|
||||||
}
|
|
||||||
|
|
||||||
onOpened: {
|
|
||||||
passwordValidationError = "";
|
|
||||||
passwordInput.text = "";
|
|
||||||
accountNameInput.reset()
|
|
||||||
accountNameInput.text = "";
|
|
||||||
accountNameInput.input.icon.emoji = StatusQUtils.Emoji.getRandomEmoji()
|
|
||||||
colorSelectionGrid.selectedColorIndex = Math.floor(Math.random() * colorSelectionGrid.model.length)
|
|
||||||
passwordInput.forceActiveFocus(Qt.MouseFocusReason)
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
enabled: popup.opened
|
|
||||||
target: emojiPopup
|
|
||||||
onEmojiSelected: function (emojiText, atCursor) {
|
|
||||||
popup.contentItem.accountNameInput.input.icon.emoji = emojiText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Column {
|
|
||||||
property alias accountNameInput: accountNameInput
|
|
||||||
width: popup.width
|
|
||||||
spacing: 8
|
|
||||||
topPadding: 20
|
|
||||||
|
|
||||||
// To-Do Password hidden option not supported in StatusQ StatusBaseInput
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: passwordInput.height
|
|
||||||
Input {
|
|
||||||
id: passwordInput
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: Style.current.padding
|
|
||||||
anchors.rightMargin: Style.current.padding
|
|
||||||
|
|
||||||
//% "Enter your password…"
|
|
||||||
placeholderText: qsTrId("enter-your-password…")
|
|
||||||
//% "Password"
|
|
||||||
label: qsTrId("password")
|
|
||||||
textField.echoMode: TextInput.Password
|
|
||||||
validationError: popup.passwordValidationError
|
|
||||||
inputLabel.font.pixelSize: 15
|
|
||||||
inputLabel.font.weight: Font.Normal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusInput {
|
|
||||||
id: accountNameInput
|
|
||||||
//% "Enter an account name..."
|
|
||||||
input.placeholderText: qsTrId("enter-an-account-name...")
|
|
||||||
//% "Account name"
|
|
||||||
label: qsTrId("account-name")
|
|
||||||
input.isIconSelectable: true
|
|
||||||
input.icon.color: colorSelectionGrid.selectedColor ? colorSelectionGrid.selectedColor : Theme.palette.directColor1
|
|
||||||
onIconClicked: {
|
|
||||||
popup.emojiPopup.open()
|
|
||||||
popup.emojiPopup.x = popup.x + accountNameInput.x + Style.current.padding
|
|
||||||
popup.emojiPopup.y = popup.y + contentItem.y + accountNameInput.y + accountNameInput.height + Style.current.halfPadding
|
|
||||||
}
|
|
||||||
validators: [
|
|
||||||
StatusMinLengthValidator {
|
|
||||||
//% "You need to enter an account name"
|
|
||||||
errorMessage: qsTrId("you-need-to-enter-an-account-name")
|
|
||||||
minLength: 1
|
|
||||||
},
|
|
||||||
StatusRegularExpressionValidator {
|
|
||||||
regularExpression: /^[^<>]+$/
|
|
||||||
errorMessage: qsTr("This is not a valid account name")
|
|
||||||
}
|
|
||||||
]
|
|
||||||
charLimit: 40
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusColorSelectorGrid {
|
|
||||||
id: colorSelectionGrid
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
//% "color"
|
|
||||||
titleText: qsTr("color").toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rightButtons: [
|
|
||||||
StatusButton {
|
|
||||||
text: loading ?
|
|
||||||
//% "Loading..."
|
|
||||||
qsTrId("loading") :
|
|
||||||
//% "Add account"
|
|
||||||
qsTrId("add-account")
|
|
||||||
|
|
||||||
enabled: !loading && passwordInput.text !== "" && accountNameInput.text !== "" && accountNameInput.valid
|
|
||||||
|
|
||||||
MessageDialog {
|
|
||||||
id: accountError
|
|
||||||
title: "Adding the account failed"
|
|
||||||
icon: StandardIcon.Critical
|
|
||||||
standardButtons: StandardButton.Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked : {
|
|
||||||
// TODO the loaidng doesn't work because the function freezes th eview. Might need to use threads
|
|
||||||
loading = true
|
|
||||||
if (!validate()) {
|
|
||||||
Global.playErrorSound();
|
|
||||||
return loading = false
|
|
||||||
}
|
|
||||||
|
|
||||||
const errMessage = RootStore.generateNewAccount(passwordInput.text, accountNameInput.text, colorSelectionGrid.selectedColor, accountNameInput.input.icon.emoji)
|
|
||||||
console.log(errMessage)
|
|
||||||
loading = false
|
|
||||||
if (errMessage) {
|
|
||||||
Global.playErrorSound();
|
|
||||||
if (Utils.isInvalidPasswordMessage(errMessage)) {
|
|
||||||
//% "Wrong password"
|
|
||||||
popup.passwordValidationError = qsTrId("wrong-password")
|
|
||||||
} else {
|
|
||||||
accountError.text = errMessage;
|
|
||||||
accountError.open();
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
popup.afterAddAccount();
|
|
||||||
popup.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -8,6 +8,7 @@ QtObject {
|
||||||
id: root
|
id: root
|
||||||
property var currentAccount: Constants.isCppApp ? walletSectionAccounts.currentAccount: walletSectionCurrent
|
property var currentAccount: Constants.isCppApp ? walletSectionAccounts.currentAccount: walletSectionCurrent
|
||||||
property var accounts: walletSectionAccounts.model
|
property var accounts: walletSectionAccounts.model
|
||||||
|
property var generatedAccounts: walletSectionAccounts.generated
|
||||||
property var appSettings: localAppSettings
|
property var appSettings: localAppSettings
|
||||||
property var accountSensitiveSettings: localAccountSensitiveSettings
|
property var accountSensitiveSettings: localAccountSensitiveSettings
|
||||||
property string locale: appSettings.locale
|
property string locale: appSettings.locale
|
||||||
|
|
|
@ -0,0 +1,354 @@
|
||||||
|
import QtQuick 2.14
|
||||||
|
import QtQuick.Controls 2.14
|
||||||
|
import QtQuick.Layouts 1.14
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Popups 0.1
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
import StatusQ.Core.Utils 0.1
|
||||||
|
import StatusQ.Controls.Validators 0.1
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
import "../stores"
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: advancedSection
|
||||||
|
|
||||||
|
property alias privateKey: privateKey.text
|
||||||
|
property int addAccountType: AdvancedAddAccountView.AddAccountType.GenerateNew
|
||||||
|
property string mnemonicText: getSeedPhraseString()
|
||||||
|
property string errorString: ""
|
||||||
|
property bool isValid: addAccountType === AdvancedAddAccountView.AddAccountType.ImportSeedPhrase ? grid.isValid :
|
||||||
|
addAccountType === AdvancedAddAccountView.AddAccountType.ImportPrivateKey ? (privateKey.text !== "" && privateKey.valid) : true
|
||||||
|
|
||||||
|
enum AddAccountType {
|
||||||
|
GenerateNew,
|
||||||
|
ImportSeedPhrase,
|
||||||
|
ImportPrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
mnemonicText = ""
|
||||||
|
errorString = ""
|
||||||
|
select.currentIndex = 0
|
||||||
|
addAccountType = AdvancedAddAccountView.AddAccountType.GenerateNew
|
||||||
|
privateKey.text = ""
|
||||||
|
privateKey.reset()
|
||||||
|
for(var i = 0; i < grid.model; i++) {
|
||||||
|
if(grid.itemAtIndex(i)) {
|
||||||
|
grid.itemAtIndex(i).textEdit.text = ""
|
||||||
|
grid.itemAtIndex(i).textEdit.reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate() {
|
||||||
|
errorString = "";
|
||||||
|
if(addAccountType == AdvancedAddAccountView.AddAccountType.ImportSeedPhrase) {
|
||||||
|
mnemonicText = getSeedPhraseString()
|
||||||
|
|
||||||
|
if (!Utils.isMnemonic(mnemonicText)) {
|
||||||
|
//% "Invalid seed phrase"
|
||||||
|
errorString = qsTrId("custom-seed-phrase")
|
||||||
|
} else {
|
||||||
|
errorString = onboardingModule.validateMnemonic(mnemonicText)
|
||||||
|
const regex = new RegExp('word [a-z]+ not found in the dictionary', 'i');
|
||||||
|
if (regex.test(errorString)) {
|
||||||
|
//% "Invalid seed phrase"
|
||||||
|
errorString = qsTrId("custom-seed-phrase") + '. ' +
|
||||||
|
//% "This seed phrase doesn't match our supported dictionary. Check for misspelled words."
|
||||||
|
qsTrId("custom-seed-phrase-text-1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errorString === ""
|
||||||
|
}
|
||||||
|
else if(addAccountType == AdvancedAddAccountView.AddAccountType.ImportPrivateKey) {
|
||||||
|
if (privateKey.text === "") {
|
||||||
|
//% "You need to enter a private key"
|
||||||
|
errorString = qsTrId("you-need-to-enter-a-private-key")
|
||||||
|
} else if (!Utils.isPrivateKey(privateKey.text)) {
|
||||||
|
//% "Enter a valid private key (64 characters hexadecimal string)"
|
||||||
|
errorString = qsTrId("enter-a-valid-private-key-(64-characters-hexadecimal-string)")
|
||||||
|
} else {
|
||||||
|
errorString = ""
|
||||||
|
}
|
||||||
|
return errorString === ""
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSeedPhraseString() {
|
||||||
|
var seedPhrase = ""
|
||||||
|
for(var i = 0; i < grid.model; i++) {
|
||||||
|
seedPhrase += grid.itemAtIndex(i).text + " "
|
||||||
|
}
|
||||||
|
return seedPhrase
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: _internal
|
||||||
|
property int seedPhraseInputHeight: 44
|
||||||
|
property int seedPhraseInputWidth: 220
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: Style.current.padding
|
||||||
|
|
||||||
|
StatusSelect {
|
||||||
|
id: select
|
||||||
|
//% "Origin"
|
||||||
|
label: qsTr("Origin")
|
||||||
|
Layout.margins: Style.current.padding
|
||||||
|
property int currentIndex: 0
|
||||||
|
selectedItemComponent: StatusListItem {
|
||||||
|
id: selectedItem
|
||||||
|
icon.background.color: "transparent"
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.palette.baseColor2
|
||||||
|
tagsDelegate: StatusListItemTag {
|
||||||
|
color: model.color
|
||||||
|
height: Style.current.bigPadding
|
||||||
|
radius: 6
|
||||||
|
closeButtonVisible: false
|
||||||
|
icon.emoji: model.emoji
|
||||||
|
icon.emojiSize: Emoji.size.verySmall
|
||||||
|
icon.isLetterIdenticon: true
|
||||||
|
title: model.name
|
||||||
|
titleText.font.pixelSize: 12
|
||||||
|
titleText.color: Theme.palette.indirectColor1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
model: ListModel {
|
||||||
|
Component.onCompleted: {
|
||||||
|
//% "Default"
|
||||||
|
append({"name": qsTr("Default"), "iconName": "status", "accountsModel": RootStore.generatedAccounts, "enabled": true})
|
||||||
|
//% "Add new"
|
||||||
|
append({"name": qsTr("Add new"), "iconName": "", "enabled": false})
|
||||||
|
//% "Import new Seed Phrase"
|
||||||
|
append({"name": qsTr("Import new Seed Phrase"), "iconName": "seed-phrase", "enabled": true})
|
||||||
|
//% "Import new Private Key"
|
||||||
|
append({"name": qsTr("Import new Private Key"), "iconName": "password", "enabled": true})
|
||||||
|
selectedItem.title = Qt.binding(function() {return get(select.currentIndex).name})
|
||||||
|
selectedItem.icon.name = Qt.binding(function() {return get(select.currentIndex).iconName})
|
||||||
|
selectedItem.tagsModel = Qt.binding(function() {return get(select.currentIndex).accountsModel})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectMenu.delegate: StatusListItem {
|
||||||
|
id: defaultListItem
|
||||||
|
title: model.name
|
||||||
|
icon.name: model.iconName
|
||||||
|
tagsModel : model.accountsModel
|
||||||
|
enabled: model.enabled
|
||||||
|
icon.background.color: "transparent"
|
||||||
|
icon.color: model.accountsModel ? Theme.palette.primaryColor1 : Theme.palette.directColor5
|
||||||
|
tagsDelegate: StatusListItemTag {
|
||||||
|
color: model.color
|
||||||
|
height: 24
|
||||||
|
radius: 6
|
||||||
|
closeButtonVisible: false
|
||||||
|
icon.emoji: model.emoji
|
||||||
|
icon.emojiSize: Emoji.size.verySmall
|
||||||
|
icon.isLetterIdenticon: true
|
||||||
|
title: model.name
|
||||||
|
titleText.font.pixelSize: 12
|
||||||
|
titleText.color: Theme.palette.indirectColor1
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
advancedSection.addAccountType = (index === 2) ? AdvancedAddAccountView.AddAccountType.ImportSeedPhrase :
|
||||||
|
(index === 3) ? AdvancedAddAccountView.AddAccountType.ImportPrivateKey :
|
||||||
|
AdvancedAddAccountView.AddAccountType.GenerateNew
|
||||||
|
select.currentIndex = index
|
||||||
|
select.selectMenu.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusInput {
|
||||||
|
id: privateKey
|
||||||
|
//% "Private key"
|
||||||
|
label: qsTrId("private-key")
|
||||||
|
charLimit: 64
|
||||||
|
input.multiline: true
|
||||||
|
input.minimumHeight: 80
|
||||||
|
input.maximumHeight: 108
|
||||||
|
//% "Paste the contents of your private key"
|
||||||
|
input.placeholderText: qsTrId("paste-the-contents-of-your-private-key")
|
||||||
|
visible: advancedSection.addAccountType === AdvancedAddAccountView.AddAccountType.ImportPrivateKey && advancedSection.visible
|
||||||
|
errorMessage: advancedSection.errorString
|
||||||
|
validators: [
|
||||||
|
StatusMinLengthValidator {
|
||||||
|
minLength: 1
|
||||||
|
//% "You need to enter a private key"
|
||||||
|
errorMessage: qsTrId("you-need-to-enter-a-private-key")
|
||||||
|
},
|
||||||
|
StatusValidator {
|
||||||
|
property var validate: function (value) {
|
||||||
|
return Utils.isPrivateKey(value)
|
||||||
|
}
|
||||||
|
//% "Enter a valid private key (64 characters hexadecimal string)"
|
||||||
|
errorMessage: qsTrId("enter-a-valid-private-key-(64-characters-hexadecimal-string)")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
onVisibleChanged: {
|
||||||
|
if(visible)
|
||||||
|
privateKey.input.edit.forceActiveFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GridView {
|
||||||
|
id: grid
|
||||||
|
Layout.preferredWidth: parent.width
|
||||||
|
Layout.preferredHeight: visible ? (cellHeight * model/2) + footerItem.height: 0
|
||||||
|
Layout.leftMargin: Style.current.padding
|
||||||
|
Layout.rightMargin: Style.current.padding
|
||||||
|
visible: advancedSection.addAccountType === AdvancedAddAccountView.AddAccountType.ImportSeedPhrase && advancedSection.visible
|
||||||
|
cellHeight: _internal.seedPhraseInputHeight + Style.current.halfPadding
|
||||||
|
cellWidth: _internal.seedPhraseInputWidth + Style.current.halfPadding
|
||||||
|
model: 12
|
||||||
|
interactive: false
|
||||||
|
property bool isValid: checkIsValid()
|
||||||
|
function checkIsValid() {
|
||||||
|
var valid = model > 0 ? true: false
|
||||||
|
for(var i = 0; i < model; i++) {
|
||||||
|
if(grid.itemAtIndex(i))
|
||||||
|
valid &= grid.itemAtIndex(i).isValid
|
||||||
|
}
|
||||||
|
return valid
|
||||||
|
}
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if(visible)
|
||||||
|
grid.itemAtIndex(0).textEdit.input.edit.forceActiveFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// To-do Alex has introduced a model for bip39 dictonary, need to use it once its available
|
||||||
|
// https://github.com/status-im/status-desktop/pull/5058
|
||||||
|
delegate: StatusSeedPhraseInput {
|
||||||
|
id: statusSeedInput
|
||||||
|
width: _internal.seedPhraseInputWidth
|
||||||
|
height: _internal.seedPhraseInputHeight
|
||||||
|
textEdit.errorMessageCmp.visible: false
|
||||||
|
textEdit.input.anchors.topMargin: 11
|
||||||
|
leftComponentText: index + 1
|
||||||
|
property bool isValid: !!text
|
||||||
|
onIsValidChanged: {
|
||||||
|
grid.isValid = grid.checkIsValid()
|
||||||
|
}
|
||||||
|
onTextChanged: {
|
||||||
|
if (text !== "") {
|
||||||
|
grid.currentIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// To-do Alex has introduced a model for bip39 dictonary, need to use it once its available
|
||||||
|
// https://github.com/status-im/status-desktop/pull/5058
|
||||||
|
// onDoneInsertingWord: {
|
||||||
|
// advancedSection.mnemonicText += (index === 0) ? word : (" " + word);
|
||||||
|
// for (var i = !grid.atXBeginning ? 12 : 0; i < grid.count; i++) {
|
||||||
|
// if (parseInt(grid.itemAtIndex(i).leftComponentText) === (parseInt(leftComponentText)+1)) {
|
||||||
|
// grid.itemAtIndex(i).textEdit.input.edit.forceActiveFocus();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
textEdit.validators: [
|
||||||
|
StatusMinLengthValidator {
|
||||||
|
errorMessage: qsTr("Enter a valid word")
|
||||||
|
minLength: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
footer: Item {
|
||||||
|
width: grid.width - Style.current.padding
|
||||||
|
height: button.height + errorMessage.height + 16*2
|
||||||
|
StatusBaseText {
|
||||||
|
id: errorMessage
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: Style.current.padding
|
||||||
|
|
||||||
|
height: visible ? implicitHeight : 0
|
||||||
|
visible: !!text
|
||||||
|
text: errorString
|
||||||
|
|
||||||
|
font.pixelSize: 12
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
StatusButton {
|
||||||
|
id: button
|
||||||
|
anchors.top: errorMessage.bottom
|
||||||
|
anchors.topMargin: Style.current.padding
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
//% "Use 24 word seed phrase"
|
||||||
|
text: grid.model === 12 ? qsTr("Use 24 word seed phrase"):
|
||||||
|
qsTr("Use 12 word seed phrase")
|
||||||
|
onClicked: grid.model = grid.model === 12 ? 24 : 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.margins: Style.current.padding
|
||||||
|
Layout.preferredWidth: parent.width
|
||||||
|
spacing: Style.current.bigPadding
|
||||||
|
StatusSelect {
|
||||||
|
Layout.preferredWidth: 213
|
||||||
|
//% "Origin"
|
||||||
|
label: qsTr("Derivation Path")
|
||||||
|
selectedItemComponent: StatusListItem {
|
||||||
|
width: parent.width
|
||||||
|
icon.background.color: "transparent"
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.palette.baseColor2
|
||||||
|
title: "Default"
|
||||||
|
subTitle: "m/44’/61’/0’/1"
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
StatusSelect {
|
||||||
|
Layout.preferredWidth: 213
|
||||||
|
//% "Origin"
|
||||||
|
label: qsTr("Account")
|
||||||
|
width: parent.width
|
||||||
|
enabled: false
|
||||||
|
selectedItemComponent: StatusListItem {
|
||||||
|
icon.background.color: "transparent"
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.palette.baseColor2
|
||||||
|
title: "0x1234...abcd"
|
||||||
|
subTitle: "No activity"
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import shared.controls 1.0
|
||||||
|
|
||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
|
||||||
import "../controls"
|
import "../controls"
|
||||||
import "../popups"
|
import "../popups"
|
||||||
|
@ -73,66 +74,10 @@ Rectangle {
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
font.pixelSize: 13
|
font.pixelSize: 13
|
||||||
}
|
}
|
||||||
|
|
||||||
AddAccountButton {
|
|
||||||
id: addAccountButton
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.right: parent.right
|
|
||||||
onClicked: {
|
|
||||||
if (newAccountMenu.opened) {
|
|
||||||
newAccountMenu.close()
|
|
||||||
} else {
|
|
||||||
newAccountMenu.popup(addAccountButton.x + addAccountButton.width/2 - newAccountMenu.width/2 ,
|
|
||||||
addAccountButton.y + addAccountButton.height + 55)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AddNewAccountMenu {
|
AddAccountModal {
|
||||||
id: newAccountMenu
|
id: addAccountModal
|
||||||
onAboutToShow: addAccountButton.state = "pressed"
|
|
||||||
onAboutToHide: {
|
|
||||||
addAccountButton.state = "default";
|
|
||||||
addAccountButton.checked = false;
|
|
||||||
}
|
|
||||||
onGenerateNewAccountTriggered: {
|
|
||||||
generateAccountModal.open();
|
|
||||||
}
|
|
||||||
onAddWatchAccountTriggered: {
|
|
||||||
addWatchOnlyAccountModal.open();
|
|
||||||
}
|
|
||||||
onEnterSeedPhraseTriggered: {
|
|
||||||
addAccountWithSeedModal.open();
|
|
||||||
}
|
|
||||||
onEnterPrivateKeyTriggered: {
|
|
||||||
addAccountWithPrivateKeydModal.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GenerateAccountModal {
|
|
||||||
id: generateAccountModal
|
|
||||||
anchors.centerIn: parent
|
|
||||||
onAfterAddAccount: walletInfoContainer.onAfterAddAccount()
|
|
||||||
emojiPopup: walletInfoContainer.emojiPopup
|
|
||||||
}
|
|
||||||
|
|
||||||
AddAccountWithSeedModal {
|
|
||||||
id: addAccountWithSeedModal
|
|
||||||
anchors.centerIn: parent
|
|
||||||
onAfterAddAccount: walletInfoContainer.onAfterAddAccount()
|
|
||||||
emojiPopup: walletInfoContainer.emojiPopup
|
|
||||||
}
|
|
||||||
|
|
||||||
AddAccountWithPrivateKeyModal {
|
|
||||||
id: addAccountWithPrivateKeydModal
|
|
||||||
anchors.centerIn: parent
|
|
||||||
onAfterAddAccount: walletInfoContainer.onAfterAddAccount()
|
|
||||||
emojiPopup: walletInfoContainer.emojiPopup
|
|
||||||
}
|
|
||||||
|
|
||||||
AddWatchOnlyAccountModal {
|
|
||||||
id: addWatchOnlyAccountModal
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
onAfterAddAccount: walletInfoContainer.onAfterAddAccount()
|
onAfterAddAccount: walletInfoContainer.onAfterAddAccount()
|
||||||
emojiPopup: walletInfoContainer.emojiPopup
|
emojiPopup: walletInfoContainer.emojiPopup
|
||||||
|
@ -176,6 +121,20 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
footer: Item {
|
||||||
|
width: parent.width
|
||||||
|
height: addAccountBtn.height + Style.current.xlPadding
|
||||||
|
StatusButton {
|
||||||
|
id: addAccountBtn
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.margins: Style.current.bigPadding
|
||||||
|
//% "Add account"
|
||||||
|
text: qsTrId("add-account")
|
||||||
|
onClicked: addAccountModal.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
model: RootStore.accounts
|
model: RootStore.accounts
|
||||||
// model: RootStore.exampleWalletModel
|
// model: RootStore.exampleWalletModel
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue