electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit aac0fe9ae6d36b0500f00ad0d5d3378c54a30a5a
parent cd86bec894c138dfb74e9c35a072ac02f97c6306
Author: ThomasV <thomasv@electrum.org>
Date:   Tue, 22 Oct 2019 15:41:45 +0200

kivy: show status with color. show inflight attempts.

Diffstat:
Melectrum/gui/kivy/main_window.py | 7++++---
Melectrum/gui/kivy/uix/dialogs/invoice_dialog.py | 19+++++++++++++------
Melectrum/gui/kivy/uix/dialogs/request_dialog.py | 14++++++++++----
Melectrum/gui/kivy/uix/screens.py | 13+++++++++----
Melectrum/gui/kivy/uix/ui_screens/send.kv | 9++++++---
Melectrum/gui/qt/invoice_list.py | 20+++++++++-----------
Melectrum/gui/qt/main_window.py | 4++--
Melectrum/lnworker.py | 7++++---
Melectrum/util.py | 9+++++++++
9 files changed, 66 insertions(+), 36 deletions(-)

diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py @@ -216,14 +216,14 @@ class ElectrumWindow(App): self.show_info(_('Payment Received') + '\n' + key) self._trigger_update_history() - def on_invoice_status(self, event, key, status, log): + def on_invoice_status(self, event, key, status): # todo: update single item self.update_tab('send') + if self.invoice_popup and self.invoice_popup.key == key: + self.invoice_popup.set_status(status) if status == PR_PAID: self.show_info(_('Payment was sent')) self._trigger_update_history() - elif status == PR_INFLIGHT: - pass elif status == PR_FAILED: self.show_info(_('Payment failed')) @@ -443,6 +443,7 @@ class ElectrumWindow(App): status = invoice['status'] data = invoice['invoice'] if is_lightning else key self.invoice_popup = InvoiceDialog('Invoice', data, key) + self.invoice_popup.set_status(status) self.invoice_popup.open() def qr_dialog(self, title, data, show_text=False, text_for_clipboard=None): diff --git a/electrum/gui/kivy/uix/dialogs/invoice_dialog.py b/electrum/gui/kivy/uix/dialogs/invoice_dialog.py @@ -7,7 +7,8 @@ from kivy.app import App from kivy.clock import Clock from electrum.gui.kivy.i18n import _ -from electrum.util import pr_tooltips +from electrum.util import pr_tooltips, pr_color +from electrum.util import PR_UNKNOWN, PR_UNPAID if TYPE_CHECKING: from electrum.gui.kivy.main_window import ElectrumWindow @@ -18,7 +19,8 @@ Builder.load_string(''' id: popup title: '' data: '' - status: 'unknown' + status_color: 1,1,1,1 + status_str:'' shaded: False show_text: False AnchorLayout: @@ -31,7 +33,8 @@ Builder.load_string(''' TopLabel: text: root.data TopLabel: - text: _('Status') + ': ' + root.status + text: _('Status') + ': ' + root.status_str + color: root.status_color Widget: size_hint: 1, 0.2 BoxLayout: @@ -57,22 +60,26 @@ Builder.load_string(''' height: '48dp' text: _('Pay') on_release: root.do_pay() + disabled: not root.can_pay() ''') class InvoiceDialog(Factory.Popup): def __init__(self, title, data, key): + self.status = PR_UNKNOWN Factory.Popup.__init__(self) self.app = App.get_running_app() # type: ElectrumWindow self.title = title self.data = data self.key = key - #def on_open(self): - # self.ids.qr.set_data(self.data) + def can_pay(self): + return self.status == PR_UNPAID def set_status(self, status): - self.status = pr_tooltips[status] + self.status = status + self.status_str = pr_tooltips[status] + self.status_color = pr_color[status] def on_dismiss(self): self.app.request_popup = None diff --git a/electrum/gui/kivy/uix/dialogs/request_dialog.py b/electrum/gui/kivy/uix/dialogs/request_dialog.py @@ -5,7 +5,8 @@ from kivy.app import App from kivy.clock import Clock from electrum.gui.kivy.i18n import _ -from electrum.util import pr_tooltips +from electrum.util import pr_tooltips, pr_color +from electrum.util import PR_UNKNOWN Builder.load_string(''' @@ -13,7 +14,8 @@ Builder.load_string(''' id: popup title: '' data: '' - status: 'unknown' + status_str: '' + status_color: 1,1,1,1 shaded: False show_text: False AnchorLayout: @@ -33,7 +35,8 @@ Builder.load_string(''' TopLabel: text: root.data TopLabel: - text: _('Status') + ': ' + root.status + text: _('Status') + ': ' + root.status_str + color: root.status_color Widget: size_hint: 1, 0.2 BoxLayout: @@ -64,6 +67,7 @@ Builder.load_string(''' class RequestDialog(Factory.Popup): def __init__(self, title, data, key): + self.status = PR_UNKNOWN Factory.Popup.__init__(self) self.app = App.get_running_app() self.title = title @@ -74,7 +78,9 @@ class RequestDialog(Factory.Popup): self.ids.qr.set_data(self.data) def set_status(self, status): - self.status = pr_tooltips[status] + self.status = status + self.status_str = pr_tooltips[status] + self.status_color = pr_color[status] def on_dismiss(self): self.app.request_popup = None diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py @@ -28,7 +28,7 @@ from electrum.util import PR_TYPE_ONCHAIN, PR_TYPE_LN from electrum import bitcoin, constants from electrum.transaction import TxOutput, Transaction, tx_from_str from electrum.util import send_exception_to_crash_reporter, parse_URI, InvalidBitcoinURI -from electrum.util import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED, TxMinedInfo, get_request_status, pr_expiration_values +from electrum.util import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED, PR_INFLIGHT, TxMinedInfo, get_request_status, pr_expiration_values from electrum.plugin import run_hook from electrum.wallet import InternalAddressCorruption from electrum import simple_config @@ -223,20 +223,24 @@ class SendScreen(CScreen): self.set_URI(self.payment_request_queued) self.payment_request_queued = None _list = self.app.wallet.get_invoices() + _list = [x for x in _list if x and x.get('status') != PR_PAID or x.get('rhash') in self.app.wallet.lnworker.logs] payments_container = self.screen.ids.payments_container - payments_container.data = [self.get_card(item) for item in _list if item['status'] != PR_PAID] + payments_container.data = [self.get_card(item) for item in _list] def show_item(self, obj): self.app.show_invoice(obj.is_lightning, obj.key) def get_card(self, item): invoice_type = item['type'] + status = item['status'] + status_str = get_request_status(item) # convert to str if invoice_type == PR_TYPE_LN: key = item['rhash'] - status = get_request_status(item) # convert to str + log = self.app.wallet.lnworker.logs.get(key) + if item['status'] == PR_INFLIGHT and log: + status_str += '... (%d)'%len(log) elif invoice_type == PR_TYPE_ONCHAIN: key = item['id'] - status = get_request_status(item) # convert to str else: raise Exception('unknown invoice type') return { @@ -244,6 +248,7 @@ class SendScreen(CScreen): 'is_bip70': 'bip70' in item, 'screen': self, 'status': status, + 'status_str': status_str, 'key': key, 'memo': item['message'], 'amount': self.app.format_amount_and_units(item['amount'] or 0), diff --git a/electrum/gui/kivy/uix/ui_screens/send.kv b/electrum/gui/kivy/uix/ui_screens/send.kv @@ -1,4 +1,6 @@ #:import _ electrum.gui.kivy.i18n._ +#:import pr_color electrum.util.pr_color +#:import PR_UNKNOWN electrum.util.PR_UNKNOWN #:import Factory kivy.factory.Factory #:import Decimal decimal.Decimal #:set btc_symbol chr(171) @@ -15,7 +17,8 @@ key: '' memo: '' amount: '' - status: '' + status: PR_UNKNOWN + status_str: '' date: '' BoxLayout: spacing: '8dp' @@ -44,10 +47,10 @@ font_size: '15sp' Widget PaymentLabel: - text: root.status + text: root.status_str halign: 'right' font_size: '13sp' - color: .699, .699, .699, 1 + color: pr_color[root.status] Widget <PaymentRecycleView>: diff --git a/electrum/gui/qt/invoice_list.py b/electrum/gui/qt/invoice_list.py @@ -68,12 +68,11 @@ class InvoiceList(MyTreeView): super().__init__(parent, self.create_menu, stretch_column=self.Columns.DESCRIPTION, editable_columns=[]) - self.logs = {} self.setSortingEnabled(True) self.setModel(QStandardItemModel(self)) self.update() - def update_item(self, key, status, log): + def update_item(self, key, status): req = self.parent.wallet.get_invoice(key) if req is None: return @@ -86,17 +85,16 @@ class InvoiceList(MyTreeView): return status_item = model.item(row, self.Columns.STATUS) status_str = get_request_status(req) - if log: - self.logs[key] = log - if status == PR_INFLIGHT: - status_str += '... (%d)'%len(log) + log = self.parent.wallet.lnworker.logs.get(key) + if log and status == PR_INFLIGHT: + status_str += '... (%d)'%len(log) status_item.setText(status_str) status_item.setIcon(read_QIcon(pr_icons.get(status))) def update(self): _list = self.parent.wallet.get_invoices() # filter out paid invoices unless we have the log - _list = [x for x in _list if x and x.get('status') != PR_PAID or x.get('rhash') in self.logs] + _list = [x for x in _list if x and x.get('status') != PR_PAID or x.get('rhash') in self.parent.wallet.lnworker.logs] self.model().clear() self.update_headers(self.__class__.headers) for idx, item in enumerate(_list): @@ -157,13 +155,13 @@ class InvoiceList(MyTreeView): menu.addAction(_("Details"), lambda: self.parent.show_invoice(key)) if invoice['status'] == PR_UNPAID: menu.addAction(_("Pay"), lambda: self.parent.do_pay_invoice(invoice)) - if key in self.logs: - menu.addAction(_("View log"), lambda: self.show_log(key)) + log = self.parent.wallet.lnworker.logs.get(key) + if log: + menu.addAction(_("View log"), lambda: self.show_log(key, log)) menu.addAction(_("Delete"), lambda: self.parent.delete_invoice(key)) menu.exec_(self.viewport().mapToGlobal(position)) - def show_log(self, key): - log = self.logs.get(key) + def show_log(self, key, log): d = WindowModalDialog(self, _("Payment log")) vbox = QVBoxLayout(d) log_w = QTreeWidget() diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py @@ -1694,10 +1694,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): if status == PR_PAID: self.notify(_('Payment received') + '\n' + key) - def on_invoice_status(self, key, status, log): + def on_invoice_status(self, key, status): if key not in self.wallet.invoices: return - self.invoice_list.update_item(key, status, log) + self.invoice_list.update_item(key, status) if status == PR_PAID: self.show_message(_('Payment succeeded')) self.need_update.set() diff --git a/electrum/lnworker.py b/electrum/lnworker.py @@ -320,6 +320,7 @@ class LNWallet(LNWorker): self.preimages = self.storage.get('lightning_preimages', {}) # RHASH -> preimage self.sweep_address = wallet.get_receiving_address() self.lock = threading.RLock() + self.logs = defaultdict(list) # note: accessing channels (besides simple lookup) needs self.lock! self.channels = {} # type: Dict[bytes, Channel] @@ -842,21 +843,21 @@ class LNWallet(LNWorker): self.save_payment_info(info) self._check_invoice(invoice, amount_sat) self.wallet.set_label(key, lnaddr.get_description()) - log = [] + log = self.logs[key] for i in range(attempts): try: route = await self._create_route_from_invoice(decoded_invoice=lnaddr) except NoPathFound: success = False break - self.network.trigger_callback('invoice_status', key, PR_INFLIGHT, log) + self.network.trigger_callback('invoice_status', key, PR_INFLIGHT) success, preimage, failure_log = await self._pay_to_route(route, lnaddr) if success: log.append((route, True, preimage)) break else: log.append((route, False, failure_log)) - self.network.trigger_callback('invoice_status', key, PR_PAID if success else PR_FAILED, log) + self.network.trigger_callback('invoice_status', key, PR_PAID if success else PR_FAILED) return success async def _pay_to_route(self, route, lnaddr): diff --git a/electrum/util.py b/electrum/util.py @@ -85,6 +85,15 @@ PR_PAID = 3 # send and propagated PR_INFLIGHT = 4 # unconfirmed PR_FAILED = 5 +pr_color = { + PR_UNPAID: (.7, .7, .7, 1), + PR_PAID: (.2, .9, .2, 1), + PR_UNKNOWN: (.7, .7, .7, 1), + PR_EXPIRED: (.9, .2, .2, 1), + PR_INFLIGHT: (.9, .6, .3, 1), + PR_FAILED: (.9, .2, .2, 1), +} + pr_tooltips = { PR_UNPAID:_('Pending'), PR_PAID:_('Paid'),