mirror of
https://github.com/monero-project/monero-gui.git
synced 2024-11-16 15:58:11 +00:00
Merge pull request #217
3752ec7
send page: check daemon status after every refresh (Jaquee)01549a3
walletManager: coding conventions (Jaquee)0ae3d67
daemon console -> log + adjusted height (Jaquee)0e8cd14
Wallet: add m_initialized (Jaquee)c7232e1
set wallet connection status before querying sync status (Jaquee)3f8e05d
Wallet: Cache connection status query (Jaquee)36a6b89
connect onWalletConnectionStatusChanged() (Jaquee)93a8200
Transfer: new connectionStatus handling + show status msg (Jaquee)76e6ae8
remove reference to old pw-dialog (Jaquee)18b7a67
Wallet: add connectionStatusChanged signal (Jaquee)d9f4ab4
daemonManager: wait for daemon stop on app close (Jaquee)f62bb68
daemonManagerDialog: use new ConnectionStatus enum (Jaquee)dd01f59
hide daemon sync progress after disconnecting wallet (Jaquee)8d19a03
DaemonManager: add stateChanged() (Jaquee)760e01b
daemonManagerDialog: show processdialog when starting (Jaquee)bb881d9
show processingSplash when starting/stopping daemon (Jaquee)8361dda
resolve rebase conflict (Jaquee)8dfa79e
Shutdown daemon and close wallet properly on app exit (Jaquee)7876957
DaemonManager::closing() (Jacob Brydolf)065b060
main: debug messages (Jacob Brydolf)b4eb489
DaemonManager: forward command line arguments to monerod (Jacob Brydolf)752ff26
forward command line arguments to DaemonManager (Jaquee)7840dab
DaemonManager: console debug output (Jacob Brydolf)14a5bd5
settings: added daemon console (Jacob Brydolf)b53ef00
history tx details: resized popup (Jacob Brydolf)b4f29b2
StandardDialog: changed default sizes (Jacob Brydolf)5855700
daemonManagerDialog: added starting signal (Jacob Brydolf)3a43154
DaemonManager: added console updated signals (Jacob Brydolf)3df9e44
DaemonManager: windows support (Jacob Brydolf)5da9f8f
standardDialog: close window before continue (Jacob Brydolf)5a110f4
replace message dialog with custom dialog (Jacob Brydolf)d465780
resized standard dialog (Jacob Brydolf)482bd30
settings: enable/disable daemon start/stop buttons (Jacob Brydolf)4e7de8c
proper daemon shutdown on app exit (Jaquee)48471f3
onDaemonStarted/stopped signals/slots (Jaquee)de635cb
pw dialog: close popup before continue (Jacob Brydolf)86772be
added standardDialog component (Jacob Brydolf)ae977af
settings: add daemon manager (Jacob Brydolf)2775124
small error in daemon manager dialog (Jacob Brydolf)82c39e0
WalletManager: include dependencies (Jaquee)1c6884e
Show daemon manager dialog if daemon isnt started (Jacob Brydolf)9fbfbc4
daemonManager: embed as context property (Jaquee)4cdc258
WalletManager: basic functionality (Jacob Brydolf)
This commit is contained in:
commit
bfcdf113f9
16 changed files with 567 additions and 38 deletions
|
@ -32,6 +32,7 @@ import QtQuick 2.2
|
|||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtGraphicalEffects 1.0
|
||||
import moneroComponents.Wallet 1.0
|
||||
|
||||
import "./pages"
|
||||
|
||||
|
@ -56,9 +57,6 @@ Rectangle {
|
|||
signal generatePaymentIdInvoked()
|
||||
signal checkPaymentClicked(string address, string txid, string txkey);
|
||||
|
||||
// Disable transfer page if daemon isnt fully synced
|
||||
enabled: (currentView !== transferView || appWindow.daemonSynced)
|
||||
|
||||
color: "#F0EEEE"
|
||||
|
||||
onCurrentViewChanged: {
|
||||
|
@ -72,6 +70,10 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
function updateStatus(){
|
||||
transferView.updateStatus();
|
||||
}
|
||||
|
||||
|
||||
// XXX: just for memo, to be removed
|
||||
// states: [
|
||||
|
@ -292,14 +294,6 @@ Rectangle {
|
|||
color: "#DBDBDB"
|
||||
|
||||
}
|
||||
Rectangle {
|
||||
id:desaturate
|
||||
color:"black"
|
||||
anchors.fill: parent
|
||||
opacity: 0.1
|
||||
visible: (root.enabled)? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
/* connect "payment" click */
|
||||
Connections {
|
||||
|
|
120
components/DaemonManagerDialog.qml
Normal file
120
components/DaemonManagerDialog.qml
Normal file
|
@ -0,0 +1,120 @@
|
|||
// Copyright (c) 2014-2015, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Dialogs 1.2
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtQuick.Window 2.0
|
||||
|
||||
import "../components" as MoneroComponents
|
||||
|
||||
Window {
|
||||
id: root
|
||||
modality: Qt.ApplicationModal
|
||||
flags: Qt.Window | Qt.FramelessWindowHint
|
||||
|
||||
signal rejected()
|
||||
signal started();
|
||||
|
||||
function open() {
|
||||
show()
|
||||
}
|
||||
|
||||
// TODO: implement without hardcoding sizes
|
||||
width: 480
|
||||
height: 200
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
spacing: 10
|
||||
anchors { fill: parent; margins: 35 }
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
//anchors {fill: parent; margins: 16 }
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
Label {
|
||||
text: qsTr("Daemon doesn't appear to be running")
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.columnSpan: 2
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 24
|
||||
font.family: "Arial"
|
||||
color: "#555555"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: buttons
|
||||
spacing: 60
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: okButton
|
||||
width: 120
|
||||
fontSize: 14
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
text: qsTr("Start daemon")
|
||||
KeyNavigation.tab: cancelButton
|
||||
onClicked: {
|
||||
root.close()
|
||||
appWindow.startDaemon();
|
||||
root.started()
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: cancelButton
|
||||
width: 120
|
||||
fontSize: 14
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
text: qsTr("Cancel")
|
||||
|
||||
onClicked: {
|
||||
root.close()
|
||||
root.rejected()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import QtQuick 2.0
|
||||
import moneroComponents.Wallet 1.0
|
||||
|
||||
Item {
|
||||
id: item
|
||||
|
@ -45,7 +46,7 @@ Item {
|
|||
|
||||
// TODO: lower daemon block height cache, ttl and refresh interval?
|
||||
|
||||
item.visible = (currentBlock < targetBlock)
|
||||
item.visible = (currentWallet.connected !== Wallet.ConnectionStatus_Disconnected) && (currentBlock < targetBlock)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ ListView {
|
|||
|
||||
StandardDialog {
|
||||
id: detailsPopup
|
||||
width:600
|
||||
cancelVisible: false
|
||||
okVisible: true
|
||||
}
|
||||
|
|
|
@ -152,8 +152,8 @@ Window {
|
|||
text: qsTr("Cancel")
|
||||
KeyNavigation.tab: passwordInput
|
||||
onClicked: {
|
||||
root.rejected()
|
||||
root.close()
|
||||
root.rejected()
|
||||
}
|
||||
}
|
||||
MoneroComponents.StandardButton {
|
||||
|
@ -167,8 +167,8 @@ Window {
|
|||
text: qsTr("Ok")
|
||||
KeyNavigation.tab: cancelButton
|
||||
onClicked: {
|
||||
root.accepted()
|
||||
root.close()
|
||||
root.accepted()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,9 +40,12 @@ Window {
|
|||
modality: Qt.ApplicationModal
|
||||
flags: Qt.Window | Qt.FramelessWindowHint
|
||||
property alias title: dialogTitle.text
|
||||
property alias content: dialogContent.text
|
||||
property alias text: dialogContent.text
|
||||
property alias content: root.text
|
||||
property alias cancelVisible: cancelButton.visible
|
||||
property alias okVisible: okButton.visible
|
||||
property alias textArea: dialogContent
|
||||
property var icon
|
||||
|
||||
// same signals as Dialog has
|
||||
signal accepted()
|
||||
|
@ -54,8 +57,8 @@ Window {
|
|||
}
|
||||
|
||||
// TODO: implement without hardcoding sizes
|
||||
width: 600
|
||||
height: 480
|
||||
width: 480
|
||||
height: 280
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
|
@ -81,6 +84,7 @@ Window {
|
|||
TextArea {
|
||||
id : dialogContent
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
font.family: "Arial"
|
||||
textFormat: TextEdit.AutoText
|
||||
readOnly: true
|
||||
|
@ -105,8 +109,9 @@ Window {
|
|||
text: qsTr("Ok")
|
||||
KeyNavigation.tab: cancelButton
|
||||
onClicked: {
|
||||
root.accepted()
|
||||
root.close()
|
||||
root.accepted()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,8 +125,8 @@ Window {
|
|||
pressedColor: "#FF4304"
|
||||
text: qsTr("Cancel")
|
||||
onClicked: {
|
||||
root.rejected()
|
||||
root.close()
|
||||
root.rejected()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
8
main.cpp
8
main.cpp
|
@ -31,6 +31,7 @@
|
|||
#include <QtQml>
|
||||
#include <QStandardPaths>
|
||||
#include <QDebug>
|
||||
#include <QObject>
|
||||
#include "clipboardAdapter.h"
|
||||
#include "filter.h"
|
||||
#include "oscursor.h"
|
||||
|
@ -44,6 +45,7 @@
|
|||
#include "TransactionHistory.h"
|
||||
#include "model/TransactionHistoryModel.h"
|
||||
#include "model/TransactionHistorySortFilterModel.h"
|
||||
#include "daemon/DaemonManager.h"
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -88,6 +90,8 @@ int main(int argc, char *argv[])
|
|||
qmlRegisterUncreatableType<TransactionInfo>("moneroComponents.TransactionInfo", 1, 0, "TransactionInfo",
|
||||
"TransactionHistory can't be instantiated directly");
|
||||
|
||||
qmlRegisterUncreatableType<DaemonManager>("moneroComponents.DaemonManager", 1, 0, "DaemonManager",
|
||||
"DaemonManager can't be instantiated directly");
|
||||
qRegisterMetaType<PendingTransaction::Priority>();
|
||||
qRegisterMetaType<TransactionInfo::Direction>();
|
||||
qRegisterMetaType<TransactionHistoryModel::TransactionInfoRole>();
|
||||
|
@ -104,6 +108,10 @@ int main(int argc, char *argv[])
|
|||
engine.rootContext()->setContextProperty("translationManager", TranslationManager::instance());
|
||||
|
||||
engine.addImageProvider(QLatin1String("qrcode"), new QRCodeImageProvider());
|
||||
const QStringList arguments = QCoreApplication::arguments();
|
||||
DaemonManager * daemonManager = DaemonManager::instance(&arguments);
|
||||
QObject::connect(&app, SIGNAL(aboutToQuit()), daemonManager, SLOT(closing()));
|
||||
engine.rootContext()->setContextProperty("daemonManager", daemonManager);
|
||||
|
||||
// export to QML monero accounts root directory
|
||||
// wizard is talking about where
|
||||
|
|
70
main.qml
70
main.qml
|
@ -59,6 +59,7 @@ ApplicationWindow {
|
|||
property int restoreHeight:0
|
||||
property bool daemonSynced: false
|
||||
property int maxWindowHeight: (Screen.height < 900)? 720 : 800;
|
||||
property bool daemonRunning: false
|
||||
|
||||
// true if wallet ever synchronized
|
||||
property bool walletInitialized : false
|
||||
|
@ -212,6 +213,7 @@ ApplicationWindow {
|
|||
currentWallet.moneySpent.disconnect(onWalletMoneySent)
|
||||
currentWallet.moneyReceived.disconnect(onWalletMoneyReceived)
|
||||
currentWallet.transactionCreated.disconnect(onTransactionCreated)
|
||||
currentWallet.connectionStatusChanged.disconnect(onWalletConnectionStatusChanged)
|
||||
|
||||
currentWallet.refreshed.connect(onWalletRefresh)
|
||||
currentWallet.updated.connect(onWalletUpdate)
|
||||
|
@ -219,6 +221,7 @@ ApplicationWindow {
|
|||
currentWallet.moneySpent.connect(onWalletMoneySent)
|
||||
currentWallet.moneyReceived.connect(onWalletMoneyReceived)
|
||||
currentWallet.transactionCreated.connect(onTransactionCreated)
|
||||
currentWallet.connectionStatusChanged.connect(onWalletConnectionStatusChanged)
|
||||
|
||||
|
||||
console.log("initializing with daemon address: ", persistentSettings.daemon_address)
|
||||
|
@ -232,6 +235,11 @@ ApplicationWindow {
|
|||
return wallet_path;
|
||||
}
|
||||
|
||||
function onWalletConnectionStatusChanged(){
|
||||
console.log("Wallet connection status changed")
|
||||
middlePanel.updateStatus();
|
||||
}
|
||||
|
||||
function onWalletOpened(wallet) {
|
||||
console.log(">>> wallet opened: " + wallet)
|
||||
if (wallet.status !== Wallet.Status_Ok) {
|
||||
|
@ -280,18 +288,23 @@ ApplicationWindow {
|
|||
hideProcessingSplash()
|
||||
}
|
||||
|
||||
// Daemon connected
|
||||
leftPanel.networkStatus.connected = currentWallet.connected
|
||||
|
||||
// Check daemon status
|
||||
var dCurrentBlock = currentWallet.daemonBlockChainHeight();
|
||||
var dTargetBlock = currentWallet.daemonBlockChainTargetHeight();
|
||||
leftPanel.daemonProgress.updateProgress(dCurrentBlock,dTargetBlock);
|
||||
|
||||
// Daemon connected
|
||||
leftPanel.networkStatus.connected = currentWallet.connected
|
||||
|
||||
// Daemon fully synced
|
||||
// TODO: implement onDaemonSynced or similar in wallet API and don't start refresh thread before daemon is synced
|
||||
daemonSynced = (currentWallet.connected != Wallet.ConnectionStatus_Disconnected && dCurrentBlock >= dTargetBlock)
|
||||
leftPanel.daemonProgress.updateProgress(dCurrentBlock,dTargetBlock);
|
||||
middlePanel.updateStatus();
|
||||
|
||||
// If wallet isnt connected and no daemon is running - Ask
|
||||
if(currentWallet.connected === Wallet.ConnectionStatus_Disconnected && !daemonManager.running() && !walletInitialized){
|
||||
daemonManagerDialog.open();
|
||||
}
|
||||
|
||||
// Refresh is succesfull if blockchain height > 1
|
||||
if (currentWallet.blockChainHeight() > 1){
|
||||
|
@ -317,10 +330,29 @@ ApplicationWindow {
|
|||
walletInitialized = true
|
||||
}
|
||||
|
||||
|
||||
onWalletUpdate();
|
||||
}
|
||||
|
||||
function startDaemon(){
|
||||
appWindow.showProcessingSplash(qsTr("Waiting for daemon to start..."))
|
||||
daemonManager.start();
|
||||
}
|
||||
|
||||
function stopDaemon(){
|
||||
appWindow.showProcessingSplash(qsTr("Waiting for daemon to stop..."))
|
||||
daemonManager.stop();
|
||||
}
|
||||
|
||||
function onDaemonStarted(){
|
||||
console.log("daemon started");
|
||||
daemonRunning = true;
|
||||
}
|
||||
function onDaemonStopped(){
|
||||
console.log("daemon stopped");
|
||||
daemonRunning = false;
|
||||
}
|
||||
|
||||
|
||||
function onWalletNewBlock(blockHeight) {
|
||||
if (splash.visible) {
|
||||
var currHeight = blockHeight
|
||||
|
@ -633,6 +665,9 @@ ApplicationWindow {
|
|||
walletManager.walletOpened.connect(onWalletOpened);
|
||||
walletManager.walletClosed.connect(onWalletClosed);
|
||||
|
||||
daemonManager.daemonStarted.connect(onDaemonStarted);
|
||||
daemonManager.daemonStopped.connect(onDaemonStopped);
|
||||
|
||||
if(!walletsFound()) {
|
||||
rootItem.state = "wizard"
|
||||
} else {
|
||||
|
@ -668,11 +703,11 @@ ApplicationWindow {
|
|||
// TODO: replace with customized popups
|
||||
|
||||
// Information dialog
|
||||
MessageDialog {
|
||||
StandardDialog {
|
||||
// dynamically change onclose handler
|
||||
property var onCloseCallback
|
||||
id: informationPopup
|
||||
standardButtons: StandardButton.Ok
|
||||
cancelVisible: false
|
||||
onAccepted: {
|
||||
if (onCloseCallback) {
|
||||
onCloseCallback()
|
||||
|
@ -681,10 +716,10 @@ ApplicationWindow {
|
|||
}
|
||||
|
||||
// Confrirmation aka question dialog
|
||||
MessageDialog {
|
||||
StandardDialog {
|
||||
id: transactionConfirmationPopup
|
||||
standardButtons: StandardButton.Ok + StandardButton.Cancel
|
||||
onAccepted: {
|
||||
close();
|
||||
handleTransactionConfirmed()
|
||||
}
|
||||
}
|
||||
|
@ -721,6 +756,10 @@ ApplicationWindow {
|
|||
|
||||
}
|
||||
|
||||
DaemonManagerDialog {
|
||||
id: daemonManagerDialog
|
||||
|
||||
}
|
||||
|
||||
ProcessingSplash {
|
||||
id: splash
|
||||
|
@ -1032,10 +1071,13 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
onClosing: {
|
||||
// Close and save to disk on app close
|
||||
if (currentWallet != undefined) {
|
||||
walletManager.closeWallet(currentWallet);
|
||||
currentWallet = undefined
|
||||
}
|
||||
// Make sure wallet is closed before app exit (~Wallet() isn't always invoked)
|
||||
// Daemon shutdown is handled with signal/slot in main.cpp
|
||||
if (currentWallet != undefined) {
|
||||
walletManager.closeWallet(currentWallet);
|
||||
currentWallet = undefined
|
||||
}
|
||||
// Stop daemon
|
||||
daemonManager.stop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@ HEADERS += \
|
|||
src/model/TransactionHistorySortFilterModel.h \
|
||||
src/QR-Code-generator/BitBuffer.hpp \
|
||||
src/QR-Code-generator/QrCode.hpp \
|
||||
src/QR-Code-generator/QrSegment.hpp
|
||||
src/QR-Code-generator/QrSegment.hpp \
|
||||
src/daemon/DaemonManager.h
|
||||
|
||||
|
||||
SOURCES += main.cpp \
|
||||
|
@ -49,7 +50,8 @@ SOURCES += main.cpp \
|
|||
src/model/TransactionHistorySortFilterModel.cpp \
|
||||
src/QR-Code-generator/BitBuffer.cpp \
|
||||
src/QR-Code-generator/QrCode.cpp \
|
||||
src/QR-Code-generator/QrSegment.cpp
|
||||
src/QR-Code-generator/QrSegment.cpp \
|
||||
src/daemon/DaemonManager.cpp
|
||||
|
||||
lupdate_only {
|
||||
SOURCES = *.qml \
|
||||
|
|
|
@ -37,7 +37,6 @@ import "../components"
|
|||
import moneroComponents.Clipboard 1.0
|
||||
|
||||
Rectangle {
|
||||
|
||||
property var daemonAddress
|
||||
|
||||
color: "#F0EEEE"
|
||||
|
@ -272,15 +271,94 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Label {
|
||||
id: manageDaemonLabel
|
||||
color: "#4A4949"
|
||||
text: qsTr("Manage daemon") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
visible: true
|
||||
enabled: !appWindow.daemonRunning
|
||||
id: startDaemonButton
|
||||
width: 110
|
||||
text: qsTr("Start daemon") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
onClicked: {
|
||||
appWindow.startDaemon()
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
visible: true
|
||||
enabled: appWindow.daemonRunning
|
||||
id: stopDaemonButton
|
||||
width: 110
|
||||
text: qsTr("Stop daemon") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
onClicked: {
|
||||
appWindow.stopDaemon()
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
visible: true
|
||||
// enabled: appWindow.daemonRunning
|
||||
id: daemonConsolePopupButton
|
||||
width: 110
|
||||
text: qsTr("Show log") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
onClicked: {
|
||||
daemonConsolePopup.open();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Daemon console
|
||||
StandardDialog {
|
||||
id: daemonConsolePopup
|
||||
height:500
|
||||
width:800
|
||||
cancelVisible: false
|
||||
title: qsTr("Daemon log")
|
||||
onAccepted: {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// fires on every page load
|
||||
function onPageCompleted() {
|
||||
console.log("Settings page loaded");
|
||||
initSettings();
|
||||
}
|
||||
|
||||
// fires only once
|
||||
Component.onCompleted: {
|
||||
daemonManager.daemonConsoleUpdated.connect(onDaemonConsoleUpdated)
|
||||
}
|
||||
|
||||
function onDaemonConsoleUpdated(message){
|
||||
// Update daemon console
|
||||
daemonConsolePopup.textArea.append(message)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
import QtQuick 2.0
|
||||
import moneroComponents.PendingTransaction 1.0
|
||||
import "../components"
|
||||
import moneroComponents.Wallet 1.0
|
||||
|
||||
|
||||
Rectangle {
|
||||
|
@ -331,4 +332,69 @@ Rectangle {
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id:desaturate
|
||||
color:"black"
|
||||
anchors.fill: parent
|
||||
opacity: 0.1
|
||||
visible: (root.enabled)? 0 : 1;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
x: root.width/2 - width/2
|
||||
y: root.height/2 - height/2
|
||||
height:statusText.paintedHeight + 50
|
||||
width:statusText.paintedWidth + 40
|
||||
visible: statusText.text != ""
|
||||
opacity: 0.9
|
||||
|
||||
Text {
|
||||
id: statusText
|
||||
anchors.fill:parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
//Disable password page until enabled by updateStatus
|
||||
root.enabled = false
|
||||
}
|
||||
|
||||
// fires on every page load
|
||||
function onPageCompleted() {
|
||||
console.log("transfer page loaded")
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
//TODO: Add daemon sync status
|
||||
//TODO: enable send page when we're connected and daemon is synced
|
||||
|
||||
function updateStatus() {
|
||||
console.log("updated transfer page status")
|
||||
if(typeof currentWallet === "undefined") {
|
||||
statusText.text = qsTr("Wallet is not connected to daemon.")
|
||||
return;
|
||||
}
|
||||
|
||||
switch (currentWallet.connected) {
|
||||
case Wallet.ConnectionStatus_Disconnected:
|
||||
statusText.text = qsTr("Wallet is not connected to daemon.")
|
||||
break
|
||||
case Wallet.ConnectionStatus_WrongVersion:
|
||||
statusText.text = qsTr("Connected daemon is not compatible with GUI. \n" +
|
||||
"Please upgrade or connect to another daemon")
|
||||
break
|
||||
default:
|
||||
if(!appWindow.daemonSynced){
|
||||
statusText.text = qsTr("Waiting on daemon synchronization to finish")
|
||||
} else {
|
||||
// everything OK, enable transfer page
|
||||
root.enabled = true;
|
||||
statusText.text = "";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2
qml.qrc
2
qml.qrc
|
@ -120,5 +120,7 @@
|
|||
<file>components/DaemonProgress.qml</file>
|
||||
<file>components/StandardDialog.qml</file>
|
||||
<file>pages/Sign.qml</file>
|
||||
<file>components/DaemonManagerDialog.qml</file>
|
||||
<file>components/StandardDialog.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
142
src/daemon/DaemonManager.cpp
Normal file
142
src/daemon/DaemonManager.cpp
Normal file
|
@ -0,0 +1,142 @@
|
|||
#include "DaemonManager.h"
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#include <QApplication>
|
||||
#include <QProcess>
|
||||
|
||||
DaemonManager * DaemonManager::m_instance = nullptr;
|
||||
QStringList DaemonManager::m_clArgs;
|
||||
|
||||
DaemonManager *DaemonManager::instance(const QStringList *args)
|
||||
{
|
||||
if (!m_instance) {
|
||||
m_instance = new DaemonManager;
|
||||
// store command line arguments for later use
|
||||
m_clArgs = *args;
|
||||
m_clArgs.removeFirst();
|
||||
}
|
||||
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
bool DaemonManager::start()
|
||||
{
|
||||
//
|
||||
QString process;
|
||||
#ifdef Q_OS_WIN
|
||||
process = QApplication::applicationDirPath() + "/monerod.exe";
|
||||
#elif defined(Q_OS_UNIX)
|
||||
process = QApplication::applicationDirPath() + "/monerod";
|
||||
#endif
|
||||
|
||||
if (process.length() == 0) {
|
||||
qDebug() << "no daemon binary defined for current platform";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// prepare command line arguments and pass to monerod
|
||||
QStringList arguments;
|
||||
foreach (const QString &str, m_clArgs) {
|
||||
qDebug() << QString(" [%1] ").arg(str);
|
||||
arguments << str;
|
||||
}
|
||||
|
||||
qDebug() << "starting monerod " + process;
|
||||
qDebug() << "With command line arguments " << arguments;
|
||||
|
||||
m_daemon = new QProcess();
|
||||
initialized = true;
|
||||
|
||||
// Connect output slots
|
||||
connect (m_daemon, SIGNAL(readyReadStandardOutput()), this, SLOT(printOutput()));
|
||||
connect (m_daemon, SIGNAL(readyReadStandardError()), this, SLOT(printError()));
|
||||
|
||||
// Start monerod
|
||||
m_daemon->start(process,arguments);
|
||||
bool started = m_daemon->waitForStarted();
|
||||
|
||||
// add state changed listener
|
||||
connect(m_daemon,SIGNAL(stateChanged(QProcess::ProcessState)),this,SLOT(stateChanged(QProcess::ProcessState)));
|
||||
|
||||
if (!started) {
|
||||
qDebug() << "Daemon start error: " + m_daemon->errorString();
|
||||
} else {
|
||||
emit daemonStarted();
|
||||
}
|
||||
|
||||
return started;
|
||||
}
|
||||
|
||||
bool DaemonManager::stop()
|
||||
{
|
||||
if (initialized) {
|
||||
qDebug() << "stopping daemon";
|
||||
// we can't use QProcess::terminate() on windows console process
|
||||
// write exit command to stdin
|
||||
m_daemon->write("exit\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DaemonManager::stateChanged(QProcess::ProcessState state)
|
||||
{
|
||||
qDebug() << "STATE CHANGED: " << state;
|
||||
if (state == QProcess::NotRunning) {
|
||||
emit daemonStopped();
|
||||
}
|
||||
}
|
||||
|
||||
void DaemonManager::printOutput()
|
||||
{
|
||||
QByteArray byteArray = m_daemon->readAllStandardOutput();
|
||||
QStringList strLines = QString(byteArray).split("\n");
|
||||
|
||||
foreach (QString line, strLines) {
|
||||
emit daemonConsoleUpdated(line);
|
||||
qDebug() << "Daemon: " + line;
|
||||
}
|
||||
}
|
||||
|
||||
void DaemonManager::printError()
|
||||
{
|
||||
QByteArray byteArray = m_daemon->readAllStandardError();
|
||||
QStringList strLines = QString(byteArray).split("\n");
|
||||
|
||||
foreach (QString line, strLines) {
|
||||
emit daemonConsoleUpdated(line);
|
||||
qDebug() << "Daemon ERROR: " + line;
|
||||
}
|
||||
}
|
||||
|
||||
bool DaemonManager::running() const
|
||||
{
|
||||
if (initialized) {
|
||||
qDebug() << m_daemon->state();
|
||||
qDebug() << QProcess::NotRunning;
|
||||
// m_daemon->write("status\n");
|
||||
return m_daemon->state() > QProcess::NotRunning;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DaemonManager::DaemonManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void DaemonManager::closing()
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
stop();
|
||||
// Wait for daemon to stop before exiting (max 10 secs)
|
||||
if (initialized) {
|
||||
m_daemon->waitForFinished(10000);
|
||||
}
|
||||
}
|
43
src/daemon/DaemonManager.h
Normal file
43
src/daemon/DaemonManager.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#ifndef DAEMONMANAGER_H
|
||||
#define DAEMONMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
#include <QProcess>
|
||||
|
||||
class DaemonManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
static DaemonManager * instance(const QStringList *args);
|
||||
|
||||
Q_INVOKABLE bool start();
|
||||
Q_INVOKABLE bool stop();
|
||||
|
||||
// return true if daemon process is started
|
||||
Q_INVOKABLE bool running() const;
|
||||
|
||||
signals:
|
||||
void daemonStarted();
|
||||
void daemonStopped();
|
||||
void daemonConsoleUpdated(QString message);
|
||||
|
||||
public slots:
|
||||
void printOutput();
|
||||
void printError();
|
||||
void closing();
|
||||
void stateChanged(QProcess::ProcessState state);
|
||||
|
||||
private:
|
||||
|
||||
explicit DaemonManager(QObject *parent = 0);
|
||||
static DaemonManager * m_instance;
|
||||
static QStringList m_clArgs;
|
||||
QProcess *m_daemon;
|
||||
bool initialized = false;
|
||||
|
||||
};
|
||||
|
||||
#endif // DAEMONMANAGER_H
|
|
@ -15,6 +15,7 @@
|
|||
namespace {
|
||||
static const int DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS = 10;
|
||||
static const int DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS = 60;
|
||||
static const int WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS = 5;
|
||||
}
|
||||
|
||||
class WalletListenerImpl : public Bitmonero::WalletListener
|
||||
|
@ -89,7 +90,18 @@ Wallet::Status Wallet::status() const
|
|||
|
||||
Wallet::ConnectionStatus Wallet::connected() const
|
||||
{
|
||||
return static_cast<ConnectionStatus>(m_walletImpl->connected());
|
||||
// cache connection status
|
||||
if (!m_initialized || m_connectionStatusTime.elapsed() / 1000 > m_connectionStatusTtl) {
|
||||
m_initialized = true;
|
||||
ConnectionStatus newStatus = static_cast<ConnectionStatus>(m_walletImpl->connected());
|
||||
if (newStatus != m_connectionStatus) {
|
||||
m_connectionStatus = newStatus;
|
||||
emit connectionStatusChanged();
|
||||
}
|
||||
m_connectionStatusTime.restart();
|
||||
}
|
||||
|
||||
return m_connectionStatus;
|
||||
}
|
||||
|
||||
bool Wallet::synchronized() const
|
||||
|
@ -424,9 +436,16 @@ Wallet::Wallet(Bitmonero::Wallet *w, QObject *parent)
|
|||
, m_daemonBlockChainHeightTtl(DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS)
|
||||
, m_daemonBlockChainTargetHeight(0)
|
||||
, m_daemonBlockChainTargetHeightTtl(DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS)
|
||||
, m_connectionStatusTtl(WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS)
|
||||
{
|
||||
m_history = new TransactionHistory(m_walletImpl->history(), this);
|
||||
m_walletImpl->setListener(new WalletListenerImpl(this));
|
||||
m_connectionStatus = Wallet::ConnectionStatus_Disconnected;
|
||||
// start cache timers
|
||||
m_connectionStatusTime.restart();
|
||||
m_daemonBlockChainHeightTime.restart();
|
||||
m_daemonBlockChainTargetHeightTime.restart();
|
||||
m_initialized = false;
|
||||
}
|
||||
|
||||
Wallet::~Wallet()
|
||||
|
|
|
@ -200,6 +200,8 @@ signals:
|
|||
// emitted when transaction is created async
|
||||
void transactionCreated(PendingTransaction * transaction, QString address, QString paymentId, quint32 mixinCount);
|
||||
|
||||
void connectionStatusChanged() const;
|
||||
|
||||
private:
|
||||
Wallet(QObject * parent = nullptr);
|
||||
Wallet(Bitmonero::Wallet *w, QObject * parent = 0);
|
||||
|
@ -221,6 +223,10 @@ private:
|
|||
mutable QTime m_daemonBlockChainTargetHeightTime;
|
||||
mutable quint64 m_daemonBlockChainTargetHeight;
|
||||
int m_daemonBlockChainTargetHeightTtl;
|
||||
mutable ConnectionStatus m_connectionStatus;
|
||||
int m_connectionStatusTtl;
|
||||
mutable QTime m_connectionStatusTime;
|
||||
mutable bool m_initialized;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue