0ef4e8ed4f
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
430 lines
16 KiB
QML
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
|
|
}
|
|
}
|