import QtQuick import QtQuick.Layouts import QtQuick.Controls import QtQuick.Controls.Material import org.electrum 1.0 import "controls" Pane { id: rootItem objectName: 'WalletDetails' padding: 0 property bool _is2fa: Daemon.currentWallet && Daemon.currentWallet.walletType == '2fa' function enableLightning() { var dialog = app.messageDialog.createObject(rootItem, { title: qsTr('Enable Lightning for this wallet?'), yesno: true }) dialog.accepted.connect(function() { Daemon.currentWallet.enableLightning() }) dialog.open() } function importAddressesKeys() { var dialog = importAddressesKeysDialog.createObject(rootItem) dialog.open() } ColumnLayout { id: rootLayout anchors.fill: parent spacing: 0 Flickable { Layout.fillWidth: true Layout.fillHeight: true contentHeight: flickableRoot.height clip: true interactive: height < contentHeight Pane { id: flickableRoot width: parent.width padding: constants.paddingLarge ColumnLayout { width: parent.width spacing: constants.paddingLarge Heading { text: qsTr('Wallet details') } GridLayout { columns: 3 Layout.alignment: Qt.AlignHCenter Tag { Layout.alignment: Qt.AlignHCenter text: Daemon.currentWallet.walletType font.pixelSize: constants.fontSizeSmall font.bold: true iconSource: '../../../icons/wallet.png' } Tag { Layout.alignment: Qt.AlignHCenter text: Daemon.currentWallet.txinType font.pixelSize: constants.fontSizeSmall font.bold: true iconSource: '../../../icons/script_white.png' } Tag { Layout.alignment: Qt.AlignHCenter text: qsTr('HD') visible: Daemon.currentWallet.isDeterministic font.pixelSize: constants.fontSizeSmall font.bold: true iconSource: '../../../icons/hd_white.png' } Tag { Layout.alignment: Qt.AlignHCenter text: qsTr('Watch only') visible: Daemon.currentWallet.isWatchOnly font.pixelSize: constants.fontSizeSmall font.bold: true iconSource: '../../../icons/eye1.png' } Tag { Layout.alignment: Qt.AlignHCenter text: qsTr('Encrypted') visible: Daemon.currentWallet.isEncrypted font.pixelSize: constants.fontSizeSmall font.bold: true iconSource: '../../../icons/key.png' } Tag { Layout.alignment: Qt.AlignHCenter text: qsTr('HW') visible: Daemon.currentWallet.isHardware font.pixelSize: constants.fontSizeSmall font.bold: true iconSource: '../../../icons/seed.png' } Tag { Layout.alignment: Qt.AlignHCenter text: qsTr('Lightning') visible: Daemon.currentWallet.isLightning font.pixelSize: constants.fontSizeSmall font.bold: true iconSource: '../../../icons/lightning.png' } Tag { Layout.alignment: Qt.AlignHCenter text: qsTr('Seed') visible: Daemon.currentWallet.hasSeed font.pixelSize: constants.fontSizeSmall font.bold: true iconSource: '../../../icons/seed.png' } } GridLayout { Layout.preferredWidth: parent.width visible: Daemon.currentWallet columns: 2 Label { Layout.columnSpan: 2 Layout.topMargin: constants.paddingSmall text: qsTr('Name') color: Material.accentColor } TextHighlightPane { id: walletNameContent property bool editmode: false Layout.columnSpan: 2 Layout.fillWidth: true RowLayout { width: parent.width Label { visible: !walletNameContent.editmode text: Daemon.currentWallet.name wrapMode: Text.Wrap Layout.fillWidth: true font.pixelSize: constants.fontSizeLarge } ToolButton { visible: !walletNameContent.editmode icon.source: '../../icons/pen.png' icon.color: 'transparent' onClicked: { walletNameEdit.text = Daemon.currentWallet.name walletNameContent.editmode = true walletNameEdit.focus = true } } TextField { id: walletNameEdit visible: walletNameContent.editmode Layout.fillWidth: true font.pixelSize: constants.fontSizeLarge } ToolButton { visible: walletNameContent.editmode icon.source: '../../icons/confirmed.png' icon.color: enabled ? 'transparent' : constants.mutedForeground enabled: walletNameEdit.text !== Daemon.currentWallet.name && Daemon.isValidWalletName(walletNameEdit.text) onClicked: { walletNameContent.editmode = false Daemon.renameWallet(walletNameEdit.text) } } ToolButton { visible: walletNameContent.editmode icon.source: '../../icons/closebutton.png' icon.color: 'transparent' onClicked: walletNameContent.editmode = false } } } Label { Layout.columnSpan: 2 Layout.topMargin: constants.paddingSmall visible: Daemon.currentWallet.hasSeed text: qsTr('Seed') color: Material.accentColor } TextHighlightPane { Layout.columnSpan: 2 Layout.fillWidth: true visible: Daemon.currentWallet.hasSeed RowLayout { width: parent.width Label { id: seedText visible: false Layout.fillWidth: true text: Daemon.currentWallet.seed wrapMode: Text.Wrap font.family: FixedFont font.pixelSize: constants.fontSizeMedium } Label { id: showSeedText Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter text: qsTr('Tap to show seed') wrapMode: Text.Wrap font.pixelSize: constants.fontSizeLarge } MouseArea { anchors.fill: parent onClicked: { if (showSeedText.visible) { Daemon.currentWallet.requestShowSeed() } else { seedText.visible = false showSeedText.visible = true } } } } } Label { id: seed_extension_label Layout.columnSpan: 2 Layout.topMargin: constants.paddingSmall visible: seedText.visible && Daemon.currentWallet.seedPassphrase text: qsTr('Seed Extension') color: Material.accentColor } TextHighlightPane { Layout.columnSpan: 2 Layout.fillWidth: true visible: seed_extension_label.visible Label { width: parent.width text: Daemon.currentWallet.seedPassphrase wrapMode: Text.Wrap font.family: FixedFont font.pixelSize: constants.fontSizeMedium } } Label { Layout.columnSpan: 2 Layout.topMargin: constants.paddingSmall visible: Daemon.currentWallet.isLightning text: qsTr('Lightning Node ID') color: Material.accentColor } TextHighlightPane { Layout.columnSpan: 2 Layout.fillWidth: true visible: Daemon.currentWallet.isLightning RowLayout { width: parent.width Label { Layout.fillWidth: true text: Daemon.currentWallet.lightningNodePubkey wrapMode: Text.Wrap font.family: FixedFont font.pixelSize: constants.fontSizeMedium } ToolButton { icon.source: '../../icons/share.png' icon.color: 'transparent' onClicked: { var dialog = app.genericShareDialog.createObject(rootItem, { title: qsTr('Lightning Node ID'), text: Daemon.currentWallet.lightningNodePubkey }) dialog.open() } } } } Label { visible: _is2fa text: qsTr('2FA') color: Material.accentColor } Label { Layout.fillWidth: true visible: _is2fa text: Daemon.currentWallet.canSignWithoutServer ? qsTr('disabled (can sign without server)') : qsTr('enabled') } Label { visible: _is2fa && !Daemon.currentWallet.canSignWithoutServer text: qsTr('Remaining TX') color: Material.accentColor } Label { Layout.fillWidth: true visible: _is2fa && !Daemon.currentWallet.canSignWithoutServer text: 'tx_remaining' in Daemon.currentWallet.billingInfo ? Daemon.currentWallet.billingInfo['tx_remaining'] : qsTr('unknown') } Label { Layout.columnSpan: 2 Layout.topMargin: constants.paddingSmall visible: _is2fa && !Daemon.currentWallet.canSignWithoutServer text: qsTr('Billing') color: Material.accentColor } TextHighlightPane { Layout.columnSpan: 2 Layout.fillWidth: true visible: _is2fa && !Daemon.currentWallet.canSignWithoutServer ColumnLayout { spacing: 0 ButtonGroup { id: billinggroup onCheckedButtonChanged: { Config.trustedcoinPrepay = checkedButton.value } } Repeater { model: AppController.plugin('trustedcoin').billingModel delegate: RowLayout { RadioButton { ButtonGroup.group: billinggroup property string value: modelData.value text: modelData.text checked: modelData.value == Config.trustedcoinPrepay } Label { text: Config.formatSats(modelData.sats_per_tx) font.family: FixedFont } Label { text: Config.baseUnit + '/tx' color: Material.accentColor } } } } } Repeater { id: keystores model: Daemon.currentWallet.keystores delegate: ColumnLayout { Layout.columnSpan: 2 Layout.topMargin: constants.paddingSmall RowLayout { Label { text: qsTr('Keystore') color: Material.accentColor } Label { text: '#' + index visible: keystores.count > 1 } Image { Layout.preferredWidth: constants.iconSizeXSmall Layout.preferredHeight: constants.iconSizeXSmall source: modelData.watch_only ? '../../icons/eye1.png' : '../../icons/key.png' } } TextHighlightPane { Layout.fillWidth: true leftPadding: constants.paddingLarge GridLayout { width: parent.width columns: 2 Label { text: qsTr('Keystore type') visible: modelData.keystore_type color: Material.accentColor } Label { Layout.fillWidth: true text: modelData.keystore_type visible: modelData.keystore_type } Label { text: modelData.watch_only ? qsTr('Imported addresses') : qsTr('Imported keys') visible: modelData.num_imported color: Material.accentColor } Label { Layout.fillWidth: true text: modelData.num_imported visible: modelData.num_imported } Label { text: qsTr('Derivation prefix') visible: modelData.derivation_prefix color: Material.accentColor } Label { Layout.fillWidth: true text: modelData.derivation_prefix visible: modelData.derivation_prefix font.family: FixedFont } Label { text: qsTr('BIP32 fingerprint') visible: modelData.fingerprint color: Material.accentColor } Label { Layout.fillWidth: true text: modelData.fingerprint visible: modelData.fingerprint font.family: FixedFont } Label { Layout.columnSpan: 2 visible: modelData.master_pubkey text: qsTr('Master Public Key') color: Material.accentColor } RowLayout { Layout.fillWidth: true Layout.columnSpan: 2 Layout.leftMargin: constants.paddingLarge visible: modelData.master_pubkey Label { text: modelData.master_pubkey wrapMode: Text.Wrap Layout.fillWidth: true font.family: FixedFont font.pixelSize: constants.fontSizeMedium } ToolButton { icon.source: '../../icons/share.png' icon.color: 'transparent' onClicked: { var dialog = app.genericShareDialog.createObject(rootItem, { title: qsTr('Master Public Key'), text: modelData.master_pubkey }) dialog.open() } } } } } } } } } } } ButtonContainer { Layout.fillWidth: true FlatButton { Layout.fillWidth: true Layout.preferredWidth: 1 text: qsTr('Delete Wallet...') onClicked: Daemon.checkThenDeleteWallet(Daemon.currentWallet) icon.source: '../../icons/delete.png' } FlatButton { Layout.fillWidth: true Layout.preferredWidth: 1 text: qsTr('Change Password') onClicked: Daemon.startChangePassword() icon.source: '../../icons/lock.png' } FlatButton { Layout.fillWidth: true Layout.preferredWidth: 1 visible: Daemon.currentWallet.walletType == 'imported' text: Daemon.currentWallet.isWatchOnly ? qsTr('Add addresses') : qsTr('Add keys') icon.source: '../../icons/add.png' onClicked: rootItem.importAddressesKeys() } FlatButton { Layout.fillWidth: true Layout.preferredWidth: 1 text: qsTr('Enable Lightning') onClicked: rootItem.enableLightning() visible: Daemon.currentWallet && Daemon.currentWallet.canHaveLightning && !Daemon.currentWallet.isLightning icon.source: '../../icons/lightning.png' } } } Connections { target: Daemon function onWalletLoaded() { Daemon.availableWallets.reload() app.stack.pop() } function onRequestNewPassword() { // new unified password (all wallets) var dialog = app.passwordDialog.createObject(app, { confirmPassword: true, title: qsTr('Enter new password'), infotext: qsTr('If you forget your password, you\'ll need to restore from seed. Please make sure you have your seed stored safely') }) dialog.passwordEntered.connect(function(password) { dialog.close() var success = Daemon.setPassword(password) if (success && Biometrics.isEnabled) { if (Biometrics.isAvailable) { // also update the biometric authentication Biometrics.enable(password) } else { // disable biometric authentication as it is not available Biometrics.disable() } } var done_dialog = app.messageDialog.createObject(app, { title: success ? qsTr('Success') : qsTr('Error'), iconSource: success ? Qt.resolvedUrl('../../icons/info.png') : Qt.resolvedUrl('../../icons/warning.png'), text: success ? qsTr('Password changed') : qsTr('Password change failed') }) done_dialog.open() }) dialog.open() } function onWalletDeleteError(code, message) { if (code == 'unpaid_requests') { var dialog = app.messageDialog.createObject(app, { title: qsTr('Warning'), text: message, yesno: true }) dialog.accepted.connect(function() { Daemon.checkThenDeleteWallet(Daemon.currentWallet, true) }) dialog.open() } else if (code == 'balance') { var dialog = app.messageDialog.createObject(app, { title: qsTr('Warning'), text: message, yesno: true }) dialog.accepted.connect(function() { Daemon.checkThenDeleteWallet(Daemon.currentWallet, true, true) }) dialog.open() } else { var dialog = app.messageDialog.createObject(app, { title: qsTr('Error'), iconSource: Qt.resolvedUrl('../../icons/warning.png'), text: message }) dialog.open() } } function onWalletRenameError(message) { var dialog = app.messageDialog.createObject(app, { title: qsTr('Error'), iconSource: Qt.resolvedUrl('../../icons/warning.png'), text: message }) dialog.open() } } Connections { target: Daemon.currentWallet function onRequestNewPassword() { // new wallet password var dialog = app.passwordDialog.createObject(app, { confirmPassword: true, title: qsTr('Enter new password'), infotext: qsTr('If you forget your password, you\'ll need to restore from seed. Please make sure you have your seed stored safely') + (Daemon.availableWallets.rowCount() > 1 && Config.walletShouldUseSinglePassword ? "\n\n" + qsTr('The new password needs to match the password of any other existing wallet.') : "") }) dialog.passwordEntered.connect(function(password) { if (Config.walletShouldUseSinglePassword // android && Daemon.availableWallets.rowCount() > 1 // has more than one wallet && Daemon.numWalletsWithPassword(password) < 1 // no other wallet uses this new password ) { dialog.errorMessage = [ qsTr('You need to use the password of any other existing wallet.'), qsTr('Using different wallet passwords is not supported.'), ].join("\n") dialog.clearPassword() return } else { var success = Daemon.currentWallet.setPassword(password) if (success && Config.walletShouldUseSinglePassword) { Daemon.singlePassword = password } var error_msg = qsTr('Password change failed') } dialog.close() if (success && Biometrics.isEnabled) { // unlikely to happen as this means the user somehow moved from // a unified password to differing passwords Biometrics.disable() } var done_dialog = app.messageDialog.createObject(app, { title: success ? qsTr('Success') : qsTr('Error'), iconSource: success ? Qt.resolvedUrl('../../icons/info.png') : Qt.resolvedUrl('../../icons/warning.png'), text: success ? qsTr('Password changed') : error_msg }) done_dialog.open() }) dialog.open() } function onSeedRetrieved() { seedText.visible = true showSeedText.visible = false } } Connections { target: Biometrics function onEnablingFailed(error) { if (error === 'CANCELLED') { var biometrics_disabled_dialog = app.messageDialog.createObject(app, { title: qsTr('Biometric Authentication'), iconSource: Qt.resolvedUrl('../../icons/warning.png'), text: qsTr('Biometric authentication disabled. You can enable it again in the settings.') }) biometrics_disabled_dialog.open() return } var err = app.messageDialog.createObject(app, { text: qsTr('Failed to update biometric authentication to new password: ') + error }) err.open() } } Component { id: importAddressesKeysDialog ImportAddressesKeysDialog { width: parent.width height: parent.height onClosed: destroy() } } Binding { target: AppController property: 'secureWindow' value: seedText.visible } }