when paying an lnurl-pay that provides an amount interval, the amount field is editable by the user and it expects no spaces
279 lines
10 KiB
Python
279 lines
10 KiB
Python
import copy
|
|
from decimal import Decimal
|
|
from typing import TYPE_CHECKING
|
|
|
|
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QRegularExpression
|
|
|
|
from electrum.bitcoin import TOTAL_COIN_SUPPLY_LIMIT_IN_BTC
|
|
from electrum.i18n import set_language, languages
|
|
from electrum.logging import get_logger
|
|
from electrum.util import DECIMAL_POINT_DEFAULT, base_unit_name_to_decimal_point
|
|
from electrum.invoices import PR_DEFAULT_EXPIRATION_WHEN_CREATING
|
|
from electrum.simple_config import SimpleConfig
|
|
|
|
from .qetypes import QEAmount
|
|
from .auth import AuthMixin, auth_protect
|
|
|
|
|
|
class QEConfig(AuthMixin, QObject):
|
|
_logger = get_logger(__name__)
|
|
|
|
def __init__(self, config: 'SimpleConfig', parent=None):
|
|
super().__init__(parent)
|
|
self.config = config
|
|
|
|
languageChanged = pyqtSignal()
|
|
@pyqtProperty(str, notify=languageChanged)
|
|
def language(self):
|
|
return self.config.LOCALIZATION_LANGUAGE
|
|
|
|
@language.setter
|
|
def language(self, language):
|
|
if language not in languages:
|
|
return
|
|
if self.config.LOCALIZATION_LANGUAGE != language:
|
|
self.config.LOCALIZATION_LANGUAGE = language
|
|
set_language(language)
|
|
self.languageChanged.emit()
|
|
|
|
languagesChanged = pyqtSignal()
|
|
@pyqtProperty('QVariantList', notify=languagesChanged)
|
|
def languagesAvailable(self):
|
|
# sort on translated languages, then re-add Default on top
|
|
langs = copy.deepcopy(languages)
|
|
default = langs.pop('')
|
|
langs_sorted = sorted(list(map(lambda x: {'value': x[0], 'text': x[1]}, langs.items())), key=lambda x: x['text'])
|
|
langs_sorted.insert(0, {'value': '', 'text': default})
|
|
return langs_sorted
|
|
|
|
autoConnectChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=autoConnectChanged)
|
|
def autoConnect(self):
|
|
return self.config.NETWORK_AUTO_CONNECT
|
|
|
|
@autoConnect.setter
|
|
def autoConnect(self, auto_connect):
|
|
self.config.NETWORK_AUTO_CONNECT = auto_connect
|
|
self.autoConnectChanged.emit()
|
|
|
|
# auto_connect is actually a tri-state, expose the undefined case
|
|
@pyqtProperty(bool, notify=autoConnectChanged)
|
|
def autoConnectDefined(self):
|
|
return self.config.cv.NETWORK_AUTO_CONNECT.is_set()
|
|
|
|
baseUnitChanged = pyqtSignal()
|
|
@pyqtProperty(str, notify=baseUnitChanged)
|
|
def baseUnit(self):
|
|
return self.config.get_base_unit()
|
|
|
|
@baseUnit.setter
|
|
def baseUnit(self, unit):
|
|
self.config.set_base_unit(unit)
|
|
self.baseUnitChanged.emit()
|
|
|
|
@pyqtProperty('QRegularExpression', notify=baseUnitChanged)
|
|
def btcAmountRegex(self):
|
|
decimal_point = base_unit_name_to_decimal_point(self.config.get_base_unit())
|
|
max_digits_before_dp = (
|
|
len(str(TOTAL_COIN_SUPPLY_LIMIT_IN_BTC))
|
|
+ (base_unit_name_to_decimal_point("BTC") - decimal_point))
|
|
exp = '[0-9]{0,%d}' % max_digits_before_dp
|
|
if decimal_point > 0:
|
|
exp += '\\.'
|
|
exp += '[0-9]{0,%d}' % decimal_point
|
|
return QRegularExpression(exp)
|
|
|
|
thousandsSeparatorChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=thousandsSeparatorChanged)
|
|
def thousandsSeparator(self):
|
|
return self.config.BTC_AMOUNTS_ADD_THOUSANDS_SEP
|
|
|
|
@thousandsSeparator.setter
|
|
def thousandsSeparator(self, checked):
|
|
self.config.BTC_AMOUNTS_ADD_THOUSANDS_SEP = checked
|
|
self.config.amt_add_thousands_sep = checked
|
|
self.thousandsSeparatorChanged.emit()
|
|
|
|
spendUnconfirmedChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=spendUnconfirmedChanged)
|
|
def spendUnconfirmed(self):
|
|
return not self.config.WALLET_SPEND_CONFIRMED_ONLY
|
|
|
|
@spendUnconfirmed.setter
|
|
def spendUnconfirmed(self, checked):
|
|
self.config.WALLET_SPEND_CONFIRMED_ONLY = not checked
|
|
self.spendUnconfirmedChanged.emit()
|
|
|
|
requestExpiryChanged = pyqtSignal()
|
|
@pyqtProperty(int, notify=requestExpiryChanged)
|
|
def requestExpiry(self):
|
|
return self.config.WALLET_PAYREQ_EXPIRY_SECONDS
|
|
|
|
@requestExpiry.setter
|
|
def requestExpiry(self, expiry):
|
|
self.config.WALLET_PAYREQ_EXPIRY_SECONDS = expiry
|
|
self.requestExpiryChanged.emit()
|
|
|
|
pinCodeChanged = pyqtSignal()
|
|
@pyqtProperty(str, notify=pinCodeChanged)
|
|
def pinCode(self):
|
|
return self.config.CONFIG_PIN_CODE or ""
|
|
|
|
@pinCode.setter
|
|
def pinCode(self, pin_code):
|
|
if pin_code == '':
|
|
self.pinCodeRemoveAuth()
|
|
else:
|
|
self.config.CONFIG_PIN_CODE = pin_code
|
|
self.pinCodeChanged.emit()
|
|
|
|
@auth_protect(method='wallet')
|
|
def pinCodeRemoveAuth(self):
|
|
self.config.CONFIG_PIN_CODE = ""
|
|
self.pinCodeChanged.emit()
|
|
|
|
useGossipChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=useGossipChanged)
|
|
def useGossip(self):
|
|
return self.config.LIGHTNING_USE_GOSSIP
|
|
|
|
@useGossip.setter
|
|
def useGossip(self, gossip):
|
|
self.config.LIGHTNING_USE_GOSSIP = gossip
|
|
self.useGossipChanged.emit()
|
|
|
|
useFallbackAddressChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=useFallbackAddressChanged)
|
|
def useFallbackAddress(self):
|
|
return self.config.WALLET_BOLT11_FALLBACK
|
|
|
|
@useFallbackAddress.setter
|
|
def useFallbackAddress(self, use_fallback):
|
|
self.config.WALLET_BOLT11_FALLBACK = use_fallback
|
|
self.useFallbackAddressChanged.emit()
|
|
|
|
enableDebugLogsChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=enableDebugLogsChanged)
|
|
def enableDebugLogs(self):
|
|
gui_setting = self.config.GUI_ENABLE_DEBUG_LOGS
|
|
return gui_setting or bool(self.config.get('verbosity'))
|
|
|
|
@pyqtProperty(bool, notify=enableDebugLogsChanged)
|
|
def canToggleDebugLogs(self):
|
|
gui_setting = self.config.GUI_ENABLE_DEBUG_LOGS
|
|
return not self.config.get('verbosity') or gui_setting
|
|
|
|
@enableDebugLogs.setter
|
|
def enableDebugLogs(self, enable):
|
|
self.config.GUI_ENABLE_DEBUG_LOGS = enable
|
|
self.enableDebugLogsChanged.emit()
|
|
|
|
useRecoverableChannelsChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=useRecoverableChannelsChanged)
|
|
def useRecoverableChannels(self):
|
|
return self.config.LIGHTNING_USE_RECOVERABLE_CHANNELS
|
|
|
|
@useRecoverableChannels.setter
|
|
def useRecoverableChannels(self, useRecoverableChannels):
|
|
self.config.LIGHTNING_USE_RECOVERABLE_CHANNELS = useRecoverableChannels
|
|
self.useRecoverableChannelsChanged.emit()
|
|
|
|
trustedcoinPrepayChanged = pyqtSignal()
|
|
@pyqtProperty(int, notify=trustedcoinPrepayChanged)
|
|
def trustedcoinPrepay(self):
|
|
return self.config.PLUGIN_TRUSTEDCOIN_NUM_PREPAY
|
|
|
|
@trustedcoinPrepay.setter
|
|
def trustedcoinPrepay(self, num_prepay):
|
|
if num_prepay != self.config.PLUGIN_TRUSTEDCOIN_NUM_PREPAY:
|
|
self.config.PLUGIN_TRUSTEDCOIN_NUM_PREPAY = num_prepay
|
|
self.trustedcoinPrepayChanged.emit()
|
|
|
|
preferredRequestTypeChanged = pyqtSignal()
|
|
@pyqtProperty(str, notify=preferredRequestTypeChanged)
|
|
def preferredRequestType(self):
|
|
return self.config.GUI_QML_PREFERRED_REQUEST_TYPE
|
|
|
|
@preferredRequestType.setter
|
|
def preferredRequestType(self, preferred_request_type):
|
|
if preferred_request_type != self.config.GUI_QML_PREFERRED_REQUEST_TYPE:
|
|
self.config.GUI_QML_PREFERRED_REQUEST_TYPE = preferred_request_type
|
|
self.preferredRequestTypeChanged.emit()
|
|
|
|
userKnowsPressAndHoldChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=userKnowsPressAndHoldChanged)
|
|
def userKnowsPressAndHold(self):
|
|
return self.config.GUI_QML_USER_KNOWS_PRESS_AND_HOLD
|
|
|
|
@userKnowsPressAndHold.setter
|
|
def userKnowsPressAndHold(self, userKnowsPressAndHold):
|
|
if userKnowsPressAndHold != self.config.GUI_QML_USER_KNOWS_PRESS_AND_HOLD:
|
|
self.config.GUI_QML_USER_KNOWS_PRESS_AND_HOLD = userKnowsPressAndHold
|
|
self.userKnowsPressAndHoldChanged.emit()
|
|
|
|
@pyqtSlot('qint64', result=str)
|
|
@pyqtSlot(QEAmount, result=str)
|
|
def formatSatsForEditing(self, satoshis):
|
|
if isinstance(satoshis, QEAmount):
|
|
satoshis = satoshis.satsInt
|
|
return self.config.format_amount(
|
|
satoshis,
|
|
add_thousands_sep=False,
|
|
)
|
|
|
|
@pyqtSlot('qint64', result=str)
|
|
@pyqtSlot('qint64', bool, result=str)
|
|
@pyqtSlot(QEAmount, result=str)
|
|
@pyqtSlot(QEAmount, bool, result=str)
|
|
def formatSats(self, satoshis, with_unit=False):
|
|
if isinstance(satoshis, QEAmount):
|
|
satoshis = satoshis.satsInt
|
|
if with_unit:
|
|
return self.config.format_amount_and_units(satoshis)
|
|
else:
|
|
return self.config.format_amount(satoshis)
|
|
|
|
@pyqtSlot(QEAmount, result=str)
|
|
@pyqtSlot(QEAmount, bool, result=str)
|
|
def formatMilliSats(self, amount, with_unit=False):
|
|
if isinstance(amount, QEAmount):
|
|
msats = amount.msatsInt
|
|
else:
|
|
return '---'
|
|
precision = 3 # config.amt_precision_post_satoshi is not exposed in preferences
|
|
if with_unit:
|
|
return self.config.format_amount_and_units(msats/1000, precision=precision)
|
|
else:
|
|
return self.config.format_amount(msats/1000, precision=precision)
|
|
|
|
# TODO delegate all this to config.py/util.py
|
|
def decimal_point(self):
|
|
return self.config.BTC_AMOUNTS_DECIMAL_POINT
|
|
|
|
def max_precision(self):
|
|
return self.decimal_point() + 0 #self.extra_precision
|
|
|
|
@pyqtSlot(str, result=QEAmount)
|
|
def unitsToSats(self, unitAmount):
|
|
self._amount = QEAmount()
|
|
try:
|
|
x = Decimal(unitAmount)
|
|
except Exception:
|
|
return self._amount
|
|
|
|
# scale it to max allowed precision, make it an int
|
|
max_prec_amount = int(pow(10, self.max_precision()) * x)
|
|
# if the max precision is simply what unit conversion allows, just return
|
|
if self.max_precision() == self.decimal_point():
|
|
self._amount = QEAmount(amount_sat=max_prec_amount)
|
|
return self._amount
|
|
self._logger.debug('fallthrough')
|
|
# otherwise, scale it back to the expected unit
|
|
#amount = Decimal(max_prec_amount) / Decimal(pow(10, self.max_precision()-self.decimal_point()))
|
|
#return int(amount) #Decimal(amount) if not self.is_int else int(amount)
|
|
return self._amount
|
|
|
|
@pyqtSlot('quint64', result=float)
|
|
def satsToUnits(self, satoshis):
|
|
return satoshis / pow(10,self.config.decimal_point)
|