Browse Source

initial lightning channel details, action menu

patch-4
Sander van Grieken 3 years ago
parent
commit
04ce548e42
  1. 248
      electrum/gui/qml/components/ChannelDetails.qml
  2. 4
      electrum/gui/qml/components/Channels.qml
  3. 2
      electrum/gui/qml/qeapp.py
  4. 132
      electrum/gui/qml/qechanneldetails.py
  5. 6
      electrum/gui/qml/qechannellistmodel.py
  6. 3
      electrum/gui/qml/qeinvoice.py

248
electrum/gui/qml/components/ChannelDetails.qml

@ -0,0 +1,248 @@
import QtQuick 2.6
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.0
import org.electrum 1.0
import "controls"
Pane {
id: root
width: parent.width
height: parent.height
property string channelid
property string title: qsTr("Channel details")
property QtObject menu: Menu {
id: menu
MenuItem {
icon.color: 'transparent'
action: Action {
text: qsTr('Backup');
enabled: false
onTriggered: {}
//icon.source: '../../icons/wallet.png'
}
}
MenuItem {
icon.color: 'transparent'
action: Action {
text: qsTr('Close channel');
enabled: false
onTriggered: {}
//icon.source: '../../icons/wallet.png'
}
}
MenuItem {
icon.color: 'transparent'
action: Action {
text: qsTr('Force-close');
enabled: false
onTriggered: {}
//icon.source: '../../icons/wallet.png'
}
}
MenuItem {
icon.color: 'transparent'
action: Action {
text: channeldetails.frozenForSending ? qsTr('Unfreeze (for sending)') : qsTr('Freeze (for sending)')
onTriggered: channeldetails.freezeForSending()
//icon.source: '../../icons/wallet.png'
}
}
MenuItem {
icon.color: 'transparent'
action: Action {
text: channeldetails.frozenForReceiving ? qsTr('Unfreeze (for receiving)') : qsTr('Freeze (for receiving)')
onTriggered: channeldetails.freezeForReceiving()
//icon.source: '../../icons/wallet.png'
}
}
}
Flickable {
anchors.fill: parent
contentHeight: rootLayout.height
clip:true
interactive: height < contentHeight
GridLayout {
id: rootLayout
width: parent.width
columns: 2
Label {
text: qsTr('Channel name')
color: Material.accentColor
}
Label {
text: channeldetails.name
}
Label {
text: qsTr('Short channel ID')
color: Material.accentColor
}
Label {
text: channeldetails.short_cid
}
Label {
text: qsTr('State')
color: Material.accentColor
}
Label {
text: channeldetails.state
}
Label {
text: qsTr('Initiator')
color: Material.accentColor
}
Label {
text: channeldetails.initiator
}
Label {
text: qsTr('Capacity')
color: Material.accentColor
}
RowLayout {
Label {
font.family: FixedFont
text: Config.formatSats(channeldetails.capacity)
}
Label {
color: Material.accentColor
text: Config.baseUnit
}
Label {
text: Daemon.fx.enabled
? '(' + Daemon.fx.fiatValue(channeldetails.capacity) + ' ' + Daemon.fx.fiatCurrency + ')'
: ''
}
}
Label {
text: qsTr('Can send')
color: Material.accentColor
}
RowLayout {
visible: !channeldetails.frozenForSending && channeldetails.isOpen
Label {
font.family: FixedFont
text: Config.formatSats(channeldetails.canSend)
}
Label {
color: Material.accentColor
text: Config.baseUnit
}
Label {
text: Daemon.fx.enabled
? '(' + Daemon.fx.fiatValue(channeldetails.canSend) + ' ' + Daemon.fx.fiatCurrency + ')'
: ''
}
}
Label {
visible: channeldetails.frozenForSending && channeldetails.isOpen
text: qsTr('n/a (frozen)')
}
Label {
visible: !channeldetails.isOpen
text: qsTr('n/a (channel not open)')
}
Label {
text: qsTr('Can Receive')
color: Material.accentColor
}
RowLayout {
visible: !channeldetails.frozenForReceiving && channeldetails.isOpen
Label {
font.family: FixedFont
text: Config.formatSats(channeldetails.canReceive)
}
Label {
color: Material.accentColor
text: Config.baseUnit
}
Label {
text: Daemon.fx.enabled
? '(' + Daemon.fx.fiatValue(channeldetails.canReceive) + ' ' + Daemon.fx.fiatCurrency + ')'
: ''
}
}
Label {
visible: channeldetails.frozenForReceiving && channeldetails.isOpen
text: qsTr('n/a (frozen)')
}
Label {
visible: !channeldetails.isOpen
text: qsTr('n/a (channel not open)')
}
Label {
text: qsTr('Channel type')
color: Material.accentColor
}
Label {
text: channeldetails.channelType
}
Label {
text: qsTr('Remote node ID')
Layout.columnSpan: 2
color: Material.accentColor
}
TextHighlightPane {
Layout.columnSpan: 2
Layout.fillWidth: true
padding: 0
leftPadding: constants.paddingSmall
RowLayout {
width: parent.width
Label {
text: channeldetails.pubkey
font.pixelSize: constants.fontSizeLarge
font.family: FixedFont
Layout.fillWidth: true
wrapMode: Text.Wrap
}
ToolButton {
icon.source: '../../icons/share.png'
icon.color: 'transparent'
onClicked: {
var dialog = share.createObject(root, { 'title': qsTr('Channel node ID'), 'text': channeldetails.pubkey })
dialog.open()
}
}
}
}
}
}
ChannelDetails {
id: channeldetails
wallet: Daemon.currentWallet
channelid: root.channelid
}
Component {
id: share
GenericShareDialog {}
}
}

