diff --git a/src/qt/dogecash/forms/masternodeswidget.ui b/src/qt/dogecash/forms/masternodeswidget.ui index 168c58441b..87b6fd357a 100644 --- a/src/qt/dogecash/forms/masternodeswidget.ui +++ b/src/qt/dogecash/forms/masternodeswidget.ui @@ -283,6 +283,32 @@ + + + + + 125 + 50 + + + + Start All + + + + + + + + 125 + 50 + + + + Start Missing + + + diff --git a/src/qt/dogecash/masternodeswidget.cpp b/src/qt/dogecash/masternodeswidget.cpp index 53ecd4c282..259a1ac591 100644 --- a/src/qt/dogecash/masternodeswidget.cpp +++ b/src/qt/dogecash/masternodeswidget.cpp @@ -29,6 +29,8 @@ #define DECORATION_SIZE 65 #define NUM_ITEMS 3 +#define REQUEST_START_ALL 1 +#define REQUEST_START_MISSING 2 class MNHolder : public FurListRow { @@ -65,7 +67,8 @@ class MNHolder : public FurListRow MasterNodesWidget::MasterNodesWidget(DogeCashGUI *parent) : PWidget(parent), - ui(new Ui::MasterNodesWidget) + ui(new Ui::MasterNodesWidget), + isLoading(false) { ui->setupUi(this); @@ -99,6 +102,8 @@ MasterNodesWidget::MasterNodesWidget(DogeCashGUI *parent) : /* Buttons */ ui->pushButtonSave->setText(tr("Create Masternode Controller")); setCssBtnPrimary(ui->pushButtonSave); + setCssBtnPrimary(ui->pushButtonStartAll); + setCssBtnPrimary(ui->pushButtonStartMissing); /* Options */ ui->btnAbout->setTitleClassAndText("btn-title-grey", "What is a Masternode?"); @@ -119,6 +124,12 @@ MasterNodesWidget::MasterNodesWidget(DogeCashGUI *parent) : setCssProperty(ui->labelEmpty, "text-empty"); connect(ui->pushButtonSave, SIGNAL(clicked()), this, SLOT(onCreateMNClicked())); + connect(ui->pushButtonStartAll, &QPushButton::clicked, [this]() { + onStartAllClicked(REQUEST_START_ALL); + }); + connect(ui->pushButtonStartMissing, &QPushButton::clicked, [this]() { + onStartAllClicked(REQUEST_START_MISSING); + }); connect(ui->listMn, SIGNAL(clicked(QModelIndex)), this, SLOT(onMNClicked(QModelIndex))); connect(ui->btnAbout, &OptionButton::clicked, [this](){window->openFAQ(9);}); connect(ui->btnAboutController, &OptionButton::clicked, [this](){window->openFAQ(10);}); @@ -146,13 +157,10 @@ void MasterNodesWidget::loadWalletModel(){ } void MasterNodesWidget::updateListState() { - if (mnModel->rowCount() > 0) { - ui->listMn->setVisible(true); - ui->emptyContainer->setVisible(false); - } else { - ui->listMn->setVisible(false); - ui->emptyContainer->setVisible(true); - } + bool show = mnModel->rowCount() > 0; + ui->listMn->setVisible(show); + ui->emptyContainer->setVisible(!show); + ui->pushButtonStartAll->setVisible(show); } void MasterNodesWidget::onMNClicked(const QModelIndex &index){ @@ -206,19 +214,81 @@ void MasterNodesWidget::startAlias(QString strAlias){ for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { if (mne.getAlias() == strAlias.toStdString()) { std::string strError; - CMasternodeBroadcast mnb; - if (CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb)) { - strStatusHtml += "successfully started."; - mnodeman.UpdateMasternodeList(mnb); - mnb.Relay(); - mnModel->updateMNList(); - } else { - strStatusHtml += "failed to start.\nError: " + QString::fromStdString(strError); - } + strStatusHtml += (!startMN(mne, strError)) ? ("failed to start.\nError: " + QString::fromStdString(strError)) : "successfully started."; break; } } - inform(strStatusHtml); + // update UI and notify + updateModelAndInform(strStatusHtml); +} + +void MasterNodesWidget::updateModelAndInform(QString informText) { + mnModel->updateMNList(); + inform(informText); +} + +bool MasterNodesWidget::startMN(CMasternodeConfig::CMasternodeEntry mne, std::string& strError) { + CMasternodeBroadcast mnb; + if (!CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb)) + return false; + + mnodeman.UpdateMasternodeList(mnb); + mnb.Relay(); + return true; +} + +void MasterNodesWidget::onStartAllClicked(int type) { + if(!verifyWalletUnlocked()) return; + if (isLoading) { + inform(tr("Background task is being executed, please wait")); + } else { + isLoading = true; + if (!execute(type)) { + isLoading = false; + inform(tr("Cannot perform Mastenodes start")); + } + } +} + +bool MasterNodesWidget::startAll(QString& failText, bool onlyMissing) { + int amountOfMnFailed = 0; + int amountOfMnStarted = 0; + for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { + // Check for only missing + if (onlyMissing && !mnModel->isMNMissingOrExpired(QString::fromStdString(mne.getAlias()))) + continue; + + std::string strError; + if (!startMN(mne, strError)) { + amountOfMnFailed++; + } else { + amountOfMnStarted++; + } + } + if (amountOfMnFailed > 0) { + failText = tr("%1 Masternodes failed to start, %2 started").arg(amountOfMnFailed).arg(amountOfMnStarted); + return false; + } + return true; +} + +void MasterNodesWidget::run(int type) { + bool isStartMissing = type == REQUEST_START_MISSING; + if (type == REQUEST_START_ALL || isStartMissing) { + QString failText; + QString inform = startAll(failText, isStartMissing) ? tr("All Masternodes started!") : failText; + QMetaObject::invokeMethod(this, "updateModelAndInform", Qt::QueuedConnection, + Q_ARG(QString, inform)); + } + + isLoading = false; +} + +void MasterNodesWidget::onError(QString error, int type) { + if (type == REQUEST_START_ALL) { + QMetaObject::invokeMethod(this, "inform", Qt::QueuedConnection, + Q_ARG(QString, "Error starting all Masternodes")); + } } void MasterNodesWidget::onInfoMNClicked(){ diff --git a/src/qt/dogecash/masternodeswidget.h b/src/qt/dogecash/masternodeswidget.h index 6beb7b0e5c..d90b9e11f6 100644 --- a/src/qt/dogecash/masternodeswidget.h +++ b/src/qt/dogecash/masternodeswidget.h @@ -13,6 +13,7 @@ #include "qt/dogecash/mnrow.h" #include "qt/dogecash/tooltipmenu.h" #include +#include class DogeCashGUI; @@ -34,17 +35,21 @@ class MasterNodesWidget : public PWidget ~MasterNodesWidget(); void loadWalletModel() override; + void run(int type) override; + void onError(QString error, int type) override; void showEvent(QShowEvent *event) override; void hideEvent(QHideEvent *event) override; private slots: void onCreateMNClicked(); void changeTheme(bool isLightTheme, QString &theme) override; + void onStartAllClicked(int type); void onMNClicked(const QModelIndex &index); void onEditMNClicked(); void onDeleteMNClicked(); void onInfoMNClicked(); void updateListState(); + void updateModelAndInform(QString informText); private: Ui::MasterNodesWidget *ui; @@ -53,8 +58,11 @@ private slots: TooltipMenu* menu = nullptr; QModelIndex index; QTimer *timer = nullptr; + std::atomic isLoading; void startAlias(QString strAlias); + bool startAll(QString& failedMN, bool onlyMissing); + bool startMN(CMasternodeConfig::CMasternodeEntry mne, std::string& strError); }; #endif // MASTERNODESWIDGET_H diff --git a/src/qt/dogecash/mnmodel.cpp b/src/qt/dogecash/mnmodel.cpp index ff3acb7f2a..a8f4609abd 100644 --- a/src/qt/dogecash/mnmodel.cpp +++ b/src/qt/dogecash/mnmodel.cpp @@ -150,4 +150,13 @@ bool MNModel::addMn(CMasternodeConfig::CMasternodeEntry* mne){ nodes.insert(QString::fromStdString(mne->getAlias()), std::make_pair(QString::fromStdString(mne->getIp()), pmn)); endInsertRows(); return true; +} + +bool MNModel::isMNMissingOrExpired(QString mnAlias) { + QMap>::const_iterator it = nodes.find(mnAlias); + if (it != nodes.end()) { + int activeState = it.value().second->activeState; + return activeState == CMasternode::MASTERNODE_MISSING || activeState == CMasternode::MASTERNODE_EXPIRED; + } + throw std::runtime_error(std::string("Masternode alias not found")); } \ No newline at end of file diff --git a/src/qt/dogecash/mnmodel.h b/src/qt/dogecash/mnmodel.h index 87d8d06062..c5fc898b3d 100644 --- a/src/qt/dogecash/mnmodel.h +++ b/src/qt/dogecash/mnmodel.h @@ -42,10 +42,12 @@ class MNModel : public QAbstractTableModel bool addMn(CMasternodeConfig::CMasternodeEntry* entry); void updateMNList(); + // Checks if the masternode is in missing state + bool isMNMissingOrExpired(QString mnAlias); private: // alias mn node ---> pair - QMap> nodes; + QMap> nodes; QMap collateralTxAccepted; };