URI: 
       tauto-remove paid invoices from GUI - delay 3 seconds in GUI - kivy remove 'delete' buttons from send/receive screens - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit a2122a8c19579a2dc3d91f0d41633c9d4d129988
   DIR parent 90abfda12bf78ce13332ab0474dcade418a211b1
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Sun,  6 Dec 2020 10:58:04 +0100
       
       auto-remove paid invoices from GUI
        - delay 3 seconds in GUI
        - kivy remove 'delete' buttons from send/receive screens
       
       Diffstat:
         M electrum/gui/kivy/main_window.py    |      14 +++++++++-----
         M electrum/gui/kivy/uix/screens.py    |      55 +++++++++++++++----------------
         M electrum/gui/kivy/uix/ui_screens/r… |       5 -----
         M electrum/gui/kivy/uix/ui_screens/s… |       4 ----
         M electrum/gui/qt/invoice_list.py     |       2 +-
         M electrum/gui/qt/main_window.py      |      16 +++++++++++++++-
         M electrum/gui/qt/request_list.py     |      17 ++++++++++++++++-
         M electrum/wallet.py                  |      11 ++++++++++-
       
       8 files changed, 77 insertions(+), 47 deletions(-)
       ---
   DIR diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py
       t@@ -242,9 +242,12 @@ class ElectrumWindow(App, Logger):
                self._trigger_update_history()
        
            def on_request_status(self, event, wallet, key, status):
       -        if key not in self.wallet.receive_requests:
       +        req = self.wallet.receive_requests.get(key)
       +        if req is None:
                    return
       -        self.update_tab('receive')
       +        if self.receive_screen:
       +            self.receive_screen.update_item(key, req)
       +            Clock.schedule_once(lambda dt: self.receive_screen.update(), 3)
                if self.request_popup and self.request_popup.key == key:
                    self.request_popup.update_status()
                if status == PR_PAID:
       t@@ -255,9 +258,10 @@ class ElectrumWindow(App, Logger):
                req = self.wallet.get_invoice(key)
                if req is None:
                    return
       -        status = self.wallet.get_invoice_status(req)
       -        # todo: update single item
       -        self.update_tab('send')
       +        if self.send_screen:
       +            self.send_screen.update_item(key, req)
       +            Clock.schedule_once(lambda dt: self.send_screen.update(), 3)
       +
                if self.invoice_popup and self.invoice_popup.key == key:
                    self.invoice_popup.update_status()
        
   DIR diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py
       t@@ -218,11 +218,23 @@ class SendScreen(CScreen, Logger):
            def update(self):
                if self.app.wallet is None:
                    return
       -        _list = self.app.wallet.get_invoices()
       +        _list = self.app.wallet.get_unpaid_invoices()
                _list.reverse()
                payments_container = self.ids.payments_container
                payments_container.data = [self.get_card(item) for item in _list]
        
       +    def update_item(self, key, invoice):
       +        payments_container = self.ids.payments_container
       +        data = payments_container.data
       +        for item in data:
       +            if item['key'] == key:
       +                status = self.app.wallet.get_invoice_status(invoice)
       +                status_str = invoice.get_status_str(status)
       +                item['status'] = status
       +                item['status_str'] = status_str
       +        payments_container.data = data
       +        payments_container.refresh_from_data()
       +
            def show_item(self, obj):
                self.app.show_invoice(obj.is_lightning, obj.key)
        
       t@@ -421,20 +433,6 @@ class SendScreen(CScreen, Logger):
                else:
                    self.app.tx_dialog(tx)
        
       -    def clear_invoices_dialog(self):
       -        invoices = self.app.wallet.get_invoices()
       -        if not invoices:
       -            return
       -        def callback(c):
       -            if c:
       -                for req in invoices:
       -                    key = req.rhash if req.is_lightning() else req.get_address()
       -                    self.app.wallet.delete_invoice(key)
       -                self.update()
       -        n = len(invoices)
       -        d = Question(_('Delete {} invoices?').format(n), callback)
       -        d.open()
       -
        
        class ReceiveScreen(CScreen):
        
       t@@ -531,11 +529,23 @@ class ReceiveScreen(CScreen):
            def update(self):
                if self.app.wallet is None:
                    return
       -        _list = self.app.wallet.get_sorted_requests()
       +        _list = self.app.wallet.get_unpaid_requests()
                _list.reverse()
                requests_container = self.ids.requests_container
                requests_container.data = [self.get_card(item) for item in _list]
        
       +    def update_item(self, key, request):
       +        payments_container = self.ids.requests_container
       +        data = payments_container.data
       +        for item in data:
       +            if item['key'] == key:
       +                status = self.app.wallet.get_request_status(key)
       +                status_str = request.get_status_str(status)
       +                item['status'] = status
       +                item['status_str'] = status_str
       +        payments_container.data = data # needed?
       +        payments_container.refresh_from_data()
       +
            def show_item(self, obj):
                self.app.show_request(obj.is_lightning, obj.key)
        
       t@@ -546,19 +556,6 @@ class ReceiveScreen(CScreen):
                d = ChoiceDialog(_('Expiration date'), pr_expiration_values, self.expiry(), callback)
                d.open()
        
       -    def clear_requests_dialog(self):
       -        requests = self.app.wallet.get_sorted_requests()
       -        if not requests:
       -            return
       -        def callback(c):
       -            if c:
       -                self.app.wallet.clear_requests()
       -                self.update()
       -        n = len(requests)
       -        d = Question(_('Delete {} requests?').format(n), callback)
       -        d.open()
       -
       -
        
        class TabbedCarousel(Factory.TabbedPanel):
            '''Custom TabbedPanel using a carousel used in the Main Screen
   DIR diff --git a/electrum/gui/kivy/uix/ui_screens/receive.kv b/electrum/gui/kivy/uix/ui_screens/receive.kv
       t@@ -135,11 +135,6 @@
                    size_hint: 1, None
                    height: '48dp'
                    IconButton:
       -                icon: f'atlas://{KIVY_GUI_PATH}/theming/light/delete'
       -                size_hint: 0.5, None
       -                height: '48dp'
       -                on_release: Clock.schedule_once(lambda dt: s.clear_requests_dialog())
       -            IconButton:
                        icon: f'atlas://{KIVY_GUI_PATH}/theming/light/clock1'
                        size_hint: 0.5, None
                        height: '48dp'
   DIR diff --git a/electrum/gui/kivy/uix/ui_screens/send.kv b/electrum/gui/kivy/uix/ui_screens/send.kv
       t@@ -151,10 +151,6 @@
                    size_hint: 1, None
                    height: '48dp'
                    IconButton:
       -                icon: f'atlas://{KIVY_GUI_PATH}/theming/light/delete'
       -                size_hint: 0.5, 1
       -                on_release: Clock.schedule_once(lambda dt: s.clear_invoices_dialog())
       -            IconButton:
                        size_hint: 0.5, 1
                        on_release: s.do_save()
                        icon: f'atlas://{KIVY_GUI_PATH}/theming/light/save'
   DIR diff --git a/electrum/gui/qt/invoice_list.py b/electrum/gui/qt/invoice_list.py
       t@@ -98,7 +98,7 @@ class InvoiceList(MyTreeView):
                self.proxy.setDynamicSortFilter(False)  # temp. disable re-sorting after every change
                self.std_model.clear()
                self.update_headers(self.__class__.headers)
       -        for idx, item in enumerate(self.parent.wallet.get_invoices()):
       +        for idx, item in enumerate(self.parent.wallet.get_unpaid_invoices()):
                    if item.is_lightning():
                        key = item.rhash
                        icon_name = 'lightning.png'
   DIR diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
       t@@ -40,6 +40,7 @@ from typing import Optional, TYPE_CHECKING, Sequence, List, Union
        
        from PyQt5.QtGui import QPixmap, QKeySequence, QIcon, QCursor, QFont
        from PyQt5.QtCore import Qt, QRect, QStringListModel, QSize, pyqtSignal
       +from PyQt5.QtCore import QTimer
        from PyQt5.QtWidgets import (QMessageBox, QComboBox, QSystemTrayIcon, QTabWidget,
                                     QMenuBar, QFileDialog, QCheckBox, QLabel,
                                     QVBoxLayout, QGridLayout, QLineEdit,
       t@@ -1516,8 +1517,16 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
            def on_request_status(self, wallet, key, status):
                if wallet != self.wallet:
                    return
       -        if key not in self.wallet.receive_requests:
       +        req = self.wallet.receive_requests.get(key)
       +        if req is None:
                    return
       +        # update item
       +        self.request_list.update_item(key, req)
       +        # update list later
       +        self.timer = QTimer()
       +        self.timer.timeout.connect(self.request_list.update)
       +        self.timer.start(3000)
       +
                if status == PR_PAID:
                    self.notify(_('Payment received') + '\n' + key)
                    self.need_update.set()
       t@@ -1528,7 +1537,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
                req = self.wallet.get_invoice(key)
                if req is None:
                    return
       +        # update item
                self.invoice_list.update_item(key, req)
       +        # update list later.
       +        self.timer = QTimer()
       +        self.timer.timeout.connect(self.invoice_list.update)
       +        self.timer.start(3000)
        
            def on_payment_succeeded(self, wallet, key):
                description = self.wallet.get_label(key)
   DIR diff --git a/electrum/gui/qt/request_list.py b/electrum/gui/qt/request_list.py
       t@@ -34,6 +34,7 @@ from electrum.i18n import _
        from electrum.util import format_time
        from electrum.invoices import PR_TYPE_ONCHAIN, PR_TYPE_LN, LNInvoice, OnchainInvoice
        from electrum.plugin import run_hook
       +from electrum.invoices import Invoice
        
        from .util import MyTreeView, pr_icons, read_QIcon, webopen, MySortModel
        
       t@@ -126,13 +127,27 @@ class RequestList(MyTreeView):
                        status_item.setText(status_str)
                        status_item.setIcon(read_QIcon(pr_icons.get(status)))
        
       +    def update_item(self, key, invoice: Invoice):
       +        model = self.std_model
       +        for row in range(0, model.rowCount()):
       +            item = model.item(row, 0)
       +            if item.data(ROLE_KEY) == key:
       +                break
       +        else:
       +            return
       +        status_item = model.item(row, self.Columns.STATUS)
       +        status = self.parent.wallet.get_request_status(key)
       +        status_str = invoice.get_status_str(status)
       +        status_item.setText(status_str)
       +        status_item.setIcon(read_QIcon(pr_icons.get(status)))
       +
            def update(self):
                # not calling maybe_defer_update() as it interferes with conditional-visibility
                self.parent.update_receive_address_styling()
                self.proxy.setDynamicSortFilter(False)  # temp. disable re-sorting after every change
                self.std_model.clear()
                self.update_headers(self.__class__.headers)
       -        for req in self.wallet.get_sorted_requests():
       +        for req in self.wallet.get_unpaid_requests():
                    if req.is_lightning():
                        assert isinstance(req, LNInvoice)
                        key = req.rhash
   DIR diff --git a/electrum/wallet.py b/electrum/wallet.py
       t@@ -761,10 +761,13 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
        
            def get_invoices(self):
                out = list(self.invoices.values())
       -        #out = list(filter(None, out)) filter out ln
                out.sort(key=lambda x:x.time)
                return out
        
       +    def get_unpaid_invoices(self):
       +        invoices = self.get_invoices()
       +        return [x for x in invoices if self.get_invoice_status(x) != PR_PAID]
       +
            def get_invoice(self, key):
                return self.invoices.get(key)
        
       t@@ -2035,6 +2038,12 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
                out.sort(key=lambda x: x.time)
                return out
        
       +    def get_unpaid_requests(self):
       +        out = [self.get_request(x) for x in self.receive_requests.keys() if self.get_request_status(x) != PR_PAID]
       +        out = [x for x in out if x is not None]
       +        out.sort(key=lambda x: x.time)
       +        return out
       +
            @abstractmethod
            def get_fingerprint(self) -> str:
                """Returns a string that can be used to identify this wallet.