Skip to content

Commit

Permalink
auto-remove paid invoices from GUI
Browse files Browse the repository at this point in the history
 - delay 3 seconds in GUI
 - rename invoice lists to "send queue", "receive queue"
 - kivy remove 'delete' buttons from send/receive screens
  • Loading branch information
ecdsa committed Dec 7, 2020
1 parent dc810f1 commit 406f83e
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 64 deletions.
14 changes: 9 additions & 5 deletions electrum/gui/kivy/main_window.py
Expand Up @@ -238,9 +238,12 @@ def on_fee_histogram(self, *args):
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:
Expand All @@ -251,9 +254,10 @@ def on_invoice_status(self, event, wallet, key):
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()

Expand Down
55 changes: 26 additions & 29 deletions electrum/gui/kivy/uix/screens.py
Expand Up @@ -217,11 +217,23 @@ def set_ln_invoice(self, invoice: str):
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)

Expand Down Expand Up @@ -418,20 +430,6 @@ def on_failure(error):
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):

Expand Down Expand Up @@ -525,11 +523,23 @@ def get_card(self, req: Invoice) -> Dict[str, Any]:
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)

Expand All @@ -540,19 +550,6 @@ def callback(c):
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
Expand Down
5 changes: 0 additions & 5 deletions electrum/gui/kivy/uix/ui_screens/receive.kv
Expand Up @@ -133,11 +133,6 @@
BoxLayout:
size_hint: 1, None
height: '48dp'
IconButton:
icon: 'atlas://electrum/gui/kivy/theming/light/delete'
size_hint: 0.5, None
height: '48dp'
on_release: Clock.schedule_once(lambda dt: s.clear_requests_dialog())
IconButton:
icon: 'atlas://electrum/gui/kivy/theming/light/clock1'
size_hint: 0.5, None
Expand Down
4 changes: 0 additions & 4 deletions electrum/gui/kivy/uix/ui_screens/send.kv
Expand Up @@ -149,10 +149,6 @@
BoxLayout:
size_hint: 1, None
height: '48dp'
IconButton:
icon: 'atlas://electrum/gui/kivy/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()
Expand Down
2 changes: 1 addition & 1 deletion electrum/gui/qt/invoice_list.py
Expand Up @@ -98,7 +98,7 @@ def update(self):
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'
Expand Down
21 changes: 17 additions & 4 deletions electrum/gui/qt/main_window.py
Expand Up @@ -40,6 +40,7 @@

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,
Expand Down Expand Up @@ -868,7 +869,6 @@ def getSaveFileName(self, title, filename, filter="",
return selected_path

def timer_actions(self):
self.request_list.refresh_status()
# Note this runs in the GUI thread
if self.need_update.is_set():
self.need_update.clear()
Expand Down Expand Up @@ -1139,7 +1139,7 @@ def on_expiry(i):
qr_icon = "qrcode_white.png" if ColorScheme.dark_scheme else "qrcode.png"
self.receive_address_e.addButton(qr_icon, qr_show, _("Show as QR code"))

self.receive_requests_label = QLabel(_('Incoming payments'))
self.receive_requests_label = QLabel(_('Receive queue'))

from .request_list import RequestList
self.request_list = RequestList(self)
Expand Down Expand Up @@ -1395,7 +1395,7 @@ def reset_max(text):

self.set_onchain(False)

self.invoices_label = QLabel(_('Outgoing payments'))
self.invoices_label = QLabel(_('Send queue'))
from .invoice_list import InvoiceList
self.invoice_list = InvoiceList(self)

Expand Down Expand Up @@ -1535,8 +1535,16 @@ def task():
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()
Expand All @@ -1547,7 +1555,12 @@ def on_invoice_status(self, wallet, key):
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)
Expand Down
30 changes: 15 additions & 15 deletions electrum/gui/qt/request_list.py
Expand Up @@ -34,6 +34,7 @@
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

Expand Down Expand Up @@ -108,28 +109,27 @@ def clearSelection(self):
super().clearSelection()
self.selectionModel().clearCurrentIndex()

def refresh_status(self):
m = self.std_model
for r in range(m.rowCount()):
idx = m.index(r, self.Columns.STATUS)
date_idx = idx.sibling(idx.row(), self.Columns.DATE)
date_item = m.itemFromIndex(date_idx)
status_item = m.itemFromIndex(idx)
key = date_item.data(ROLE_KEY)
req = self.wallet.get_request(key)
if req:
status = self.parent.wallet.get_request_status(key)
status_str = req.get_status_str(status)
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
Expand Down
11 changes: 10 additions & 1 deletion electrum/wallet.py
Expand Up @@ -749,10 +749,13 @@ def clear_requests(self):

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)

Expand Down Expand Up @@ -1949,6 +1952,12 @@ def get_sorted_requests(self) -> List[Invoice]:
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.
Expand Down

0 comments on commit 406f83e

Please sign in to comment.