Skip to content

Commit f76218e

Browse files
accumulatorecdsa
authored andcommitted
qml: introduce InfoBanner allowing a clickable sticky message to stay below header and
implement ln utxo reserve check with warning. Clicking shows a suggestion to swap.
1 parent 3fd64b6 commit f76218e

File tree

5 files changed

+181
-12
lines changed

5 files changed

+181
-12
lines changed

electrum/gui/qml/components/WalletMainView.qml

+22
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@ Item {
447447
Connections {
448448
target: Daemon
449449
function onWalletLoaded() {
450+
infobanner.hide() // start hidden when switching wallets
450451
if (_intentUri) {
451452
invoiceParser.recipient = _intentUri
452453
_intentUri = ''
@@ -497,6 +498,27 @@ Item {
497498
})
498499
dialog.open()
499500
}
501+
function onBalanceChanged() {
502+
// ln low reserve warning
503+
if (Daemon.currentWallet.isLowReserve) {
504+
var message = [
505+
qsTr('You do not have enough on-chain funds to protect your Lightning channels.'),
506+
qsTr('You should have at least %1 on-chain in order to be able to sweep channel outputs.').arg(Config.formatSats(Config.lnUtxoReserve) + ' ' + Config.baseUnit)
507+
].join(' ')
508+
infobanner.show(message, function() {
509+
var dialog = app.messageDialog.createObject(app, {
510+
text: message + '\n\n' + qsTr('Do you want to perform a swap?'),
511+
yesno: true
512+
})
513+
dialog.accepted.connect(function() {
514+
app.startSwap()
515+
})
516+
dialog.open()
517+
})
518+
} else {
519+
infobanner.hide()
520+
}
521+
}
500522
}
501523

502524
Component {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import QtQuick
2+
import QtQuick.Layouts
3+
import QtQuick.Controls
4+
import QtQuick.Controls.Material
5+
import QtQuick.Controls.Material.impl
6+
7+
Item {
8+
id: root
9+
10+
property string message
11+
property bool autohide: false
12+
property color color: constants.colorAlpha(constants.colorWarning, 0.1)
13+
property url icon: Qt.resolvedUrl('../../../icons/warning.png')
14+
property alias font: messageLabel.font
15+
16+
property bool _hide: true
17+
property var _clicked_fn
18+
19+
clip:true
20+
z: 1
21+
layer.enabled: height > 0
22+
layer.effect: ElevationEffect {
23+
elevation: constants.paddingXLarge
24+
fullWidth: true
25+
}
26+
27+
state: 'hidden'
28+
29+
states: [
30+
State {
31+
name: 'hidden'; when: _hide
32+
PropertyChanges { target: root; implicitHeight: 0 }
33+
},
34+
State {
35+
name: 'expanded'; when: !_hide
36+
PropertyChanges { target: root; implicitHeight: layout.implicitHeight }
37+
}
38+
]
39+
40+
transitions: [
41+
Transition {
42+
from: 'hidden'; to: 'expanded'
43+
SequentialAnimation {
44+
PropertyAction { target: root; property: 'visible'; value: true }
45+
NumberAnimation { target: root; properties: 'implicitHeight'; duration: 300; easing.type: Easing.OutQuad }
46+
}
47+
},
48+
Transition {
49+
from: 'expanded'; to: 'hidden'
50+
SequentialAnimation {
51+
NumberAnimation { target: root; properties: 'implicitHeight'; duration: 100; easing.type: Easing.OutQuad }
52+
PropertyAction { target: root; property: 'visible'; value: false }
53+
}
54+
}
55+
]
56+
57+
function show(message, on_clicked=undefined) {
58+
root.message = message
59+
root._clicked_fn = on_clicked
60+
root._hide = false
61+
if (autohide)
62+
closetimer.start()
63+
}
64+
65+
function hide() {
66+
closetimer.stop()
67+
root._hide = true
68+
}
69+
70+
Rectangle {
71+
id: rect
72+
width: root.width
73+
height: layout.height
74+
color: root.color
75+
anchors.bottom: root.bottom
76+
77+
ColumnLayout {
78+
id: layout
79+
width: parent.width
80+
spacing: 0
81+
82+
RowLayout {
83+
Layout.margins: constants.paddingLarge
84+
spacing: constants.paddingSmall
85+
86+
Image {
87+
source: root.icon
88+
Layout.preferredWidth: constants.iconSizeLarge
89+
Layout.preferredHeight: constants.iconSizeLarge
90+
}
91+
92+
Label {
93+
id: messageLabel
94+
Layout.fillWidth: true
95+
font.pixelSize: constants.fontSizeSmall
96+
color: Material.foreground
97+
wrapMode: Text.Wrap
98+
text: root.message
99+
}
100+
}
101+
Rectangle {
102+
Layout.preferredHeight: 2
103+
Layout.fillWidth: true
104+
color: Material.accentColor
105+
}
106+
}
107+
}
108+
109+
MouseArea {
110+
anchors.fill: parent
111+
onClicked: {
112+
if (root._clicked_fn)
113+
root._clicked_fn()
114+
}
115+
}
116+
117+
Timer {
118+
id: closetimer
119+
interval: 5000
120+
repeat: false
121+
onTriggered: _hide = true
122+
}
123+
124+
}

electrum/gui/qml/components/main.qml

+25-12
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ ApplicationWindow
3333

3434
property alias stack: mainStackView
3535
property alias keyboardFreeZone: _keyboardFreeZone
36+
property alias infobanner: _infobanner
3637

3738
property variant activeDialogs: []
3839

@@ -244,22 +245,34 @@ ApplicationWindow
244245
}
245246
}
246247

