2020-11-04 18:33:50 +01:00
/*
* This file is part of the Flowee project
2025-02-03 16:06:23 +01:00
* Copyright (C) 2021-2025 Tom Zander <tom@flowee.org>
2020-11-04 18:33:50 +01:00
*
* 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/>.
*/
2022-11-26 10:46:57 +01:00
import QtQuick
2025-06-18 17:48:29 +02:00
import QtQuick . Controls . Basic as QQC2
2022-11-26 10:46:57 +01:00
import QtQuick . Layouts
2022-11-14 21:19:31 +01:00
import "../Flowee" as Flowee
2020-11-04 18:33:50 +01:00
2021-07-30 23:07:14 +02:00
Item {
2020-11-04 18:33:50 +01:00
id: root
2021-07-30 23:07:14 +02:00
property QtObject account: portfolio . current
2021-11-30 22:19:40 +01:00
focus: true
2021-01-29 10:27:24 +01:00
2025-02-03 16:57:13 +01:00
Item {
// non-layoutable items.
2025-06-19 14:47:19 +02:00
Flowee . Popup {
2025-02-03 16:57:13 +01:00
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 {
2025-11-12 00:18:14 +01:00
id: theQrWidget
2025-02-03 16:57:13 +01:00
qrSize: 250
textVisible: false
useRawString: true
anchors.centerIn: parent
}
}
2025-11-12 00:18:14 +01:00
Flowee . Popup {
id: pluginPopup
width: root . width - 40
height: {
let page = thePile . currentItem ;
let h = root . height - 40 ;
let headerHeight = pageTitle . height + 10
if ( page != null )
return Math . min ( headerHeight + page . implicitHeight , h ) ;
return h ;
}
x: ( root . width - width ) / 2
modal: true
closePolicy: QQC2 . Popup . CloseOnEscape
background: Rectangle {
color: palette . window
border.color: palette . midlight
border.width: 1
radius: 6
Flowee . Label {
id: pageTitle
font.pixelSize: pluginPopup . font . pixelSize * 2
width: parent . width
horizontalAlignment: Qt . AlignHCenter
}
Flowee . CloseIcon {
anchors.right: parent . right
anchors.rightMargin: 6
anchors.bottom: pageTitle . baseline
onClicked: thePile . clear ( ) ;
color: "#bbbbbb"
}
}
QQC2 . StackView {
id: thePile
anchors.fill: parent
anchors.topMargin: pageTitle . height + 10
anchors.margins: 10
onCurrentItemChanged: {
if ( currentItem != null ) {
pluginPopup . open ( ) ;
currentItem . takeFocus ( ) ;
pageTitle . text = currentItem . title
}
else {
pluginPopup . close ( ) ;
}
}
pushEnter: Transition { }
popExit: Transition { }
pushExit: Transition { }
popEnter: Transition { }
}
}
2025-02-03 16:57:13 +01:00
}
2021-11-02 11:41:55 +01:00
2024-05-28 23:23:31 +02:00
Flowee . Label {
2021-07-31 14:16:28 +02:00
id: walletDetailsLabel
2021-10-30 15:23:43 +02:00
text: qsTr ( "Wallet Details" )
2021-07-31 14:16:28 +02:00
font.pointSize: 18
color: "white"
2021-07-31 17:19:34 +02:00
anchors.horizontalCenter: parent . horizontalCenter
2021-07-30 23:07:14 +02:00
}
2021-05-08 14:40:08 +02:00
2021-11-02 19:32:13 +01:00
Flowee . CloseIcon {
2021-07-30 23:07:14 +02:00
id: closeIcon
2021-07-31 14:16:28 +02:00
anchors.bottom: walletDetailsLabel . bottom
2021-10-29 18:20:42 +02:00
anchors.margins: 6
anchors.right: parent . right
2022-05-18 15:44:28 +02:00
onClicked: accountOverlay . state = "showTransactions"
2025-05-04 19:42:50 +02:00
color: "#bbbbbb"
2021-10-29 18:20:42 +02:00
}
2021-07-31 14:16:28 +02:00
2022-06-24 15:38:25 +02:00
GridLayout {
id: basicProperties
2021-10-29 18:20:42 +02:00
anchors.top: walletDetailsLabel . bottom
anchors.topMargin: 20
x: 20
width: parent . width - 30
2022-06-24 15:38:25 +02:00
columns: 2
rowSpacing: 10
2021-10-29 18:20:42 +02:00
2024-05-28 23:23:31 +02:00
Flowee . Label {
2021-10-29 18:20:42 +02:00
text: qsTr ( "Name" ) + ":"
2021-07-31 14:16:28 +02:00
}
2021-11-02 19:38:36 +01:00
Flowee . TextField {
2021-10-29 18:20:42 +02:00
id: accountNameEdit
text: root . account . name
onTextEdited: root . account . name = text
Layout.fillWidth: true
2021-10-30 16:12:41 +02:00
focus: true
2021-07-31 14:16:28 +02:00
}
2022-07-14 20:48:05 +02:00
}
2022-06-24 15:38:25 +02:00
2025-11-12 00:18:14 +01:00
Column {
id: detailsPane
2022-06-24 15:38:25 +02:00
anchors.top: basicProperties . bottom
2021-10-29 18:20:42 +02:00
anchors.left: parent . left
anchors.right: parent . right
2021-07-31 14:16:28 +02:00
anchors.bottom: parent . bottom
2021-10-29 18:20:42 +02:00
anchors.margins: 10
2025-11-12 00:18:14 +01:00
spacing: 10
2022-10-21 16:39:43 +02:00
GridLayout {
2021-10-29 18:20:42 +02:00
width: parent . width - 20
x: 10
2022-10-21 16:39:43 +02:00
columns: 2
2021-07-31 14:16:28 +02:00
2025-11-12 00:18:14 +01:00
Flowee . AccountTypeLabel {
Layout.columnSpan: 2
account: root . account
}
Flowee . Label {
text: qsTr ( "Sync Status" ) + ":"
Layout.alignment: Qt . AlignRight
}
2024-05-28 23:23:31 +02:00
Flowee . Label {
2023-05-16 22:00:47 +02:00
id: syncLabel
property string time: ""
2021-11-03 13:58:31 +01:00
text: {
2022-04-06 20:58:47 +02:00
var height = root . account . lastBlockSynched
if ( height < 1 )
2021-11-03 13:58:31 +01:00
return ""
2023-05-16 22:00:47 +02:00
var time = "" ;
if ( syncLabel . time !== "" )
time = " (" + syncLabel . time + ")" ;
2025-11-12 00:18:14 +01:00
return height + " / " + Pay . chainHeight + time ;
2021-11-03 13:58:31 +01:00
}
2024-01-25 19:47:55 +01:00
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 ( ) ;
}
}
}
2023-05-16 22:00:47 +02:00
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.
2024-01-25 19:47:55 +01:00
id: timeTimer
2023-05-16 22:00:47 +02:00
running: ! root . account . isArchived
interval: 30000 // 30 sec
repeat: true
triggeredOnStart: true
onTriggered: {
syncLabel . time = Pay . formatDateTime (
root . account . lastBlockSynchedTime ) ;
2024-01-25 19:47:55 +01:00
interval = 30000 ; // 30 sec
2023-05-16 22:00:47 +02:00
}
}
2021-07-31 17:19:34 +02:00
}
2021-10-29 18:20:42 +02:00
2024-05-28 23:23:31 +02:00
Flowee . Label {
2022-10-21 16:39:43 +02:00
id: encLabel
text: qsTr ( "Encryption" ) + ":"
visible: encStatus . visible
2025-11-12 00:18:14 +01:00
Layout.alignment: Qt . AlignRight
2021-07-31 14:16:28 +02:00
}
2022-10-21 16:39:43 +02:00
WalletEncryptionStatus {
id: encStatus
Layout.fillWidth: true
account: root . account
2021-07-31 14:16:28 +02:00
}
2022-10-21 16:39:43 +02:00
2024-05-28 23:23:31 +02:00
Flowee . Label {
2022-10-21 16:39:43 +02:00
id: pwdLabel
text: qsTr ( "Password" ) + ":"
visible: encStatus . visible
2025-11-12 00:18:14 +01:00
Layout.alignment: Qt . AlignRight
2022-10-21 16:39:43 +02:00
}
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
2022-12-10 23:37:25 +01:00
text: qsTr ( "Open" )
2022-10-21 16:39:43 +02:00
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 ( ) ;
}
}
}
2021-07-31 14:16:28 +02:00
}
2023-05-15 15:49:02 +02:00
Flowee . CheckBox {
id: balanceSetting
checked: root . account . countBalance
onCheckedChanged: root . account . countBalance = checked
2024-06-30 22:59:17 +02:00
visible: ! portfolio . singleAccountSetup
2025-11-12 00:18:14 +01:00
Layout.alignment: Qt . AlignRight
2023-05-15 15:49:02 +02:00
}
Flowee . CheckBoxLabel {
Layout.fillWidth: true
buddy: balanceSetting
text: qsTr ( "Include balance in total" )
2024-06-30 22:59:17 +02:00
visible: balanceSetting . visible
2023-05-15 15:49:02 +02:00
}
2023-05-16 19:55:52 +02:00
Flowee . CheckBox {
id: privateCB
checked: root . account . isPrivate
onClicked: root . account . isPrivate = checked
2024-06-30 22:59:17 +02:00
visible: balanceSetting . visible
2025-11-12 00:18:14 +01:00
Layout.alignment: Qt . AlignRight
2023-05-16 19:55:52 +02:00
}
Flowee . CheckBoxLabel {
Layout.fillWidth: true
buddy: privateCB
text: qsTr ( "Hide in private mode" )
2024-06-30 22:59:17 +02:00
visible: balanceSetting . visible
2023-05-16 19:55:52 +02:00
}
2021-07-31 14:16:28 +02:00
}
2021-11-02 19:38:36 +01:00
Flowee . GroupBox {
2021-07-31 14:16:28 +02:00
width: parent . width
2025-11-12 00:18:14 +01:00
title: qsTr ( "xpub" )
visible: root . account . isHDWallet
enabled: ! root . account . needsPinToOpen || root . account . isDecrypted
collapsed: false
collapsable: false
columns: 2
2021-07-31 14:16:28 +02:00
2025-11-12 00:18:14 +01:00
Flowee . Label {
id: titleText
text: qsTr ( "To connect this wallet" )
font.italic: true
Layout.columnSpan: 2
2021-10-29 18:20:42 +02:00
}
2025-11-12 00:18:14 +01:00
Flowee . Label {
text: root . account . xpub
wrapMode: Text . WrapAnywhere
Layout.fillWidth: true
font.pixelSize: titleText . font . pixelSize * 0.9
}
Image {
Layout.maximumWidth: 28
Layout.maximumHeight: 28
source: "qrc:/qr-code" + ( Pay . useDarkSkin ? "-light.svg" : ".svg" ) ;
Layout.alignment: Qt . AlignTop
MouseArea {
anchors.fill: parent
onClicked: {
theQrWidget . qrText = root . account . xpub
qrPopup . open ( ) ;
}
}
}
Flowee . Label {
Layout.columnSpan: 2
text: qsTr ( "Be careful who you share the xpub with!" )
font.italic: true
}
}
Flowee . GroupBox {
width: parent . width
title: qsTr ( "Address List" )
collapsable: false
enabled: root . account . isDecrypted || ! root . account . needsPinToOpen
columns: 2
Flowee . Label {
text: qsTr ( "Browse addresses owned by this wallet." )
Layout.fillWidth: true
wrapMode: Text . WrapAtWordBoundaryOrAnywhere
}
Flowee . Button {
text: qsTr ( "Open" )
onClicked: thePile . push ( addressesPane )
}
}
Flowee . GroupBox {
width: parent . width
title: qsTr ( "Backup details" )
collapsable: false
visible: root . account . isHDWallet
enabled: root . account . isDecrypted || ! root . account . needsPinToOpen
columns: 2
Flowee . Label {
text: qsTr ( "Write down a copy of your wallet seed-phrase in order to be able to restore the wallet and the money in it." )
Layout.fillWidth: true
wrapMode: Text . WrapAtWordBoundaryOrAnywhere
}
Flowee . Button {
text: qsTr ( "Open" )
onClicked: thePile . push ( paperBackupPanel )
}
Flowee . Label {
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
Layout.fillWidth: true
wrapMode: Text . WrapAtWordBoundaryOrAnywhere
}
}
Flowee . GroupBox {
id: backupSync
width: parent . width
title: module != null ? module.title : ""
visible: module != null && root . account . isHDWallet
collapsable: false
enabled: root . account . isDecrypted || ! root . account . needsPinToOpen
columns: 2
property QtObject module: ModuleManager . moduleInfo ( "backupSyncModule" ) ;
Flowee . Label {
text: backupSync . module != null ? backupSync.module.description : ""
Layout.fillWidth: true
}
Flowee . Button {
text: qsTr ( "Open" )
onClicked: thePile . push ( backupSync . module . sectionUrl ( "walletOptions" ) ,
{ "pageData" : root . account } )
}
}
}
Component {
id: addressesPane
FocusScope {
property string title: qsTr ( "Address List" )
function takeFocus ( ) { historyView . forceActiveFocus ( ) ; }
implicitWidth: 600
implicitHeight: checkboxes . implicitHeight + 10 + historyView . implicitHeight
Column {
id: checkboxes
width: parent . width
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" )
}
2021-10-29 18:20:42 +02:00
}
2022-12-19 16:06:19 +01:00
Flowee . WalletSecretsView {
2021-11-30 22:19:40 +01:00
id: historyView
2025-11-12 00:18:14 +01:00
width: parent . width
y: checkboxes . height + 10
implicitHeight: contentHeight
height: parent . height - y
2023-05-30 19:19:57 +02:00
clip: true
2024-05-28 23:23:31 +02:00
QQC2.ScrollBar.vertical: Flowee . ScrollThumb {
2021-11-30 22:19:40 +01:00
id: thumb
minimumSize: 20 / activityView . height
visible: size < 0.9
}
2022-12-19 16:06:19 +01:00
account: root . account
2021-07-31 14:16:28 +02:00
}
2025-11-12 00:18:14 +01:00
Keys.forwardTo: Flowee . ListViewKeyHandler {
target: historyView
2021-10-30 16:12:41 +02:00
}
}
2021-07-31 14:16:28 +02:00
}
2025-11-12 00:18:14 +01:00
Component {
id: paperBackupPanel
Item {
width: parent . width
property string title: qsTr ( "Paper Backup Details" )
implicitHeight: grid . height + helpText . height + warningText . height + 40
function takeFocus ( ) { phrase . forceActiveFocus ( ) }
GridLayout {
id: grid
visible: root . account . isDecrypted
width: parent . width
columns: 3
Flowee . Label {
text: qsTr ( "Seed-phrase" ) + ":"
Layout.alignment: Qt . AlignTop
}
QQC2 . TextArea {
id: phrase
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: 28
Layout.maximumHeight: 28
source: "qrc:/qr-code" + ( Pay . useDarkSkin ? "-light.svg" : ".svg" ) ;
Layout.alignment: Qt . AlignTop
MouseArea {
anchors.fill: parent
onClicked: {
theQrWidget . 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 ( "Starting Height" + ":" , "height refers to block-height" )
}
Flowee . LabelWithClipboard {
Layout.columnSpan: 2
text: root . account . accountStartBlockHeight
}
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
}
}
2021-11-30 22:19:40 +01:00
}
2020-11-04 18:33:50 +01:00
}