2022-03-15 22:27:36 +00:00
import QtQuick 2.12
2022-05-10 15:42:35 +00:00
import QtQuick . Controls 2.14
2022-03-15 22:27:36 +00:00
import QtGraphicalEffects 1.13
2022-04-14 19:10:25 +00:00
import QtQuick . Dialogs 1.3
2022-03-15 22:27:36 +00:00
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
2022-03-31 11:46:25 +00:00
import shared . stores 1.0
2022-03-15 22:27:36 +00:00
import "../controls"
import "../stores"
OnboardingBasePage {
id: root
state: "existingUser"
property bool existingUser: ( root . state === "existingUser" )
property var mnemonicInput: [ ]
property string mnemonicString
signal seedValidated ( )
2022-04-14 19:10:25 +00:00
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
}
2022-03-15 22:27:36 +00:00
Item {
implicitWidth: 731
implicitHeight: 472
anchors.centerIn: parent
2022-04-28 07:32:17 +00:00
StatusBaseText {
id: headlineText
font.pixelSize: 22
font.weight: Font . Bold
color: Theme . palette . directColor1
anchors.horizontalCenter: parent . horizontalCenter
text: qsTr ( "Enter seed phrase" )
}
2022-03-15 22:27:36 +00:00
StatusSwitchTabBar {
id: switchTabBar
2022-04-28 07:32:17 +00:00
anchors.top: headlineText . bottom
2022-03-15 22:27:36 +00:00
anchors.horizontalCenter: parent . horizontalCenter
2022-04-28 07:32:17 +00:00
anchors.topMargin: 24
2022-03-15 22:27:36 +00:00
StatusSwitchTabButton {
text: qsTr ( "12 words" )
}
StatusSwitchTabButton {
text: qsTr ( "18 words" )
}
StatusSwitchTabButton {
text: qsTr ( "24 words" )
}
onCurrentIndexChanged: {
root . mnemonicString = "" ;
root . mnemonicInput = [ ] ;
2022-04-06 21:47:31 +00:00
submitButton . enabled = false ;
2022-03-15 22:27:36 +00:00
}
}
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
2022-05-10 15:42:35 +00:00
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 ( )
}
2022-03-15 22:27:36 +00:00
delegate: StatusSeedPhraseInput {
id: seedWordInput
width: ( grid . cellWidth - 24 )
height: ( grid . cellHeight - 28 )
textEdit.input.anchors.leftMargin: 16
textEdit.input.anchors.rightMargin: 16
2022-04-07 16:17:30 +00:00
textEdit.text: {
for ( var i in root . mnemonicInput ) {
let p = root . mnemonicInput [ i ]
if ( p . pos === seedWordInput . leftComponentText ) {
return p . seed
}
}
return ""
}
2022-04-06 08:39:23 +00:00
visible: grid . count !== 20 || ( index !== 13 && index !== 19 )
2022-03-15 22:27:36 +00:00
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: {
2022-05-10 15:42:35 +00:00
grid . addWord ( leftComponentText , word )
2022-03-15 22:27:36 +00:00
}
onEditClicked: {
grid . currentIndex = index ;
grid . itemAtIndex ( index ) . textEdit . input . edit . forceActiveFocus ( ) ;
}
onKeyPressed: {
2022-05-10 15:42:35 +00:00
if ( event . key === Qt . Key_Backtab ) {
2022-03-15 22:27:36 +00:00
for ( var i = ! grid . atXBeginning ? 12 : 0 ; i < grid . count ; i ++ ) {
2022-05-10 15:42:35 +00:00
if ( parseInt ( grid . itemAtIndex ( i ) . leftComponentText ) === ( ( parseInt ( leftComponentText ) - 1 ) >= 0 ? ( parseInt ( leftComponentText ) - 1 ) : 0 ) ) {
grid . itemAtIndex ( i ) . textEdit . input . edit . forceActiveFocus ( Qt . TabFocusReason ) ;
2022-03-15 22:27:36 +00:00
textEdit . input . tabNavItem = grid . itemAtIndex ( i ) . textEdit . input . edit ;
2022-05-10 15:42:35 +00:00
event . accepted = true
break
2022-03-15 22:27:36 +00:00
}
}
2022-05-10 15:42:35 +00:00
} else if ( event . key === Qt . Key_Tab ) {
2022-03-15 22:27:36 +00:00
for ( var i = ! grid . atXBeginning ? 12 : 0 ; i < grid . count ; i ++ ) {
2022-05-10 15:42:35 +00:00
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
2022-03-15 22:27:36 +00:00
}
}
2022-05-10 15:42:35 +00:00
}
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
}
2022-03-15 22:27:36 +00:00
}
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 ) ;
2022-05-10 15:42:35 +00:00
submitButton . checkMnemonicLength ( )
2022-03-15 22:27:36 +00:00
}
}
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
2022-05-10 15:42:35 +00:00
function checkMnemonicLength ( ) {
submitButton . enabled = ( root . mnemonicInput . length === ( grid . atXBeginning ? 12 : submitButton . gridCount ) ) ;
}
2022-03-15 22:27:36 +00:00
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 {
2022-04-07 16:17:30 +00:00
root . mnemonicInput = [ ] ;
2022-03-15 22:27:36 +00:00
root . exit ( ) ;
}
}
}