247-
StackView {
248-
id: mainStackView
248+
ColumnLayout {
249249
width: parent.width
250250
height: _keyboardFreeZone.height - header.height
251-
initialItem: Component {
252-
WalletMainView {}
253-
}
251+
spacing: 0
254252

255-
function getRoot() {
256-
return mainStackView.get(0)
253+
InfoBanner {
254+
id: _infobanner
255+
Layout.fillWidth: true
257256
}
258-
function pushOnRoot(item) {
259-
if (mainStackView.depth > 1) {
260-
mainStackView.replace(mainStackView.get(1), item)
261-
} else {
262-
mainStackView.push(item)
257+
258+
StackView {
259+
id: mainStackView
260+
Layout.fillHeight: true
261+
Layout.fillWidth: true
262+
263+
initialItem: Component {
264+
WalletMainView {}
265+
}
266+
267+
function getRoot() {
268+
return mainStackView.get(0)
269+
}
270+
function pushOnRoot(item) {
271+
if (mainStackView.depth > 1) {
272+
mainStackView.replace(mainStackView.get(1), item)
273+
} else {
274+
mainStackView.push(item)
275+
}
263276
}
264277
}
265278
}

electrum/gui/qml/qeconfig.py

+6
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,12 @@ def swapServerNPub(self, swapserver_npub):
291291
self.config.SWAPSERVER_NPUB = swapserver_npub
292292
self.swapServerNPubChanged.emit()
293293

294+
lnUtxoReserveChanged = pyqtSignal()
295+
@pyqtProperty(QEAmount, notify=lnUtxoReserveChanged)
296+
def lnUtxoReserve(self):
297+
self._lnutxoreserve = QEAmount(amount_sat=self.config.LN_UTXO_RESERVE)
298+
return self._lnutxoreserve
299+
294300
@pyqtSlot('qint64', result=str)
295301
@pyqtSlot(QEAmount, result=str)
296302
def formatSatsForEditing(self, satoshis):

electrum/gui/qml/qewallet.py

+4
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,10 @@ def lightningCanReceive(self):
513513
self._lightningcanreceive.satsInt = int(self.wallet.lnworker.num_sats_can_receive())
514514
return self._lightningcanreceive
515515

516+
@pyqtProperty(bool, notify=balanceChanged)
517+
def isLowReserve(self):
518+
return self.wallet.is_low_reserve()
519+
516520
@pyqtProperty(QEAmount, notify=dataChanged)
517521
def minChannelFunding(self):
518522
return self._minchannelfunding

0 commit comments

Comments
 (0)