mirror of
https://github.com/logos-blockchain/logos-execution-zone-wallet-ui.git
synced 2026-07-29 22:23:30 +00:00
wip
This commit is contained in:
parent
97bae8ad1f
commit
753a92fb81
@ -504,6 +504,10 @@ QString LEZWalletBackend::createNew(QString configPath, QString storagePath, QSt
|
||||
const QString localConfigPath = toLocalPath(configPath);
|
||||
const QString localStoragePath = toLocalPath(storagePath);
|
||||
|
||||
// Clear any mnemonic left over from a previous attempt in this session —
|
||||
// only a freshly generated one (below) should ever be shown to the user.
|
||||
setLastCreatedMnemonic(QString());
|
||||
|
||||
// Both files already on disk: this is most likely an existing wallet the
|
||||
// user pointed us at (e.g. from the setup screen), not a request to
|
||||
// overwrite it. Try to load it instead of blindly creating a new one.
|
||||
@ -524,11 +528,73 @@ QString LEZWalletBackend::createNew(QString configPath, QString storagePath, QSt
|
||||
if (!sequencerAddr.isEmpty())
|
||||
applySequencerAddrToConfig(localConfigPath, sequencerAddr);
|
||||
|
||||
// create_new() writes storage.json directly to this path — unlike the
|
||||
// config path (handled inside applySequencerAddrToConfig above), nothing
|
||||
// else ensures its parent directory exists, so wallet_ffi_create_new()
|
||||
// silently fails whenever storagePath points into a not-yet-created folder.
|
||||
QDir().mkpath(QFileInfo(localStoragePath).absolutePath());
|
||||
|
||||
const QString mnemonic = m_logos->lez_core.create_new(
|
||||
localConfigPath, localStoragePath, password);
|
||||
if (mnemonic.isEmpty())
|
||||
return QStringLiteral("Failed to create wallet. Check paths and try again.");
|
||||
|
||||
// Surfaced to the QML layer via the lastCreatedMnemonic PROP so the setup
|
||||
// screen can show a one-time "save your recovery phrase" prompt; cleared
|
||||
// once the user acknowledges it (see clearLastCreatedMnemonic()).
|
||||
setLastCreatedMnemonic(mnemonic);
|
||||
|
||||
persistConfigPath(localConfigPath);
|
||||
persistStoragePath(localStoragePath);
|
||||
finishOpeningWallet();
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString LEZWalletBackend::restoreFromMnemonic(QString configPath, QString storagePath, QString mnemonic, QString password, QString sequencerAddr)
|
||||
{
|
||||
// Depth of the key tree to reconstruct per keychain (public/private) when
|
||||
// re-deriving from the mnemonic. NOT a linear BIP44-style gap limit — the
|
||||
// wallet FFI builds an actual tree of 2^depth candidate keys at this depth
|
||||
// and docs explicitly warn it "induces exponential growth in execution
|
||||
// time". Keep this small; anything much bigger can take a very long time
|
||||
// (or effectively hang) before accounts are found and the wallet opens.
|
||||
constexpr int RESTORE_SCAN_DEPTH = 5;
|
||||
|
||||
const QString localConfigPath = toLocalPath(configPath);
|
||||
const QString localStoragePath = toLocalPath(storagePath);
|
||||
|
||||
// The user already has this mnemonic (they just typed it in), so there's
|
||||
// nothing new to show — and any leftover from a prior createNew() in this
|
||||
// session shouldn't linger either.
|
||||
setLastCreatedMnemonic(QString());
|
||||
|
||||
if (QFile::exists(localConfigPath) || QFile::exists(localStoragePath)) {
|
||||
return QStringLiteral(
|
||||
"A wallet already exists at the selected paths. Choose paths "
|
||||
"that don't exist yet to restore a wallet there.");
|
||||
}
|
||||
|
||||
if (!sequencerAddr.isEmpty())
|
||||
applySequencerAddrToConfig(localConfigPath, sequencerAddr);
|
||||
|
||||
// Same not-yet-created-folder gap as createNew() above — make sure the
|
||||
// storage path's parent directory exists before create_new() tries to
|
||||
// write storage.json there.
|
||||
QDir().mkpath(QFileInfo(localStoragePath).absolutePath());
|
||||
|
||||
// restore_storage() needs an already-open wallet handle to restore into,
|
||||
// so first create fresh (empty) storage at the chosen paths, then
|
||||
// overwrite its key material by re-deriving from the given mnemonic.
|
||||
const QString created = m_logos->lez_core.create_new(
|
||||
localConfigPath, localStoragePath, password);
|
||||
if (created.isEmpty())
|
||||
return QStringLiteral("Failed to initialize wallet storage. Check paths and try again.");
|
||||
|
||||
const int err = m_logos->lez_core.restore_storage(
|
||||
mnemonic.trimmed(), password, QVariant(RESTORE_SCAN_DEPTH));
|
||||
if (err != WALLET_FFI_SUCCESS)
|
||||
return QStringLiteral("Failed to restore wallet. Check the recovery phrase and try again.");
|
||||
|
||||
persistConfigPath(localConfigPath);
|
||||
persistStoragePath(localStoragePath);
|
||||
finishOpeningWallet();
|
||||
@ -541,6 +607,11 @@ void LEZWalletBackend::copyToClipboard(QString text)
|
||||
QGuiApplication::clipboard()->setText(text);
|
||||
}
|
||||
|
||||
void LEZWalletBackend::clearLastCreatedMnemonic()
|
||||
{
|
||||
setLastCreatedMnemonic(QString());
|
||||
}
|
||||
|
||||
bool LEZWalletBackend::checkLabelAvailable(QString label)
|
||||
{
|
||||
return m_logos->lez_core.check_label_available(label.trimmed());
|
||||
|
||||
@ -53,7 +53,9 @@ public slots:
|
||||
void refreshVaultBalances() override;
|
||||
QString vaultClaim(QString fromHex, bool isPublic, QString amountStr) override;
|
||||
QString createNew(QString configPath, QString storagePath, QString password, QString sequencerAddr) override;
|
||||
QString restoreFromMnemonic(QString configPath, QString storagePath, QString mnemonic, QString password, QString sequencerAddr) override;
|
||||
void copyToClipboard(QString text) override;
|
||||
void clearLastCreatedMnemonic() override;
|
||||
bool checkLabelAvailable(QString label) override;
|
||||
QString addLabel(QString label, QString accountIdHex, bool isPublic) override;
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ class LEZWalletBackend
|
||||
PROP(int lastSyncedBlock READONLY)
|
||||
PROP(int currentBlockHeight READONLY)
|
||||
PROP(QString sequencerAddr READONLY)
|
||||
PROP(QString lastCreatedMnemonic READONLY)
|
||||
|
||||
SLOT(QString createAccountPublic())
|
||||
SLOT(QString createAccountPrivate())
|
||||
@ -30,8 +31,10 @@ class LEZWalletBackend
|
||||
SLOT(QString vaultClaim(QString fromHex, bool isPublic, QString amountStr))
|
||||
|
||||
SLOT(QString createNew(QString configPath, QString storagePath, QString password, QString sequencerAddr))
|
||||
SLOT(QString restoreFromMnemonic(QString configPath, QString storagePath, QString mnemonic, QString password, QString sequencerAddr))
|
||||
|
||||
SLOT(void copyToClipboard(QString text))
|
||||
SLOT(void clearLastCreatedMnemonic())
|
||||
|
||||
SLOT(bool checkLabelAvailable(QString label))
|
||||
SLOT(QString addLabel(QString label, QString accountIdHex, bool isPublic))
|
||||
|
||||
@ -112,6 +112,27 @@ Rectangle {
|
||||
visible: false
|
||||
}
|
||||
|
||||
MnemonicRevealDialog {
|
||||
id: mnemonicDialog
|
||||
|
||||
onCopyRequested: (text) => {
|
||||
clipHelper.text = text
|
||||
clipHelper.selectAll()
|
||||
clipHelper.copy()
|
||||
}
|
||||
onAcknowledged: if (backend) backend.clearLastCreatedMnemonic()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: backend
|
||||
function onLastCreatedMnemonicChanged() {
|
||||
if (backend.lastCreatedMnemonic.length > 0) {
|
||||
mnemonicDialog.mnemonic = backend.lastCreatedMnemonic
|
||||
mnemonicDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetLabelDialog {
|
||||
id: setLabelDialog
|
||||
|
||||
@ -186,6 +207,20 @@ Rectangle {
|
||||
}
|
||||
)
|
||||
}
|
||||
onRecoverWallet: function(configPath, storagePath, mnemonic, password, sequencerUrl) {
|
||||
if (!backend) return
|
||||
// restoreFromMnemonic() returns an empty string on success, or a
|
||||
// human-readable error message otherwise.
|
||||
logos.watch(backend.restoreFromMnemonic(configPath, storagePath, mnemonic, password, sequencerUrl),
|
||||
function(errorMessage) {
|
||||
if (errorMessage)
|
||||
createError = errorMessage
|
||||
},
|
||||
function(error) {
|
||||
createError = qsTr("Error restoring wallet: %1").arg(error)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
94
src/qml/popups/MnemonicRevealDialog.qml
Normal file
94
src/qml/popups/MnemonicRevealDialog.qml
Normal file
@ -0,0 +1,94 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Logos.Theme
|
||||
import Logos.Controls
|
||||
|
||||
import "../controls"
|
||||
|
||||
// Shown right after a new wallet is created, so the user can reveal, copy,
|
||||
// and write down their recovery phrase before it's cleared from memory.
|
||||
// Hidden by default — the caller drives `mnemonic` and listens for
|
||||
// `acknowledged()` to clear its backend-side copy once the user is done.
|
||||
Popup {
|
||||
id: root
|
||||
|
||||
property string mnemonic: ""
|
||||
property bool revealed: false
|
||||
|
||||
signal copyRequested(string text)
|
||||
signal acknowledged()
|
||||
|
||||
modal: true
|
||||
dim: true
|
||||
padding: Theme.spacing.large
|
||||
// No dismissal via Escape/outside click — the user must explicitly
|
||||
// confirm they've saved the phrase via the Continue button below.
|
||||
closePolicy: Popup.NoAutoClose
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
onOpened: root.revealed = false
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.palette.backgroundSecondary
|
||||
radius: Theme.spacing.radiusXlarge
|
||||
border.color: Theme.palette.backgroundElevated
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
width: 360
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Save your recovery phrase")
|
||||
font.pixelSize: Theme.typography.titleText
|
||||
font.weight: Theme.typography.weightBold
|
||||
color: Theme.palette.text
|
||||
}
|
||||
LogosText {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Write these words down in order and store them somewhere safe. Anyone with this phrase can access your funds, and it cannot be recovered if lost.")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
LogosTextArea {
|
||||
id: mnemonicText
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 90
|
||||
readOnly: true
|
||||
text: root.revealed ? root.mnemonic : "•••• ".repeat(6)
|
||||
}
|
||||
|
||||
LogosCopyButton {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
onCopyText: root.copyRequested(root.mnemonic)
|
||||
}
|
||||
}
|
||||
|
||||
FeedbackButton {
|
||||
text: root.revealed ? qsTr("Hide phrase") : qsTr("Show phrase")
|
||||
onClicked: root.revealed = !root.revealed
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.topMargin: Theme.spacing.medium
|
||||
Layout.fillWidth: true
|
||||
Item { Layout.fillWidth: true }
|
||||
FeedbackButton {
|
||||
text: qsTr("Continue")
|
||||
onClicked: {
|
||||
root.acknowledged()
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,12 +14,20 @@ Control {
|
||||
property string configPath: ""
|
||||
property string storePath: ""
|
||||
property string createError: ""
|
||||
property bool recoverMode: false
|
||||
// Create/restore is a blocking round-trip to the wallet backend (it can take
|
||||
// a while, especially restoring), which freezes the UI thread for its
|
||||
// duration — this at least shows a message right before that happens
|
||||
// instead of the form just going inert with no explanation.
|
||||
property bool busy: false
|
||||
|
||||
signal createWallet(string configPath, string storagePath, string password, string sequencerUrl)
|
||||
signal recoverWallet(string configPath, string storagePath, string mnemonic, string password, string sequencerUrl)
|
||||
|
||||
readonly property string testnetUrl: "https://testnet.lez.logos.co"
|
||||
readonly property string localhostUrl: "http://127.0.0.1:3040"
|
||||
|
||||
onCreateErrorChanged: if (createError.length > 0) root.busy = false
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
@ -33,142 +41,217 @@ Control {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: cardColumn
|
||||
// Fires the actual (blocking) request one tick after the button click, so
|
||||
// the "Restoring…"/"Creating…" busy state above has a chance to actually
|
||||
// render before the UI thread freezes for the call.
|
||||
Timer {
|
||||
id: submitTimer
|
||||
interval: 50
|
||||
onTriggered: {
|
||||
if (root.recoverMode) {
|
||||
root.recoverWallet(configPathField.text, storagePathField.text, mnemonicField.text.trim(), passwordField.text, sequencerUrlField.text)
|
||||
} else {
|
||||
root.createWallet(configPathField.text, storagePathField.text, passwordField.text, sequencerUrlField.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scrollable so the form stays reachable (including the submit button)
|
||||
// when its content is taller than the window, e.g. with the recovery
|
||||
// phrase field shown — rather than silently clipping/overflowing.
|
||||
LogosScrollView {
|
||||
id: scrollView
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacing.xlarge
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Set up LEZ Wallet")
|
||||
font.pixelSize: Theme.typography.titleText
|
||||
font.weight: Theme.typography.weightBold
|
||||
color: Theme.palette.text
|
||||
}
|
||||
LogosText {
|
||||
text: qsTr("Configure storage and secure with a password.")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
Layout.topMargin: -Theme.spacing.small
|
||||
}
|
||||
Item {
|
||||
width: scrollView.availableWidth
|
||||
implicitHeight: cardColumn.implicitHeight + 2 * Theme.spacing.xlarge
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Storage")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
color: Theme.palette.text
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.spacing.small
|
||||
LogosTextField {
|
||||
id: storagePathField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Add store path")
|
||||
text: root.storePath
|
||||
}
|
||||
FeedbackButton {
|
||||
text: qsTr("Browse")
|
||||
onClicked: storageFolderDialog.open()
|
||||
}
|
||||
}
|
||||
LogosText {
|
||||
text: qsTr("Config file")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
color: Theme.palette.text
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.spacing.small
|
||||
LogosTextField {
|
||||
id: configPathField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Add path to config")
|
||||
text: root.configPath
|
||||
}
|
||||
FeedbackButton {
|
||||
Layout.preferredHeight: configPathField.height
|
||||
text: qsTr("Browse")
|
||||
onClicked: configFileDialog.open()
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
id: cardColumn
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Theme.spacing.medium
|
||||
spacing: Theme.spacing.small
|
||||
LogosText {
|
||||
text: qsTr("Network")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
color: Theme.palette.text
|
||||
}
|
||||
LogosTextField {
|
||||
id: sequencerUrlField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Sequencer URL")
|
||||
text: root.testnetUrl
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.spacing.small
|
||||
FeedbackButton {
|
||||
text: qsTr("Testnet")
|
||||
opacity: sequencerUrlField.text === root.testnetUrl ? 1.0 : 0.4
|
||||
onClicked: sequencerUrlField.text = root.testnetUrl
|
||||
}
|
||||
FeedbackButton {
|
||||
text: qsTr("Localhost")
|
||||
opacity: sequencerUrlField.text === root.localhostUrl ? 1.0 : 0.4
|
||||
onClicked: sequencerUrlField.text = root.localhostUrl
|
||||
}
|
||||
}
|
||||
x: Theme.spacing.xlarge
|
||||
y: Theme.spacing.xlarge
|
||||
width: parent.width - 2 * Theme.spacing.xlarge
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Security")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
color: Theme.palette.text
|
||||
Layout.topMargin: Theme.spacing.medium
|
||||
}
|
||||
LogosTextField {
|
||||
id: passwordField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Password")
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
LogosTextField {
|
||||
id: confirmField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Confirm")
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
LogosText {
|
||||
text: qsTr("Set up LEZ Wallet")
|
||||
font.pixelSize: Theme.typography.titleText
|
||||
font.weight: Theme.typography.weightBold
|
||||
color: Theme.palette.text
|
||||
}
|
||||
LogosText {
|
||||
text: qsTr("Configure storage and secure with a password.")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
Layout.topMargin: -Theme.spacing.small
|
||||
}
|
||||
|
||||
LogosText {
|
||||
id: errorLabel
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.error
|
||||
wrapMode: Text.WordWrap
|
||||
visible: text.length > 0
|
||||
text: root.createError
|
||||
}
|
||||
LogosTabBar {
|
||||
id: modeTabBar
|
||||
Layout.fillWidth: true
|
||||
currentIndex: root.recoverMode ? 1 : 0
|
||||
onCurrentIndexChanged: {
|
||||
root.recoverMode = currentIndex === 1
|
||||
root.createError = ""
|
||||
}
|
||||
|
||||
FeedbackButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: qsTr("Create Wallet")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
onClicked: {
|
||||
if (passwordField.text.length === 0) {
|
||||
root.createError = qsTr("Password cannot be empty.")
|
||||
} else if (passwordField.text !== confirmField.text) {
|
||||
root.createError = qsTr("Passwords do not match.")
|
||||
} else {
|
||||
root.createError = ""
|
||||
root.createWallet(configPathField.text, storagePathField.text, passwordField.text, sequencerUrlField.text)
|
||||
LogosTabButton { text: qsTr("Create New") }
|
||||
LogosTabButton { text: qsTr("Recover from Mnemonic") }
|
||||
}
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Storage")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
color: Theme.palette.text
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.spacing.small
|
||||
LogosTextField {
|
||||
id: storagePathField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Add store path")
|
||||
text: root.storePath
|
||||
}
|
||||
FeedbackButton {
|
||||
text: qsTr("Browse")
|
||||
onClicked: storageFolderDialog.open()
|
||||
}
|
||||
}
|
||||
LogosText {
|
||||
text: qsTr("Config file")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
color: Theme.palette.text
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.spacing.small
|
||||
LogosTextField {
|
||||
id: configPathField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Add path to config")
|
||||
text: root.configPath
|
||||
}
|
||||
FeedbackButton {
|
||||
Layout.preferredHeight: configPathField.height
|
||||
text: qsTr("Browse")
|
||||
onClicked: configFileDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Theme.spacing.medium
|
||||
spacing: Theme.spacing.small
|
||||
LogosText {
|
||||
text: qsTr("Network")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
color: Theme.palette.text
|
||||
}
|
||||
LogosTextField {
|
||||
id: sequencerUrlField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Sequencer URL")
|
||||
text: root.testnetUrl
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.spacing.small
|
||||
FeedbackButton {
|
||||
text: qsTr("Testnet")
|
||||
opacity: sequencerUrlField.text === root.testnetUrl ? 1.0 : 0.4
|
||||
onClicked: sequencerUrlField.text = root.testnetUrl
|
||||
}
|
||||
FeedbackButton {
|
||||
text: qsTr("Localhost")
|
||||
opacity: sequencerUrlField.text === root.localhostUrl ? 1.0 : 0.4
|
||||
onClicked: sequencerUrlField.text = root.localhostUrl
|
||||
}
|
||||
}
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Recovery phrase")
|
||||
visible: root.recoverMode
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
color: Theme.palette.text
|
||||
Layout.topMargin: Theme.spacing.medium
|
||||
}
|
||||
LogosTextArea {
|
||||
id: mnemonicField
|
||||
visible: root.recoverMode
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 90
|
||||
placeholderText: qsTr("Enter your 12 or 24-word recovery phrase")
|
||||
}
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Security")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
color: Theme.palette.text
|
||||
Layout.topMargin: Theme.spacing.medium
|
||||
}
|
||||
LogosTextField {
|
||||
id: passwordField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Password")
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
LogosTextField {
|
||||
id: confirmField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Confirm")
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
|
||||
LogosText {
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
visible: root.busy
|
||||
text: root.recoverMode
|
||||
? qsTr("Restoring your wallet from the recovery phrase… this can take a little while. Please don't close the app.")
|
||||
: qsTr("Creating your wallet…")
|
||||
}
|
||||
|
||||
LogosText {
|
||||
id: errorLabel
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.error
|
||||
wrapMode: Text.WordWrap
|
||||
visible: !root.busy && text.length > 0
|
||||
text: root.createError
|
||||
}
|
||||
|
||||
FeedbackButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
enabled: !root.busy
|
||||
text: root.busy
|
||||
? (root.recoverMode ? qsTr("Restoring…") : qsTr("Creating…"))
|
||||
: (root.recoverMode ? qsTr("Restore Wallet") : qsTr("Create Wallet"))
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
onClicked: {
|
||||
if (passwordField.text.length === 0) {
|
||||
root.createError = qsTr("Password cannot be empty.")
|
||||
} else if (passwordField.text !== confirmField.text) {
|
||||
root.createError = qsTr("Passwords do not match.")
|
||||
} else if (root.recoverMode && mnemonicField.text.trim().length === 0) {
|
||||
root.createError = qsTr("Recovery phrase cannot be empty.")
|
||||
} else {
|
||||
root.createError = ""
|
||||
root.busy = true
|
||||
submitTimer.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user