mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
new kivy gui
This commit is contained in:
parent
ba3ac1b648
commit
c750ec153b
12 changed files with 600 additions and 454 deletions
|
@ -372,9 +372,6 @@
|
|||
tab_height: '48dp'
|
||||
tab_width: panel.width/3
|
||||
strip_border: 0, 0, 0, 0
|
||||
InvoicesScreen:
|
||||
id: invoices_screen
|
||||
tab: invoices_tab
|
||||
SendScreen:
|
||||
id: send_screen
|
||||
tab: send_tab
|
||||
|
@ -384,29 +381,18 @@
|
|||
ReceiveScreen:
|
||||
id: receive_screen
|
||||
tab: receive_tab
|
||||
AddressScreen:
|
||||
id: address_screen
|
||||
tab: address_tab
|
||||
CleanHeader:
|
||||
id: invoices_tab
|
||||
text: _('Invoices')
|
||||
slide: 0
|
||||
CleanHeader:
|
||||
id: send_tab
|
||||
text: _('Send')
|
||||
slide: 1
|
||||
slide: 0
|
||||
CleanHeader:
|
||||
id: history_tab
|
||||
text: _('Balance')
|
||||
slide: 2
|
||||
slide: 1
|
||||
CleanHeader:
|
||||
id: receive_tab
|
||||
text: _('Receive')
|
||||
slide: 3
|
||||
CleanHeader:
|
||||
id: address_tab
|
||||
text: _('Addresses')
|
||||
slide: 4
|
||||
slide: 2
|
||||
|
||||
|
||||
<ActionOvrButton@ActionButton>
|
||||
|
|
|
@ -824,7 +824,6 @@ class ElectrumWindow(App):
|
|||
d = LabelDialog(_('Enter description'), text, callback)
|
||||
d.open()
|
||||
|
||||
@profiler
|
||||
def amount_dialog(self, screen, show_max):
|
||||
from .uix.dialogs.amount_dialog import AmountDialog
|
||||
amount = screen.amount
|
||||
|
@ -836,6 +835,24 @@ class ElectrumWindow(App):
|
|||
popup = AmountDialog(show_max, amount, cb)
|
||||
popup.open()
|
||||
|
||||
def invoices_dialog(self, screen):
|
||||
from .uix.dialogs.invoices import InvoicesDialog
|
||||
popup = InvoicesDialog(self, screen, None)
|
||||
popup.update()
|
||||
popup.open()
|
||||
|
||||
def requests_dialog(self, screen):
|
||||
from .uix.dialogs.requests import RequestsDialog
|
||||
popup = RequestsDialog(self, screen, None)
|
||||
popup.update()
|
||||
popup.open()
|
||||
|
||||
def addresses_dialog(self, screen):
|
||||
from .uix.dialogs.addresses import AddressesDialog
|
||||
popup = AddressesDialog(self, screen, None)
|
||||
popup.update()
|
||||
popup.open()
|
||||
|
||||
def fee_dialog(self, label, dt):
|
||||
from .uix.dialogs.fee_dialog import FeeDialog
|
||||
def cb():
|
||||
|
|
|
@ -47,7 +47,6 @@ class ContextMenu(Bubble):
|
|||
l = MenuItem()
|
||||
l.text = _(k)
|
||||
def func(f=v):
|
||||
Clock.schedule_once(lambda dt: self.hide(), 0.1)
|
||||
Clock.schedule_once(lambda dt: f(obj), 0.15)
|
||||
l.on_release = func
|
||||
self.ids.buttons.add_widget(l)
|
||||
|
|
212
gui/kivy/uix/dialogs/addresses.py
Normal file
212
gui/kivy/uix/dialogs/addresses.py
Normal file
|
@ -0,0 +1,212 @@
|
|||
from kivy.app import App
|
||||
from kivy.factory import Factory
|
||||
from kivy.properties import ObjectProperty
|
||||
from kivy.lang import Builder
|
||||
from decimal import Decimal
|
||||
|
||||
Builder.load_string('''
|
||||
<AddressLabel@Label>
|
||||
text_size: self.width, None
|
||||
halign: 'left'
|
||||
valign: 'top'
|
||||
|
||||
<AddressItem@CardItem>
|
||||
address: ''
|
||||
memo: ''
|
||||
amount: ''
|
||||
status: ''
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
AddressLabel:
|
||||
text: root.address
|
||||
shorten: True
|
||||
Widget
|
||||
AddressLabel:
|
||||
text: (root.amount if root.status == 'Funded' else root.status) + ' ' + root.memo
|
||||
color: .699, .699, .699, 1
|
||||
font_size: '13sp'
|
||||
shorten: True
|
||||
Widget
|
||||
|
||||
<AddressesDialog@Popup>
|
||||
id: popup
|
||||
title: _('Addresses')
|
||||
message: ''
|
||||
pr_status: 'Pending'
|
||||
show_change: 0
|
||||
show_used: 0
|
||||
on_message:
|
||||
self.parent.update()
|
||||
BoxLayout:
|
||||
id:box
|
||||
padding: '12dp', '70dp', '12dp', '12dp'
|
||||
spacing: '12dp'
|
||||
orientation: 'vertical'
|
||||
size_hint: 1, 1.1
|
||||
BoxLayout:
|
||||
spacing: '6dp'
|
||||
size_hint: 1, None
|
||||
orientation: 'horizontal'
|
||||
AddressFilter:
|
||||
opacity: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '5dp'
|
||||
AddressButton:
|
||||
id: search
|
||||
text: {0:_('Receiving'), 1:_('Change'), 2:_('All')}[root.show_change]
|
||||
on_release:
|
||||
root.show_change = (root.show_change + 1) % 3
|
||||
Clock.schedule_once(lambda dt: root.update())
|
||||
AddressFilter:
|
||||
opacity: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '5dp'
|
||||
AddressButton:
|
||||
id: search
|
||||
text: {0:_('All'), 1:_('Unused'), 2:_('Funded'), 3:_('Used')}[root.show_used]
|
||||
on_release:
|
||||
root.show_used = (root.show_used + 1) % 4
|
||||
Clock.schedule_once(lambda dt: root.update())
|
||||
AddressFilter:
|
||||
opacity: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '5dp'
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: 0.9, 0.9, 0.9, 1
|
||||
AddressButton:
|
||||
id: change
|
||||
text: root.message if root.message else _('Search')
|
||||
on_release: Clock.schedule_once(lambda dt: app.description_dialog(addr_screen))
|
||||
ScrollView:
|
||||
GridLayout:
|
||||
cols: 1
|
||||
id: search_container
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
''')
|
||||
|
||||
|
||||
from electrum_gui.kivy.i18n import _
|
||||
from electrum_gui.kivy.uix.context_menu import ContextMenu
|
||||
|
||||
|
||||
class AddressesDialog(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, addr, balance, is_used, label):
|
||||
ci = self.cards.get(addr)
|
||||
if ci is None:
|
||||
ci = Factory.AddressItem()
|
||||
ci.screen = self
|
||||
ci.address = addr
|
||||
self.cards[addr] = ci
|
||||
|
||||
ci.memo = label
|
||||
ci.amount = self.app.format_amount_and_units(balance)
|
||||
request = self.app.wallet.get_payment_request(addr, self.app.electrum_config)
|
||||
if is_used:
|
||||
ci.status = _('Used')
|
||||
else:
|
||||
ci.status = _('Funded') if balance > 0 else _('Unused')
|
||||
return ci
|
||||
|
||||
|
||||
def update(self):
|
||||
self.menu_actions = [(_('Use'), self.do_show), (_('Details'), self.do_view)]
|
||||
wallet = self.app.wallet
|
||||
if self.show_change == 0:
|
||||
_list = wallet.get_receiving_addresses()
|
||||
elif self.show_change == 1:
|
||||
_list = wallet.get_change_addresses()
|
||||
else:
|
||||
_list = wallet.get_addresses()
|
||||
search = self.screen.message
|
||||
container = self.ids.search_container
|
||||
container.clear_widgets()
|
||||
n = 0
|
||||
for address in _list:
|
||||
label = wallet.labels.get(address, '')
|
||||
balance = sum(wallet.get_addr_balance(address))
|
||||
is_used = wallet.is_used(address)
|
||||
if self.show_used == 1 and (balance or is_used):
|
||||
continue
|
||||
if self.show_used == 2 and balance == 0:
|
||||
continue
|
||||
if self.show_used == 3 and not is_used:
|
||||
continue
|
||||
card = self.get_card(address, balance, is_used, label)
|
||||
if search and not self.ext_search(card, search):
|
||||
continue
|
||||
container.add_widget(card)
|
||||
n += 1
|
||||
if not n:
|
||||
msg = _('No address matching your search')
|
||||
container.add_widget(EmptyLabel(text=msg))
|
||||
|
||||
def do_show(self, obj):
|
||||
self.hide_menu()
|
||||
self.dismiss()
|
||||
self.app.show_request(obj.address)
|
||||
|
||||
def do_view(self, obj):
|
||||
req = self.app.wallet.get_payment_request(obj.address, self.app.electrum_config)
|
||||
if req:
|
||||
c, u, x = self.app.wallet.get_addr_balance(obj.address)
|
||||
balance = c + u + x
|
||||
if balance > 0:
|
||||
req['fund'] = balance
|
||||
status = req.get('status')
|
||||
amount = req.get('amount')
|
||||
address = req['address']
|
||||
if amount:
|
||||
status = req.get('status')
|
||||
status = request_text[status]
|
||||
else:
|
||||
received_amount = self.app.wallet.get_addr_received(address)
|
||||
status = self.app.format_amount_and_units(received_amount)
|
||||
self.app.show_pr_details(req, status, False)
|
||||
|
||||
else:
|
||||
req = { 'address': obj.address, 'status' : obj.status }
|
||||
status = obj.status
|
||||
c, u, x = self.app.wallet.get_addr_balance(obj.address)
|
||||
balance = c + u + x
|
||||
if balance > 0:
|
||||
req['fund'] = balance
|
||||
self.app.show_addr_details(req, status)
|
||||
|
||||
def do_delete(self, obj):
|
||||
from .dialogs.question import Question
|
||||
def cb(result):
|
||||
if result:
|
||||
self.app.wallet.remove_payment_request(obj.address, self.app.electrum_config)
|
||||
self.update()
|
||||
d = Question(_('Delete request?'), cb)
|
||||
d.open()
|
||||
|
||||
def ext_search(self, card, search):
|
||||
return card.memo.find(search) >= 0 or card.amount.find(search) >= 0
|
||||
|
||||
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
|
172
gui/kivy/uix/dialogs/invoices.py
Normal file
172
gui/kivy/uix/dialogs/invoices.py
Normal file
|
@ -0,0 +1,172 @@
|
|||
from kivy.app import App
|
||||
from kivy.factory import Factory
|
||||
from kivy.properties import ObjectProperty
|
||||
from kivy.lang import Builder
|
||||
from decimal import Decimal
|
||||
|
||||
Builder.load_string('''
|
||||
<InvoicesLabel@Label>
|
||||
#color: .305, .309, .309, 1
|
||||
text_size: self.width, None
|
||||
halign: 'left'
|
||||
valign: 'top'
|
||||
|
||||
<InvoiceItem@CardItem>
|
||||
requestor: ''
|
||||
memo: ''
|
||||
amount: ''
|
||||
status: ''
|
||||
date: ''
|
||||
icon: 'atlas://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
|
||||
InvoicesLabel:
|
||||
text: root.requestor
|
||||
shorten: True
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.memo
|
||||
color: .699, .699, .699, 1
|
||||
font_size: '13sp'
|
||||
shorten: True
|
||||
Widget
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.amount
|
||||
font_size: '15sp'
|
||||
halign: 'right'
|
||||
width: '110sp'
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.status
|
||||
font_size: '13sp'
|
||||
halign: 'right'
|
||||
color: .699, .699, .699, 1
|
||||
Widget
|
||||
|
||||
|
||||
<InvoicesDialog@Popup>
|
||||
id: popup
|
||||
title: _('Invoices')
|
||||
BoxLayout:
|
||||
id: box
|
||||
orientation: 'vertical'
|
||||
spacing: '1dp'
|
||||
ScrollView:
|
||||
GridLayout:
|
||||
cols: 1
|
||||
id: invoices_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
|
||||
|
||||
invoice_text = {
|
||||
PR_UNPAID:_('Pending'),
|
||||
PR_UNKNOWN:_('Unknown'),
|
||||
PR_PAID:_('Paid'),
|
||||
PR_EXPIRED:_('Expired')
|
||||
}
|
||||
pr_icon = {
|
||||
PR_UNPAID: 'atlas://gui/kivy/theming/light/important',
|
||||
PR_UNKNOWN: 'atlas://gui/kivy/theming/light/important',
|
||||
PR_PAID: 'atlas://gui/kivy/theming/light/confirmed',
|
||||
PR_EXPIRED: 'atlas://gui/kivy/theming/light/close'
|
||||
}
|
||||
|
||||
|
||||
class InvoicesDialog(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, pr):
|
||||
key = pr.get_id()
|
||||
ci = self.cards.get(key)
|
||||
if ci is None:
|
||||
ci = Factory.InvoiceItem()
|
||||
ci.key = key
|
||||
ci.screen = self
|
||||
self.cards[key] = ci
|
||||
ci.requestor = pr.get_requestor()
|
||||
ci.memo = pr.get_memo()
|
||||
amount = pr.get_amount()
|
||||
if amount:
|
||||
ci.amount = self.app.format_amount_and_units(amount)
|
||||
status = self.app.wallet.invoices.get_status(ci.key)
|
||||
ci.status = invoice_text[status]
|
||||
ci.icon = pr_icon[status]
|
||||
else:
|
||||
ci.amount = _('No Amount')
|
||||
ci.status = ''
|
||||
exp = pr.get_expiration_date()
|
||||
ci.date = format_time(exp) if exp else _('Never')
|
||||
return ci
|
||||
|
||||
def update(self):
|
||||
self.menu_actions = [('Pay', self.do_pay), ('Details', self.do_view), ('Delete', self.do_delete)]
|
||||
invoices_list = self.ids.invoices_container
|
||||
invoices_list.clear_widgets()
|
||||
_list = self.app.wallet.invoices.sorted_list()
|
||||
for pr in _list:
|
||||
ci = self.get_card(pr)
|
||||
invoices_list.add_widget(ci)
|
||||
if not _list:
|
||||
msg = _('This screen shows the list of payment requests that have been sent to you. You may also use it to store contact addresses.')
|
||||
invoices_list.add_widget(EmptyLabel(text=msg))
|
||||
|
||||
def do_pay(self, obj):
|
||||
self.hide_menu()
|
||||
self.dismiss()
|
||||
pr = self.app.wallet.invoices.get(obj.key)
|
||||
self.app.on_pr(pr)
|
||||
|
||||
def do_view(self, obj):
|
||||
pr = self.app.wallet.invoices.get(obj.key)
|
||||
pr.verify(self.app.wallet.contacts)
|
||||
self.app.show_pr_details(pr.get_dict(), obj.status, True)
|
||||
|
||||
def do_delete(self, obj):
|
||||
from .question import Question
|
||||
def cb(result):
|
||||
if result:
|
||||
self.app.wallet.invoices.remove(obj.key)
|
||||
self.hide_menu()
|
||||
self.update()
|
||||
d = Question(_('Delete invoice?'), 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
|
157
gui/kivy/uix/dialogs/requests.py
Normal file
157
gui/kivy/uix/dialogs/requests.py
Normal file
|
@ -0,0 +1,157 @@
|
|||
from kivy.app import App
|
||||
from kivy.factory import Factory
|
||||
from kivy.properties import ObjectProperty
|
||||
from kivy.lang import Builder
|
||||
from decimal import Decimal
|
||||
|
||||
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://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: _('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://gui/kivy/theming/light/important',
|
||||
PR_UNKNOWN: 'atlas://gui/kivy/theming/light/important',
|
||||
PR_PAID: 'atlas://gui/kivy/theming/light/confirmed',
|
||||
PR_EXPIRED: 'atlas://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, req):
|
||||
address = req['address']
|
||||
ci = self.cards.get(address)
|
||||
if ci is None:
|
||||
ci = Factory.RequestItem()
|
||||
ci.address = address
|
||||
ci.screen = self
|
||||
self.cards[address] = ci
|
||||
|
||||
amount = req.get('amount')
|
||||
ci.amount = self.app.format_amount_and_units(amount) if amount else ''
|
||||
ci.memo = req.get('memo', '')
|
||||
status, conf = self.app.wallet.get_request_status(address)
|
||||
ci.status = request_text[status]
|
||||
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 pr in _list:
|
||||
ci = self.get_card(pr)
|
||||
requests_list.add_widget(ci)
|
||||
|
||||
def do_show(self, obj):
|
||||
self.hide_menu()
|
||||
self.dismiss()
|
||||
self.app.show_request(obj.address)
|
||||
|
||||
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
|
|
@ -390,205 +390,10 @@ class ReceiveScreen(CScreen):
|
|||
addr = self.get_new_address()
|
||||
if not addr:
|
||||
self.app.show_info(_('Please use the existing requests first.'))
|
||||
else:
|
||||
self.save_request()
|
||||
self.app.show_info(_('New request added to your list.'))
|
||||
|
||||
|
||||
invoice_text = {
|
||||
PR_UNPAID:_('Pending'),
|
||||
PR_UNKNOWN:_('Unknown'),
|
||||
PR_PAID:_('Paid'),
|
||||
PR_EXPIRED:_('Expired')
|
||||
}
|
||||
request_text = {
|
||||
PR_UNPAID: _('Pending'),
|
||||
PR_UNKNOWN: _('Unknown'),
|
||||
PR_PAID: _('Received'),
|
||||
PR_EXPIRED: _('Expired')
|
||||
}
|
||||
pr_icon = {
|
||||
PR_UNPAID: 'atlas://gui/kivy/theming/light/important',
|
||||
PR_UNKNOWN: 'atlas://gui/kivy/theming/light/important',
|
||||
PR_PAID: 'atlas://gui/kivy/theming/light/confirmed',
|
||||
PR_EXPIRED: 'atlas://gui/kivy/theming/light/close'
|
||||
}
|
||||
|
||||
|
||||
class InvoicesScreen(CScreen):
|
||||
kvname = 'invoices'
|
||||
cards = {}
|
||||
|
||||
def get_card(self, pr):
|
||||
key = pr.get_id()
|
||||
ci = self.cards.get(key)
|
||||
if ci is None:
|
||||
ci = Factory.InvoiceItem()
|
||||
ci.key = key
|
||||
ci.screen = self
|
||||
self.cards[key] = ci
|
||||
|
||||
ci.requestor = pr.get_requestor()
|
||||
ci.memo = pr.get_memo()
|
||||
amount = pr.get_amount()
|
||||
if amount:
|
||||
ci.amount = self.app.format_amount_and_units(amount)
|
||||
status = self.app.wallet.invoices.get_status(ci.key)
|
||||
ci.status = invoice_text[status]
|
||||
ci.icon = pr_icon[status]
|
||||
else:
|
||||
ci.amount = _('No Amount')
|
||||
ci.status = ''
|
||||
exp = pr.get_expiration_date()
|
||||
ci.date = format_time(exp) if exp else _('Never')
|
||||
return ci
|
||||
|
||||
def update(self):
|
||||
self.menu_actions = [('Pay', self.do_pay), ('Details', self.do_view), ('Delete', self.do_delete)]
|
||||
invoices_list = self.screen.ids.invoices_container
|
||||
invoices_list.clear_widgets()
|
||||
_list = self.app.wallet.invoices.sorted_list()
|
||||
for pr in _list:
|
||||
ci = self.get_card(pr)
|
||||
invoices_list.add_widget(ci)
|
||||
if not _list:
|
||||
msg = _('This screen shows the list of payment requests that have been sent to you. You may also use it to store contact addresses.')
|
||||
invoices_list.add_widget(EmptyLabel(text=msg))
|
||||
|
||||
def do_pay(self, obj):
|
||||
pr = self.app.wallet.invoices.get(obj.key)
|
||||
self.app.on_pr(pr)
|
||||
|
||||
def do_view(self, obj):
|
||||
pr = self.app.wallet.invoices.get(obj.key)
|
||||
pr.verify(self.app.wallet.contacts)
|
||||
self.app.show_pr_details(pr.get_dict(), obj.status, True)
|
||||
|
||||
def do_delete(self, obj):
|
||||
from .dialogs.question import Question
|
||||
def cb(result):
|
||||
if result:
|
||||
self.app.wallet.invoices.remove(obj.key)
|
||||
self.app.update_tab('invoices')
|
||||
d = Question(_('Delete invoice?'), cb)
|
||||
d.open()
|
||||
|
||||
|
||||
address_icon = {
|
||||
'Pending' : 'atlas://gui/kivy/theming/light/important',
|
||||
'Paid' : 'atlas://gui/kivy/theming/light/confirmed'
|
||||
}
|
||||
|
||||
class AddressScreen(CScreen):
|
||||
kvname = 'address'
|
||||
cards = {}
|
||||
|
||||
def get_card(self, addr, balance, is_used, label):
|
||||
ci = self.cards.get(addr)
|
||||
if ci is None:
|
||||
ci = Factory.AddressItem()
|
||||
ci.screen = self
|
||||
ci.address = addr
|
||||
self.cards[addr] = ci
|
||||
|
||||
ci.memo = label
|
||||
ci.amount = self.app.format_amount_and_units(balance)
|
||||
request = self.app.wallet.get_payment_request(addr, self.app.electrum_config)
|
||||
if is_used:
|
||||
ci.status = _('Used')
|
||||
elif request:
|
||||
status, conf = self.app.wallet.get_request_status(addr)
|
||||
requested_amount = request.get('amount')
|
||||
# make sure that requested amount is > 0
|
||||
if status == PR_PAID:
|
||||
s = _('Request paid')
|
||||
elif status == PR_UNPAID:
|
||||
s = _('Request pending')
|
||||
elif status == PR_EXPIRED:
|
||||
s = _('Request expired')
|
||||
else:
|
||||
s = ''
|
||||
ci.status = s + ': ' + self.app.format_amount_and_units(requested_amount)
|
||||
else:
|
||||
ci.status = _('Funded') if balance>0 else _('Unused')
|
||||
return ci
|
||||
|
||||
|
||||
def update(self):
|
||||
self.menu_actions = [('Receive', self.do_show), ('Details', self.do_view)]
|
||||
wallet = self.app.wallet
|
||||
if self.screen.show_change == 0:
|
||||
_list = wallet.get_receiving_addresses()
|
||||
elif self.screen.show_change == 1:
|
||||
_list = wallet.get_change_addresses()
|
||||
else:
|
||||
_list = wallet.get_addresses()
|
||||
search = self.screen.message
|
||||
container = self.screen.ids.search_container
|
||||
container.clear_widgets()
|
||||
n = 0
|
||||
for address in _list:
|
||||
label = wallet.labels.get(address, '')
|
||||
balance = sum(wallet.get_addr_balance(address))
|
||||
is_used = wallet.is_used(address)
|
||||
if self.screen.show_used == 1 and (balance or is_used):
|
||||
continue
|
||||
if self.screen.show_used == 2 and balance == 0:
|
||||
continue
|
||||
if self.screen.show_used == 3 and not is_used:
|
||||
continue
|
||||
card = self.get_card(address, balance, is_used, label)
|
||||
if search and not self.ext_search(card, search):
|
||||
continue
|
||||
container.add_widget(card)
|
||||
n += 1
|
||||
if not n:
|
||||
msg = _('No address matching your search')
|
||||
container.add_widget(EmptyLabel(text=msg))
|
||||
|
||||
def do_show(self, obj):
|
||||
self.app.show_request(obj.address)
|
||||
|
||||
def do_view(self, obj):
|
||||
req = self.app.wallet.get_payment_request(obj.address, self.app.electrum_config)
|
||||
if req:
|
||||
c, u, x = self.app.wallet.get_addr_balance(obj.address)
|
||||
balance = c + u + x
|
||||
if balance > 0:
|
||||
req['fund'] = balance
|
||||
status = req.get('status')
|
||||
amount = req.get('amount')
|
||||
address = req['address']
|
||||
if amount:
|
||||
status = req.get('status')
|
||||
status = request_text[status]
|
||||
else:
|
||||
received_amount = self.app.wallet.get_addr_received(address)
|
||||
status = self.app.format_amount_and_units(received_amount)
|
||||
self.app.show_pr_details(req, status, False)
|
||||
|
||||
else:
|
||||
req = { 'address': obj.address, 'status' : obj.status }
|
||||
status = obj.status
|
||||
c, u, x = self.app.wallet.get_addr_balance(obj.address)
|
||||
balance = c + u + x
|
||||
if balance > 0:
|
||||
req['fund'] = balance
|
||||
self.app.show_addr_details(req, status)
|
||||
|
||||
def do_delete(self, obj):
|
||||
from .dialogs.question import Question
|
||||
def cb(result):
|
||||
if result:
|
||||
self.app.wallet.remove_payment_request(obj.address, self.app.electrum_config)
|
||||
self.update()
|
||||
d = Question(_('Delete request?'), cb)
|
||||
d.open()
|
||||
|
||||
def ext_search(self, card, search):
|
||||
return card.memo.find(search) >= 0 or card.amount.find(search) >= 0
|
||||
|
||||
|
||||
def do_save(self):
|
||||
self.save_request()
|
||||
self.app.show_info(_('Request was saved.'))
|
||||
|
||||
|
||||
class TabbedCarousel(Factory.TabbedPanel):
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
#:import _ electrum_gui.kivy.i18n._
|
||||
#:import Decimal decimal.Decimal
|
||||
#:set btc_symbol chr(171)
|
||||
#:set mbtc_symbol chr(187)
|
||||
#:set font_light 'gui/kivy/data/fonts/Roboto-Condensed.ttf'
|
||||
|
||||
<AddressLabel@Label>
|
||||
text_size: self.width, None
|
||||
halign: 'left'
|
||||
valign: 'top'
|
||||
|
||||
<AddressItem@CardItem>
|
||||
address: ''
|
||||
memo: ''
|
||||
amount: ''
|
||||
status: ''
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
AddressLabel:
|
||||
text: root.address
|
||||
shorten: True
|
||||
Widget
|
||||
AddressLabel:
|
||||
text: (root.amount if root.status == 'Funded' else root.status) + ' ' + root.memo
|
||||
color: .699, .699, .699, 1
|
||||
font_size: '13sp'
|
||||
shorten: True
|
||||
Widget
|
||||
|
||||
AddressScreen:
|
||||
id: addr_screen
|
||||
name: 'address'
|
||||
message: ''
|
||||
pr_status: 'Pending'
|
||||
show_change: 0
|
||||
show_used: 0
|
||||
on_message:
|
||||
self.parent.update()
|
||||
BoxLayout
|
||||
padding: '12dp', '70dp', '12dp', '12dp'
|
||||
spacing: '12dp'
|
||||
orientation: 'vertical'
|
||||
size_hint: 1, 1.1
|
||||
BoxLayout:
|
||||
spacing: '6dp'
|
||||
size_hint: 1, None
|
||||
orientation: 'horizontal'
|
||||
AddressFilter:
|
||||
opacity: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '5dp'
|
||||
AddressButton:
|
||||
id: search
|
||||
text: {0:_('Receiving'), 1:_('Change'), 2:_('All')}[root.show_change]
|
||||
on_release:
|
||||
root.show_change = (root.show_change + 1) % 3
|
||||
Clock.schedule_once(lambda dt: app.address_screen.update())
|
||||
AddressFilter:
|
||||
opacity: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '5dp'
|
||||
AddressButton:
|
||||
id: search
|
||||
text: {0:_('All'), 1:_('Unused'), 2:_('Funded'), 3:_('Used')}[root.show_used]
|
||||
on_release:
|
||||
root.show_used = (root.show_used + 1) % 4
|
||||
Clock.schedule_once(lambda dt: app.address_screen.update())
|
||||
AddressFilter:
|
||||
opacity: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '5dp'
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: 0.9, 0.9, 0.9, 1
|
||||
AddressButton:
|
||||
id: change
|
||||
text: root.message if root.message else _('Search')
|
||||
on_release: Clock.schedule_once(lambda dt: app.description_dialog(addr_screen))
|
||||
ScrollView:
|
||||
GridLayout:
|
||||
cols: 1
|
||||
id: search_container
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
|
@ -1,66 +0,0 @@
|
|||
<InvoicesLabel@Label>
|
||||
#color: .305, .309, .309, 1
|
||||
text_size: self.width, None
|
||||
halign: 'left'
|
||||
valign: 'top'
|
||||
|
||||
<InvoiceItem@CardItem>
|
||||
requestor: ''
|
||||
memo: ''
|
||||
amount: ''
|
||||
status: ''
|
||||
date: ''
|
||||
icon: 'atlas://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
|
||||
InvoicesLabel:
|
||||
text: root.requestor
|
||||
shorten: True
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.memo
|
||||
color: .699, .699, .699, 1
|
||||
font_size: '13sp'
|
||||
shorten: True
|
||||
Widget
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.amount
|
||||
font_size: '15sp'
|
||||
halign: 'right'
|
||||
width: '110sp'
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.status
|
||||
font_size: '13sp'
|
||||
halign: 'right'
|
||||
color: .699, .699, .699, 1
|
||||
Widget
|
||||
|
||||
|
||||
InvoicesScreen:
|
||||
name: 'invoices'
|
||||
BoxLayout:
|
||||
orientation: 'vertical'
|
||||
spacing: '1dp'
|
||||
ScrollView:
|
||||
GridLayout:
|
||||
cols: 1
|
||||
id: invoices_container
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '2dp'
|
||||
padding: '12dp'
|
|
@ -70,7 +70,7 @@ ReceiveScreen:
|
|||
id: address_label
|
||||
text: s.address if s.address else _('Bitcoin Address')
|
||||
shorten: True
|
||||
disabled: True
|
||||
on_release: Clock.schedule_once(lambda dt: app.addresses_dialog(s))
|
||||
CardSeparator:
|
||||
opacity: message_selection.opacity
|
||||
color: blue_bottom.foreground_color
|
||||
|
@ -110,16 +110,31 @@ ReceiveScreen:
|
|||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
IconButton:
|
||||
icon: 'atlas://gui/kivy/theming/light/save'
|
||||
size_hint: 0.6, None
|
||||
height: '48dp'
|
||||
on_release: s.parent.do_save()
|
||||
Button:
|
||||
text: _('Requests')
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
on_release: Clock.schedule_once(lambda dt: app.requests_dialog(s))
|
||||
Button:
|
||||
text: _('Copy')
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
on_release: s.parent.do_copy()
|
||||
Button:
|
||||
text: _('Share')
|
||||
size_hint: 1, None
|
||||
IconButton:
|
||||
icon: 'atlas://gui/kivy/theming/light/share'
|
||||
size_hint: 0.6, None
|
||||
height: '48dp'
|
||||
on_release: s.parent.do_share()
|
||||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
Widget
|
||||
size_hint: 2, 1
|
||||
Button:
|
||||
text: _('New')
|
||||
size_hint: 1, None
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
<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://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
|
||||
|
||||
|
||||
|
||||
RequestsScreen:
|
||||
name: 'requests'
|
||||
BoxLayout:
|
||||
orientation: 'vertical'
|
||||
spacing: '1dp'
|
||||
ScrollView:
|
||||
GridLayout:
|
||||
cols: 1
|
||||
id: requests_container
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
spacing: '2dp'
|
||||
padding: '12dp'
|
|
@ -34,6 +34,7 @@ SendScreen:
|
|||
text: s.address if s.address else _('Recipient')
|
||||
shorten: True
|
||||
on_release: Clock.schedule_once(lambda dt: app.show_info(_('Copy and paste the recipient address using the Paste button, or use the camera to scan a QR code.')))
|
||||
#on_release: Clock.schedule_once(lambda dt: app.popup_dialog('contacts'))
|
||||
CardSeparator:
|
||||
opacity: int(not root.is_pr)
|
||||
color: blue_bottom.foreground_color
|
||||
|
@ -92,26 +93,30 @@ SendScreen:
|
|||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
IconButton:
|
||||
size_hint: 0.6, 1
|
||||
on_release: s.parent.do_save()
|
||||
icon: 'atlas://gui/kivy/theming/light/save'
|
||||
Button:
|
||||
text: _('Invoices')
|
||||
size_hint: 1, 1
|
||||
on_release: Clock.schedule_once(lambda dt: app.invoices_dialog(s))
|
||||
Button:
|
||||
text: _('Paste')
|
||||
on_release: s.parent.do_paste()
|
||||
IconButton:
|
||||
id: qr
|
||||
size_hint: 0.6, 1
|
||||
on_release: Clock.schedule_once(lambda dt: app.scan_qr(on_complete=app.on_qr))
|
||||
icon: 'atlas://gui/kivy/theming/light/camera'
|
||||
Button:
|
||||
text: _('Paste')
|
||||
on_release: s.parent.do_paste()
|
||||
Button:
|
||||
text: _('Clear')
|
||||
on_release: s.parent.do_clear()
|
||||
IconButton:
|
||||
size_hint: 0.6, 1
|
||||
on_release: s.parent.do_save()
|
||||
icon: 'atlas://gui/kivy/theming/light/save'
|
||||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
Button:
|
||||
text: _('Clear')
|
||||
on_release: s.parent.do_clear()
|
||||
Widget:
|
||||
size_hint: 2, 1
|
||||
size_hint: 1, 1
|
||||
Button:
|
||||
text: _('Pay')
|
||||
size_hint: 1, 1
|
||||
|
|
Loading…
Add table
Reference in a new issue