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.
This commit is contained in:
committed by
ThomasV
parent
3fd64b60ab
commit
f76218ea83
@@ -447,6 +447,7 @@ Item {
|
||||
Connections {
|
||||
target: Daemon
|
||||
function onWalletLoaded() {
|
||||
infobanner.hide() // start hidden when switching wallets
|
||||
if (_intentUri) {
|
||||
invoiceParser.recipient = _intentUri
|
||||
_intentUri = ''
|
||||
@@ -497,6 +498,27 @@ Item {
|
||||
})
|
||||
dialog.open()
|
||||
}
|
||||
function onBalanceChanged() {
|
||||
// ln low reserve warning
|
||||
if (Daemon.currentWallet.isLowReserve) {
|
||||
var message = [
|
||||
qsTr('You do not have enough on-chain funds to protect your Lightning channels.'),
|
||||
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)
|
||||
].join(' ')
|
||||
infobanner.show(message, function() {
|
||||
var dialog = app.messageDialog.createObject(app, {
|
||||
text: message + '\n\n' + qsTr('Do you want to perform a swap?'),
|
||||
yesno: true
|
||||
})
|
||||
dialog.accepted.connect(function() {
|
||||
app.startSwap()
|
||||
})
|
||||
dialog.open()
|
||||
})
|
||||
} else {
|
||||
infobanner.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
|
||||
124
electrum/gui/qml/components/controls/InfoBanner.qml
Normal file
124
electrum/gui/qml/components/controls/InfoBanner.qml
Normal file
@@ -0,0 +1,124 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Controls.Material.impl
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string message
|
||||
property bool autohide: false
|
||||
property color color: constants.colorAlpha(constants.colorWarning, 0.1)
|
||||
property url icon: Qt.resolvedUrl('../../../icons/warning.png')
|
||||
property alias font: messageLabel.font
|
||||
|
||||
property bool _hide: true
|
||||
property var _clicked_fn
|
||||
|
||||
clip:true
|
||||
z: 1
|
||||
layer.enabled: height > 0
|
||||
layer.effect: ElevationEffect {
|
||||
elevation: constants.paddingXLarge
|
||||
fullWidth: true
|
||||
}
|
||||
|
||||
state: 'hidden'
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: 'hidden'; when: _hide
|
||||
PropertyChanges { target: root; implicitHeight: 0 }
|
||||
},
|
||||
State {
|
||||
name: 'expanded'; when: !_hide
|
||||
PropertyChanges { target: root; implicitHeight: layout.implicitHeight }
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: 'hidden'; to: 'expanded'
|
||||
SequentialAnimation {
|
||||
PropertyAction { target: root; property: 'visible'; value: true }
|
||||
NumberAnimation { target: root; properties: 'implicitHeight'; duration: 300; easing.type: Easing.OutQuad }
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: 'expanded'; to: 'hidden'
|
||||
SequentialAnimation {
|
||||
NumberAnimation { target: root; properties: 'implicitHeight'; duration: 100; easing.type: Easing.OutQuad }
|
||||
PropertyAction { target: root; property: 'visible'; value: false }
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
function show(message, on_clicked=undefined) {
|
||||
root.message = message
|
||||
root._clicked_fn = on_clicked
|
||||
root._hide = false
|
||||
if (autohide)
|
||||
closetimer.start()
|
||||
}
|
||||
|
||||
function hide() {
|
||||
closetimer.stop()
|
||||
root._hide = true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: rect
|
||||
width: root.width
|
||||
height: layout.height
|
||||
color: root.color
|
||||
anchors.bottom: root.bottom
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
Layout.margins: constants.paddingLarge
|
||||
spacing: constants.paddingSmall
|
||||
|
||||
Image {
|
||||
source: root.icon
|
||||
Layout.preferredWidth: constants.iconSizeLarge
|
||||
Layout.preferredHeight: constants.iconSizeLarge
|
||||
}
|
||||
|
||||
Label {
|
||||
id: messageLabel
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: constants.fontSizeSmall
|
||||
color: Material.foreground
|
||||
wrapMode: Text.Wrap
|
||||
text: root.message
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
Layout.preferredHeight: 2
|
||||
Layout.fillWidth: true
|
||||
color: Material.accentColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (root._clicked_fn)
|
||||
root._clicked_fn()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: closetimer
|
||||
interval: 5000
|
||||
repeat: false
|
||||
onTriggered: _hide = true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,6 +33,7 @@ ApplicationWindow
|
||||
|
||||
property alias stack: mainStackView
|
||||
property alias keyboardFreeZone: _keyboardFreeZone
|
||||
property alias infobanner: _infobanner
|
||||
|
||||
property variant activeDialogs: []
|
||||
|
||||
@@ -244,22 +245,34 @@ ApplicationWindow
|
||||
}
|
||||
}
|
||||
|
||||
StackView {
|
||||
id: mainStackView
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
height: _keyboardFreeZone.height - header.height
|
||||
initialItem: Component {
|
||||
WalletMainView {}
|
||||
spacing: 0
|
||||
|
||||
InfoBanner {
|
||||
id: _infobanner
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
function getRoot() {
|
||||
return mainStackView.get(0)
|
||||
}
|
||||
function pushOnRoot(item) {
|
||||
if (mainStackView.depth > 1) {
|
||||
mainStackView.replace(mainStackView.get(1), item)
|
||||
} else {
|
||||
mainStackView.push(item)
|
||||
StackView {
|
||||
id: mainStackView
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
initialItem: Component {
|
||||
WalletMainView {}
|
||||
}
|
||||
|
||||
function getRoot() {
|
||||
return mainStackView.get(0)
|
||||
}
|
||||
function pushOnRoot(item) {
|
||||
if (mainStackView.depth > 1) {
|
||||
mainStackView.replace(mainStackView.get(1), item)
|
||||
} else {
|
||||
mainStackView.push(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,6 +291,12 @@ class QEConfig(AuthMixin, QObject):
|
||||
self.config.SWAPSERVER_NPUB = swapserver_npub
|
||||
self.swapServerNPubChanged.emit()
|
||||
|
||||
lnUtxoReserveChanged = pyqtSignal()
|
||||
@pyqtProperty(QEAmount, notify=lnUtxoReserveChanged)
|
||||
def lnUtxoReserve(self):
|
||||
self._lnutxoreserve = QEAmount(amount_sat=self.config.LN_UTXO_RESERVE)
|
||||
return self._lnutxoreserve
|
||||
|
||||
@pyqtSlot('qint64', result=str)
|
||||
@pyqtSlot(QEAmount, result=str)
|
||||
def formatSatsForEditing(self, satoshis):
|
||||
|
||||
@@ -513,6 +513,10 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
|
||||
self._lightningcanreceive.satsInt = int(self.wallet.lnworker.num_sats_can_receive())
|
||||
return self._lightningcanreceive
|
||||
|
||||
@pyqtProperty(bool, notify=balanceChanged)
|
||||
def isLowReserve(self):
|
||||
return self.wallet.is_low_reserve()
|
||||
|
||||
@pyqtProperty(QEAmount, notify=dataChanged)
|
||||
def minChannelFunding(self):
|
||||
return self._minchannelfunding
|
||||
|
||||
Reference in New Issue
Block a user