import QtQuick 2.12 import QtQuick.Controls 2.14 import QtGraphicalEffects 1.13 import QtQuick.Dialogs 1.3 import StatusQ.Controls 0.1 import StatusQ.Popups 0.1 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 import utils 1.0 import shared.stores 1.0 import "../controls" import "../stores" OnboardingBasePage { id: root state: "existingUser" property bool existingUser: (root.state === "existingUser") property var mnemonicInput: [] property string mnemonicString signal seedValidated() Connections { target: OnboardingStore.onboardingModuleInst onAccountImportError: { if (error === Constants.existingAccountError) { importSeedError.title = qsTr("Keys for this account already exist") importSeedError.text = qsTr("Keys for this account already exist and can't be added again. If you've lost your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase") } else { importSeedError.title = qsTr("Error importing seed") importSeedError.text = error } importSeedError.open() } onAccountImportSuccess: { root.seedValidated() } } MessageDialog { id: importSeedError icon: StandardIcon.Critical standardButtons: StandardButton.Ok } Item { implicitWidth: 731 implicitHeight: 472 anchors.centerIn: parent StatusBaseText { id: headlineText font.pixelSize: 22 font.weight: Font.Bold color: Theme.palette.directColor1 anchors.horizontalCenter: parent.horizontalCenter text: qsTr("Enter seed phrase") } StatusSwitchTabBar { id: switchTabBar anchors.top: headlineText.bottom anchors.horizontalCenter: parent.horizontalCenter anchors.topMargin: 24 StatusSwitchTabButton { text: qsTr("12 words") } StatusSwitchTabButton { text: qsTr("18 words") } StatusSwitchTabButton { text: qsTr("24 words") } onCurrentIndexChanged: { root.mnemonicString = ""; root.mnemonicInput = []; submitButton.enabled = false; } } clip: true GridView { id: grid width: parent.width property var wordIndex: ["1", "5", "9", "2", "6", "10", "3", "7", "11", "4", "8", "12", "13", "17", "21", "14", "18", "22", "15", "19", "23", "16", "20", "24"] property var wordIndex18: ["1", "5", "9", "2", "6", "10", "3", "7", "11", "4", "8", "12", "13", "", "14", "17", "15", "18", "16", ""] height: (grid.count === 20 && !grid.atXBeginning) ? 144 : 244 anchors.left: parent.left anchors.leftMargin: 12 anchors.top: switchTabBar.bottom anchors.topMargin: ((grid.count === 20) && !grid.atXBeginning) ? 74 : 24 flow: GridView.FlowTopToBottom cellWidth: (parent.width/4) cellHeight: 72 interactive: false z: 100000 model: switchTabBar.currentItem.text.substring(0,2) === "12" ? 12 : switchTabBar.currentItem.text.substring(0,2) === "18" ? 20 : 24 function addWord(pos, word, ignoreGoingNext) { root.mnemonicInput.push({pos: pos, seed: word.replace(/\s/g, '')}); for (var j = 0; j < mnemonicInput.length; j++) { if (mnemonicInput[j].pos === pos && mnemonicInput[j].seed !== word) { mnemonicInput[j].seed = word; break; } } //remove duplicates var valueArr = mnemonicInput.map(function(item){ return item.pos }); var isDuplicate = valueArr.some(function(item, idx){ if (valueArr.indexOf(item) !== idx) { root.mnemonicInput.splice(idx, 1); } return valueArr.indexOf(item) !== idx }); if (!ignoreGoingNext) { for (var i = !grid.atXBeginning ? 12 : 0; i < grid.count; i++) { if (parseInt(grid.itemAtIndex(i).leftComponentText) !== (parseInt(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 width: (grid.cellWidth - 24) height: (grid.cellHeight - 28) textEdit.input.anchors.leftMargin: 16 textEdit.input.anchors.rightMargin: 16 textEdit.text: { for (var i in root.mnemonicInput) { let p = root.mnemonicInput[i] if (p.pos === seedWordInput.leftComponentText) { return p.seed } } return "" } visible: grid.count !== 20 || (index !== 13 && index !== 19) leftComponentText: (grid.count === 20) ? grid.wordIndex18[index] : grid.wordIndex[index] inputList: BIP39_en { } property int itemIndex: index z: (grid.currentIndex === index) ? 150000000 : 0 onTextChanged: { invalidSeedTxt.visible = false; } onDoneInsertingWord: { grid.addWord(leftComponentText, word) } onEditClicked: { grid.currentIndex = index; grid.itemAtIndex(index).textEdit.input.edit.forceActiveFocus(); } onKeyPressed: { if (event.key === Qt.Key_Backtab) { 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(Qt.TabFocusReason); textEdit.input.tabNavItem = grid.itemAtIndex(i).textEdit.input.edit; event.accepted = true break } } } else if (event.key === Qt.Key_Tab) { 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(Qt.TabFocusReason); textEdit.input.tabNavItem = grid.itemAtIndex(i).textEdit.input.edit; event.accepted = true break } } } if (event.matches(StandardKey.Paste)) { const clipboardText = globalUtils.getFromClipboard() event.accepted = true let words = clipboardText.split(' ') root.mnemonicInput = [] for (let i = 0; i < words.length; i++) { grid.itemAtIndex(i).setWord(words[i]) grid.addWord(i, words[i], true) } submitButton.checkMnemonicLength() 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) { var wordIndex = mnemonicInput.findIndex(x => x.pos === leftComponentText); if (wordIndex > -1) { mnemonicInput.splice(wordIndex , 1); submitButton.checkMnemonicLength() } } grid.currentIndex = index; } Component.onCompleted: { grid.itemAtIndex(0).textEdit.input.edit.forceActiveFocus(); } } } StatusBaseText { id: invalidSeedTxt anchors.horizontalCenter: parent.horizontalCenter anchors.top: grid.bottom anchors.topMargin: (grid.count === 20 && !grid.atXBeginning) ? 74 : 24 color: Theme.palette.dangerColor1 visible: false text: qsTr("Invalid seed") } StatusButton { id: submitButton anchors.horizontalCenter: parent.horizontalCenter anchors.top: invalidSeedTxt.bottom anchors.topMargin: 24 enabled: false property int gridCount: (grid.count === 20) ? 18 : grid.count function checkMnemonicLength() { submitButton.enabled = (root.mnemonicInput.length === (grid.atXBeginning ? 12 : submitButton.gridCount)); } text: root.existingUser ? qsTr("Restore Status Profile") : ((grid.count > 12) && grid.atXBeginning) ? qsTr("Next") : qsTr("Import") onClicked: { if ((grid.count > 12) && grid.atXBeginning) { grid.positionViewAtEnd(); if (grid.count === 20) { grid.contentX = 1500; } } else { root.mnemonicString = ""; var sortTable = mnemonicInput.sort(function (a, b) { return a.pos - b.pos; }); for (var i = 0; i < mnemonicInput.length; i++) { root.mnemonicString += sortTable[i].seed + ((i === (gridCount-1)) ? "" : " "); } if (Utils.isMnemonic(root.mnemonicString) && !OnboardingStore.validateMnemonic(root.mnemonicString)) { OnboardingStore.importMnemonic(root.mnemonicString) root.mnemonicString = ""; root.mnemonicInput = []; } else { invalidSeedTxt.visible = true; enabled = false; } } } } } onBackClicked: { root.mnemonicString = ""; if (!grid.atXBeginning) { grid.positionViewAtBeginning(); } else { root.mnemonicInput = []; root.exit(); } } }