fe226c4bf5
Replace all user-visible "Bitcoin" and "Electrum" strings across Qt and QML GUIs with "Bitcoin Purple" and "Electrum Purple" respectively. Update the Help menu: replace the Bitcoin Paper link with the Bitcoin Purple whitepaper and point the official website to bitcoinpurpleblockchain.com. Remove the "Distributed by Electrum Technologies GmbH" attribution from the About dialog. No code identifiers, class names or technical references were modified.
904 lines
29 KiB
QML
904 lines
29 KiB
QML
import QtQuick
|
|
import QtQuick.Layouts
|
|
import QtQuick.Controls
|
|
import QtQuick.Controls.Basic
|
|
import QtQuick.Controls.Material
|
|
import QtQuick.Controls.Material.impl
|
|
import QtQuick.Window
|
|
|
|
import QtQml
|
|
|
|
import org.electrum 1.0
|
|
|
|
import "controls"
|
|
|
|
ApplicationWindow
|
|
{
|
|
id: app
|
|
|
|
visible: false // initial value
|
|
|
|
readonly property int statusBarHeight: AppController ? AppController.getStatusBarHeight() : 0
|
|
readonly property int navigationBarHeight: AppController ? AppController.getNavigationBarHeight() : 0
|
|
|
|
// dimensions ignored on android
|
|
width: 480
|
|
height: 800
|
|
|
|
Material.theme: Material.Dark
|
|
Material.primary: Material.Indigo
|
|
Material.accent: Material.LightBlue
|
|
font.pixelSize: constants.fontSizeMedium
|
|
|
|
property QtObject constants: appconstants
|
|
Constants { id: appconstants }
|
|
|
|
property alias stack: mainStackView
|
|
property alias keyboardFreeZone: _keyboardFreeZone
|
|
property alias infobanner: _infobanner
|
|
property color _navigationBarBackgroundColor: 'transparent'
|
|
|
|
property string pendingIntent: ""
|
|
|
|
property variant activeDialogs: []
|
|
|
|
property var _exceptionDialog
|
|
|
|
property var pluginobjects: ({})
|
|
|
|
property QtObject appMenu: Menu {
|
|
id: menu
|
|
|
|
parent: Overlay.overlay
|
|
dim: true
|
|
modal: true
|
|
Overlay.modal: Rectangle {
|
|
color: "#44000000"
|
|
}
|
|
|
|
property int implicitChildrenWidth: 64
|
|
width: implicitChildrenWidth + 60 + constants.paddingLarge
|
|
|
|
MenuItem {
|
|
icon.color: action.enabled ? 'transparent' : Material.iconDisabledColor
|
|
icon.source: '../../icons/network.png'
|
|
action: Action {
|
|
text: qsTr('Network')
|
|
onTriggered: menu.openPage(Qt.resolvedUrl('NetworkOverview.qml'))
|
|
enabled: stack.currentItem.objectName != 'NetworkOverview'
|
|
}
|
|
}
|
|
|
|
MenuItem {
|
|
icon.color: action.enabled ? 'transparent' : Material.iconDisabledColor
|
|
icon.source: '../../icons/preferences.png'
|
|
action: Action {
|
|
text: qsTr('Preferences')
|
|
onTriggered: menu.openPage(Qt.resolvedUrl('Preferences.qml'))
|
|
enabled: stack.currentItem.objectName != 'Properties'
|
|
}
|
|
}
|
|
|
|
MenuItem {
|
|
icon.color: action.enabled ? 'transparent' : Material.iconDisabledColor
|
|
icon.source: '../../icons/electrum-purple.png'
|
|
action: Action {
|
|
text: qsTr('About');
|
|
onTriggered: menu.openPage(Qt.resolvedUrl('About.qml'))
|
|
enabled: stack.currentItem.objectName != 'About'
|
|
}
|
|
}
|
|
|
|
function openPage(url) {
|
|
stack.pushOnRoot(url)
|
|
currentIndex = -1
|
|
}
|
|
|
|
// determine widest element and store in implicitChildrenWidth
|
|
function updateImplicitWidth() {
|
|
for (let i = 0; i < menu.count; i++) {
|
|
var item = menu.itemAt(i)
|
|
var txt = item.text
|
|
var txtwidth = fontMetrics.advanceWidth(txt)
|
|
if (txtwidth > menu.implicitChildrenWidth) {
|
|
menu.implicitChildrenWidth = txtwidth
|
|
}
|
|
}
|
|
}
|
|
|
|
FontMetrics {
|
|
id: fontMetrics
|
|
font: menu.font
|
|
}
|
|
|
|
Component.onCompleted: updateImplicitWidth()
|
|
}
|
|
|
|
function openAppMenu() {
|
|
appMenu.open()
|
|
appMenu.x = app.width - appMenu.width
|
|
appMenu.y = toolbar.height
|
|
}
|
|
|
|
header: ToolBar {
|
|
id: toolbar
|
|
|
|
// Add top margin for status bar on Android when using edge-to-edge
|
|
topPadding: app.statusBarHeight
|
|
|
|
background: Rectangle {
|
|
implicitHeight: 48
|
|
color: constants.dialogColor
|
|
|
|
layer.enabled: true
|
|
layer.effect: ElevationEffect {
|
|
elevation: 4
|
|
fullWidth: true
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
spacing: 0
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
height: toolbar.availableHeight
|
|
|
|
RowLayout {
|
|
id: toolbarTopLayout
|
|
|
|
Layout.fillWidth: true
|
|
Layout.rightMargin: constants.paddingMedium
|
|
Layout.alignment: Qt.AlignVCenter
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: Math.max(implicitHeight, toolbarTopLayout.height)
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
enabled: Daemon.currentWallet &&
|
|
(!stack.currentItem || !stack.currentItem.title || stack.currentItem.title == Daemon.currentWallet.name)
|
|
onClicked: {
|
|
stack.getRoot().menu.open() // open wallet-menu
|
|
stack.getRoot().menu.y = toolbar.height
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
width: parent.width
|
|
|
|
Item {
|
|
Layout.preferredWidth: constants.paddingXLarge
|
|
Layout.preferredHeight: 1
|
|
}
|
|
|
|
Image {
|
|
Layout.preferredWidth: constants.iconSizeSmall
|
|
Layout.preferredHeight: constants.iconSizeSmall
|
|
visible: Daemon.currentWallet &&
|
|
(!stack.currentItem || !stack.currentItem.title || stack.currentItem.title == Daemon.currentWallet.name)
|
|
source: '../../icons/wallet.png'
|
|
}
|
|
|
|
Label {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: Math.max(implicitHeight, toolbarTopLayout.height)
|
|
text: stack.currentItem && stack.currentItem.title
|
|
? stack.currentItem.title
|
|
: Daemon.currentWallet.name
|
|
elide: Label.ElideRight
|
|
verticalAlignment: Qt.AlignVCenter
|
|
font.pixelSize: constants.fontSizeMedium
|
|
font.bold: true
|
|
}
|
|
}
|
|
}
|
|
|
|
Item {
|
|
implicitHeight: 48
|
|
implicitWidth: statusIconsLayout.width
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: openAppMenu() // open global-app-menu
|
|
}
|
|
|
|
RowLayout {
|
|
id: statusIconsLayout
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
Item {
|
|
Layout.preferredWidth: constants.paddingLarge
|
|
Layout.preferredHeight: 1
|
|
}
|
|
|
|
Item {
|
|
visible: Network.isTestNet
|
|
width: column.width
|
|
height: column.height
|
|
|
|
ColumnLayout {
|
|
id: column
|
|
spacing: 0
|
|
Image {
|
|
Layout.alignment: Qt.AlignHCenter
|
|
Layout.preferredWidth: constants.iconSizeSmall
|
|
Layout.preferredHeight: constants.iconSizeSmall
|
|
source: "../../icons/info.png"
|
|
}
|
|
|
|
Label {
|
|
id: networkNameLabel
|
|
text: Network.networkName
|
|
color: Material.accentColor
|
|
font.pixelSize: constants.fontSizeXSmall
|
|
}
|
|
}
|
|
}
|
|
|
|
LightningNetworkStatusIndicator {
|
|
id: lnnsi
|
|
}
|
|
OnchainNetworkStatusIndicator { }
|
|
}
|
|
}
|
|
}
|
|
|
|
// hack to force relayout of toolbar
|
|
// since qt6 LightningNetworkStatusIndicator.visible doesn't trigger relayout(?)
|
|
Item {
|
|
Layout.preferredHeight: 1
|
|
Layout.topMargin: -1
|
|
Layout.preferredWidth: lnnsi.visible
|
|
? 1
|
|
: 2
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
width: parent.width
|
|
height: _keyboardFreeZone.height - header.height
|
|
spacing: 0
|
|
|
|
InfoBanner {
|
|
id: _infobanner
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
StackView {
|
|
id: mainStackView
|
|
Layout.fillHeight: true
|
|
Layout.fillWidth: true
|
|
|
|
initialItem: Component {
|
|
Wallets {}
|
|
}
|
|
|
|
function getRoot() {
|
|
return mainStackView.get(0)
|
|
}
|
|
function pushOnRoot(item) {
|
|
if (mainStackView.depth > 1) {
|
|
mainStackView.replace(mainStackView.get(1), item)
|
|
} else {
|
|
mainStackView.push(item)
|
|
}
|
|
}
|
|
function replaceRoot(item_url) {
|
|
mainStackView.clear()
|
|
mainStackView.push(Qt.resolvedUrl(item_url))
|
|
}
|
|
function updateStylingFromItem(item) {
|
|
_navigationBarBackgroundColor = item && 'navigationBarBackgroundColor' in item
|
|
? item.navigationBarBackgroundColor
|
|
: 'transparent'
|
|
}
|
|
onCurrentItemChanged: updateStylingFromItem(currentItem)
|
|
}
|
|
|
|
// Add bottom padding for navigation bar on Android when UI is edge-to-edge
|
|
Item {
|
|
visible: app.navigationBarHeight > 0 && _keyboardFreeZone.state != 'visible'
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: app.navigationBarHeight
|
|
|
|
Rectangle { anchors.fill: parent; color: _navigationBarBackgroundColor }
|
|
}
|
|
}
|
|
|
|
Timer {
|
|
id: coverTimer
|
|
interval: 10
|
|
onTriggered: {
|
|
app.visible = true
|
|
cover.opacity = 0
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: cover
|
|
parent: Overlay.overlay
|
|
anchors.fill: parent
|
|
|
|
z: 1000
|
|
color: 'black'
|
|
|
|
Behavior on opacity {
|
|
enabled: AppController ? AppController.isAndroid() : false
|
|
NumberAnimation {
|
|
duration: 1000
|
|
easing.type: Easing.OutQuad;
|
|
}
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: _keyboardFreeZone
|
|
// Item as first child in Overlay that adjusts its size to the available
|
|
// screen space minus the virtual keyboard (e.g. to center dialogs in)
|
|
// see also ElDialog.resizeWithKeyboard property
|
|
parent: Overlay.overlay
|
|
width: parent.width
|
|
height: parent.height
|
|
|
|
states: [
|
|
State {
|
|
name: 'visible'
|
|
when: Qt.inputMethod.keyboardRectangle.y
|
|
PropertyChanges {
|
|
target: _keyboardFreeZone
|
|
height: _keyboardFreeZone.parent.height - (Screen.desktopAvailableHeight - (Qt.inputMethod.keyboardRectangle.y/Screen.devicePixelRatio))
|
|
}
|
|
}
|
|
]
|
|
|
|
transitions: [
|
|
Transition {
|
|
from: ''
|
|
to: 'visible'
|
|
NumberAnimation {
|
|
properties: 'height'
|
|
duration: 100
|
|
easing.type: Easing.OutQuad
|
|
}
|
|
},
|
|
Transition {
|
|
from: 'visible'
|
|
to: ''
|
|
SequentialAnimation {
|
|
PauseAnimation {
|
|
duration: 200
|
|
}
|
|
NumberAnimation {
|
|
properties: 'height'
|
|
duration: 50
|
|
easing.type: Easing.OutQuad
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
property alias newWalletWizard: _newWalletWizard
|
|
Component {
|
|
id: _newWalletWizard
|
|
NewWalletWizard {
|
|
onClosed: destroy()
|
|
}
|
|
}
|
|
|
|
property alias termsOfUseWizard: _termsOfUseWizard
|
|
Component {
|
|
id: _termsOfUseWizard
|
|
TermsOfUseWizard {
|
|
onClosed: destroy()
|
|
}
|
|
}
|
|
|
|
property alias serverConnectWizard: _serverConnectWizard
|
|
Component {
|
|
id: _serverConnectWizard
|
|
ServerConnectWizard {
|
|
onClosed: destroy()
|
|
}
|
|
}
|
|
|
|
property alias messageDialog: _messageDialog
|
|
Component {
|
|
id: _messageDialog
|
|
MessageDialog {
|
|
onClosed: destroy()
|
|
}
|
|
}
|
|
|
|
property alias helpDialog: _helpDialog
|
|
Component {
|
|
id: _helpDialog
|
|
HelpDialog {
|
|
onClosed: destroy()
|
|
}
|
|
}
|
|
|
|
property alias passwordDialog: _passwordDialog
|
|
Component {
|
|
id: _passwordDialog
|
|
PasswordDialog {
|
|
onClosed: destroy()
|
|
}
|
|
}
|
|
|
|
property alias genericShareDialog: _genericShareDialog
|
|
Component {
|
|
id: _genericShareDialog
|
|
GenericShareDialog {
|
|
onClosed: destroy()
|
|
}
|
|
}
|
|
|
|
property alias openWalletDialog: _openWalletDialog
|
|
Component {
|
|
id: _openWalletDialog
|
|
OpenWalletDialog {
|
|
onClosed: destroy()
|
|
}
|
|
}
|
|
|
|
property alias loadingWalletDialog: _loadingWalletDialog
|
|
Component {
|
|
id: _loadingWalletDialog
|
|
LoadingWalletDialog {
|
|
onClosed: destroy()
|
|
}
|
|
}
|
|
|
|
property Component scanDialog // set in Component.onCompleted
|
|
Component {
|
|
id: _scanDialog
|
|
QRScanner {
|
|
onFinished: destroy()
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: crashDialog
|
|
ExceptionDialog {
|
|
onClosed: destroy()
|
|
}
|
|
}
|
|
|
|
property alias channelOpenProgressDialog: _channelOpenProgressDialog
|
|
ChannelOpenProgressDialog {
|
|
id: _channelOpenProgressDialog
|
|
}
|
|
|
|
property alias signVerifyMessageDialog: _signVerifyMessageDialog
|
|
Component {
|
|
id: _signVerifyMessageDialog
|
|
SignVerifyMessageDialog {
|
|
onClosed: destroy()
|
|
}
|
|
}
|
|
|
|
property alias nostrSwapServersDialog: _nostrSwapServersDialog
|
|
Component {
|
|
id: _nostrSwapServersDialog
|
|
NostrSwapServersDialog {
|
|
onClosed: destroy()
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: swapDialog
|
|
SwapDialog {
|
|
id: _swapdialog
|
|
onClosed: destroy()
|
|
swaphelper: SwapHelper {
|
|
id: _swaphelper
|
|
wallet: Daemon.currentWallet
|
|
onAuthRequired: (method, authMessage) => {
|
|
app.handleAuthRequired(_swaphelper, method, authMessage)
|
|
}
|
|
onError: (message) => {
|
|
var dialog = app.messageDialog.createObject(app, {
|
|
title: qsTr('Error'),
|
|
iconSource: Qt.resolvedUrl('../../icons/warning.png'),
|
|
text: message
|
|
})
|
|
dialog.open()
|
|
}
|
|
onUndefinedNPub: {
|
|
var dialog = app.nostrSwapServersDialog.createObject(app, {
|
|
swaphelper: _swaphelper,
|
|
selectedPubkey: Config.swapServerNPub
|
|
})
|
|
dialog.accepted.connect(function() {
|
|
Config.swapServerNPub = dialog.selectedPubkey
|
|
_swaphelper.setReadyState()
|
|
})
|
|
dialog.rejected.connect(function() {
|
|
_swaphelper.npubSelectionCancelled()
|
|
})
|
|
dialog.open()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NotificationPopup {
|
|
id: notificationPopup
|
|
width: parent.width
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
coverTimer.start()
|
|
|
|
if (AppController.isAndroid()) {
|
|
app.scanDialog = _scanDialog
|
|
} else {
|
|
// for running on Desktop. uses QtMultimedia.
|
|
app.scanDialog = Qt.createComponent('ScanDialog.qml')
|
|
}
|
|
|
|
function continueWithServerConnection() {
|
|
if (!Network.autoConnectDefined) {
|
|
var dialog = serverConnectWizard.createObject(app)
|
|
// without completed serverConnectWizard we can't start
|
|
dialog.rejected.connect(function() {
|
|
app.visible = false
|
|
AppController.wantClose = true
|
|
Qt.callLater(Qt.quit)
|
|
})
|
|
dialog.accepted.connect(function() {
|
|
Daemon.startNetwork()
|
|
var newww = app.newWalletWizard.createObject(app)
|
|
newww.walletCreated.connect(function() {
|
|
Daemon.availableWallets.reload()
|
|
// and load the new wallet
|
|
Daemon.loadWallet(newww.path, newww.wizard_data['password'])
|
|
})
|
|
newww.open()
|
|
})
|
|
dialog.open()
|
|
} else {
|
|
Daemon.startNetwork()
|
|
if (Daemon.availableWallets.rowCount() > 0) {
|
|
Daemon.loadWallet()
|
|
} else {
|
|
var newww = app.newWalletWizard.createObject(app)
|
|
newww.walletCreated.connect(function() {
|
|
Daemon.availableWallets.reload()
|
|
// and load the new wallet
|
|
Daemon.loadWallet(newww.path, newww.wizard_data['password'])
|
|
})
|
|
newww.open()
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Config.termsOfUseAccepted) {
|
|
var dialog = termsOfUseWizard.createObject(app)
|
|
|
|
dialog.rejected.connect(function() {
|
|
app.visible = false
|
|
AppController.wantClose = true
|
|
Qt.callLater(Qt.quit)
|
|
})
|
|
dialog.accepted.connect(function() {
|
|
Config.termsOfUseAccepted = true
|
|
continueWithServerConnection()
|
|
})
|
|
dialog.open()
|
|
} else {
|
|
continueWithServerConnection()
|
|
}
|
|
}
|
|
|
|
onClosing: (close) => {
|
|
if (AppController.wantClose) {
|
|
// destroy most GUI components so that we don't dump so many null reference warnings on exit
|
|
app.header.visible = false
|
|
mainStackView.clear()
|
|
return
|
|
}
|
|
if (activeDialogs.length > 0) {
|
|
var activeDialog = activeDialogs[activeDialogs.length - 1]
|
|
if (activeDialog.allowClose) {
|
|
console.log('main: dialog.doClose')
|
|
activeDialog.doClose()
|
|
} else {
|
|
console.log('dialog disallowed close')
|
|
}
|
|
close.accepted = false
|
|
return
|
|
}
|
|
if (stack.depth > 1) {
|
|
close.accepted = false
|
|
stack.pop()
|
|
} else {
|
|
var dialog = app.messageDialog.createObject(app, {
|
|
title: qsTr('Close Electrum Purple?'),
|
|
yesno: true
|
|
})
|
|
dialog.accepted.connect(function() {
|
|
AppController.wantClose = true
|
|
app.close()
|
|
})
|
|
dialog.open()
|
|
close.accepted = false
|
|
}
|
|
}
|
|
|
|
property var _pendingBiometricAuth: null
|
|
property var _loadingWalletContext: null
|
|
|
|
Connections {
|
|
target: Biometrics
|
|
function onUnlockSuccess(password) {
|
|
if (app._pendingBiometricAuth) {
|
|
if (app._pendingBiometricAuth.action === 'load_wallet') {
|
|
app._loadingWalletContext = _pendingBiometricAuth
|
|
Daemon.loadWallet(app._pendingBiometricAuth.path, password)
|
|
app._pendingBiometricAuth = null
|
|
return
|
|
}
|
|
|
|
let qtobject = app._pendingBiometricAuth.qtobject
|
|
let method = app._pendingBiometricAuth.method
|
|
|
|
if (Daemon.currentWallet.verifyPassword(password)) {
|
|
qtobject.authProceed()
|
|
} else {
|
|
console.warn("Biometric password invalid falling back to manual input")
|
|
// this shouldn't really happen so we better disable biometric auth
|
|
Biometrics.disable()
|
|
handleManualAuth(qtobject, method, app._pendingBiometricAuth.authMessage)
|
|
}
|
|
app._pendingBiometricAuth = null
|
|
}
|
|
}
|
|
|
|
function onUnlockError(error) {
|
|
console.log("Biometric auth failed: " + error)
|
|
// we end up here if QEBiometrics fails to give us the decrypted password. The user might
|
|
// have cancelled the biometric auth popup or the key got invalidated because a new fingerprint got registered.
|
|
if (app._pendingBiometricAuth) {
|
|
if (app._pendingBiometricAuth.action === 'load_wallet') {
|
|
// set loadingWalletContext to disable biometric auth until the OpenWalletDialog is closed
|
|
app._loadingWalletContext = app._pendingBiometricAuth
|
|
showOpenWalletDialog(app._pendingBiometricAuth.name, app._pendingBiometricAuth.path)
|
|
} else {
|
|
console.log('biometric auth failed, not falling back to passwordDialog')
|
|
app._pendingBiometricAuth.qtobject.authCancel() // no fallback to password dialog
|
|
}
|
|
app._pendingBiometricAuth = null
|
|
}
|
|
}
|
|
|
|
function onAuthRequired(method, authMessage) {
|
|
handleAuthRequired(Biometrics, method, authMessage)
|
|
}
|
|
}
|
|
|
|
property var _opendialog: null
|
|
property var _opendialog_startup: true
|
|
|
|
function showOpenWalletDialog(name, path) {
|
|
if (!_opendialog) {
|
|
_opendialog = openWalletDialog.createObject(app, {
|
|
name: name,
|
|
path: path,
|
|
isStartup: _opendialog_startup,
|
|
})
|
|
_opendialog.closed.connect(function() {
|
|
_opendialog = null
|
|
app._loadingWalletContext = null // dialog closed, we can allow trying biometric auth again
|
|
_opendialog_startup = false
|
|
})
|
|
_opendialog.open()
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: Daemon
|
|
function onWalletRequiresPassword(name, path) {
|
|
console.log('wallet requires password')
|
|
if (Biometrics.isAvailable && Biometrics.isEnabled && !app._loadingWalletContext) {
|
|
app._pendingBiometricAuth = {
|
|
action: 'load_wallet',
|
|
name: name,
|
|
path: path
|
|
}
|
|
Biometrics.unlock()
|
|
} else {
|
|
showOpenWalletDialog(name, path)
|
|
}
|
|
}
|
|
function onWalletOpenError(error) {
|
|
console.log('wallet open error')
|
|
var dialog = app.messageDialog.createObject(app, {
|
|
title: qsTr('Error'),
|
|
iconSource: Qt.resolvedUrl('../../icons/warning.png'),
|
|
text: error
|
|
})
|
|
dialog.open()
|
|
}
|
|
function onAuthRequired(method, authMessage) {
|
|
handleAuthRequired(Daemon, method, authMessage)
|
|
}
|
|
function onLoadingChanged() {
|
|
if (!Daemon.loading)
|
|
return
|
|
console.log('wallet loading')
|
|
var dialog = loadingWalletDialog.createObject(app, { allowClose: false } )
|
|
dialog.open()
|
|
}
|
|
function onWalletLoaded() {
|
|
app._loadingWalletContext = null // either biometric auth or manual auth was successful
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: AppController
|
|
function onUserNotify(wallet_name, message) {
|
|
notificationPopup.show(wallet_name, message)
|
|
}
|
|
function onShowException(crash_data) {
|
|
if (app._exceptionDialog)
|
|
return
|
|
app._exceptionDialog = crashDialog.createObject(app, {
|
|
crashData: crash_data
|
|
})
|
|
app._exceptionDialog.onClosed.connect(function() {
|
|
app._exceptionDialog = null
|
|
})
|
|
app._exceptionDialog.open()
|
|
}
|
|
function onPluginLoaded(name) {
|
|
console.log('plugin ' + name + ' loaded')
|
|
var loader = AppController.plugin(name).loader
|
|
if (loader == undefined)
|
|
return
|
|
var url = Qt.resolvedUrl('../../../plugins/' + name + '/qml/' + loader)
|
|
var comp = Qt.createComponent(url)
|
|
if (comp.status == Component.Error) {
|
|
console.log('Could not find/parse PluginLoader for plugin ' + name)
|
|
console.log(comp.errorString())
|
|
return
|
|
}
|
|
var obj = comp.createObject(app)
|
|
if (obj != null)
|
|
app.pluginobjects[name] = obj
|
|
}
|
|
function onUriReceived(uri) {
|
|
console.log('uri received (main): ' + uri)
|
|
app.pendingIntent = uri
|
|
}
|
|
}
|
|
|
|
function pluginsComponentsByName(comp_name) {
|
|
// return named QML components from plugins
|
|
var plugins = AppController.plugins
|
|
var result = []
|
|
for (var i=0; i < plugins.length; i++) {
|
|
if (!plugins[i].enabled)
|
|
continue
|
|
var pluginobject = app.pluginobjects[plugins[i].name]
|
|
if (!pluginobject)
|
|
continue
|
|
if (!(comp_name in pluginobject))
|
|
continue
|
|
var comp = pluginobject[comp_name]
|
|
if (!comp)
|
|
continue
|
|
|
|
result.push(comp)
|
|
}
|
|
return result
|
|
}
|
|
|
|
Connections {
|
|
target: Daemon.currentWallet
|
|
function onAuthRequired(method, authMessage) {
|
|
handleAuthRequired(Daemon.currentWallet, method, authMessage)
|
|
}
|
|
// TODO: add to notification queue instead of barging through
|
|
function onPaymentSucceeded(key) {
|
|
notificationPopup.show(Daemon.currentWallet.name, qsTr('Payment succeeded'))
|
|
}
|
|
function onPaymentFailed(key, reason) {
|
|
notificationPopup.show(Daemon.currentWallet.name, qsTr('Payment failed') + ': ' + reason)
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: Config
|
|
function onAuthRequired(method, authMessage) {
|
|
handleAuthRequired(Config, method, authMessage)
|
|
}
|
|
}
|
|
|
|
function handleAuthRequired(qtobject, method, authMessage) {
|
|
console.log('auth using method ' + method)
|
|
|
|
if (method === 'payment_auth') {
|
|
if (Config.paymentAuthentication) {
|
|
// treat like a wallet auth request
|
|
method = 'wallet'
|
|
} else {
|
|
handleAuthConfirmationOnly(qtobject, authMessage)
|
|
return
|
|
}
|
|
}
|
|
|
|
if (Daemon.currentWallet.verifyPassword('')) {
|
|
// wallet has no password
|
|
qtobject.authProceed()
|
|
return
|
|
}
|
|
|
|
if (method !== 'wallet_password_only') {
|
|
if (Biometrics.isAvailable && Biometrics.isEnabled) {
|
|
app._pendingBiometricAuth = {
|
|
qtobject: qtobject,
|
|
method: method,
|
|
authMessage: authMessage
|
|
}
|
|
Biometrics.unlock(authMessage)
|
|
return
|
|
}
|
|
}
|
|
|
|
handleManualAuth(qtobject, method, authMessage)
|
|
}
|
|
|
|
function handleManualAuth(qtobject, method, authMessage) {
|
|
// 'payment_auth' should have been converted to 'wallet' at this point
|
|
if (method === 'wallet' || method === 'wallet_password_only') {
|
|
var dialog = app.passwordDialog.createObject(app, authMessage ? {'title': authMessage} : {})
|
|
dialog.passwordEntered.connect(function(password) {
|
|
if (Daemon.currentWallet.verifyPassword(password)) {
|
|
dialog.close()
|
|
qtobject.authProceed()
|
|
} else {
|
|
dialog.clearPassword()
|
|
dialog.errorMessage = qsTr("Invalid Password")
|
|
}
|
|
})
|
|
dialog.rejected.connect(function() {
|
|
qtobject.authCancel()
|
|
})
|
|
dialog.open()
|
|
} else {
|
|
console.log('unknown auth method ' + method)
|
|
qtobject.authCancel()
|
|
}
|
|
}
|
|
|
|
function handleAuthConfirmationOnly(qtobject, authMessage) {
|
|
if (!authMessage) {
|
|
qtobject.authProceed()
|
|
return
|
|
}
|
|
var dialog = app.messageDialog.createObject(app, {
|
|
title: authMessage,
|
|
yesno: true
|
|
})
|
|
dialog.accepted.connect(function() {
|
|
qtobject.authProceed()
|
|
})
|
|
dialog.rejected.connect(function() {
|
|
qtobject.authCancel()
|
|
})
|
|
dialog.open()
|
|
}
|
|
|
|
function startSwap() {
|
|
var swapdialog = swapDialog.createObject(app)
|
|
swapdialog.open()
|
|
}
|
|
|
|
property var _lastActive: 0 // record time of last activity
|
|
property bool _lockDialogShown: false
|
|
|
|
}
|