aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRiccardo Spagni <ric@spagni.net>2016-12-08 23:54:50 +0200
committerRiccardo Spagni <ric@spagni.net>2016-12-08 23:54:50 +0200
commitbfcdf113f97532a79e55049bae89e7ee018b766a (patch)
treede9aaf94d9b446b63d6e849aa1280bbf9f38e46e
parent4286b5c1c90c2777325eb403f17cf6e33485c536 (diff)
parent3752ec7899d19a2202574f25e113e5bb900496e3 (diff)
downloadmonzero-gui-bfcdf113f97532a79e55049bae89e7ee018b766a.tar.gz
monzero-gui-bfcdf113f97532a79e55049bae89e7ee018b766a.tar.xz
monzero-gui-bfcdf113f97532a79e55049bae89e7ee018b766a.zip
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)
-rw-r--r--MiddlePanel.qml16
-rw-r--r--components/DaemonManagerDialog.qml120
-rw-r--r--components/DaemonProgress.qml3
-rw-r--r--components/HistoryTable.qml1
-rw-r--r--components/PasswordDialog.qml4
-rw-r--r--components/StandardDialog.qml15
-rw-r--r--main.cpp8
-rw-r--r--main.qml70
-rw-r--r--monero-core.pro6
-rw-r--r--pages/Settings.qml80
-rw-r--r--pages/Transfer.qml66
-rw-r--r--qml.qrc2
-rw-r--r--src/daemon/DaemonManager.cpp142
-rw-r--r--src/daemon/DaemonManager.h43
-rw-r--r--src/libwalletqt/Wallet.cpp21
-rw-r--r--src/libwalletqt/Wallet.h6
16 files changed, 566 insertions, 37 deletions
diff --git a/MiddlePanel.qml b/MiddlePanel.qml
index e14255a5..16cc9492 100644
--- a/MiddlePanel.qml
+++ b/MiddlePanel.qml
@@ -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 {
diff --git a/components/DaemonManagerDialog.qml b/components/DaemonManagerDialog.qml
new file mode 100644
index 00000000..da3cf582
--- /dev/null
+++ b/components/DaemonManagerDialog.qml
@@ -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()
+ }
+ }
+ }
+ }
+
+}
+
+
+
diff --git a/components/DaemonProgress.qml b/components/DaemonProgress.qml
index 5fac2079..53a2f5c7 100644
--- a/components/DaemonProgress.qml
+++ b/components/DaemonProgress.qml
@@ -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)
}
}
diff --git a/components/HistoryTable.qml b/components/HistoryTable.qml
index 5f4584c7..0c9e9b60 100644
--- a/components/HistoryTable.qml
+++ b/components/HistoryTable.qml
@@ -69,6 +69,7 @@ ListView {
StandardDialog {
id: detailsPopup
+ width:600
cancelVisible: false
okVisible: true
}
diff --git a/components/PasswordDialog.qml b/components/PasswordDialog.qml
index 325bf2b3..19414987 100644
--- a/components/PasswordDialog.qml
+++ b/components/PasswordDialog.qml
@@ -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()
}
}
}
diff --git a/components/StandardDialog.qml b/components/StandardDialog.qml
index 0e081ae5..03c64a64 100644
--- a/components/StandardDialog.qml
+++ b/components/StandardDialog.qml
@@ -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()
}
}
}
diff --git a/main.cpp b/main.cpp
index fe42cb61..e9ea5a56 100644
--- a/main.cpp
+++ b/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
diff --git a/main.qml b/main.qml
index 5de0d429..d8be1e6a 100644
--- a/main.qml
+++ b/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();
}
}
diff --git a/monero-core.pro b/monero-core.pro
index 5e47d732..249a65f5 100644
--- a/monero-core.pro
+++ b/monero-core.pro
@@ -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 \
diff --git a/pages/Settings.qml b/pages/Settings.qml
index 4f1af78d..c2fc08ef 100644
--- a/pages/Settings.qml
+++ b/pages/Settings.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)
+ }
+
+
+
}
diff --git a/pages/Transfer.qml b/pages/Transfer.qml
index 5552902c..127c74cb 100644
--- a/pages/Transfer.qml
+++ b/pages/Transfer.qml
@@ -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 = "";
+ }
+
+ }
+ }
}
diff --git a/qml.qrc b/qml.qrc
index 17873589..c1aa0ece 100644
--- a/qml.qrc
+++ b/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>
diff --git a/src/daemon/DaemonManager.cpp b/src/daemon/DaemonManager.cpp
new file mode 100644
index 00000000..185cde1e
--- /dev/null
+++ b/src/daemon/DaemonManager.cpp
@@ -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);
+ }
+}
diff --git a/src/daemon/DaemonManager.h b/src/daemon/DaemonManager.h
new file mode 100644
index 00000000..8f425fd5
--- /dev/null
+++ b/src/daemon/DaemonManager.h
@@ -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
diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp
index 9d814d82..915b4d4f 100644
--- a/src/libwalletqt/Wallet.cpp
+++ b/src/libwalletqt/Wallet.cpp
@@ -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()
diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h
index 492080d4..ad4c393e 100644
--- a/src/libwalletqt/Wallet.h
+++ b/src/libwalletqt/Wallet.h
@@ -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;
};