diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py index c90296ff7..277507ad3 100644 --- a/electrum/gui/kivy/main_window.py +++ b/electrum/gui/kivy/main_window.py @@ -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: @@ -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() diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py index 99f685069..e5501b4c5 100644 --- a/electrum/gui/kivy/uix/screens.py +++ b/electrum/gui/kivy/uix/screens.py @@ -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) @@ -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): @@ -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) @@ -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 diff --git a/electrum/gui/kivy/uix/ui_screens/receive.kv b/electrum/gui/kivy/uix/ui_screens/receive.kv index fb1f3e447..5c8e123fa 100644 --- a/electrum/gui/kivy/uix/ui_screens/receive.kv +++ b/electrum/gui/kivy/uix/ui_screens/receive.kv @@ -134,11 +134,6 @@ BoxLayout: 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 diff --git a/electrum/gui/kivy/uix/ui_screens/send.kv b/electrum/gui/kivy/uix/ui_screens/send.kv index 87bfa467e..b704c5629 100644 --- a/electrum/gui/kivy/uix/ui_screens/send.kv +++ b/electrum/gui/kivy/uix/ui_screens/send.kv @@ -150,10 +150,6 @@ BoxLayout: 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() diff --git a/electrum/gui/qt/invoice_list.py b/electrum/gui/qt/invoice_list.py index 7191c9eca..2c53b7431 100644 --- a/electrum/gui/qt/invoice_list.py +++ b/electrum/gui/qt/invoice_list.py @@ -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' diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index a9e98c7e4..e58d27057 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -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, @@ -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() @@ -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) diff --git a/electrum/gui/qt/request_list.py b/electrum/gui/qt/request_list.py index 3fea867ae..0c6d37cfa 100644 --- a/electrum/gui/qt/request_list.py +++ b/electrum/gui/qt/request_list.py @@ -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 @@ -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 diff --git a/electrum/wallet.py b/electrum/wallet.py index 5841b6f42..9ddcb37e1 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -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) @@ -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.