tkivy: show pending requests in receive tab instead of dialog - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 46c2d7821fb3a8e60b5a41cd46ecdc12cd1dc154 DIR parent f8038d024b899d624326de71abbc60688e1339c8 HTML Author: ThomasV <thomasv@electrum.org> Date: Fri, 9 Aug 2019 15:51:45 +0200 kivy: show pending requests in receive tab instead of dialog Diffstat: D electrum/gui/kivy/uix/dialogs/requ… | 175 ------------------------------- M electrum/gui/kivy/uix/screens.py | 67 ++++++++++++++++++++++++++++++- M electrum/gui/kivy/uix/ui_screens/r… | 94 +++++++++++++++++++++++++------ 3 files changed, 141 insertions(+), 195 deletions(-) --- DIR diff --git a/electrum/gui/kivy/uix/dialogs/requests.py b/electrum/gui/kivy/uix/dialogs/requests.py t@@ -1,175 +0,0 @@ -from kivy.app import App -from kivy.factory import Factory -from kivy.properties import ObjectProperty -from kivy.lang import Builder -from decimal import Decimal - -from electrum.util import age, PR_UNPAID -from electrum.lnutil import SENT, RECEIVED -from electrum.lnaddr import lndecode -import electrum.constants as constants -from electrum.bitcoin import COIN - -Builder.load_string(''' -<RequestLabel@Label> - #color: .305, .309, .309, 1 - text_size: self.width, None - halign: 'left' - valign: 'top' - -<RequestItem@CardItem> - address: '' - memo: '' - amount: '' - status: '' - date: '' - icon: 'atlas://electrum/gui/kivy/theming/light/important' - Image: - id: icon - source: root.icon - size_hint: None, 1 - width: self.height *.54 - mipmap: True - BoxLayout: - spacing: '8dp' - height: '32dp' - orientation: 'vertical' - Widget - RequestLabel: - text: root.address - shorten: True - Widget - RequestLabel: - text: root.memo - color: .699, .699, .699, 1 - font_size: '13sp' - shorten: True - Widget - BoxLayout: - spacing: '8dp' - height: '32dp' - orientation: 'vertical' - Widget - RequestLabel: - text: root.amount - halign: 'right' - font_size: '15sp' - Widget - RequestLabel: - text: root.status - halign: 'right' - font_size: '13sp' - color: .699, .699, .699, 1 - Widget - -<RequestsDialog@Popup> - id: popup - title: _('Pending requests') - BoxLayout: - id:box - orientation: 'vertical' - spacing: '1dp' - ScrollView: - GridLayout: - cols: 1 - id: requests_container - size_hint: 1, None - height: self.minimum_height - spacing: '2dp' - padding: '12dp' -''') - -from kivy.properties import BooleanProperty -from electrum.gui.kivy.i18n import _ -from electrum.util import format_time -from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED -from electrum.gui.kivy.uix.context_menu import ContextMenu - -pr_icon = { - PR_UNPAID: 'atlas://electrum/gui/kivy/theming/light/important', - PR_UNKNOWN: 'atlas://electrum/gui/kivy/theming/light/important', - PR_PAID: 'atlas://electrum/gui/kivy/theming/light/confirmed', - PR_EXPIRED: 'atlas://electrum/gui/kivy/theming/light/close' -} -request_text = { - PR_UNPAID: _('Pending'), - PR_UNKNOWN: _('Unknown'), - PR_PAID: _('Received'), - PR_EXPIRED: _('Expired') -} - - -class RequestsDialog(Factory.Popup): - - def __init__(self, app, screen, callback): - Factory.Popup.__init__(self) - self.app = app - self.screen = screen - self.callback = callback - self.cards = {} - self.context_menu = None - - def get_card(self, is_lightning, key, address, amount, memo, timestamp): - ci = self.cards.get(key) - if ci is None: - ci = Factory.RequestItem() - ci.address = address - ci.screen = self - ci.is_lightning = is_lightning - ci.key = key - self.cards[key] = ci - - ci.amount = self.app.format_amount_and_units(amount) if amount else '' - ci.memo = memo - ci.status = age(timestamp) - #ci.icon = pr_icon[status] - #exp = pr.get_expiration_date() - #ci.date = format_time(exp) if exp else _('Never') - return ci - - def update(self): - self.menu_actions = [(_('Show'), self.do_show), (_('Delete'), self.do_delete)] - requests_list = self.ids.requests_container - requests_list.clear_widgets() - _list = self.app.wallet.get_sorted_requests(self.app.electrum_config) - for req in _list[::-1]: - is_lightning = req.get('lightning', False) - status = req['status'] - if status != PR_UNPAID: - continue - if not is_lightning: - address = req['address'] - key = address - else: - key = req['rhash'] - address = req['invoice'] - timestamp = req.get('time', 0) - amount = req.get('amount') - description = req.get('memo', '') - ci = self.get_card(is_lightning, key, address, amount, description, timestamp) - requests_list.add_widget(ci) - - def do_show(self, obj): - self.hide_menu() - self.dismiss() - self.app.show_request(obj.is_lightning, obj.key) - - def do_delete(self, req): - from .question import Question - def cb(result): - if result: - self.app.wallet.remove_payment_request(req.address, self.app.electrum_config) - self.hide_menu() - self.update() - d = Question(_('Delete request'), cb) - d.open() - - def show_menu(self, obj): - self.hide_menu() - self.context_menu = ContextMenu(obj, self.menu_actions) - self.ids.box.add_widget(self.context_menu) - - def hide_menu(self): - if self.context_menu is not None: - self.ids.box.remove_widget(self.context_menu) - self.context_menu = None DIR diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py t@@ -27,7 +27,7 @@ from electrum.util import profiler, parse_URI, format_time, InvalidPassword, Not 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 +from electrum.util import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED, TxMinedInfo, age from electrum.plugin import run_hook from electrum.wallet import InternalAddressCorruption from electrum import simple_config t@@ -47,6 +47,9 @@ class Destination(Enum): class HistoryRecycleView(RecycleView): pass +class RequestRecycleView(RecycleView): + pass + class CScreen(Factory.Screen): __events__ = ('on_activate', 'on_deactivate', 'on_enter', 'on_leave') action_view = ObjectProperty(None) t@@ -396,9 +399,15 @@ class SendScreen(CScreen): self.app.tx_dialog(tx) + class ReceiveScreen(CScreen): kvname = 'receive' + cards = {} + + def __init__(self, **kwargs): + super(ReceiveScreen, self).__init__(**kwargs) + self.menu_actions = [(_('Show'), self.do_show), (_('Delete'), self.do_delete)] def clear(self): self.screen.address = '' t@@ -454,11 +463,65 @@ class ReceiveScreen(CScreen): self.screen.address = addr req = self.app.wallet.make_payment_request(addr, amount, message, expiration) self.app.wallet.add_payment_request(req, self.app.electrum_config) - #request = self.get_URI() key = addr self.app.show_request(lightning, key) + def get_card(self, req): + is_lightning = req.get('lightning', False) + status = req['status'] + #if status != PR_UNPAID: + # continue + if not is_lightning: + address = req['address'] + key = address + else: + key = req['rhash'] + address = req['invoice'] + timestamp = req.get('time', 0) + amount = req.get('amount') + description = req.get('memo', '') + ci = self.cards.get(key) + if ci is None: + ci = {} + ci['address'] = address + ci['is_lightning'] = is_lightning + ci['key'] = key + ci['screen'] = self + self.cards[key] = ci + ci['amount'] = self.app.format_amount_and_units(amount) if amount else '' + ci['memo'] = description + ci['status'] = age(timestamp) + return ci + + def update(self): + self.menu_actions = [(_('Show'), self.do_show), (_('Delete'), self.do_delete)] + _list = self.app.wallet.get_sorted_requests(self.app.electrum_config) + requests_container = self.screen.ids.requests_container + requests_container.data = [self.get_card(item) for item in _list] + def do_show(self, obj): + self.hide_menu() + self.app.show_request(obj.is_lightning, obj.key) + + def do_delete(self, req): + from .dialogs.question import Question + def cb(result): + if result: + self.app.wallet.remove_payment_request(req.address, self.app.electrum_config) + self.hide_menu() + self.update() + d = Question(_('Delete request'), cb) + d.open() + + def show_menu(self, obj): + self.hide_menu() + self.context_menu = ContextMenu(obj, self.menu_actions) + self.add_widget(self.context_menu) + + def hide_menu(self): + if self.context_menu is not None: + self.remove_widget(self.context_menu) + self.context_menu = None 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@@ -1,10 +1,71 @@ #:import _ electrum.gui.kivy.i18n._ +#:import Factory kivy.factory.Factory #:import Decimal decimal.Decimal #:set btc_symbol chr(171) #:set mbtc_symbol chr(187) #:set font_light 'electrum/gui/kivy/data/fonts/Roboto-Condensed.ttf' +<RequestLabel@Label> + #color: .305, .309, .309, 1 + text_size: self.width, None + halign: 'left' + valign: 'top' + +<RequestItem@CardItem> + address: '' + memo: '' + amount: '' + status: '' + date: '' + icon: 'atlas://electrum/gui/kivy/theming/light/important' + Image: + id: icon + source: root.icon + size_hint: None, 1 + width: self.height *.54 + mipmap: True + BoxLayout: + spacing: '8dp' + height: '32dp' + orientation: 'vertical' + Widget + RequestLabel: + text: root.address + shorten: True + Widget + RequestLabel: + text: root.memo + color: .699, .699, .699, 1 + font_size: '13sp' + shorten: True + Widget + BoxLayout: + spacing: '8dp' + height: '32dp' + orientation: 'vertical' + Widget + RequestLabel: + text: root.amount + halign: 'right' + font_size: '15sp' + Widget + RequestLabel: + text: root.status + halign: 'right' + font_size: '13sp' + color: .699, .699, .699, 1 + Widget + +<RequestRecycleView>: + viewclass: 'RequestItem' + RecycleBoxLayout: + default_size: None, dp(56) + default_size_hint: 1, None + size_hint: 1, None + height: self.minimum_height + orientation: 'vertical' + ReceiveScreen: id: s t@@ -14,6 +75,7 @@ ReceiveScreen: message: '' status: '' is_lightning: False + show_list: True BoxLayout padding: '12dp', '12dp', '12dp', '12dp' t@@ -29,7 +91,7 @@ ReceiveScreen: height: blue_bottom.item_height spacing: '5dp' Image: - source: 'atlas://electrum/gui/kivy/theming/light/globe' + source: 'atlas://electrum/gui/kivy/theming/light/lightning' if root.is_lightning else 'atlas://electrum/gui/kivy/theming/light/globe' size_hint: None, None size: '22dp', '22dp' pos_hint: {'center_y': .5} t@@ -37,9 +99,8 @@ ReceiveScreen: id: address_label text: _('Lightning') if root.is_lightning else (s.address if s.address else _('Bitcoin Address')) shorten: True + on_release: root.is_lightning = not root.is_lightning #on_release: Clock.schedule_once(lambda dt: app.addresses_dialog(s)) - on_release: - root.is_lightning = not root.is_lightning CardSeparator: opacity: message_selection.opacity color: blue_bottom.foreground_color t@@ -81,11 +142,12 @@ ReceiveScreen: height: '48dp' IconButton: icon: 'atlas://electrum/gui/kivy/theming/light/list' - size_hint: 1, None + size_hint: 0.5, None height: '48dp' - on_release: Clock.schedule_once(lambda dt: app.requests_dialog(s)) - #Widget: - # size_hint: 0.5, 1 + on_release: root.show_list = not root.show_list + #Clock.schedule_once(lambda dt: app.requests_dialog(s)) + Widget: + size_hint: 0.5, None Button: text: _('Clear') size_hint: 1, None t@@ -97,14 +159,10 @@ ReceiveScreen: height: '48dp' on_release: Clock.schedule_once(lambda dt: s.parent.new_request(root.is_lightning)) Widget: - size_hint: 1, 1 - #BoxLayout: - # size_hint: 1, None - # height: '48dp' - # IconButton: - # icon: 'atlas://electrum/gui/kivy/theming/light/list' - # size_hint: 0.5, None - # height: '48dp' - # on_release: Clock.schedule_once(lambda dt: app.requests_dialog(s)) - # Widget: - # size_hint: 2.5, 1 + size_hint: 1, 0.1 + RequestRecycleView: + id: requests_container + scroll_type: ['bars', 'content'] + bar_width: '25dp' + opacity: 1 if root.show_list else 0 + disabled: not root.show_list