fix(SeedPhrase): Fixing seed phrase validation (#13496)
* fix(SeedPhrase): Fixing seed phrase validation 1. Validate each word after the editing is finished 2. Fixing the seed phrase suggestions where the suggestions box was hidden behind other ui elements 3. Propagate editingFinished signal in StatusBaseInput, StatusInput, StatusSeedPhraseInput 4. Fixing undefined `mnemonicIndex` errors * fix: Refactoring of SeedPhraseInputView Remove duplicated code and use EnterSeedPhrase component + Added storybook page * fix(Onboarding): Fixing seed phrase validation on windows The seed phrase validation fails on windows due to the dictionary line endings * chore(squish): Update e2e tests to the new enter seed phrase panel construction * fix: Load english dictionary from local file using StringUtils
This commit is contained in:
parent
46e256673c
commit
8a69f3bc63
|
@ -0,0 +1,14 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
import shared.panels 1.0
|
||||
|
||||
import Storybook 1.0
|
||||
|
||||
|
||||
Item {
|
||||
EnterSeedPhrase {
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
// category: Panels
|
|
@ -0,0 +1,72 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import AppLayouts.Onboarding.views 1.0
|
||||
import AppLayouts.Onboarding.stores 1.0
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import Storybook 1.0
|
||||
|
||||
SplitView {
|
||||
Logs { id: logs }
|
||||
|
||||
SeedPhraseInputView {
|
||||
SplitView.fillWidth: true
|
||||
SplitView.fillHeight: true
|
||||
|
||||
startupStore: StartupStore {
|
||||
id: startupStore
|
||||
function validMnemonic(mnemonic) {
|
||||
return true
|
||||
}
|
||||
property QtObject startupModuleInst: QtObject {
|
||||
property int keycardData: keycardDataCheckbox.checked ? 0 : Constants.predefinedKeycardData.wrongSeedPhrase
|
||||
property string flowType: flowTypeComboBox.currentText
|
||||
}
|
||||
property QtObject currentStartupState: QtObject {
|
||||
property string flowType: flowTypeComboBox.currentText
|
||||
}
|
||||
|
||||
function doPrimaryAction() {
|
||||
logs.logEvent("Primary action clicked")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogsAndControlsPanel {
|
||||
id: logsAndControlsPanel
|
||||
|
||||
SplitView.minimumHeight: 100
|
||||
SplitView.preferredHeight: 200
|
||||
SplitView.preferredWidth: 300
|
||||
|
||||
logsView.logText: logs.logText
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 10
|
||||
width: parent.width
|
||||
|
||||
CheckBox {
|
||||
id: keycardDataCheckbox
|
||||
text: "Has keycard data"
|
||||
checked: startupStore.startupModuleInst.keycardData != Constants.predefinedKeycardData.wrongSeedPhrase
|
||||
}
|
||||
|
||||
|
||||
Label {
|
||||
text: "Current startup flow type"
|
||||
}
|
||||
ComboBox {
|
||||
id: flowTypeComboBox
|
||||
Layout.fillWidth: true
|
||||
model: Object.values(Constants.startupFlow).filter(flow => flow != "" && typeof flow === "string")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// category: Panels
|
|
@ -0,0 +1,413 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQml 2.15
|
||||
import QtQml.Models 2.15
|
||||
|
||||
import StatusQ.Core.Utils 0.1
|
||||
|
||||
import QtTest 1.15
|
||||
|
||||
import shared.panels 1.0
|
||||
import utils 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: 600
|
||||
height: 400
|
||||
|
||||
TestCase {
|
||||
name: "EnterSeedPhraseTest"
|
||||
when: windowShown
|
||||
|
||||
Component {
|
||||
id: enterSeedPhraseComponent
|
||||
EnterSeedPhrase {
|
||||
id: enterSeedPhrase
|
||||
anchors.fill: parent
|
||||
dictionary: ListModel {}
|
||||
|
||||
readonly property SignalSpy submitSpy: SignalSpy { target: enterSeedPhrase; signalName: "submitSeedPhrase" }
|
||||
readonly property SignalSpy seedPhraseUpdatedSpy: SignalSpy { target: enterSeedPhrase; signalName: "seedPhraseUpdated" }
|
||||
}
|
||||
}
|
||||
|
||||
property EnterSeedPhrase itemUnderTest: null
|
||||
|
||||
function generateDictionaryVariation(baseDictionary) {
|
||||
let dictionaryVariation = baseDictionary.map((word) => word + "a")
|
||||
dictionaryVariation = baseDictionary.map((word) => word + "b").concat(dictionaryVariation)
|
||||
dictionaryVariation = baseDictionary.map((word) => word + "c").concat(dictionaryVariation)
|
||||
dictionaryVariation = baseDictionary.map((word) => word + "d").concat(dictionaryVariation)
|
||||
dictionaryVariation.sort()
|
||||
return dictionaryVariation
|
||||
}
|
||||
|
||||
function init() {
|
||||
itemUnderTest = createTemporaryObject(enterSeedPhraseComponent, root)
|
||||
waitForItemPolished(itemUnderTest)
|
||||
waitForRendering(itemUnderTest)
|
||||
}
|
||||
|
||||
function test_componentCreation() {
|
||||
verify(itemUnderTest !== null, "Component creation failed")
|
||||
}
|
||||
|
||||
// Test seed phrase input by typing on the keyboard
|
||||
// The seed phrase is valid and the typing is done without any space between words
|
||||
// This is the most common way to input a seed phrase
|
||||
function test_seedPhraseKeyboardInput() {
|
||||
//generate a seed phrase
|
||||
const expectedSeedPhrase = ["abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abuse", "access", "accident"]
|
||||
expectedSeedPhrase.sort();
|
||||
|
||||
let isSeedPhraseValidCalled = false
|
||||
itemUnderTest.isSeedPhraseValid = (seedPhrase) => {
|
||||
verify(seedPhrase === expectedSeedPhrase.join(" "), "Seed phrase is not valid")
|
||||
isSeedPhraseValidCalled = true
|
||||
return true
|
||||
}
|
||||
|
||||
itemUnderTest.dictionary.append(expectedSeedPhrase.map((word) => ({seedWord: word})))
|
||||
|
||||
//Type the seed phrase. No space is needed between words
|
||||
const str = expectedSeedPhrase.join("")
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
keyPress(str.charAt(i))
|
||||
}
|
||||
|
||||
verify(isSeedPhraseValidCalled, "isSeedPhraseValid was not called")
|
||||
|
||||
keyClick(Qt.Key_Enter)
|
||||
verify(itemUnderTest.submitSpy.count === 1, "submitSeedPhrase signal was not emitted")
|
||||
// This signal is emitted multiple times due to the way the seed phrase is updated and validated
|
||||
// The minimum is the length if the seed phrase
|
||||
verify(itemUnderTest.seedPhraseUpdatedSpy.count >= expectedSeedPhrase.length, "seedPhraseUpdate signal was not emitted")
|
||||
}
|
||||
|
||||
// Test seed phrase input by typing on the keyboard
|
||||
// The seed phrase is valid and the typing is done with a space between words
|
||||
// The space between words is ignored and the seed should be valid
|
||||
function test_seedPhraseKeyboardInputWithExtraSpace() {
|
||||
//generate a seed phrase
|
||||
const expectedSeedPhrase = ["abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abuse", "access", "accident"]
|
||||
expectedSeedPhrase.sort();
|
||||
|
||||
let isSeedPhraseValidCalled = false
|
||||
itemUnderTest.isSeedPhraseValid = (seedPhrase) => {
|
||||
verify(seedPhrase === expectedSeedPhrase.join(" "), "Seed phrase is not valid")
|
||||
isSeedPhraseValidCalled = true
|
||||
return true
|
||||
}
|
||||
|
||||
itemUnderTest.dictionary.append(expectedSeedPhrase.map((word) => ({seedWord: word})))
|
||||
|
||||
//Type the seed phrase. A space is needed between words
|
||||
const str = expectedSeedPhrase.join(" ")
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
keyPress(str.charAt(i))
|
||||
}
|
||||
|
||||
verify(isSeedPhraseValidCalled, "isSeedPhraseValid was not called")
|
||||
keyClick(Qt.Key_Enter)
|
||||
verify(itemUnderTest.submitSpy.count === 1, "submitSeedPhrase signal was not emitted")
|
||||
// This signal is emitted multiple times due to the way the seed phrase is updated and validated
|
||||
// The minimum is the length if the seed phrase
|
||||
verify(itemUnderTest.seedPhraseUpdatedSpy.count >= expectedSeedPhrase.length, "seedPhraseUpdate signal was not emitted")
|
||||
}
|
||||
|
||||
// Test seed phrase input by pasting from clipboard
|
||||
// The seed phrase is valid and the clipboard seed is space separated
|
||||
function test_seedPhrasePaste() {
|
||||
//generate a seed phrase
|
||||
const expectedSeedPhrase = ["abandona", "abilityb", "ablec", "aboutd", "abovea", "absentb", "absorbc", "abstractd", "absurda", "abuseb", "accessc", "accidentd"]
|
||||
const baseDictionary = ["abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abuse", "access", "accident"]
|
||||
|
||||
expectedSeedPhrase.sort();
|
||||
|
||||
let isSeedPhraseValidCalled = false
|
||||
itemUnderTest.isSeedPhraseValid = (seedPhrase) => {
|
||||
verify(seedPhrase === expectedSeedPhrase.join(" "), "Seed phrase is not valid")
|
||||
isSeedPhraseValidCalled = true
|
||||
return true
|
||||
}
|
||||
|
||||
itemUnderTest.dictionary.append(expectedSeedPhrase.map((word) => ({seedWord: word})))
|
||||
|
||||
const clipboardHelper = createTemporaryQmlObject("import QtQuick 2.15; QtObject { property var getFromClipboard }", root)
|
||||
clipboardHelper.getFromClipboard = () => expectedSeedPhrase.join(" ")
|
||||
Utils.globalUtilsInst = clipboardHelper
|
||||
|
||||
// Trigger the paste action
|
||||
keyClick("v", Qt.ControlModifier)
|
||||
|
||||
verify(isSeedPhraseValidCalled, "isSeedPhraseValid was not called")
|
||||
|
||||
keyClick(Qt.Key_Enter)
|
||||
verify(itemUnderTest.submitSpy.count === 1, "submitSeedPhrase signal was not emitted")
|
||||
// This signal is emitted multiple times due to the way the seed phrase is updated and validated
|
||||
// The minimum is the length if the seed phrase
|
||||
verify(itemUnderTest.seedPhraseUpdatedSpy.count >= expectedSeedPhrase.length, "seedPhraseUpdate signal was not emitted")
|
||||
}
|
||||
|
||||
// Test the seed phrase by choosing from the suggestions
|
||||
// The seed phrase is valid and the user selects the words from the suggestions
|
||||
function test_seedPhraseChooseFromSuggestions() {
|
||||
//generate a seed phrase
|
||||
const expectedSeedPhrase = ["abandona", "abilityb", "ablec", "aboutd", "abovea", "absentb", "absorbc", "abstractd", "absurda", "abuseb", "accessc", "accidentd"]
|
||||
expectedSeedPhrase.sort()
|
||||
|
||||
const baseDictionary = ["abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abuse", "access", "accident"]
|
||||
let dictionaryVariation = generateDictionaryVariation(baseDictionary)
|
||||
|
||||
let isSeedPhraseValidCalled = false
|
||||
itemUnderTest.isSeedPhraseValid = (seedPhrase) => {
|
||||
verify(seedPhrase === expectedSeedPhrase.join(" "), "Seed phrase is not valid")
|
||||
isSeedPhraseValidCalled = true
|
||||
return true
|
||||
}
|
||||
|
||||
itemUnderTest.dictionary.append(dictionaryVariation.map((word) => ({seedWord: word})))
|
||||
|
||||
// Suggestions dialog is expected to receive key events when there's multiple suggestions
|
||||
let downKeyEvents = 0
|
||||
for (let i = 0; i < expectedSeedPhrase.length; i++) {
|
||||
keySequence(expectedSeedPhrase[i].substring(0, 4).split('').join(','))
|
||||
for (let j = 0; j < downKeyEvents; j++) {
|
||||
keyClick(Qt.Key_Down)
|
||||
}
|
||||
downKeyEvents = downKeyEvents === 3 ? 0 : downKeyEvents + 1
|
||||
keyClick(Qt.Key_Tab)
|
||||
}
|
||||
|
||||
verify(isSeedPhraseValidCalled, "isSeedPhraseValid was not called")
|
||||
|
||||
keyPress(Qt.Key_Enter)
|
||||
verify(itemUnderTest.submitSpy.count === 1, "submitSeedPhrase signal was not emitted")
|
||||
// This signal is emitted multiple times due to the way the seed phrase is updated and validated
|
||||
// The minimum is the length if the seed phrase
|
||||
verify(itemUnderTest.seedPhraseUpdatedSpy.count >= expectedSeedPhrase.length, "seedPhraseUpdate signal was not emitted")
|
||||
}
|
||||
|
||||
// Test seed phrase input by typing on the keyboard
|
||||
// The seed phrase is invalidated by the external isSeedPhraseValid
|
||||
function test_invalidatedSeedPhraseKeyboardInput() {
|
||||
//generate a seed phrase
|
||||
const expectedSeedPhrase = ["abandona", "abilityb", "ablec", "aboutd", "abovea", "absentb", "absorbc", "abstractd", "absurda", "abuseb", "accessc", "accidentd"]
|
||||
expectedSeedPhrase.sort()
|
||||
|
||||
const baseDictionary = ["abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abuse", "access", "accident"]
|
||||
let dictionaryVariation = generateDictionaryVariation(baseDictionary)
|
||||
|
||||
let isSeedPhraseValidCalled = false
|
||||
itemUnderTest.isSeedPhraseValid = (seedPhrase) => {
|
||||
verify(seedPhrase === expectedSeedPhrase.join(" "), "Seed phrase is not valid")
|
||||
isSeedPhraseValidCalled = true
|
||||
return false
|
||||
}
|
||||
|
||||
itemUnderTest.dictionary.append(dictionaryVariation.map((word) => ({seedWord: word})))
|
||||
|
||||
//Type the seed phrase
|
||||
const str = expectedSeedPhrase.join("")
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
keyPress(str.charAt(i))
|
||||
}
|
||||
|
||||
verify(isSeedPhraseValidCalled, "isSeedPhraseValid was not called")
|
||||
|
||||
keyClick(Qt.Key_Enter)
|
||||
verify(itemUnderTest.submitSpy.count === 0, "submitSeedPhrase signal was emitted")
|
||||
verify(itemUnderTest.seedPhraseUpdatedSpy.count >= expectedSeedPhrase.length, "seedPhraseUpdate signal was not emitted")
|
||||
}
|
||||
|
||||
// Test seed phrase input by pasting from clipboard
|
||||
// The seed phrase is invalidated by the external isSeedPhraseValid
|
||||
function test_invalidatedSeedPhrasePaste() {
|
||||
//generate a seed phrase
|
||||
const expectedSeedPhrase = ["abandona", "abilityb", "ablec", "aboutd", "abovea", "absentb", "absorbc", "abstractd", "absurda", "abuseb", "accessc", "accidentd"]
|
||||
expectedSeedPhrase.sort()
|
||||
|
||||
const baseDictionary = ["abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abuse", "access", "accident"]
|
||||
let dictionaryVariation = generateDictionaryVariation(baseDictionary)
|
||||
|
||||
let isSeedPhraseValidCalled = false
|
||||
itemUnderTest.isSeedPhraseValid = (seedPhrase) => {
|
||||
verify(seedPhrase === expectedSeedPhrase.join(" "), "Seed phrase is not valid")
|
||||
isSeedPhraseValidCalled = true
|
||||
return false
|
||||
}
|
||||
|
||||
itemUnderTest.dictionary.append(dictionaryVariation.map((word) => ({seedWord: word})))
|
||||
|
||||
const clipboardHelper = createTemporaryQmlObject("import QtQuick 2.15; QtObject { property var getFromClipboard }", root)
|
||||
clipboardHelper.getFromClipboard = () => expectedSeedPhrase.join(" ")
|
||||
Utils.globalUtilsInst = clipboardHelper
|
||||
|
||||
// Trigger the paste action
|
||||
keyClick("v", Qt.ControlModifier)
|
||||
|
||||
verify(isSeedPhraseValidCalled, "isSeedPhraseValid was not called")
|
||||
|
||||
keyClick(Qt.Key_Enter)
|
||||
verify(itemUnderTest.submitSpy.count === 0, "submitSeedPhrase signal was emitted")
|
||||
verify(itemUnderTest.seedPhraseUpdatedSpy.count >= expectedSeedPhrase.length, "seedPhraseUpdate signal was not emitted")
|
||||
}
|
||||
|
||||
// Test seed phrase input by typing on the keyboard
|
||||
// The seed phrase is invalid due to the length
|
||||
function test_invalidLengthSeedPhrase() {
|
||||
const expectedSeedPhrase = ["abandona", "abilityb", "ablec"]
|
||||
expectedSeedPhrase.sort()
|
||||
|
||||
const baseDictionary = ["abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abuse", "access", "accident"]
|
||||
let dictionaryVariation = generateDictionaryVariation(baseDictionary)
|
||||
|
||||
let isSeedPhraseValidCalled = false
|
||||
itemUnderTest.isSeedPhraseValid = (seedPhrase) => {
|
||||
verify(seedPhrase === expectedSeedPhrase.join(" "), "Seed phrase is not valid")
|
||||
isSeedPhraseValidCalled = true
|
||||
return true
|
||||
}
|
||||
|
||||
itemUnderTest.dictionary.append(dictionaryVariation.map((word) => ({seedWord: word})))
|
||||
|
||||
//Type the seed phrase
|
||||
const str = expectedSeedPhrase.join("")
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
keyPress(str.charAt(i))
|
||||
}
|
||||
|
||||
keyClick(Qt.Key_Enter)
|
||||
verify(!isSeedPhraseValidCalled, "isSeedPhraseValid was called when it should not have been")
|
||||
verify(itemUnderTest.submitSpy.count === 0, "submitSeedPhrase signal was emitted")
|
||||
verify(itemUnderTest.seedPhraseUpdatedSpy.count >= expectedSeedPhrase.length, "seedPhraseUpdate signal was not emitted")
|
||||
}
|
||||
|
||||
// Test seed phrase input by typing on the keyboard
|
||||
// The seed phrase is invalid due to the dictionary word
|
||||
function test_invalidDictionarySeedPhrase() {
|
||||
const expectedSeedPhrase = ["abandonna", "abilityb", "ablec", "aboutd", "abovea", "absentb", "absorbc", "abstractd", "absurda", "abuseb", "accessc", "accidentd"]
|
||||
// ^^ invalid word
|
||||
expectedSeedPhrase.sort()
|
||||
|
||||
const baseDictionary = ["abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abuse", "access", "accident"]
|
||||
let dictionaryVariation = generateDictionaryVariation(baseDictionary)
|
||||
|
||||
let isSeedPhraseValidCalled = false
|
||||
itemUnderTest.isSeedPhraseValid = (seedPhrase) => {
|
||||
verify(seedPhrase === expectedSeedPhrase.join(" "), "Seed phrase is not valid")
|
||||
isSeedPhraseValidCalled = true
|
||||
return true
|
||||
}
|
||||
|
||||
itemUnderTest.dictionary.append(dictionaryVariation.map((word) => ({seedWord: word})))
|
||||
|
||||
//Type the seed phrase
|
||||
const str = expectedSeedPhrase.join("")
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
keyPress(str.charAt(i))
|
||||
if (i === 8) {
|
||||
// The first word is invalid. Move on to the next word
|
||||
keyPress(Qt.Key_Tab)
|
||||
}
|
||||
}
|
||||
|
||||
keyClick(Qt.Key_Enter)
|
||||
print (itemUnderTest.seedPhraseUpdatedSpy.count)
|
||||
verify(itemUnderTest.submitSpy.count === 0, "submitSeedPhrase signal was emitted")
|
||||
verify(itemUnderTest.seedPhraseUpdatedSpy.count >= expectedSeedPhrase.length, "seedPhraseUpdate signal was not emitted")
|
||||
}
|
||||
|
||||
// Test suggestions are active after the seed phrase word is updated
|
||||
function test_suggestionsActiveAfterUpdatingWord() {
|
||||
const expectedSeedPhrase = ["abandona", "abilityb", "ablec", "aboutd", "abovea", "absentb", "absorbc", "abstractd", "absurda", "abuseb", "accessc", "accidentd"]
|
||||
expectedSeedPhrase.sort()
|
||||
|
||||
const baseDictionary = ["abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abuse", "access", "accident"]
|
||||
let dictionaryVariation = generateDictionaryVariation(baseDictionary)
|
||||
|
||||
let isSeedPhraseValidCalled = false
|
||||
let lastVerifiedSeedPhrase = ""
|
||||
itemUnderTest.isSeedPhraseValid = (seedPhrase) => {
|
||||
lastVerifiedSeedPhrase = seedPhrase
|
||||
isSeedPhraseValidCalled = true
|
||||
return true
|
||||
}
|
||||
|
||||
itemUnderTest.dictionary.append(dictionaryVariation.map((word) => ({seedWord: word})))
|
||||
|
||||
// Suggestions dialog is expected to receive key events when there's multiple suggestions
|
||||
let downKeyEvents = 0
|
||||
for (let i = 0; i < expectedSeedPhrase.length; i++) {
|
||||
keySequence(expectedSeedPhrase[i].substring(0, 4).split('').join(','))
|
||||
for (let j = 0; j < downKeyEvents; j++) {
|
||||
keyClick(Qt.Key_Down)
|
||||
}
|
||||
downKeyEvents = downKeyEvents === 3 ? 0 : downKeyEvents + 1
|
||||
keyClick(Qt.Key_Tab)
|
||||
}
|
||||
|
||||
verify(isSeedPhraseValidCalled, "isSeedPhraseValid was not called")
|
||||
|
||||
isSeedPhraseValidCalled = false
|
||||
|
||||
keyPress(Qt.Key_Backspace)
|
||||
wait (500) // Wait for the suggestions to appear
|
||||
keyClick(Qt.Key_Tab)
|
||||
keyClick(Qt.Key_Enter)
|
||||
|
||||
verify(isSeedPhraseValidCalled, "isSeedPhraseValid was not called")
|
||||
print (lastVerifiedSeedPhrase)
|
||||
verify(lastVerifiedSeedPhrase === expectedSeedPhrase.join(" ").slice(0, -1) + "a", "Seed phrase is not updated")
|
||||
verify(itemUnderTest.seedPhraseUpdatedSpy.count >= expectedSeedPhrase.length, "seedPhraseUpdate signal was not emitted")
|
||||
|
||||
}
|
||||
|
||||
// Test suggestions are active after the seed phrase word is fixed
|
||||
function test_suggestionsActiveAfterFixingWord() {
|
||||
const expectedSeedPhrase = ["abandona", "abilityb", "ablec", "aboutd", "abovea", "absentb", "absorbc", "abstractd", "absurda", "abuseb", "accessc", "accidenntd"]
|
||||
expectedSeedPhrase.sort()
|
||||
|
||||
const baseDictionary = ["abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abuse", "access", "accident"]
|
||||
let dictionaryVariation = generateDictionaryVariation(baseDictionary)
|
||||
|
||||
let isSeedPhraseValidCalled = false
|
||||
let lastVerifiedSeedPhrase = ""
|
||||
itemUnderTest.isSeedPhraseValid = (seedPhrase) => {
|
||||
lastVerifiedSeedPhrase = seedPhrase
|
||||
isSeedPhraseValidCalled = true
|
||||
return true
|
||||
}
|
||||
|
||||
itemUnderTest.dictionary.append(dictionaryVariation.map((word) => ({seedWord: word})))
|
||||
|
||||
// Suggestions dialog is expected to receive key events when there's multiple suggestions
|
||||
let downKeyEvents = 0
|
||||
for (let i = 0; i < expectedSeedPhrase.length; i++) {
|
||||
keySequence(expectedSeedPhrase[i].substring(0, 4).split('').join(','))
|
||||
for (let j = 0; j < downKeyEvents; j++) {
|
||||
keyClick(Qt.Key_Down)
|
||||
}
|
||||
downKeyEvents = downKeyEvents === 3 ? 0 : downKeyEvents + 1
|
||||
keyClick(Qt.Key_Tab)
|
||||
}
|
||||
|
||||
verify(isSeedPhraseValidCalled, "isSeedPhraseValid is not called")
|
||||
|
||||
isSeedPhraseValidCalled = false
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
keyPress(Qt.Key_Backspace)
|
||||
}
|
||||
|
||||
wait (500) // Wait for the suggestions to appear
|
||||
keyClick(Qt.Key_Tab)
|
||||
keyClick(Qt.Key_Enter)
|
||||
|
||||
verify(isSeedPhraseValidCalled, "isSeedPhraseValid was not called")
|
||||
verify(lastVerifiedSeedPhrase === expectedSeedPhrase.join(" ").slice(0, -3) + "ta", "Seed phrase is not updated")
|
||||
verify(itemUnderTest.seedPhraseUpdatedSpy.count >= expectedSeedPhrase.length, "seedPhraseUpdate signal was not emitted")
|
||||
verify(itemUnderTest.submitSpy.count === 1, "submitSeedPhrase signal was emitted")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import QtQuick 2.13
|
||||
|
||||
ListModel {
|
||||
id: root
|
||||
|
||||
Component.onCompleted: {
|
||||
var englishWords = [
|
||||
"apple", "banana", "cat", "dog", "elephant", "fish", "grape", "horse", "ice cream", "jellyfish",
|
||||
"kiwi", "lemon", "mango", "nut", "orange", "pear", "quail", "rabbit", "strawberry", "turtle",
|
||||
"umbrella", "violet", "watermelon", "xylophone", "yogurt", "zebra"
|
||||
// Add more English words here...
|
||||
];
|
||||
|
||||
for (var i = 0; i < englishWords.length; i++) {
|
||||
root.append({ seedWord: englishWords[i] });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,3 +3,4 @@ CurrenciesStore 1.0 CurrenciesStore.qml
|
|||
NetworkConnectionStore 1.0 NetworkConnectionStore.qml
|
||||
singleton RootStore 1.0 RootStore.qml
|
||||
TokenBalanceHistoryStore 1.0 TokenBalanceHistoryStore.qml
|
||||
BIP39_en 1.0 BIP39_en.qml
|
|
@ -83,7 +83,7 @@ class AddNewAccountPopup(BasePopup):
|
|||
else:
|
||||
raise RuntimeError("Wrong amount of seed words", len(seed_phrase_words))
|
||||
for count, word in enumerate(seed_phrase_words, start=1):
|
||||
self._seed_phrase_word_text_edit.object_name['objectName'] = f'statusSeedPhraseInputField{count}'
|
||||
self._seed_phrase_word_text_edit.object_name['objectName'] = f'enterSeedPhraseInputField{count}'
|
||||
self._seed_phrase_word_text_edit.text = word
|
||||
seed_phrase_name = ''.join([word[0] for word in seed_phrase_words[:10]])
|
||||
self._seed_phrase_phrase_key_name_text_edit.text = seed_phrase_name
|
||||
|
|
|
@ -30,34 +30,34 @@ onboarding_back_button = {"container": statusDesktop_mainWindow, "objectName": "
|
|||
|
||||
# Seed phrase form:
|
||||
import_a_seed_phrase_StatusBaseText = {"container": statusDesktop_mainWindow, "text": "Import a seed phrase", "type": "StatusBaseText", "unnamed": 1, "visible": True}
|
||||
mainWindow_switchTabBar_StatusSwitchTabBar = {"container": statusDesktop_mainWindow, "objectName": "onboardingSeedPhraseSwitchBar", "type": "StatusSwitchTabBar"}
|
||||
mainWindow_switchTabBar_StatusSwitchTabBar = {"container": statusDesktop_mainWindow, "objectName": "enterSeedPhraseSwitchBar", "type": "StatusSwitchTabBar"}
|
||||
switchTabBar_12_words_Button = {"container": mainWindow_switchTabBar_StatusSwitchTabBar, "objectName": "12SeedButton", "type": "StatusSwitchTabButton"}
|
||||
switchTabBar_18_words_Button = {"container": mainWindow_switchTabBar_StatusSwitchTabBar, "objectName": "18SeedButton", "type": "StatusSwitchTabButton"}
|
||||
switchTabBar_24_words_Button = {"container": mainWindow_switchTabBar_StatusSwitchTabBar, "objectName": "24SeedButton", "type": "StatusSwitchTabButton"}
|
||||
seedPhraseView_Submit_Button = {"container": statusDesktop_mainWindow, "objectName": "seedPhraseViewSubmitButton", "type": "StatusButton"}
|
||||
onboarding_InvalidSeed_Text = {"container": statusDesktop_mainWindow, "objectName": "onboardingInvalidSeedText", "type": "StatusBaseText"}
|
||||
|
||||
onboarding_SeedPhrase_Input_TextField_1 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField1"}
|
||||
onboarding_SeedPhrase_Input_TextField_2 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField2"}
|
||||
onboarding_SeedPhrase_Input_TextField_3 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField3"}
|
||||
onboarding_SeedPhrase_Input_TextField_4 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField4"}
|
||||
onboarding_SeedPhrase_Input_TextField_5 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField5"}
|
||||
onboarding_SeedPhrase_Input_TextField_6 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField6"}
|
||||
onboarding_SeedPhrase_Input_TextField_7 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField7"}
|
||||
onboarding_SeedPhrase_Input_TextField_8 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField8"}
|
||||
onboarding_SeedPhrase_Input_TextField_9 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField9"}
|
||||
onboarding_SeedPhrase_Input_TextField_10 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField10"}
|
||||
onboarding_SeedPhrase_Input_TextField_11 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField11"}
|
||||
onboarding_SeedPhrase_Input_TextField_12 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField12"}
|
||||
onboarding_SeedPhrase_Input_TextField_13 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField13"}
|
||||
onboarding_SeedPhrase_Input_TextField_14 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField14"}
|
||||
onboarding_SeedPhrase_Input_TextField_15 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField15"}
|
||||
onboarding_SeedPhrase_Input_TextField_16 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField16"}
|
||||
onboarding_SeedPhrase_Input_TextField_17 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField17"}
|
||||
onboarding_SeedPhrase_Input_TextField_18 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField18"}
|
||||
onboarding_SeedPhrase_Input_TextField_19 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField19"}
|
||||
onboarding_SeedPhrase_Input_TextField_20 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField20"}
|
||||
onboarding_SeedPhrase_Input_TextField_21 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField21"}
|
||||
onboarding_SeedPhrase_Input_TextField_22 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField22"}
|
||||
onboarding_SeedPhrase_Input_TextField_23 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField23"}
|
||||
onboarding_SeedPhrase_Input_TextField_24 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "statusSeedPhraseInputField24"}
|
||||
onboarding_SeedPhrase_Input_TextField_1 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField1"}
|
||||
onboarding_SeedPhrase_Input_TextField_2 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField2"}
|
||||
onboarding_SeedPhrase_Input_TextField_3 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField3"}
|
||||
onboarding_SeedPhrase_Input_TextField_4 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField4"}
|
||||
onboarding_SeedPhrase_Input_TextField_5 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField5"}
|
||||
onboarding_SeedPhrase_Input_TextField_6 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField6"}
|
||||
onboarding_SeedPhrase_Input_TextField_7 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField7"}
|
||||
onboarding_SeedPhrase_Input_TextField_8 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField8"}
|
||||
onboarding_SeedPhrase_Input_TextField_9 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField9"}
|
||||
onboarding_SeedPhrase_Input_TextField_10 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField10"}
|
||||
onboarding_SeedPhrase_Input_TextField_11 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField11"}
|
||||
onboarding_SeedPhrase_Input_TextField_12 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField12"}
|
||||
onboarding_SeedPhrase_Input_TextField_13 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField13"}
|
||||
onboarding_SeedPhrase_Input_TextField_14 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField14"}
|
||||
onboarding_SeedPhrase_Input_TextField_15 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField15"}
|
||||
onboarding_SeedPhrase_Input_TextField_16 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField16"}
|
||||
onboarding_SeedPhrase_Input_TextField_17 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField17"}
|
||||
onboarding_SeedPhrase_Input_TextField_18 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField18"}
|
||||
onboarding_SeedPhrase_Input_TextField_19 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField19"}
|
||||
onboarding_SeedPhrase_Input_TextField_20 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField20"}
|
||||
onboarding_SeedPhrase_Input_TextField_21 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField21"}
|
||||
onboarding_SeedPhrase_Input_TextField_22 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField22"}
|
||||
onboarding_SeedPhrase_Input_TextField_23 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField23"}
|
||||
onboarding_SeedPhrase_Input_TextField_24 = {"container": statusDesktop_mainWindow, "type": "TextEdit", "objectName": "enterSeedPhraseInputField24"}
|
|
@ -126,34 +126,34 @@ mainWallet_AddEditAccountPopup_SeedPhraseWordAtIndex_Placeholder = {"container":
|
|||
mainWallet_AddEditAccountPopup_EnterSeedPhraseWordComponent = {"container": mainWallet_AddEditAccountPopup_Content, "objectName": "AddAccountPopup-EnterSeedPhraseWord", "type": "StatusInput", "visible": True}
|
||||
mainWallet_AddEditAccountPopup_EnterSeedPhraseWord = {"container": mainWallet_AddEditAccountPopup_EnterSeedPhraseWordComponent, "type": "TextEdit", "unnamed": 1, "visible": True}
|
||||
confirmSeedPhrasePanel_StatusSeedPhraseInput = {"container": statusDesktop_mainWindow, "type": "StatusSeedPhraseInput", "visible": True}
|
||||
mainWallet_AddEditAccountPopup_SPWord = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": RegularExpression("statusSeedPhraseInputField*")}
|
||||
mainWallet_AddEditAccountPopup_SPWord = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": RegularExpression("enterSeedPhraseInputField*")}
|
||||
mainWallet_AddEditAccountPopup_12WordsButton = {"container": mainWallet_AddEditAccountPopup_Content, "objectName": "12SeedButton", "type": "StatusSwitchTabButton"}
|
||||
mainWallet_AddEditAccountPopup_18WordsButton = {"container": mainWallet_AddEditAccountPopup_Content, "objectName": "18SeedButton", "type": "StatusSwitchTabButton"}
|
||||
mainWallet_AddEditAccountPopup_24WordsButton = {"container": mainWallet_AddEditAccountPopup_Content, "objectName": "24SeedButton", "type": "StatusSwitchTabButton"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_1 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField1"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_2 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField2"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_3 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField3"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_4 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField4"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_5 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField5"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_6 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField6"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_7 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField7"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_8 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField8"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_9 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField9"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_10 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField10"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_11 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField11"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_12 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField12"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_13 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField13"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_14 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField14"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_15 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField15"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_16 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField16"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_17 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField17"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_18 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField18"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_19 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField19"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_20 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField20"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_21 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField21"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_22 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField22"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_23 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField23"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_24 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "statusSeedPhraseInputField24"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_1 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField1"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_2 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField2"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_3 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField3"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_4 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField4"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_5 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField5"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_6 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField6"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_7 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField7"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_8 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField8"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_9 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField9"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_10 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField10"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_11 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField11"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_12 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField12"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_13 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField13"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_14 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField14"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_15 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField15"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_16 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField16"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_17 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField17"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_18 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField18"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_19 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField19"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_20 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField20"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_21 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField21"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_22 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField22"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_23 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField23"}
|
||||
mainWallet_AddEditAccountPopup_SPWord_24 = {"container": mainWallet_AddEditAccountPopup_Content, "type": "TextEdit", "objectName": "enterSeedPhraseInputField24"}
|
||||
|
||||
# Remove account popup:
|
||||
mainWallet_Remove_Account_Popup_Account_Notification = {"container": statusDesktop_mainWindow, "objectName": "RemoveAccountPopup-Notification", "type": "StatusBaseText", "visible": True}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
class QAbstractItemModel;
|
||||
class QJSEngine;
|
||||
class QQmlEngine;
|
||||
|
||||
|
|
|
@ -267,6 +267,11 @@ Item {
|
|||
This signal is emitted when the text edit is clicked.
|
||||
*/
|
||||
signal editClicked()
|
||||
/*!
|
||||
\qmlsignal editingFinished
|
||||
This signal is emitted when the text edit loses focus.
|
||||
*/
|
||||
signal editingFinished()
|
||||
|
||||
onFocusChanged: {
|
||||
if(focus) edit.forceActiveFocus()
|
||||
|
@ -368,12 +373,20 @@ Item {
|
|||
wrapMode: root.multiline ? Text.WrapAtWordBoundaryOrAnywhere : TextEdit.NoWrap
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
root.keyPressed(event)
|
||||
event.accepted = !multiline && !acceptReturn
|
||||
// Special case for single line inputs, where we want to accept the return key, but notify the parend
|
||||
// Enter and return can be used to accept the input
|
||||
if (event.accepted) {
|
||||
root.keyPressed(event)
|
||||
}
|
||||
}
|
||||
Keys.onEnterPressed: {
|
||||
root.keyPressed(event)
|
||||
event.accepted = !multiline && !acceptReturn
|
||||
// Special case for single line inputs, where we want to accept the return key, but notify the parend
|
||||
// Enter and return can be used to accept the input
|
||||
if (event.accepted) {
|
||||
root.keyPressed(event)
|
||||
}
|
||||
}
|
||||
Keys.forwardTo: [root]
|
||||
KeyNavigation.priority: !!root.tabNavItem ? KeyNavigation.BeforeItem : KeyNavigation.AfterItem
|
||||
|
@ -409,6 +422,9 @@ Item {
|
|||
previousText = text
|
||||
}
|
||||
}
|
||||
onEditingFinished: {
|
||||
root.editingFinished()
|
||||
}
|
||||
|
||||
cursorDelegate: StatusCursorDelegate {
|
||||
cursorVisible: edit.cursorVisible
|
||||
|
|
|
@ -215,6 +215,11 @@ Item {
|
|||
This signal is emitted when the text edit is clicked.
|
||||
*/
|
||||
signal editClicked()
|
||||
/*!
|
||||
\qmlsignal editingFinished
|
||||
This signal is emitted when the text edit loses focus.
|
||||
*/
|
||||
signal editingFinished()
|
||||
|
||||
/*!
|
||||
\qmltype ValidationMode
|
||||
|
@ -453,6 +458,9 @@ Item {
|
|||
onEditChanged: {
|
||||
root.editClicked();
|
||||
}
|
||||
onEditingFinished: {
|
||||
root.editingFinished()
|
||||
}
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
|
|
|
@ -97,12 +97,16 @@ Item {
|
|||
The corresponding handler is \c onEditClicked
|
||||
*/
|
||||
signal editClicked()
|
||||
/*!
|
||||
\qmlsignal editingFinished
|
||||
This signal is emitted when the text edit loses focus.
|
||||
*/
|
||||
signal editingFinished()
|
||||
|
||||
function setWord(seedWord) {
|
||||
let seedWordTrimmed = seedWord.trim()
|
||||
seedWordInput.input.edit.text = seedWordTrimmed
|
||||
seedWordInput.input.edit.cursorPosition = seedWordInput.text.length
|
||||
seedSuggestionsList.model = 0
|
||||
root.doneInsertingWord(seedWordTrimmed)
|
||||
}
|
||||
|
||||
|
@ -134,72 +138,77 @@ Item {
|
|||
d.isInputValidWord = false
|
||||
filteredList.clear();
|
||||
let textToCheck = text.trim().toLowerCase()
|
||||
if (textToCheck !== "") {
|
||||
for (var i = 0; i < inputList.count; i++) {
|
||||
if (inputList.get(i).seedWord.startsWith(textToCheck)) {
|
||||
filteredList.insert(filteredList.count, {"seedWord": inputList.get(i).seedWord});
|
||||
if(inputList.get(i).seedWord === textToCheck)
|
||||
d.isInputValidWord = true
|
||||
}
|
||||
|
||||
if (textToCheck === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < inputList.count; i++) {
|
||||
if (inputList.get(i).seedWord.startsWith(textToCheck)) {
|
||||
filteredList.insert(filteredList.count, {"seedWord": inputList.get(i).seedWord});
|
||||
if(inputList.get(i).seedWord === textToCheck)
|
||||
d.isInputValidWord = true
|
||||
}
|
||||
seedSuggestionsList.model = filteredList;
|
||||
if (filteredList.count === 1 && input.edit.keyEvent !== Qt.Key_Backspace
|
||||
&& input.edit.keyEvent !== Qt.Key_Delete
|
||||
&& filteredList.get(0).seedWord.trim() === textToCheck) {
|
||||
seedWordInput.input.edit.cursorPosition = textToCheck.length;
|
||||
seedSuggestionsList.model = 0;
|
||||
root.doneInsertingWord(textToCheck);
|
||||
}
|
||||
} else {
|
||||
seedSuggestionsList.model = 0;
|
||||
}
|
||||
|
||||
if (filteredList.count === 1 && input.edit.keyEvent !== Qt.Key_Backspace
|
||||
&& input.edit.keyEvent !== Qt.Key_Delete
|
||||
&& filteredList.get(0).seedWord.trim() === textToCheck) {
|
||||
seedWordInput.input.edit.cursorPosition = textToCheck.length;
|
||||
filteredList.clear();
|
||||
root.doneInsertingWord(textToCheck);
|
||||
}
|
||||
}
|
||||
onKeyPressed: {
|
||||
if (input.edit.keyEvent === Qt.Key_Tab || input.edit.keyEvent === Qt.Key_Return || input.edit.keyEvent === Qt.Key_Enter) {
|
||||
if (!!text && seedSuggestionsList.count > 0) {
|
||||
root.setWord(filteredList.get(seedSuggestionsList.currentIndex).seedWord)
|
||||
event.accepted = true
|
||||
return
|
||||
switch (input.edit.keyEvent) {
|
||||
case Qt.Key_Tab:
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter: {
|
||||
if (!!text && filteredList.count > 0) {
|
||||
root.setWord(filteredList.get(seedSuggestionsList.currentIndex).seedWord)
|
||||
event.accepted = true
|
||||
return
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Qt.Key_Down: {
|
||||
seedSuggestionsList.incrementCurrentIndex()
|
||||
input.edit.keyEvent = null
|
||||
break;
|
||||
}
|
||||
case Qt.Key_Up: {
|
||||
seedSuggestionsList.decrementCurrentIndex()
|
||||
input.edit.keyEvent = null
|
||||
break;
|
||||
}
|
||||
case Qt.Key_Space: {
|
||||
event.accepted = !event.text.match(/^[a-zA-Z]$/)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (input.edit.keyEvent === Qt.Key_Down) {
|
||||
seedSuggestionsList.incrementCurrentIndex()
|
||||
input.edit.keyEvent = null
|
||||
}
|
||||
if (input.edit.keyEvent === Qt.Key_Up) {
|
||||
seedSuggestionsList.decrementCurrentIndex()
|
||||
input.edit.keyEvent = null
|
||||
}
|
||||
|
||||
root.keyPressed(event);
|
||||
}
|
||||
onEditClicked: {
|
||||
root.editClicked();
|
||||
}
|
||||
// Consider word inserted if input looses focus while a valid word is present ("user" clicks outside)
|
||||
Connections {
|
||||
target: seedWordInput.input.edit
|
||||
function onActiveFocusChanged() {
|
||||
if (!seedWordInput.input.edit.activeFocus && d.isInputValidWord) {
|
||||
// There are so many side effects regarding focus and doneInsertingWord that we need to reset this flag not to be processed again.
|
||||
d.isInputValidWord = false
|
||||
root.doneInsertingWord(root.text.trim())
|
||||
}
|
||||
}
|
||||
|
||||
onEditingFinished: {
|
||||
root.editingFinished();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Popup {
|
||||
id: suggListContainer
|
||||
width: seedSuggestionsList.width
|
||||
height: (((seedSuggestionsList.count <= 5) ? seedSuggestionsList.count : 5) *34) + 16
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 16
|
||||
anchors.top: seedWordInput.bottom
|
||||
anchors.topMargin: 4
|
||||
visible: ((seedSuggestionsList.count > 0) && seedWordInput.input.edit.activeFocus)
|
||||
Rectangle {
|
||||
contentWidth: seedSuggestionsList.width
|
||||
contentHeight: ((seedSuggestionsList.count <= 5) ? seedSuggestionsList.count : 5) *34 + 16
|
||||
x: 16
|
||||
y: seedWordInput.height + 4
|
||||
topPadding: 8
|
||||
bottomPadding: 8
|
||||
visible: ((filteredList.count > 0) && seedWordInput.input.edit.activeFocus)
|
||||
background: Rectangle {
|
||||
id: statusMenuBackgroundContent
|
||||
anchors.fill: parent
|
||||
color: Theme.palette.statusMenu.backgroundColor
|
||||
radius: 8
|
||||
layer.enabled: true
|
||||
|
@ -219,9 +228,7 @@ Item {
|
|||
width: ((seedSuggestionsList.contentItem.childrenRect.width + 24) > root.width) ? root.width
|
||||
: (seedSuggestionsList.contentItem.childrenRect.width + 24)
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 8
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 8
|
||||
|
||||
onCountChanged: {
|
||||
seedSuggestionsList.currentIndex = 0
|
||||
|
@ -229,6 +236,7 @@ Item {
|
|||
|
||||
clip: true
|
||||
ScrollBar.vertical: ScrollBar { }
|
||||
model: root.filteredList
|
||||
delegate: Item {
|
||||
id: txtDelegate
|
||||
width: suggWord.contentWidth
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
AccountViewDelegate 1.0 AccountViewDelegate.qml
|
||||
OnboardingBasePage 1.0 OnboardingBasePage.qml
|
|
@ -1,35 +1,26 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.14
|
||||
import QtGraphicalEffects 1.13
|
||||
import QtQuick.Dialogs 1.3
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtGraphicalEffects 1.15
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import AppLayouts.Onboarding.controls 1.0
|
||||
import AppLayouts.Onboarding.stores 1.0
|
||||
|
||||
import utils 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.stores 1.0
|
||||
import shared.controls 1.0
|
||||
|
||||
import "../controls"
|
||||
import "../stores"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property StartupStore startupStore
|
||||
|
||||
property var mnemonicInput: []
|
||||
|
||||
signal seedValidated()
|
||||
|
||||
readonly property var tabs: [12, 18, 24]
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
|
@ -38,272 +29,43 @@ Item {
|
|||
onWrongSeedPhraseChanged: {
|
||||
if (wrongSeedPhrase) {
|
||||
if (root.startupStore.startupModuleInst.flowType === Constants.startupFlow.firstRunOldUserImportSeedPhrase) {
|
||||
invalidSeedTxt.text = qsTr("Profile keypair for the inserted seed phrase is already set up")
|
||||
seedPhraseView.setWrongSeedPhraseMessage(qsTr("Profile keypair for the inserted seed phrase is already set up"))
|
||||
return
|
||||
}
|
||||
invalidSeedTxt.text = qsTr("Seed phrase doesn’t match the profile of an existing Keycard user on this device")
|
||||
seedPhraseView.setWrongSeedPhraseMessage(qsTr("Seed phrase doesn’t match the profile of an existing Keycard user on this device"))
|
||||
}
|
||||
else {
|
||||
invalidSeedTxt.text = ""
|
||||
seedPhraseView.setWrongSeedPhraseMessage("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pasteWords () {
|
||||
const clipboardText = globalUtils.getFromClipboard()
|
||||
// Split words separated by commas and or blank spaces (spaces, enters, tabs)
|
||||
const words = clipboardText.split(/[, \s]+/)
|
||||
|
||||
let index = root.tabs.indexOf(words.length)
|
||||
if (index === -1) {
|
||||
return false
|
||||
}
|
||||
|
||||
let timeout = 0
|
||||
if (switchTabBar.currentIndex !== index) {
|
||||
switchTabBar.currentIndex = index
|
||||
// Set the teimeout to 100 so the grid has time to generate the new items
|
||||
timeout = 100
|
||||
}
|
||||
|
||||
root.mnemonicInput = []
|
||||
timer.setTimeout(() => {
|
||||
// Populate mnemonicInput
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
grid.addWord(i + 1, words[i], true)
|
||||
}
|
||||
// Populate grid
|
||||
for (let j = 0; j < grid.count; j++) {
|
||||
const item = grid.itemAtIndex(j)
|
||||
if (!item || !item.leftComponentText) {
|
||||
// The grid has gaps in it and also sometimes doesn't return the item correctly when offscreen
|
||||
// in those cases, we just add the word in the array but not in the grid.
|
||||
// The button will still work and import correctly. The Grid itself will be partly empty, but offscreen
|
||||
// With the re-design of the grid, this should be fixed
|
||||
continue
|
||||
}
|
||||
const pos = item.mnemonicIndex
|
||||
item.setWord(words[pos - 1])
|
||||
}
|
||||
submitButton.checkMnemonicLength()
|
||||
}, timeout)
|
||||
return true
|
||||
}
|
||||
|
||||
Item {
|
||||
implicitWidth: 565
|
||||
implicitHeight: parent.height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
ColumnLayout {
|
||||
width: 565
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
anchors.centerIn: parent
|
||||
spacing: 24
|
||||
|
||||
StatusBaseText {
|
||||
id: headlineText
|
||||
font.pixelSize: 22
|
||||
font.weight: Font.Bold
|
||||
color: Theme.palette.directColor1
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: (root.height - parent.childrenRect.height)/2
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Enter seed phrase")
|
||||
}
|
||||
|
||||
StatusSwitchTabBar {
|
||||
id: switchTabBar
|
||||
objectName: "onboardingSeedPhraseSwitchBar"
|
||||
anchors.top: headlineText.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 24
|
||||
Repeater {
|
||||
model: root.tabs
|
||||
StatusSwitchTabButton {
|
||||
text: qsTr("%n word(s)", "", modelData)
|
||||
id: seedPhraseWords
|
||||
objectName: `${modelData}SeedButton`
|
||||
}
|
||||
}
|
||||
onCurrentIndexChanged: {
|
||||
root.mnemonicInput = root.mnemonicInput.filter(function(value) {
|
||||
return value.pos <= root.tabs[switchTabBar.currentIndex]
|
||||
})
|
||||
submitButton.checkMnemonicLength()
|
||||
}
|
||||
}
|
||||
clip: true
|
||||
|
||||
StatusGridView {
|
||||
id: grid
|
||||
objectName: "seedPhraseGridView"
|
||||
width: parent.width
|
||||
readonly property var wordIndex: [
|
||||
["1", "3", "5", "7", "9", "11", "2", "4", "6", "8", "10", "12"]
|
||||
,["1", "4", "7", "10", "13", "16", "2", "5", "8",
|
||||
"11", "14", "17", "3", "6", "9", "12", "15", "18"]
|
||||
,["1", "5", "9", "13", "17", "21", "2", "6", "10", "14", "18", "22",
|
||||
"3", "7", "11", "15", "19", "23", "4", "8", "12", "16", "20", "24"]
|
||||
]
|
||||
height: 312
|
||||
clip: false
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 12
|
||||
anchors.top: switchTabBar.bottom
|
||||
anchors.topMargin: 24
|
||||
flow: GridView.FlowTopToBottom
|
||||
cellWidth: (parent.width/(count/6)) - 8
|
||||
cellHeight: 52
|
||||
interactive: false
|
||||
z: 100000
|
||||
cacheBuffer: 9999
|
||||
model: switchTabBar.currentItem.text.substring(0,2)
|
||||
|
||||
function addWord(pos, word, ignoreGoingNext = false) {
|
||||
mnemonicInput.push({pos: pos, seed: word.replace(/\s/g, '')})
|
||||
|
||||
for (let j = 0; j < mnemonicInput.length; j++) {
|
||||
if (mnemonicInput[j].pos === pos && mnemonicInput[j].seed !== word) {
|
||||
mnemonicInput[j].seed = word
|
||||
break
|
||||
}
|
||||
}
|
||||
//remove duplicates
|
||||
const valueArr = mnemonicInput.map(item => item.pos)
|
||||
const isDuplicate = valueArr.some((item, idx) => {
|
||||
if (valueArr.indexOf(item) !== idx) {
|
||||
root.mnemonicInput.splice(idx, 1)
|
||||
}
|
||||
return valueArr.indexOf(item) !== idx
|
||||
})
|
||||
if (!ignoreGoingNext) {
|
||||
for (let i = 0; i < grid.count; i++) {
|
||||
if (grid.itemAtIndex(i).mnemonicIndex !== (pos + 1)) {
|
||||
continue
|
||||
}
|
||||
|
||||
grid.currentIndex = grid.itemAtIndex(i).itemIndex
|
||||
grid.itemAtIndex(i).textEdit.input.edit.forceActiveFocus()
|
||||
|
||||
if (grid.currentIndex !== 12) {
|
||||
continue
|
||||
}
|
||||
|
||||
grid.positionViewAtEnd()
|
||||
|
||||
if (grid.count === 20) {
|
||||
grid.contentX = 1500
|
||||
}
|
||||
}
|
||||
}
|
||||
submitButton.checkMnemonicLength()
|
||||
}
|
||||
|
||||
delegate: StatusSeedPhraseInput {
|
||||
id: seedWordInput
|
||||
textEdit.input.edit.objectName: `statusSeedPhraseInputField${seedWordInput.leftComponentText}`
|
||||
width: (grid.cellWidth - 8)
|
||||
height: (grid.cellHeight - 8)
|
||||
Behavior on width { NumberAnimation { duration: 180 } }
|
||||
textEdit.text: {
|
||||
const pos = seedWordInput.mnemonicIndex
|
||||
for (let i in root.mnemonicInput) {
|
||||
const p = root.mnemonicInput[i]
|
||||
if (p.pos === pos) {
|
||||
return p.seed
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
readonly property int mnemonicIndex: grid.wordIndex[(grid.count / 6) - 2][index]
|
||||
|
||||
leftComponentText: mnemonicIndex
|
||||
inputList: BIP39_en {}
|
||||
|
||||
property int itemIndex: index
|
||||
z: (grid.currentIndex === index) ? 150000000 : 0
|
||||
onTextChanged: {
|
||||
invalidSeedTxt.text = ""
|
||||
}
|
||||
onDoneInsertingWord: {
|
||||
grid.addWord(mnemonicIndex, word)
|
||||
}
|
||||
onEditClicked: {
|
||||
grid.currentIndex = index
|
||||
grid.itemAtIndex(index).textEdit.input.edit.forceActiveFocus()
|
||||
}
|
||||
onKeyPressed: {
|
||||
grid.currentIndex = index
|
||||
|
||||
if (event.key === Qt.Key_Backtab) {
|
||||
for (let i = 0; i < grid.count; i++) {
|
||||
if (grid.itemAtIndex(i).mnemonicIndex === ((mnemonicIndex - 1) >= 0 ? (mnemonicIndex - 1) : 0)) {
|
||||
grid.itemAtIndex(i).textEdit.input.edit.forceActiveFocus(Qt.BacktabFocusReason)
|
||||
textEdit.input.tabNavItem = grid.itemAtIndex(i).textEdit.input.edit
|
||||
event.accepted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
for (let i = 0; i < grid.count; i++) {
|
||||
if (grid.itemAtIndex(i).mnemonicIndex === ((mnemonicIndex + 1) <= grid.count ? (mnemonicIndex + 1) : grid.count)) {
|
||||
grid.itemAtIndex(i).textEdit.input.edit.forceActiveFocus(Qt.TabFocusReason)
|
||||
textEdit.input.tabNavItem = grid.itemAtIndex(i).textEdit.input.edit
|
||||
event.accepted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.matches(StandardKey.Paste)) {
|
||||
if (root.pasteWords()) {
|
||||
// Paste was done by splitting the words
|
||||
event.accepted = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
event.accepted = true
|
||||
if (submitButton.enabled) {
|
||||
submitButton.clicked(null)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_Delete || event.key === Qt.Key_Backspace) {
|
||||
const wordIndex = mnemonicInput.findIndex(x => x.pos === mnemonicIndex)
|
||||
if (wordIndex > -1) {
|
||||
mnemonicInput.splice(wordIndex, 1)
|
||||
submitButton.checkMnemonicLength()
|
||||
}
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
const item = grid.itemAtIndex(0)
|
||||
if (item) {
|
||||
item.textEdit.input.edit.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: invalidSeedTxt
|
||||
objectName: "onboardingInvalidSeedText"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: grid.bottom
|
||||
anchors.topMargin: 24
|
||||
color: Theme.palette.dangerColor1
|
||||
visible: text !== ""
|
||||
EnterSeedPhrase {
|
||||
id: seedPhraseView
|
||||
isSeedPhraseValid: root.startupStore.validMnemonic
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
id: submitButton
|
||||
objectName: "seedPhraseViewSubmitButton"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: invalidSeedTxt.bottom
|
||||
anchors.topMargin: 24
|
||||
enabled: false
|
||||
function checkMnemonicLength() {
|
||||
submitButton.enabled = (root.mnemonicInput.length === root.tabs[switchTabBar.currentIndex])
|
||||
}
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
enabled: seedPhraseView.seedPhraseIsValid
|
||||
text: {
|
||||
if (root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunNewUserImportSeedPhrase) {
|
||||
return qsTr("Import")
|
||||
|
@ -322,21 +84,7 @@ Item {
|
|||
}
|
||||
return ""
|
||||
}
|
||||
onClicked: {
|
||||
let mnemonicString = ""
|
||||
const sortTable = mnemonicInput.sort((a, b) => a.pos - b.pos)
|
||||
for (let i = 0; i < mnemonicInput.length; i++) {
|
||||
mnemonicString += sortTable[i].seed.toLowerCase() + ((i === (grid.count-1)) ? "" : " ")
|
||||
}
|
||||
|
||||
if (Utils.isMnemonic(mnemonicString) && root.startupStore.validMnemonic(mnemonicString)) {
|
||||
root.mnemonicInput = []
|
||||
root.startupStore.doPrimaryAction()
|
||||
} else {
|
||||
invalidSeedTxt.text = qsTr("Invalid seed")
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
onClicked: root.startupStore.doPrimaryAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
LoginView 1.0 LoginView.qml
|
||||
ProfileFetchingView 1.0 ProfileFetchingView.qml
|
||||
ProfileFetchingView 1.0 ProfileFetchingView.qml
|
||||
SeedPhraseInputView 1.0 SeedPhraseInputView.qml
|
|
@ -3,6 +3,7 @@ import QtQuick.Layouts 1.14
|
|||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Controls.Validators 0.1
|
||||
|
||||
|
@ -16,17 +17,26 @@ ColumnLayout {
|
|||
//**************************************************************************
|
||||
//* This component is not refactored, just pulled out to a shared location *
|
||||
//**************************************************************************
|
||||
|
||||
spacing: Style.current.padding
|
||||
clip: true
|
||||
|
||||
readonly property bool seedPhraseIsValid: d.allEntriesValid && invalidSeedTxt.text === ""
|
||||
property var isSeedPhraseValid: function (mnemonic) { return false }
|
||||
property ListModel dictionary: BIP39_en {}
|
||||
|
||||
signal submitSeedPhrase()
|
||||
signal seedPhraseUpdated(bool valid, string seedPhrase)
|
||||
|
||||
function setWrongSeedPhraseMessage(message) {
|
||||
invalidSeedTxt.text = message
|
||||
// Validate again the seed phrase
|
||||
// This is needed because the message can be set to empty and the seed phrase is still invalid
|
||||
if (message === "")
|
||||
d.validate()
|
||||
}
|
||||
|
||||
function getSeedPhraseAsString() {
|
||||
return d.buildMnemonicString()
|
||||
}
|
||||
|
||||
QtObject {
|
||||
|
@ -34,18 +44,18 @@ ColumnLayout {
|
|||
|
||||
property bool allEntriesValid: false
|
||||
property var mnemonicInput: []
|
||||
property var incorrectWordAtIndex: []
|
||||
readonly property var tabs: [12, 18, 24]
|
||||
readonly property ListModel seedPhrases_en: BIP39_en {}
|
||||
readonly property alias seedPhrases_en: root.dictionary
|
||||
|
||||
onIncorrectWordAtIndexChanged: d.validate()
|
||||
|
||||
onAllEntriesValidChanged: {
|
||||
let mnemonicString = ""
|
||||
if (d.allEntriesValid) {
|
||||
const sortTable = mnemonicInput.sort((a, b) => a.pos - b.pos)
|
||||
for (let i = 0; i < mnemonicInput.length; i++) {
|
||||
d.checkWordExistence(sortTable[i].seed)
|
||||
mnemonicString += sortTable[i].seed + ((i === (grid.count-1)) ? "" : " ")
|
||||
}
|
||||
|
||||
if (d.allEntriesValid) {
|
||||
|
||||
mnemonicString = buildMnemonicString()
|
||||
if (!Utils.isMnemonic(mnemonicString) || !root.isSeedPhraseValid(mnemonicString)) {
|
||||
root.setWrongSeedPhraseMessage(qsTr("Invalid seed phrase"))
|
||||
d.allEntriesValid = false
|
||||
|
@ -54,25 +64,43 @@ ColumnLayout {
|
|||
root.seedPhraseUpdated(d.allEntriesValid, mnemonicString)
|
||||
}
|
||||
|
||||
function checkMnemonicLength() {
|
||||
d.allEntriesValid = d.mnemonicInput.length === d.tabs[switchTabBar.currentIndex]
|
||||
function validate() {
|
||||
if (d.incorrectWordAtIndex.length > 0) {
|
||||
invalidSeedTxt.text = qsTr("The phrase you’ve entered is invalid")
|
||||
return
|
||||
}
|
||||
|
||||
invalidSeedTxt.text = ""
|
||||
}
|
||||
|
||||
function checkWordExistence(word) {
|
||||
d.allEntriesValid = d.allEntriesValid && d.seedPhrases_en.words.includes(word)
|
||||
if (d.allEntriesValid) {
|
||||
root.setWrongSeedPhraseMessage("")
|
||||
}
|
||||
else {
|
||||
root.setWrongSeedPhraseMessage(qsTr("The phrase you’ve entered is invalid"))
|
||||
function checkMnemonicLength() {
|
||||
d.allEntriesValid = d.mnemonicInput.length === d.tabs[switchTabBar.currentIndex] && d.incorrectWordAtIndex.length === 0
|
||||
}
|
||||
|
||||
function buildMnemonicString() {
|
||||
const sortTable = mnemonicInput.sort((a, b) => a.pos - b.pos)
|
||||
return sortTable.map(x => x.seed).join(" ")
|
||||
}
|
||||
|
||||
function checkWordExistence(word, pos) {
|
||||
if (word !== "" && !ModelUtils.contains(d.seedPhrases_en, "seedWord", word)) {
|
||||
const incorrectWordAtIndex = d.incorrectWordAtIndex
|
||||
incorrectWordAtIndex.push(pos)
|
||||
d.incorrectWordAtIndex = incorrectWordAtIndex
|
||||
return
|
||||
}
|
||||
|
||||
d.incorrectWordAtIndex = d.incorrectWordAtIndex.filter(function(value) {
|
||||
return value !== pos
|
||||
})
|
||||
}
|
||||
|
||||
function pasteWords () {
|
||||
const clipboardText = globalUtils.getFromClipboard()
|
||||
const clipboardText = Utils.getFromClipboard()
|
||||
|
||||
// Split words separated by commas and or blank spaces (spaces, enters, tabs)
|
||||
const words = clipboardText.trim().split(/[, \s]+/)
|
||||
|
||||
|
||||
let index = d.tabs.indexOf(words.length)
|
||||
if (index === -1) {
|
||||
return false
|
||||
|
@ -118,7 +146,8 @@ ColumnLayout {
|
|||
|
||||
StatusSwitchTabBar {
|
||||
id: switchTabBar
|
||||
Layout.preferredWidth: parent.width
|
||||
objectName: "enterSeedPhraseSwitchBar"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Repeater {
|
||||
model: d.tabs
|
||||
StatusSwitchTabButton {
|
||||
|
@ -131,6 +160,9 @@ ColumnLayout {
|
|||
d.mnemonicInput = d.mnemonicInput.filter(function(value) {
|
||||
return value.pos <= d.tabs[switchTabBar.currentIndex]
|
||||
})
|
||||
d.incorrectWordAtIndex = d.incorrectWordAtIndex.filter(function(value) {
|
||||
return value <= d.tabs[switchTabBar.currentIndex]
|
||||
})
|
||||
d.checkMnemonicLength()
|
||||
}
|
||||
}
|
||||
|
@ -144,42 +176,46 @@ ColumnLayout {
|
|||
,["1", "5", "9", "13", "17", "21", "2", "6", "10", "14", "18", "22",
|
||||
"3", "7", "11", "15", "19", "23", "4", "8", "12", "16", "20", "24"]
|
||||
]
|
||||
Layout.preferredWidth: parent.width
|
||||
|
||||
objectName: "enterSeedPhraseGridView"
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 312
|
||||
clip: false
|
||||
flow: GridView.FlowTopToBottom
|
||||
cellWidth: (parent.width/(count/6))
|
||||
cellHeight: 52
|
||||
interactive: false
|
||||
z: 100000
|
||||
cacheBuffer: 9999
|
||||
model: switchTabBar.currentItem.text.substring(0,2)
|
||||
|
||||
function addWord(pos, word, ignoreGoingNext = false) {
|
||||
d.mnemonicInput.push({pos: pos, seed: word.replace(/\s/g, '')})
|
||||
|
||||
for (let j = 0; j < d.mnemonicInput.length; j++) {
|
||||
if (d.mnemonicInput[j].pos === pos && d.mnemonicInput[j].seed !== word) {
|
||||
d.mnemonicInput[j].seed = word
|
||||
const words = d.mnemonicInput
|
||||
|
||||
words.push({pos: pos, seed: word.replace(/\s/g, '')})
|
||||
|
||||
for (let j = 0; j < words.length; j++) {
|
||||
if (words[j].pos === pos && words[j].seed !== word) {
|
||||
words[j].seed = word
|
||||
break
|
||||
}
|
||||
}
|
||||
//remove duplicates
|
||||
const valueArr = d.mnemonicInput.map(item => item.pos)
|
||||
const valueArr = words.map(item => item.pos)
|
||||
const isDuplicate = valueArr.some((item, idx) => {
|
||||
if (valueArr.indexOf(item) !== idx) {
|
||||
d.mnemonicInput.splice(idx, 1)
|
||||
words.splice(idx, 1)
|
||||
}
|
||||
return valueArr.indexOf(item) !== idx
|
||||
})
|
||||
if (!ignoreGoingNext) {
|
||||
for (let i = 0; i < grid.count; i++) {
|
||||
if (grid.itemAtIndex(i).mnemonicIndex !== (pos + 1)) {
|
||||
const item = grid.itemAtIndex(i)
|
||||
if (!item || item.mnemonicIndex !== (pos + 1)) {
|
||||
continue
|
||||
}
|
||||
|
||||
grid.currentIndex = grid.itemAtIndex(i).itemIndex
|
||||
grid.itemAtIndex(i).textEdit.input.edit.forceActiveFocus()
|
||||
grid.currentIndex = item.itemIndex
|
||||
item.textEdit.input.edit.forceActiveFocus()
|
||||
|
||||
if (grid.currentIndex !== 12) {
|
||||
continue
|
||||
|
@ -192,12 +228,16 @@ ColumnLayout {
|
|||
}
|
||||
}
|
||||
}
|
||||
d.mnemonicInput = words
|
||||
d.checkWordExistence(word, pos)
|
||||
d.checkMnemonicLength()
|
||||
root.seedPhraseUpdated(d.allEntriesValid, d.buildMnemonicString())
|
||||
}
|
||||
|
||||
delegate: StatusSeedPhraseInput {
|
||||
id: seedWordInput
|
||||
textEdit.input.edit.objectName: `statusSeedPhraseInputField${seedWordInput.leftComponentText}`
|
||||
|
||||
textEdit.input.edit.objectName: `enterSeedPhraseInputField${seedWordInput.leftComponentText}`
|
||||
width: (grid.cellWidth - 8)
|
||||
height: (grid.cellHeight - 8)
|
||||
Behavior on width { NumberAnimation { duration: 180 } }
|
||||
|
@ -218,13 +258,16 @@ ColumnLayout {
|
|||
inputList: d.seedPhrases_en
|
||||
|
||||
property int itemIndex: index
|
||||
z: (grid.currentIndex === index) ? 150000000 : 0
|
||||
onTextChanged: {
|
||||
d.checkWordExistence(text)
|
||||
}
|
||||
onDoneInsertingWord: {
|
||||
grid.addWord(mnemonicIndex, word)
|
||||
}
|
||||
onEditingFinished: {
|
||||
if (text === "") {
|
||||
return
|
||||
}
|
||||
|
||||
grid.addWord(mnemonicIndex, text, true)
|
||||
}
|
||||
onEditClicked: {
|
||||
grid.currentIndex = index
|
||||
grid.itemAtIndex(index).textEdit.input.edit.forceActiveFocus()
|
||||
|
@ -287,7 +330,7 @@ ColumnLayout {
|
|||
|
||||
StatusBaseText {
|
||||
id: invalidSeedTxt
|
||||
visible: text !== ""
|
||||
objectName: "enterSeedPhraseInvalidSeedText"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: Theme.palette.dangerColor1
|
||||
}
|
||||
|
|
|
@ -1,23 +1,17 @@
|
|||
import QtQuick 2.13
|
||||
|
||||
import StatusQ.Core.Utils 0.1
|
||||
|
||||
ListModel {
|
||||
id: root
|
||||
|
||||
property var words: []
|
||||
|
||||
Component.onCompleted: {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "english.txt");
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
root.words = xhr.responseText.split('\n');
|
||||
for (var i = 0; i < root.words.length; i++) {
|
||||
if (root.words[i] !== "") {
|
||||
insert(count, {"seedWord": root.words[i]});
|
||||
}
|
||||
}
|
||||
const words = StringUtils.readTextFile(":/imports/shared/stores/english.txt").split(/\r?\n|\r/);
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
let word = words[i]
|
||||
if (word !== "") {
|
||||
insert(count, {"seedWord": word});
|
||||
}
|
||||
}
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -737,6 +737,10 @@ QtObject {
|
|||
globalUtilsInst.copyToClipboard(text)
|
||||
}
|
||||
|
||||
function getFromClipboard() {
|
||||
return globalUtilsInst.getFromClipboard()
|
||||
}
|
||||
|
||||
function copyImageToClipboardByUrl(content) {
|
||||
globalUtilsInst.copyImageToClipboardByUrl(content)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue