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 model: Daemon.currentWallet.channelModel
delegate: ChannelDelegate { delegate: ChannelDelegate {
//highlighted: ListView.isCurrentItem onClicked: {
app.stack.push(Qt.resolvedUrl('ChannelDetails.qml'), { 'channelid': model.cid })
}
} }
ScrollIndicator.vertical: ScrollIndicator { } ScrollIndicator.vertical: ScrollIndicator { }

2
electrum/gui/qml/qeapp.py

@ -25,6 +25,7 @@ from .qeaddressdetails import QEAddressDetails
from .qetxdetails import QETxDetails from .qetxdetails import QETxDetails
from .qechannelopener import QEChannelOpener from .qechannelopener import QEChannelOpener
from .qelnpaymentdetails import QELnPaymentDetails from .qelnpaymentdetails import QELnPaymentDetails
from .qechanneldetails import QEChannelDetails
notification = None notification = None
@ -149,6 +150,7 @@ class ElectrumQmlApplication(QGuiApplication):
qmlRegisterType(QETxDetails, 'org.electrum', 1, 0, 'TxDetails') qmlRegisterType(QETxDetails, 'org.electrum', 1, 0, 'TxDetails')
qmlRegisterType(QEChannelOpener, 'org.electrum', 1, 0, 'ChannelOpener') qmlRegisterType(QEChannelOpener, 'org.electrum', 1, 0, 'ChannelOpener')
qmlRegisterType(QELnPaymentDetails, 'org.electrum', 1, 0, 'LnPaymentDetails') 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') 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 # define listmodel rolemap
_ROLE_NAMES=('cid','state','initiator','capacity','can_send','can_receive', _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') 'type','node_id','node_alias','short_cid','funding_tx')
_ROLE_KEYS = range(Qt.UserRole, Qt.UserRole + len(_ROLE_NAMES)) _ROLE_KEYS = range(Qt.UserRole, Qt.UserRole + len(_ROLE_NAMES))
_ROLE_MAP = dict(zip(_ROLE_KEYS, [bytearray(x.encode()) for x in _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): def channel_to_model(self, lnc):
lnworker = self.wallet.lnworker lnworker = self.wallet.lnworker
item = {} 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['node_alias'] = lnworker.get_node_alias(lnc.node_id) or lnc.node_id.hex()
item['short_cid'] = lnc.short_id_for_GUI() item['short_cid'] = lnc.short_id_for_GUI()
item['state'] = lnc.get_state_for_GUI() item['state'] = lnc.get_state_for_GUI()
@ -114,7 +114,7 @@ class QEChannelListModel(QAbstractListModel):
def on_channel_updated(self, channel): def on_channel_updated(self, channel):
i = 0 i = 0
for c in self.channels: for c in self.channels:
if c['channel_id'] == channel.channel_id: if c['cid'] == channel.channel_id:
self.do_update(i,channel) self.do_update(i,channel)
break break
i = i + 1 i = i + 1

3
electrum/gui/qml/qeinvoice.py

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

Loading…
Cancel
Save