4
electrum/gui/qml/components/Channels.qml

@ -108,7 +108,9 @@ Pane {
model: Daemon.currentWallet.channelModel
delegate: ChannelDelegate {
//highlighted: ListView.isCurrentItem
onClicked: {
app.stack.push(Qt.resolvedUrl('ChannelDetails.qml'), { 'channelid': model.cid })
}
}
ScrollIndicator.vertical: ScrollIndicator { }

2
electrum/gui/qml/qeapp.py

@ -25,6 +25,7 @@ from .qeaddressdetails import QEAddressDetails
from .qetxdetails import QETxDetails
from .qechannelopener import QEChannelOpener
from .qelnpaymentdetails import QELnPaymentDetails
from .qechanneldetails import QEChannelDetails
notification = None
@ -149,6 +150,7 @@ class ElectrumQmlApplication(QGuiApplication):
qmlRegisterType(QETxDetails, 'org.electrum', 1, 0, 'TxDetails')
qmlRegisterType(QEChannelOpener, 'org.electrum', 1, 0, 'ChannelOpener')
qmlRegisterType(QELnPaymentDetails, 'org.electrum', 1, 0, 'LnPaymentDetails')
qmlRegisterType(QEChannelDetails, 'org.electrum', 1, 0, 'ChannelDetails')
qmlRegisterUncreatableType(QEAmount, 'org.electrum', 1, 0, 'Amount', 'Amount can only be used as property')

132
electrum/gui/qml/qechanneldetails.py

@ -0,0 +1,132 @@
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, Q_ENUMS
from electrum.logging import get_logger
from electrum.util import register_callback, unregister_callback
from electrum.lnutil import LOCAL, REMOTE
from .qewallet import QEWallet
from .qetypes import QEAmount
class QEChannelDetails(QObject):
_logger = get_logger(__name__)
_wallet = None
_channelid = None
_channel = None
channelChanged = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
register_callback(self.on_network, ['channel']) # TODO unregister too
def on_network(self, event, *args):
if event == 'channel':
wallet, channel = args
if wallet == self._wallet.wallet and self._channelid == channel.channel_id.hex():
self.channelChanged.emit()
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
def wallet(self):
return self._wallet
@wallet.setter
def wallet(self, wallet: QEWallet):
if self._wallet != wallet:
self._wallet = wallet
self.walletChanged.emit()
channelidChanged = pyqtSignal()
@pyqtProperty(str, notify=channelidChanged)
def channelid(self):
return self._channelid
@channelid.setter
def channelid(self, channelid: str):
if self._channelid != channelid:
self._channelid = channelid
if channelid:
self.load()
self.channelidChanged.emit()
def load(self):
lnchannels = self._wallet.wallet.lnworker.channels
for channel in lnchannels.values():
self._logger.debug('%s == %s ?' % (self._channelid, channel.channel_id))
if self._channelid == channel.channel_id.hex():
self._channel = channel
self.channelChanged.emit()
@pyqtProperty(str, notify=channelChanged)
def name(self):
if not self._channel:
return
return self._wallet.wallet.lnworker.get_node_alias(self._channel.node_id) or self._channel.node_id.hex()
@pyqtProperty(str, notify=channelChanged)
def pubkey(self):
return self._channel.node_id.hex() #if self._channel else ''
@pyqtProperty(str, notify=channelChanged)
def short_cid(self):
return self._channel.short_id_for_GUI()
@pyqtProperty(str, notify=channelChanged)
def state(self):
return self._channel.get_state_for_GUI()
@pyqtProperty(str, notify=channelChanged)
def initiator(self):
return 'Local' if self._channel.constraints.is_initiator else 'Remote'
@pyqtProperty(QEAmount, notify=channelChanged)
def capacity(self):
self._capacity = QEAmount(amount_sat=self._channel.get_capacity())
return self._capacity
@pyqtProperty(QEAmount, notify=channelChanged)
def canSend(self):
self._can_send = QEAmount(amount_sat=self._channel.available_to_spend(LOCAL)/1000)
return self._can_send
@pyqtProperty(QEAmount, notify=channelChanged)
def canReceive(self):
self._can_receive = QEAmount(amount_sat=self._channel.available_to_spend(REMOTE)/1000)
return self._can_receive
@pyqtProperty(bool, notify=channelChanged)
def frozenForSending(self):
return self._channel.is_frozen_for_sending()
@pyqtProperty(bool, notify=channelChanged)
def frozenForReceiving(self):
return self._channel.is_frozen_for_receiving()
@pyqtProperty(str, notify=channelChanged)
def channelType(self):
return self._channel.storage['channel_type'].name_minimal
@pyqtProperty(bool, notify=channelChanged)
def isOpen(self):
return self._channel.is_open()
@pyqtSlot()
def freezeForSending(self):
lnworker = self._channel.lnworker
if lnworker.channel_db or lnworker.is_trampoline_peer(self._channel.node_id):
#self.is_frozen_for_sending = not self.is_frozen_for_sending
self._channel.set_frozen_for_sending(not self.frozenForSending)
self.channelChanged.emit()
else:
self._logger.debug('TODO: messages.MSG_NON_TRAMPOLINE_CHANNEL_FROZEN_WITHOUT_GOSSIP')
@pyqtSlot()
def freezeForReceiving(self):
lnworker = self._channel.lnworker
if lnworker.channel_db or lnworker.is_trampoline_peer(self._channel.node_id):
#self.is_frozen_for_sending = not self.is_frozen_for_sending
self._channel.set_frozen_for_receiving(not self.frozenForReceiving)
self.channelChanged.emit()
else:
self._logger.debug('TODO: messages.MSG_NON_TRAMPOLINE_CHANNEL_FROZEN_WITHOUT_GOSSIP')

6
electrum/gui/qml/qechannellistmodel.py

@ -14,7 +14,7 @@ class QEChannelListModel(QAbstractListModel):
# define listmodel rolemap
_ROLE_NAMES=('cid','state','initiator','capacity','can_send','can_receive',
'l_csv_delat','r_csv_delay','send_frozen','receive_frozen',
'l_csv_delay','r_csv_delay','send_frozen','receive_frozen',
'type','node_id','node_alias','short_cid','funding_tx')
_ROLE_KEYS = range(Qt.UserRole, Qt.UserRole + len(_ROLE_NAMES))
_ROLE_MAP = dict(zip(_ROLE_KEYS, [bytearray(x.encode()) for x in _ROLE_NAMES]))
@ -81,7 +81,7 @@ class QEChannelListModel(QAbstractListModel):
def channel_to_model(self, lnc):
lnworker = self.wallet.lnworker
item = {}
item['channel_id'] = lnc.channel_id
item['cid'] = lnc.channel_id.hex()
item['node_alias'] = lnworker.get_node_alias(lnc.node_id) or lnc.node_id.hex()
item['short_cid'] = lnc.short_id_for_GUI()
item['state'] = lnc.get_state_for_GUI()
@ -114,7 +114,7 @@ class QEChannelListModel(QAbstractListModel):
def on_channel_updated(self, channel):
i = 0
for c in self.channels:
if c['channel_id'] == channel.channel_id:
if c['cid'] == channel.channel_id:
self.do_update(i,channel)
break
i = i + 1

3
electrum/gui/qml/qeinvoice.py

@ -57,9 +57,8 @@ class QEInvoice(QObject):
invoiceCreateError = pyqtSignal([str,str], arguments=['code', 'message'])
def __init__(self, config, parent=None):
def __init__(self, parent=None):
super().__init__(parent)
self.config = config
self.clear()
@pyqtProperty(int, notify=invoiceChanged)

Loading…
Cancel
Save