1
0
Files
electrum/electrum/gui/qml/components/main.qml
ThomasV 337d2a32d8 qml PIN: do not lock inactive app, and remove timeout
- the activity callback does not work properly on android
  (does not work on my phone). Also, it duplicates the lock
  screen function of most phones.

- if we do not lock inactive app, then the PIN feature does
  not need a timeout, and is easier to understand without it.

- in Preferences, explain what it does
2023-03-16 09:37:43 +01:00

533 lines
16 KiB
QML

import QtQuick 2.6
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.0
import QtQuick.Controls.Material.impl 2.12
import QtQml 2.6
import QtMultimedia 5.6
import org.electrum 1.0
import "controls"
ApplicationWindow
{
id: app
visible: false // initial value
// 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 Item constants: appconstants
Constants { id: appconstants }
property alias stack: mainStackView
property variant activeDialogs: []
property bool _wantClose: false
property var _exceptionDialog
property QtObject appMenu: Menu {
parent: Overlay.overlay
dim: true
modal: true
Overlay.modal: Rectangle {
color: "#44000000"
}
id: menu
MenuItem {
icon.color: 'transparent'
action: Action {
text: qsTr('Network')
onTriggered: menu.openPage(Qt.resolvedUrl('NetworkOverview.qml'))
icon.source: '../../icons/network.png'
}
}
MenuItem {
icon.color: 'transparent'
action: Action {
text: qsTr('Preferences');
onTriggered: menu.openPage(Qt.resolvedUrl('Preferences.qml'))
icon.source: '../../icons/preferences.png'
}
}
MenuItem {
icon.color: 'transparent'
action: Action {
text: qsTr('About');
onTriggered: menu.openPage(Qt.resolvedUrl('About.qml'))
icon.source: '../../icons/electrum.png'
}
}
function openPage(url) {
stack.pushOnRoot(url)
currentIndex = -1
}
}
function openAppMenu() {
appMenu.open()
appMenu.x = app.width - appMenu.width
appMenu.y = toolbar.height
}
header: ToolBar {
id: toolbar
background: Rectangle {
implicitHeight: 48
color: Material.dialogColor
layer.enabled: true
layer.effect: ElevationEffect {
elevation: 4
fullWidth: true
}
}
ColumnLayout {
spacing: 0
width: parent.width
height: toolbar.height
RowLayout {
id: toolbarTopLayout
Layout.fillWidth: true
Layout.rightMargin: constants.paddingMedium
Layout.alignment: Qt.AlignVCenter
Item {
Layout.preferredWidth: constants.paddingXLarge
Layout.preferredHeight: 1
}
Image {
Layout.preferredWidth: constants.iconSizeSmall
Layout.preferredHeight: constants.iconSizeSmall
visible: Daemon.currentWallet && (!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.title
? stack.currentItem.title
: Daemon.currentWallet.name
elide: Label.ElideRight
verticalAlignment: Qt.AlignVCenter
font.pixelSize: constants.fontSizeMedium
font.bold: true
MouseArea {
// height: toolbarTopLayout.height
anchors.fill: parent
onClicked: {
stack.getRoot().menu.open()
stack.getRoot().menu.y = toolbar.height
}
}
}
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
}
}
}
Image {
Layout.preferredWidth: constants.iconSizeSmall
Layout.preferredHeight: constants.iconSizeSmall
visible: Daemon.currentWallet && Daemon.currentWallet.isWatchOnly
source: '../../icons/eye1.png'
scale: 1.5
}
LightningNetworkStatusIndicator {
MouseArea {
anchors.fill: parent
onClicked: openAppMenu()
}
}
OnchainNetworkStatusIndicator {
MouseArea {
anchors.fill: parent
onClicked: openAppMenu()
}
}
}
WalletSummary {
id: walletSummary
Layout.preferredWidth: app.width
}
}
}
StackView {
id: mainStackView
anchors.fill: parent
initialItem: Qt.resolvedUrl('WalletMainView.qml')
function getRoot() {
return mainStackView.get(0)
}
function pushOnRoot(item) {
if (mainStackView.depth > 1) {
mainStackView.replace(mainStackView.get(1), item)
} else {
mainStackView.push(item)
}
}
}
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;
}
}
}
property alias newWalletWizard: _newWalletWizard
Component {
id: _newWalletWizard
NewWalletWizard {
parent: Overlay.overlay
Overlay.modal: Rectangle {
color: "#aa000000"
}
}
}
property alias serverConnectWizard: _serverConnectWizard
Component {
id: _serverConnectWizard
ServerConnectWizard {
parent: Overlay.overlay
Overlay.modal: Rectangle {
color: "#aa000000"
}
}
}
property alias messageDialog: _messageDialog
Component {
id: _messageDialog
MessageDialog {
onClosed: destroy()
}
}
property alias passwordDialog: _passwordDialog
Component {
id: _passwordDialog
PasswordDialog {
onClosed: destroy()
}
}
property alias pinDialog: _pinDialog
Component {
id: _pinDialog
Pin {
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 alias channelOpenProgressDialog: _channelOpenProgressDialog
ChannelOpenProgressDialog {
id: _channelOpenProgressDialog
}
NotificationPopup {
id: notificationPopup
width: parent.width
}
Component {
id: crashDialog
ExceptionDialog {
z: 1000
}
}
property alias swaphelper: _swaphelper
Component {
id: _swaphelper
SwapHelper {
id: __swaphelper
wallet: Daemon.currentWallet
onConfirm: {
var dialog = app.messageDialog.createObject(app, {text: message, yesno: true})
dialog.yesClicked.connect(function() {
dialog.close()
__swaphelper.executeSwap(true)
})
dialog.open()
}
onAuthRequired: {
app.handleAuthRequired(__swaphelper, method)
}
onError: {
var dialog = app.messageDialog.createObject(app, { text: message })
dialog.open()
}
}
}
Component.onCompleted: {
coverTimer.start()
if (!Config.autoConnectDefined) {
var dialog = serverConnectWizard.createObject(app)
// without completed serverConnectWizard we can't start
dialog.rejected.connect(function() {
app.visible = false
Qt.callLater(Qt.quit)
})
dialog.accepted.connect(function() {
var newww = app.newWalletWizard.createObject(app)
newww.walletCreated.connect(function() {
Daemon.availableWallets.reload()
// and load the new wallet
Daemon.load_wallet(newww.path, newww.wizard_data['password'])
})
newww.open()
})
dialog.open()
} else {
if (Daemon.availableWallets.rowCount() > 0) {
Daemon.load_wallet()
} else {
var newww = app.newWalletWizard.createObject(app)
newww.walletCreated.connect(function() {
Daemon.availableWallets.reload()
// and load the new wallet
Daemon.load_wallet(newww.path, newww.wizard_data['password'])
})
newww.open()
}
}
}
onClosing: {
if (activeDialogs.length > 0) {
var activeDialog = activeDialogs[activeDialogs.length - 1]
if (activeDialog.allowClose) {
activeDialog.doClose()
} else {
console.log('dialog disallowed close')
}
close.accepted = false
return
}
if (stack.depth > 1) {
close.accepted = false
stack.pop()
} else {
// destroy most GUI components so that we don't dump so many null reference warnings on exit
if (app._wantClose) {
app.header.visible = false
mainStackView.clear()
} else {
var dialog = app.messageDialog.createObject(app, {
text: qsTr('Close Electrum?'),
yesno: true
})
dialog.yesClicked.connect(function() {
dialog.close()
app._wantClose = true
app.close()
})
dialog.open()
close.accepted = false
}
}
}
Connections {
target: Daemon
function onWalletRequiresPassword(name, path) {
console.log('wallet requires password')
var dialog = openWalletDialog.createObject(app, { path: path, name: name })
dialog.open()
}
function onWalletOpenError(error) {
console.log('wallet open error')
var dialog = app.messageDialog.createObject(app, {'text': error})
dialog.open()
}
function onAuthRequired(method) {
handleAuthRequired(Daemon, method)
}
function onLoadingChanged() {
if (!Daemon.loading)
return
console.log('wallet loading')
var dialog = loadingWalletDialog.createObject(app, { allowClose: false } )
dialog.open()
}
}
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()
}
}
Connections {
target: Daemon.currentWallet
function onAuthRequired(method) {
handleAuthRequired(Daemon.currentWallet, method)
}
// 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) {
handleAuthRequired(Config, method)
}
}
function handleAuthRequired(qtobject, method) {
console.log('auth using method ' + method)
if (method == 'wallet') {
if (Daemon.currentWallet.verify_password('')) {
// wallet has no password
qtobject.authProceed()
} else {
var dialog = app.passwordDialog.createObject(app, {'title': qsTr('Enter current password')})
dialog.accepted.connect(function() {
if (Daemon.currentWallet.verify_password(dialog.password)) {
qtobject.authProceed()
} else {
qtobject.authCancel()
}
})
dialog.rejected.connect(function() {
qtobject.authCancel()
})
dialog.open()
}
} else if (method == 'pin') {
if (Config.pinCode == '') {
// no PIN configured
qtobject.authProceed()
} else {
var dialog = app.pinDialog.createObject(app, {mode: 'check', pincode: Config.pinCode})
dialog.accepted.connect(function() {
qtobject.authProceed()
dialog.close()
})
dialog.rejected.connect(function() {
qtobject.authCancel()
})
dialog.open()
}
} else {
console.log('unknown auth method ' + method)
qtobject.authCancel()
}
}
property var _lastActive: 0 // record time of last activity
property bool _lockDialogShown: false
}