Files
pay/guis/desktop/AccountDetails.qml
T
tomFlowee 0ef4e8ed4f Use our own Popup style
The QML design of styling is that one extends the "Template" version
of a component. With the Basic, the Fusion etc already provided.
But after upgrading to Qt6.9 that styling for Popup changed and broke
the look of Flowee Pay.

So, we no longer use the Basic styled class, but the bare one which
should never change.
This additionally simplifies a lot of the workarounds surrounding insets
and other stuff that existed in the Basic.Popup, and we just don't set
in our own "style" of Popup.
Beautiful result, lots of complexity removed from AccountDetails,
TransactionListItem and very much from PopupOverlay
2025-06-19 15:09:36 +02:00

430 lines
16 KiB
QML

/*
* This file is part of the Flowee project
* Copyright (C) 2021-2025 Tom Zander <tom@flowee.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Controls.Basic as QQC2
import QtQuick.Layouts
import "../Flowee" as Flowee
Item {
id: root
property QtObject account: portfolio.current
focus: true
Connections {
target: portfolio
function onCurrentChanged() {
// The model in this case follows the UI, so we need to have this not very
// declarative code.
// When a new model is offered, copy the UI settings into it.
if (root.account.isDecrypted) {
root.account.secrets.showChangeChain = changeAddresses.checked
root.account.secrets.showUsedAddresses = usedAddresses.checked
}
}
}
Component.onCompleted: {
if (root.account.isDecrypted) {
root.account.secrets.showChangeChain = changeAddresses.checked
root.account.secrets.showUsedAddresses = usedAddresses.checked
}
}
Item {
// non-layoutable items.
Flowee.Popup {
id: qrPopup
width: 270
height: 270
x: (root.width - width) / 2
y: 100
modal: true
closePolicy: QQC2.Popup.CloseOnEscape | QQC2.Popup.CloseOnPressOutsideParent
background: Rectangle {
color: palette.light
border.color: palette.midlight
border.width: 1
radius: 5
}
Flowee.QRWidget {
id: seedQr
qrSize: 250
textVisible: false
useRawString: true
anchors.centerIn: parent
}
}
}
Flowee.Label {
id: walletDetailsLabel
text: qsTr("Wallet Details")
font.pointSize: 18
color: "white"
anchors.horizontalCenter: parent.horizontalCenter
}
Flowee.CloseIcon {
id: closeIcon
anchors.bottom: walletDetailsLabel.bottom
anchors.margins: 6
anchors.right: parent.right
onClicked: accountOverlay.state = "showTransactions"
color: "#bbbbbb"
}
GridLayout {
id: basicProperties
anchors.top: walletDetailsLabel.bottom
anchors.topMargin: 20
x: 20
width: parent.width - 30
columns: 2
rowSpacing: 10
Flowee.Label {
text: qsTr("Name") + ":"
}
Flowee.TextField {
id: accountNameEdit
text: root.account.name
onTextEdited: root.account.name = text
Layout.fillWidth: true
focus: true
}
}
Flickable {
id: scrollablePage
anchors.top: basicProperties.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 10
clip: true
contentHeight: detailsPane.height + 10 + addressesList.height + 10 + hdDetails.height + 10
GridLayout {
id: detailsPane
width: parent.width - 20
x: 10
columns: 2
Flowee.Label {
id: syncLabel
Layout.columnSpan: 2
property string time: ""
text: {
var height = root.account.lastBlockSynched
if (height < 1)
return ""
var time = "";
if (syncLabel.time !== "")
time = " (" + syncLabel.time + ")";
return qsTr("Sync Status") + ": " + height + " / " + Pay.chainHeight + time;
}
Connections {
target: root.account;
function onLastBlockSynchedChanged() {
if (timeTimer.interval === 30000) {
// if it just changed, fetch the new time shortly thereafter.
// this makes us show the latest time much more often when doing a sync.
timeTimer.stop();
timeTimer.interval = 500;
timeTimer.start();
}
}
}
Timer {
// the lastBlockSynchedTime does not change,
// but since we render it as '12 minutes ago'
// we need to actually re-interpret that
// ever so often to keep the relative time.
id: timeTimer
running: !root.account.isArchived
interval: 30000 // 30 sec
repeat: true
triggeredOnStart: true
onTriggered: {
syncLabel.time = Pay.formatDateTime(
root.account.lastBlockSynchedTime);
interval = 30000; // 30 sec
}
}
}
Flowee.AccountTypeLabel {
Layout.columnSpan: 2
account: root.account
}
Flowee.Label {
id: xpubLabel
// at the moment I don't see a point if making this translatable. Let me know if that should change!
text: "xpub" + ":"
visible: root.account.isHDWallet
}
Flowee.LabelWithClipboard {
Layout.fillWidth: true
visible: xpubLabel.visible
text: root.account.xpub
}
Flowee.Label {
id: encLabel
text: qsTr("Encryption") + ":"
visible: encStatus.visible
}
WalletEncryptionStatus {
id: encStatus
Layout.fillWidth: true
account: root.account
}
Flowee.Label {
id: pwdLabel
text: qsTr("Password") + ":"
visible: encStatus.visible
}
Flowee.TextField {
id: passwordField
onAccepted: decryptButton.clicked()
enabled: !root.account.isDecrypted
Layout.fillWidth: true
echoMode: TextInput.Password
visible: pwdLabel.visible
}
Item {
visible: pwdLabel.visible
Layout.fillWidth: true
Layout.columnSpan: 2
implicitHeight: Math.max(decryptWarning.implicitHeight, decryptButton.implicitHeight)
Flowee.WarningLabel {
id: decryptWarning
anchors.left: parent.left
anchors.leftMargin: 20
anchors.right: decryptButton.left
anchors.rightMargin: 10
anchors.baseline: decryptButton.baseline
}
Flowee.Button {
id: decryptButton
anchors.right: parent.right
text: qsTr("Open")
enabled: passwordField.text.length > 3
onClicked: {
var rc = root.account.decrypt(passwordField.text);
if (rc) {
// decrypt went Ok
decryptWarning.text = ""
passwordField.text = ""
}
else {
decryptWarning.text = qsTr("Invalid PIN")
passwordField.forceActiveFocus();
}
}
}
}
Flowee.CheckBox {
id: balanceSetting
checked: root.account.countBalance
onCheckedChanged: root.account.countBalance = checked
visible: !portfolio.singleAccountSetup
}
Flowee.CheckBoxLabel {
Layout.fillWidth: true
buddy: balanceSetting
text: qsTr("Include balance in total")
visible: balanceSetting.visible
}
Flowee.CheckBox {
id: privateCB
checked: root.account.isPrivate
onClicked: root.account.isPrivate = checked
visible: balanceSetting.visible
}
Flowee.CheckBoxLabel {
Layout.fillWidth: true
buddy: privateCB
text: qsTr("Hide in private mode")
visible: balanceSetting.visible
}
}
Flowee.GroupBox {
id: addressesList
width: parent.width
anchors.top: detailsPane.bottom
anchors.topMargin: 10
title: qsTr("Address List")
collapsed: !root.account.isSingleAddressAccount
visible: root.account.isDecrypted || !root.account.needsPinToOpen
Flowee.CheckBox {
id: changeAddresses
text: qsTr("Change Addresses")
visible: root.account.isHDWallet
onClicked: root.account.secrets.showChangeChain = checked
toolTipText: qsTr("Switches between addresses others can pay you on, and addresses the wallet uses to send change back to yourself.")
}
Flowee.CheckBox {
id: usedAddresses
text: qsTr("Used Addresses");
visible: !root.account.isSingleAddressAccount
onClicked: root.account.secrets.showUsedAddresses = checked
toolTipText: qsTr("Switches between unused and used Bitcoin addresses")
}
Flowee.WalletSecretsView {
id: historyView
Layout.fillWidth: true
clip: true
QQC2.ScrollBar.vertical: Flowee.ScrollThumb {
id: thumb
minimumSize: 20 / activityView.height
visible: size < 0.9
}
account: root.account
implicitHeight: root.account.isSingleAddressAccount ? contentHeight : scrollablePage.height / 10 * 7
}
}
Flowee.GroupBox {
id: hdDetails
width: parent.width
anchors.top: addressesList.bottom
anchors.topMargin: 20
title: qsTr("Backup details")
visible: root.account.isHDWallet
collapsed: true
Item {
width: parent.width
implicitHeight: {
if (root.account.isDecrypted)
return grid.height + helpText.height + warningText.height + 20
return infoText.height
}
GridLayout {
id: grid
visible: root.account.isDecrypted
width: parent.width
columns: 3
Flowee.Label {
text: qsTr("Seed-phrase") + ":"
}
QQC2.TextArea {
readOnly: true
text: root.account.mnemonic
Layout.fillWidth: true
selectByMouse: true
mouseSelectionMode: TextEdit.SelectWords
wrapMode: TextEdit.WordWrap
padding: 0
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: menu.popup(parent);
QQC2.Menu {
id: menu
QQC2.MenuItem {
text: qsTr("Copy")
onTriggered: Pay.copyToClipboard(root.account.mnemonic)
}
}
}
}
Image {
Layout.maximumWidth: 20
Layout.maximumHeight: 20
source: "qrc:/qr-code" + (Pay.useDarkSkin ? "-light.svg" : ".svg");
MouseArea {
anchors.fill: parent
onClicked: {
seedQr.qrText = root.account.mnemonic
qrPopup.open();
}
}
}
Flowee.Label {
text: qsTr("Password") + ":"
visible: root.account.mnemonicPwd !== ""
}
Flowee.Label {
Layout.columnSpan: 2
text: root.account.mnemonicPwd
visible: text !== ""
}
Flowee.Label {
text: qsTr("Seed format") + ":"
visible: root.account.isElectrumMnemonic
}
Flowee.Label {
Layout.columnSpan: 2
id: seedPhraseFormat
font.bold: true
text: "Electrum"
visible: root.account.isElectrumMnemonic
}
Flowee.Label {
text: qsTr("Derivation") + ":"
}
Flowee.LabelWithClipboard {
Layout.columnSpan: 2
text: root.account.hdDerivationPath
}
}
Flowee.Label {
id: helpText
visible: grid.visible
width: parent.width
anchors.top: grid.bottom
anchors.topMargin: 10
text: qsTr("Please save the seed-phrase on paper, in the right order, with the derivation path. This seed will allow you to recover your wallet in case of computer failure.")
textFormat: Text.StyledText
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
Flowee.Label {
id: warningText
visible: grid.visible
width: parent.width
anchors.top: helpText.bottom
anchors.topMargin: 10
text: qsTr("<b>Important</b>: Never share your seed-phrase with others!")
textFormat: Text.StyledText
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
Flowee.Label {
id: infoText
visible: !root.account.isDecrypted
width: parent.width
text: qsTr("This wallet is protected by password (pin-to-pay). To see the backup details you need to provide the password.")
textFormat: Text.StyledText
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
}
}
}
Keys.forwardTo: Flowee.ListViewKeyHandler {
target: historyView
}
}