monero-gui/wizard/WizardCreateWallet2.qml
2024-01-27 20:12:09 +01:00

450 lines
26 KiB
QML

// Copyright (c) 2014-2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import QtQuick 2.9
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.2
import QtQuick.Controls 2.0
import moneroComponents.Clipboard 1.0
import "../js/Wizard.js" as Wizard
import "../js/Utils.js" as Utils
import "../components" as MoneroComponents
Rectangle {
id: wizardCreateWallet2
color: "transparent"
property alias pageHeight: pageRoot.height
property alias pageRoot: pageRoot
property string viewName: "wizardCreateWallet2"
property var seedArray: wizardController.walletOptionsSeed.split(" ")
property var seedListGrid: ""
property var hiddenWords: [0, 5, 10, 15, 20]
Clipboard { id: clipboard }
state: "default"
states: [
State {
name: "default";
}, State {
name: "verify";
when: typeof currentWallet != "undefined" && wizardStateView.state == "wizardCreateWallet2"
PropertyChanges { target: header; title: qsTr("Verify your recovery phrase") + translationManager.emptyString }
PropertyChanges { target: header; imageIcon: wizardController.layoutScale != 4 ? (MoneroComponents.Style.blackTheme ? "qrc:///images/verify.png" : "qrc:///images/verify-white.png") : "" }
PropertyChanges { target: header; subtitle: qsTr("Please confirm that you have written down your recover phrase by filling in the five blank fields with the correct words. If you have not written down your recovery phrase on a piece of paper, click on the Previous button and write it down right now!") + translationManager.emptyString}
PropertyChanges { target: walletCreationDate; opacity: 0; enabled: false}
PropertyChanges { target: walletCreationDateValue; opacity: 0; enabled: false}
PropertyChanges { target: walletRestoreHeight; opacity: 0; enabled: false}
PropertyChanges { target: walletRestoreHeightValue; opacity: 0; enabled: false}
PropertyChanges { target: createNewSeedButton; opacity: 0; enabled: false}
PropertyChanges { target: copyToClipboardButton; opacity: 0; enabled: false}
PropertyChanges { target: printPDFTemplate; opacity: 0; enabled: false}
PropertyChanges { target: navigation; onPrevClicked: {
seedListGridColumn.clearFields();
wizardCreateWallet2.state = "default";
pageRoot.forceActiveFocus();
}}
PropertyChanges { target: navigation; onNextClicked: {
seedListGridColumn.clearFields();
wizardStateView.state = "wizardCreateWallet3";
wizardCreateWallet2.state = "default";
}}
}
]
MoneroComponents.TextPlain {
//PDF template text
// the translation of these strings is used to create localized PDF templates
visible: false
text: qsTr("Print this paper, fill it out, and keep it in a safe location. Never share your recovery phrase with anybody, especially with strangers offering technical support.") +
qsTr("Recovery phrase (mnemonic seed)") +
qsTr("These words are are a backup of your wallet. They are the only thing needed to access your funds and restore your Monero wallet, so keep this paper in a safe place and do not disclose it to anybody! It is strongly not recommended to store your recovery phrase digitally (in an email, online service, screenshot, photo, or any other type of computer file).") +
qsTr("Wallet creation date") +
qsTr("Wallet restore height") +
qsTr("For instructions on how to restore this wallet, visit www.getmonero.org and go to Resources > User Guides > \"How to restore a wallet from mnemonic seed\". Use only Monero wallets that are trusted and recommended by the Monero community (see a list of them in www.getmonero.org/downloads).") + translationManager.emptyString
}
ColumnLayout {
id: pageRoot
Layout.alignment: Qt.AlignHCenter;
width: parent.width - 100
Layout.fillWidth: true
anchors.horizontalCenter: parent.horizontalCenter;
spacing: 0
KeyNavigation.down: mobileDialog.visible ? mobileHeader : header
KeyNavigation.tab: mobileDialog.visible ? mobileHeader : header
ColumnLayout {
id: mobileDialog
Layout.fillWidth: true
Layout.topMargin: wizardController.wizardSubViewTopMargin
Layout.maximumWidth: wizardController.wizardSubViewWidth
Layout.alignment: Qt.AlignHCenter
visible: wizardController.layoutScale == 4
spacing: 60
WizardHeader {
id: mobileHeader
title: qsTr("Write down your recovery phrase") + translationManager.emptyString
Accessible.role: Accessible.StaticText
Accessible.name: qsTr("Write down your recovery phrase") + translationManager.emptyString
Keys.onUpPressed: displaySeedButton.forceActiveFocus()
Keys.onBacktabPressed: displaySeedButton.forceActiveFocus()
KeyNavigation.down: mobileImage
KeyNavigation.tab: mobileImage
}
Image {
id: mobileImage
Layout.alignment: Qt.AlignHCenter
fillMode: Image.PreserveAspectCrop
source: MoneroComponents.Style.blackTheme ? "qrc:///images/write-down@2x.png" : "qrc:///images/write-down-white@2x.png"
width: 125
height: 125
sourceSize.width: 125
sourceSize.height: 125
Accessible.role: Accessible.Graphic
Accessible.name: qsTr("A pencil writing on a piece of paper") + translationManager.emptyString
KeyNavigation.up: mobileHeader
KeyNavigation.backtab: mobileHeader
KeyNavigation.down: mobileText
KeyNavigation.tab: mobileText
Rectangle {
width: mobileImage.width
height: mobileImage.height
color: mobileImage.focus ? MoneroComponents.Style.titleBarButtonHoverColor : "transparent"
}
}
Text {
id: mobileText
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
color: MoneroComponents.Style.dimmedFontColor
text: qsTr("The next page will display your recovery phrase, also known as mnemonic seed.") + " " + qsTr("These words are a backup of your wallet. Write these words down now on a piece of paper in the same order displayed. Keep this paper in a safe place and do not disclose it to anybody! Do not store these words digitally, always use a paper!") + translationManager.emptyString
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 14
wrapMode: Text.WordWrap
leftPadding: 0
topPadding: 0
Accessible.role: Accessible.StaticText
Accessible.name: qsTr("The next page will display your recovery phrase, also known as mnemonic seed.") + " " + qsTr("These words are a backup of your wallet. Write these words down now on a piece of paper in the same order displayed. Keep this paper in a safe place and do not disclose it to anybody! Do not store these words digitally, always use a paper!") + translationManager.emptyString
KeyNavigation.up: mobileImage
KeyNavigation.backtab: mobileImage
KeyNavigation.down: displaySeedButton
KeyNavigation.tab: displaySeedButton
Rectangle {
anchors.fill: parent
color: parent.focus ? MoneroComponents.Style.titleBarButtonHoverColor : "transparent"
}
}
MoneroComponents.StandardButton {
id: displaySeedButton
Layout.alignment: Qt.AlignHCenter;
text: qsTr("Display recovery phrase") + translationManager.emptyString
onClicked: {
mobileDialog.visible = false;
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("The next page will display your recovery phrase, also known as mnemonic seed. ") + qsTr("These words are a backup of your wallet. Write these words down now on a piece of paper in the same order displayed. Keep this paper in a safe place and do not disclose it to anybody! Do not store these words digitally, always use a paper!") + translationManager.emptyString
KeyNavigation.up: mobileText
KeyNavigation.backtab: mobileText
KeyNavigation.down: mobileHeader
KeyNavigation.tab: mobileHeader
}
}
ColumnLayout {
id: mainPage
Layout.fillWidth: true
Layout.topMargin: wizardController.wizardSubViewTopMargin
Layout.maximumWidth: wizardController.wizardSubViewWidth
Layout.alignment: Qt.AlignHCenter
visible: !mobileDialog.visible
spacing: 15
WizardHeader {
id: header
imageIcon: wizardController.layoutScale != 4 ? (MoneroComponents.Style.blackTheme ? "qrc:///images/write-down.png" : "qrc:///images/write-down-white.png") : ""
title: qsTr("Write down your recovery phrase") + translationManager.emptyString
subtitleVisible: wizardController.layoutScale != 4
subtitle: qsTr("These words are a backup of your wallet. Write these words down now on a piece of paper in the same order displayed. Keep this paper in a safe place and do not disclose it to anybody! Do not store these words digitally, always use a paper!") + translationManager.emptyString
Accessible.role: Accessible.StaticText
Accessible.name: title + ". " + subtitle
Keys.onUpPressed: navigation.btnNext.enabled ? navigation.btnNext.forceActiveFocus() : navigation.wizardProgress.forceActiveFocus()
Keys.onBacktabPressed: navigation.btnNext.enabled ? navigation.btnNext.forceActiveFocus() : navigation.wizardProgress.forceActiveFocus()
Keys.onDownPressed: recoveryPhraseLabel.visible ? recoveryPhraseLabel.forceActiveFocus() : focusOnListGrid()
Keys.onTabPressed: recoveryPhraseLabel.visible ? recoveryPhraseLabel.forceActiveFocus() : focusOnListGrid()
function focusOnListGrid() {
if (wizardCreateWallet2.state == "verify") {
if (seedListGridColumn.children[0].children[wizardCreateWallet2.hiddenWords[0]].lineEdit.visible) {
return seedListGridColumn.children[0].children[wizardCreateWallet2.hiddenWords[0]].lineEdit.forceActiveFocus();
} else {
return seedListGridColumn.children[0].children[wizardCreateWallet2.hiddenWords[0]].forceActiveFocus();
}
} else {
return seedListGridColumn.children[0].children[0].forceActiveFocus();
}
}
}
MoneroComponents.TextPlain {
id: recoveryPhraseLabel
visible: wizardController.layoutScale != 4
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 15
font.bold: false
textFormat: Text.RichText
color: MoneroComponents.Style.dimmedFontColor
text: qsTr("Recovery phrase (mnemonic seed)") + ":" + translationManager.emptyString
themeTransition: false
tooltip: qsTr("These words encode your private spend key in a human readable format.") + "<br>" + qsTr("It is expected that some words may be repeated.") + translationManager.emptyString
tooltipIconVisible: true
Accessible.role: Accessible.StaticText
Accessible.name: qsTr("Recovery phrase (mnemonic seed)") + translationManager.emptyString;
KeyNavigation.up: header
KeyNavigation.backtab: header
Keys.onDownPressed: header.focusOnListGrid()
Keys.onTabPressed: header.focusOnListGrid()
}
ColumnLayout {
id: seedListGridColumn
function clearFields() {
for (var i = 0; i < wizardCreateWallet2.hiddenWords.length; i++) {
seedListGridColumn.children[0].children[wizardCreateWallet2.hiddenWords[i]].wordText.visible = true;
seedListGridColumn.children[0].children[wizardCreateWallet2.hiddenWords[i]].lineEdit.text = "";
seedListGridColumn.children[0].children[wizardCreateWallet2.hiddenWords[i]].lineEdit.readOnly = false;
}
}
}
RowLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignRight
Timer {
id: checkSeedListGridDestruction
interval: 100; running: false; repeat: true
onTriggered: {
if (!wizardCreateWallet2.seedListGrid) {
var newSeedListGrid = Qt.createComponent("SeedListGrid.qml");
wizardCreateWallet2.seedListGrid = newSeedListGrid.createObject(seedListGridColumn);
appWindow.showStatusMessage(qsTr("New seed generated"),3);
pageRoot.forceActiveFocus();
checkSeedListGridDestruction.stop();
}
}
}
MoneroComponents.StandardButton {
id: createNewSeedButton
visible: appWindow.walletMode >= 2
small: true
primary: false
text: qsTr("Create new seed") + translationManager.emptyString
onClicked: {
wizardController.restart(true);
wizardController.createWallet();
wizardCreateWallet2.seedArray = wizardController.walletOptionsSeed.split(" ")
wizardCreateWallet2.seedListGrid.destroy();
checkSeedListGridDestruction.start();
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("Create new seed") + translationManager.emptyString
KeyNavigation.up: (wizardCreateWallet2.seedListGrid && seedListGridColumn.children[0]) ? seedListGridColumn.children[0].children[24] : recoveryPhraseLabel
KeyNavigation.backtab: (wizardCreateWallet2.seedListGrid && seedListGridColumn.children[0]) ? seedListGridColumn.children[0].children[24] : recoveryPhraseLabel
KeyNavigation.down: copyToClipboardButton
KeyNavigation.tab: copyToClipboardButton
}
MoneroComponents.StandardButton {
id: copyToClipboardButton
visible: appWindow.walletMode >= 2
small: true
primary: false
text: qsTr("Copy to clipboard") + translationManager.emptyString
onClicked: {
clipboard.setText(wizardController.walletOptionsSeed);
appWindow.showStatusMessage(qsTr("Recovery phrase copied to clipboard"),3);
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("Copy to clipboard") + translationManager.emptyString
KeyNavigation.up: createNewSeedButton
KeyNavigation.backtab: createNewSeedButton
KeyNavigation.down: printPDFTemplate.visible ? printPDFTemplate : walletCreationDate
KeyNavigation.tab: printPDFTemplate.visible ? printPDFTemplate : walletCreationDate
}
MoneroComponents.StandardButton {
id: printPDFTemplate
small: true
primary: false
text: qsTr("Print a template") + translationManager.emptyString
tooltip: qsTr("Print a template to write down your seed") + translationManager.emptyString
onClicked: {
oshelper.openSeedTemplate();
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("Print a template to write down your seed") + translationManager.emptyString
KeyNavigation.up: copyToClipboardButton.visible ? copyToClipboardButton : (wizardCreateWallet2.seedListGrid && seedListGridColumn.children[0]) ? seedListGridColumn.children[0].children[24] : recoveryPhraseLabel
KeyNavigation.backtab: copyToClipboardButton.visible ? copyToClipboardButton : (wizardCreateWallet2.seedListGrid && seedListGridColumn.children[0]) ? seedListGridColumn.children[0].children[24] : recoveryPhraseLabel
KeyNavigation.down: walletCreationDate
KeyNavigation.tab: walletCreationDate
}
}
RowLayout {
Layout.topMargin: 0
Layout.fillWidth: true
Layout.maximumWidth: seedListGridColumn.width
spacing: 10
ColumnLayout {
spacing: 5
Layout.fillWidth: true
MoneroComponents.TextPlain {
id: walletCreationDate
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 15
font.bold: false
textFormat: Text.RichText
color: MoneroComponents.Style.dimmedFontColor
text: qsTr("Creation date") + ": " + translationManager.emptyString
themeTransition: false
Accessible.role: Accessible.StaticText
Accessible.name: qsTr("Creation date") + " " + walletCreationDateValue.text + translationManager.emptyString
KeyNavigation.up: printPDFTemplate.visible ? printPDFTemplate : copyToClipboardButton
KeyNavigation.backtab: printPDFTemplate.visible ? printPDFTemplate : copyToClipboardButton
KeyNavigation.down: walletRestoreHeight
KeyNavigation.tab: walletRestoreHeight
}
MoneroComponents.TextPlain {
id: walletCreationDateValue
property var locale: Qt.locale()
property date currentDate: new Date()
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 16
font.bold: true
textFormat: Text.RichText
color: MoneroComponents.Style.defaultFontColor
text: currentDate.toLocaleDateString(locale, Locale.ShortFormat)
}
}
ColumnLayout {
spacing: 5
Layout.fillWidth: true
MoneroComponents.TextPlain {
id: walletRestoreHeight
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 15
font.bold: false
textFormat: Text.RichText
color: MoneroComponents.Style.dimmedFontColor
text: qsTr("Restore height") + ":" + translationManager.emptyString
tooltip: wizardController.layoutScale != 4 ? qsTr("Enter this number when restoring the wallet to make your initial wallet synchronization faster.") : "" + translationManager.emptyString
tooltipIconVisible: true
themeTransition: false
Accessible.role: Accessible.StaticText
Accessible.name: qsTr("Restore height") + " " + Utils.roundDownToNearestThousand(wizardController.m_wallet ? wizardController.m_wallet.walletCreationHeight : 0) + translationManager.emptyString
KeyNavigation.up: walletCreationDate
KeyNavigation.backtab: walletCreationDate
Keys.onDownPressed: navigation.btnPrev.forceActiveFocus();
Keys.onTabPressed: navigation.btnPrev.forceActiveFocus();
}
MoneroComponents.TextPlain {
id: walletRestoreHeightValue
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 16
font.bold: true
textFormat: Text.RichText
color: MoneroComponents.Style.defaultFontColor
text: Utils.roundDownToNearestThousand(wizardController.m_wallet ? wizardController.m_wallet.walletCreationHeight : 0)
}
}
}
WizardNav {
id: navigation
progressSteps: appWindow.walletMode <= 1 ? 4 : 5
progress: 1
onPrevClicked: {
wizardStateView.state = "wizardCreateWallet1";
mobileDialog.visible = Qt.binding(function() { return wizardController.layoutScale == 4 })
}
btnPrevKeyNavigationBackTab: wizardCreateWallet2.state == "default" ? walletRestoreHeight
: seedListGridColumn.children[0].children[wizardCreateWallet2.hiddenWords[4]].lineEdit.visible ? seedListGridColumn.children[0].children[wizardCreateWallet2.hiddenWords[4]].lineEdit
: seedListGridColumn.children[0].children[24]
btnNextKeyNavigationTab: mobileDialog.visible ? mobileHeader : header
btnNext.enabled: walletCreationDate.opacity == 1 || appWindow.ctrlPressed ? true
: seedListGridColumn.children[0].children[hiddenWords[0]].icon.wordsMatch &&
seedListGridColumn.children[0].children[hiddenWords[1]].icon.wordsMatch &&
seedListGridColumn.children[0].children[hiddenWords[2]].icon.wordsMatch &&
seedListGridColumn.children[0].children[hiddenWords[3]].icon.wordsMatch &&
seedListGridColumn.children[0].children[hiddenWords[4]].icon.wordsMatch
onNextClicked: {
//choose five random words to hide
for (var i = 0; i < hiddenWords.length; i++) {
wizardCreateWallet2.hiddenWords[i] = Math.floor(Math.random() * 5) + 5 * i
}
wizardCreateWallet2.state = "verify";
for (var i = 0; i < hiddenWords.length; i++) {
seedListGridColumn.children[0].children[wizardCreateWallet2.hiddenWords[i]].wordText.visible = false;
}
seedListGridColumn.children[0].children[wizardCreateWallet2.hiddenWords[0]].lineEdit.forceActiveFocus();
}
}
}
}
function onPageCompleted(previousView){
wizardCreateWallet2.seedArray = wizardController.walletOptionsSeed.split(" ")
if (!wizardCreateWallet2.seedListGrid) {
var component = Qt.createComponent("SeedListGrid.qml");
wizardCreateWallet2.seedListGrid = component.createObject(seedListGridColumn);
}
}
}