qt paytoedit: evaluate text on textChanged(), but no network requests
- add param to _check_text to toggle if network requests are allowed ("full check")
- every 0.5 sec, if the textedit has no focus, do full check
- on textChanged()
- detect if user copy-pasted by comparing current text against clipboard contents,
if so, do full check
- otherwise, do partial check
- on clicking ButtonsWidget btns (scan qr, paste, read file), do full check
This commit is contained in:
@@ -916,7 +916,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
self.update_status()
|
||||
# resolve aliases
|
||||
# FIXME this might do blocking network calls that has a timeout of several seconds
|
||||
self.payto_e.check_text()
|
||||
self.payto_e.on_timer_check_text()
|
||||
self.notify_transactions()
|
||||
|
||||
def format_amount(self, amount_sat, is_diff=False, whitespaces=False) -> str:
|
||||
@@ -1527,7 +1527,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
from .paytoedit import PayToEdit
|
||||
self.amount_e = BTCAmountEdit(self.get_decimal_point)
|
||||
self.payto_e = PayToEdit(self)
|
||||
self.payto_e.addPasteButton()
|
||||
msg = (_("Recipient of the funds.") + "\n\n"
|
||||
+ _("You may enter a Bitcoin address, a label from your list of contacts "
|
||||
"(a list of completions will be proposed), "
|
||||
@@ -2252,17 +2251,17 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
else:
|
||||
self.payment_request_error_signal.emit()
|
||||
|
||||
def set_lnurl6_bech32(self, lnurl: str):
|
||||
def set_lnurl6_bech32(self, lnurl: str, *, can_use_network: bool = True):
|
||||
try:
|
||||
url = decode_lnurl(lnurl)
|
||||
except LnInvoiceException as e:
|
||||
self.show_error(_("Error parsing Lightning invoice") + f":\n{e}")
|
||||
return
|
||||
self.set_lnurl6_url(url)
|
||||
self.set_lnurl6_url(url, can_use_network=can_use_network)
|
||||
|
||||
def set_lnurl6_url(self, url: str, *, lnurl_data: LNURL6Data = None):
|
||||
def set_lnurl6_url(self, url: str, *, lnurl_data: LNURL6Data = None, can_use_network: bool = True):
|
||||
domain = urlparse(url).netloc
|
||||
if lnurl_data is None:
|
||||
if lnurl_data is None and can_use_network:
|
||||
lnurl_data = request_lnurl(url, self.network.send_http_on_proxy)
|
||||
if not lnurl_data:
|
||||
return
|
||||
@@ -2303,9 +2302,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
self._is_onchain = b
|
||||
self.max_button.setEnabled(b)
|
||||
|
||||
def set_bip21(self, text: str):
|
||||
def set_bip21(self, text: str, *, can_use_network: bool = True):
|
||||
on_bip70_pr = self.on_pr if can_use_network else None
|
||||
try:
|
||||
out = util.parse_URI(text, self.on_pr)
|
||||
out = util.parse_URI(text, on_bip70_pr)
|
||||
except InvalidBitcoinURI as e:
|
||||
self.show_error(_("Error parsing URI") + f":\n{e}")
|
||||
return
|
||||
@@ -2313,7 +2313,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
r = out.get('r')
|
||||
sig = out.get('sig')
|
||||
name = out.get('name')
|
||||
if r or (name and sig):
|
||||
if (r or (name and sig)) and can_use_network:
|
||||
self.prepare_for_payment_request()
|
||||
return
|
||||
address = out.get('address')
|
||||
@@ -2322,7 +2322,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
message = out.get('message')
|
||||
lightning = out.get('lightning')
|
||||
if lightning:
|
||||
self.handle_payment_identifier(lightning)
|
||||
self.handle_payment_identifier(lightning, can_use_network=can_use_network)
|
||||
return
|
||||
# use label as description (not BIP21 compliant)
|
||||
if label and not message:
|
||||
@@ -2334,7 +2334,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
if amount:
|
||||
self.amount_e.setAmount(amount)
|
||||
|
||||
def handle_payment_identifier(self, text: str):
|
||||
def handle_payment_identifier(self, text: str, *, can_use_network: bool = True):
|
||||
"""Takes
|
||||
Lightning identifiers:
|
||||
* lightning-URI (containing bolt11 or lnurl)
|
||||
@@ -2350,7 +2350,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
invoice_or_lnurl = maybe_extract_lightning_payment_identifier(text)
|
||||
if invoice_or_lnurl:
|
||||
if invoice_or_lnurl.startswith('lnurl'):
|
||||
self.set_lnurl6_bech32(invoice_or_lnurl)
|
||||
self.set_lnurl6_bech32(invoice_or_lnurl, can_use_network=can_use_network)
|
||||
else:
|
||||
self.set_bolt11(invoice_or_lnurl)
|
||||
elif text.lower().startswith(util.BITCOIN_BIP21_URI_SCHEME + ':'):
|
||||
|
||||
@@ -63,9 +63,10 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
|
||||
|
||||
def __init__(self, win: 'ElectrumWindow'):
|
||||
CompletionTextEdit.__init__(self)
|
||||
ScanQRTextEdit.__init__(self, config=win.config)
|
||||
ScanQRTextEdit.__init__(self, config=win.config, setText=self._on_input_btn)
|
||||
Logger.__init__(self)
|
||||
self.win = win
|
||||
self.app = win.app
|
||||
self.amount_edit = win.amount_e
|
||||
self.setFont(QFont(MONOSPACE_FONT))
|
||||
document = self.document()
|
||||
@@ -84,6 +85,8 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
|
||||
self.heightMax = (self.fontSpacing * 10) + self.verticalMargins
|
||||
|
||||
self.c = None
|
||||
self.addPasteButton(setText=self._on_input_btn)
|
||||
self.textChanged.connect(self._on_text_changed)
|
||||
self.outputs = [] # type: List[PartialTxOutput]
|
||||
self.errors = [] # type: List[PayToLineError]
|
||||
self.is_pr = False
|
||||
@@ -100,8 +103,8 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
|
||||
|
||||
def setTextNoCheck(self, text: str):
|
||||
"""Sets the text, while also ensuring the new value will not be resolved/checked."""
|
||||
self.setText(text)
|
||||
self.previous_payto = text
|
||||
self.setText(text)
|
||||
|
||||
def do_clear(self):
|
||||
self.is_pr = False
|
||||
@@ -168,19 +171,27 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
|
||||
assert bitcoin.is_address(address)
|
||||
return address
|
||||
|
||||
def check_text(self):
|
||||
def _on_input_btn(self, text: str):
|
||||
self.setText(text)
|
||||
self._check_text(full_check=True)
|
||||
|
||||
def _on_text_changed(self):
|
||||
if self.app.clipboard().text() == self.toPlainText():
|
||||
# user likely pasted from clipboard
|
||||
self._check_text(full_check=True)
|
||||
else:
|
||||
self._check_text(full_check=False)
|
||||
|
||||
def on_timer_check_text(self):
|
||||
if self.hasFocus():
|
||||
return
|
||||
if self.is_pr:
|
||||
return
|
||||
text = str(self.toPlainText())
|
||||
text = text.strip() # strip whitespaces
|
||||
if text == self.previous_payto:
|
||||
return
|
||||
self.previous_payto = text
|
||||
self._check_text()
|
||||
self._check_text(full_check=True)
|
||||
|
||||
def _check_text(self):
|
||||
def _check_text(self, *, full_check: bool):
|
||||
if self.previous_payto == str(self.toPlainText()).strip():
|
||||
return
|
||||
if full_check:
|
||||
self.previous_payto = str(self.toPlainText()).strip()
|
||||
self.errors = []
|
||||
if self.is_pr:
|
||||
return
|
||||
@@ -194,10 +205,10 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
|
||||
if len(lines) == 1:
|
||||
data = lines[0]
|
||||
try:
|
||||
self.win.handle_payment_identifier(data)
|
||||
self.win.handle_payment_identifier(data, can_use_network=full_check)
|
||||
except LNURLError as e:
|
||||
self.logger.exception("")
|
||||
self.show_error(e)
|
||||
self.win.show_error(e)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
@@ -218,17 +229,18 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
|
||||
self.win.set_onchain(True)
|
||||
self.win.lock_amount(False)
|
||||
return
|
||||
# try lightning address lnurl-16 (note: names can collide with openalias, so order matters)
|
||||
lnurl_data = self._resolve_lightning_address_lnurl16(data)
|
||||
if lnurl_data:
|
||||
url = lightning_address_to_url(data)
|
||||
self.win.set_lnurl6_url(url, lnurl_data=lnurl_data)
|
||||
return
|
||||
# try openalias
|
||||
oa_data = self._resolve_openalias(data)
|
||||
if oa_data:
|
||||
self._set_openalias(key=data, data=oa_data)
|
||||
return
|
||||
if full_check: # network requests
|
||||
# try lightning address lnurl-16 (note: names can collide with openalias, so order matters)
|
||||
lnurl_data = self._resolve_lightning_address_lnurl16(data)
|
||||
if lnurl_data:
|
||||
url = lightning_address_to_url(data)
|
||||
self.win.set_lnurl6_url(url, lnurl_data=lnurl_data)
|
||||
return
|
||||
# try openalias
|
||||
oa_data = self._resolve_openalias(data)
|
||||
if oa_data:
|
||||
self._set_openalias(key=data, data=oa_data)
|
||||
return
|
||||
else:
|
||||
# there are multiple lines
|
||||
self._parse_as_multiline(lines, raise_errors=False)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from typing import Callable
|
||||
|
||||
from electrum.i18n import _
|
||||
from electrum.plugin import run_hook
|
||||
from electrum.simple_config import SimpleConfig
|
||||
@@ -21,11 +23,16 @@ class ShowQRTextEdit(ButtonsTextEdit):
|
||||
|
||||
class ScanQRTextEdit(ButtonsTextEdit, MessageBoxMixin):
|
||||
|
||||
def __init__(self, text="", allow_multi: bool = False, *, config: SimpleConfig):
|
||||
def __init__(
|
||||
self, text="", allow_multi: bool = False,
|
||||
*,
|
||||
config: SimpleConfig,
|
||||
setText: Callable[[str], None] = None,
|
||||
):
|
||||
ButtonsTextEdit.__init__(self, text)
|
||||
self.setReadOnly(False)
|
||||
self.add_file_input_button(config=config, show_error=self.show_error)
|
||||
self.add_qr_input_button(config=config, show_error=self.show_error, allow_multi=allow_multi)
|
||||
self.add_file_input_button(config=config, show_error=self.show_error, setText=setText)
|
||||
self.add_qr_input_button(config=config, show_error=self.show_error, allow_multi=allow_multi, setText=setText)
|
||||
run_hook('scan_text_edit', self)
|
||||
|
||||
def contextMenuEvent(self, e):
|
||||
|
||||
Reference in New Issue
Block a user