URI: 
       tkivy: show status with color. show inflight attempts. - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit aac0fe9ae6d36b0500f00ad0d5d3378c54a30a5a
   DIR parent cd86bec894c138dfb74e9c35a072ac02f97c6306
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Tue, 22 Oct 2019 15:41:45 +0200
       
       kivy: show status with color. show inflight attempts.
       
       Diffstat:
         M electrum/gui/kivy/main_window.py    |       7 ++++---
         M electrum/gui/kivy/uix/dialogs/invo… |      19 +++++++++++++------
         M electrum/gui/kivy/uix/dialogs/requ… |      14 ++++++++++----
         M electrum/gui/kivy/uix/screens.py    |      13 +++++++++----
         M electrum/gui/kivy/uix/ui_screens/s… |       9 ++++++---
         M electrum/gui/qt/invoice_list.py     |      20 +++++++++-----------
         M electrum/gui/qt/main_window.py      |       4 ++--
         M electrum/lnworker.py                |       7 ++++---
         M electrum/util.py                    |       9 +++++++++
       
       9 files changed, 66 insertions(+), 36 deletions(-)
       ---
   DIR diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py
       t@@ -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'))
        
       t@@ -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):
   DIR diff --git a/electrum/gui/kivy/uix/dialogs/invoice_dialog.py b/electrum/gui/kivy/uix/dialogs/invoice_dialog.py
       t@@ -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
       t@@ -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:
       t@@ -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:
       t@@ -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
   DIR diff --git a/electrum/gui/kivy/uix/dialogs/request_dialog.py b/electrum/gui/kivy/uix/dialogs/request_dialog.py
       t@@ -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('''
       t@@ -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:
       t@@ -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:
       t@@ -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
       t@@ -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
   DIR diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py
       t@@ -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
       t@@ -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 {
       t@@ -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),
   DIR diff --git a/electrum/gui/kivy/uix/ui_screens/send.kv b/electrum/gui/kivy/uix/ui_screens/send.kv
       t@@ -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)
       t@@ -15,7 +17,8 @@
            key: ''
            memo: ''
            amount: ''
       -    status: ''
       +    status: PR_UNKNOWN
       +    status_str: ''
            date: ''
            BoxLayout:
                spacing: '8dp'
       t@@ -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>:
   DIR diff --git a/electrum/gui/qt/invoice_list.py b/electrum/gui/qt/invoice_list.py
       t@@ -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
       t@@ -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):
       t@@ -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()
   DIR diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
       t@@ -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()
   DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py
       t@@ -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]
       t@@ -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):
   DIR diff --git a/electrum/util.py b/electrum/util.py
       t@@ -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'),