electrum

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

commit a2122a8c19579a2dc3d91f0d41633c9d4d129988
parent 90abfda12bf78ce13332ab0474dcade418a211b1
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:
Melectrum/gui/kivy/main_window.py | 14+++++++++-----
Melectrum/gui/kivy/uix/screens.py | 55++++++++++++++++++++++++++-----------------------------
Melectrum/gui/kivy/uix/ui_screens/receive.kv | 5-----
Melectrum/gui/kivy/uix/ui_screens/send.kv | 4----
Melectrum/gui/qt/invoice_list.py | 2+-
Melectrum/gui/qt/main_window.py | 16+++++++++++++++-
Melectrum/gui/qt/request_list.py | 17++++++++++++++++-
Melectrum/wallet.py | 11++++++++++-
8 files changed, 77 insertions(+), 47 deletions(-)

diff --git 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 @@ -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 @@ -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' diff --git a/electrum/gui/kivy/uix/ui_screens/send.kv b/electrum/gui/kivy/uix/ui_screens/send.kv @@ -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' diff --git 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 @@ -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 @@ -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 @@ -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.