Merge branch 'master' into websockets

This commit is contained in:
Aditya Kulkarni 2019-02-01 11:04:33 -08:00
commit 7c71457cc1
21 changed files with 2516 additions and 819 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -254,9 +254,6 @@ void AddressBook::readFromStorage() {
}
void AddressBook::writeToStorage() {
if (allLabels.isEmpty())
return;
QFile file(AddressBook::writeableFile());
file.open(QIODevice::ReadWrite | QIODevice::Truncate);
QDataStream out(&file); // we will serialize the data into the file

View File

@ -2,6 +2,7 @@
#include "mainwindow.h"
#include "settings.h"
#include "ui_connection.h"
#include "ui_createzcashconfdialog.h"
#include "rpc.h"
#include "precompiled.h"
@ -128,9 +129,47 @@ void ConnectionLoader::createZcashConf() {
main->logger->write("createZcashConf");
auto confLocation = zcashConfWritableLocation();
main->logger->write("Creating file " + confLocation);
QFileInfo fi(confLocation);
QDialog d(main);
Ui_createZcashConf ui;
ui.setupUi(&d);
QPixmap logo(":/img/res/zcashdlogo.gif");
ui.lblTopIcon->setBasePixmap(logo.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation));
ui.btnPickDir->setEnabled(false);
ui.grpAdvanced->setVisible(false);
QObject::connect(ui.btnAdvancedConfig, &QPushButton::toggled, [=](bool isVisible) {
ui.grpAdvanced->setVisible(isVisible);
ui.btnAdvancedConfig->setText(isVisible ? QObject::tr("Hide Advanced Config") : QObject::tr("Show Advanced Config"));
});
QObject::connect(ui.chkCustomDatadir, &QCheckBox::stateChanged, [=](int chked) {
if (chked == Qt::Checked) {
ui.btnPickDir->setEnabled(true);
}
else {
ui.btnPickDir->setEnabled(false);
}
});
QObject::connect(ui.btnPickDir, &QPushButton::clicked, [=]() {
auto datadir = QFileDialog::getExistingDirectory(main, QObject::tr("Choose data directory"), ui.lblDirName->text(), QFileDialog::ShowDirsOnly);
if (!datadir.isEmpty()) {
ui.lblDirName->setText(QDir::toNativeSeparators(datadir));
}
});
// Show the dialog
QString datadir = "";
bool useTor = false;
if (d.exec() == QDialog::Accepted) {
datadir = ui.lblDirName->text();
useTor = ui.chkUseTor->isChecked();
}
main->logger->write("Creating file " + confLocation);
QDir().mkdir(fi.dir().absolutePath());
QFile file(confLocation);
@ -145,6 +184,13 @@ void ConnectionLoader::createZcashConf() {
out << "addnode=mainnet.z.cash\n";
out << "rpcuser=zec-qt-wallet\n";
out << "rpcpassword=" % randomPassword() << "\n";
if (!datadir.isEmpty()) {
out << "datadir=" % datadir % "\n";
}
if (useTor) {
out << "proxy=127.0.0.1:9050\n";
}
file.close();
// Now that zcash.conf exists, try to autoconnect again

View File

@ -0,0 +1,212 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>createZcashConf</class>
<widget class="QDialog" name="createZcashConf">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>508</width>
<height>352</height>
</rect>
</property>
<property name="windowTitle">
<string>Configure zcash.conf</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="FilledIconLabel" name="lblTopIcon">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">background: #fff;</string>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
<property name="centerButtons">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="btnAdvancedConfig">
<property name="text">
<string>Show Advanced Configuration</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Your zcash node will be configured for you automatically</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QGroupBox" name="grpAdvanced">
<property name="title">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QCheckBox" name="chkCustomDatadir">
<property name="text">
<string>Use custom datadir</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Please chose a directory to store your wallet.dat and blockchain</string>
</property>
</widget>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btnPickDir">
<property name="text">
<string>Choose directory</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblDirName">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="chkUseTor">
<property name="text">
<string>Connect over Tor</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Please note that you'll need to already have a Tor service configured on port 9050</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>FilledIconLabel</class>
<extends>QLabel</extends>
<header>fillediconlabel.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>createZcashConf</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>createZcashConf</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -256,12 +256,18 @@ void MainWindow::turnstileDoMigration(QString fromAddr) {
auto fnUpdateSproutBalance = [=] (QString addr) {
double bal = 0;
// The currentText contains the balance as well, so strip that.
if (addr.contains("(")) {
addr = addr.left(addr.indexOf("("));
}
if (addr.startsWith("All")) {
bal = fnGetAllSproutBalance();
} else {
bal = rpc->getAllBalances()->value(addr);
}
auto balTxt = Settings::getZECUSDDisplayFormat(bal);
if (bal < Turnstile::minMigrationAmount) {
@ -634,17 +640,20 @@ void MainWindow::postToZBoard() {
tx.toAddrs.push_back(ToFields{ toAddr, Settings::getZboardAmount(), memo, memo.toUtf8().toHex() });
tx.fee = Settings::getMinerFee();
json params = json::array();
rpc->fillTxJsonParams(params, tx);
std::cout << std::setw(2) << params << std::endl;
// And send the Tx
rpc->sendZTransaction(params, [=](const json& reply) {
QString opid = QString::fromStdString(reply.get<json::string_t>());
rpc->executeTransaction(tx, [=] (QString opid) {
ui->statusBar->showMessage(tr("Computing Tx: ") % opid);
},
[=] (QString /*opid*/, QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
},
[=] (QString opid, QString errStr) {
ui->statusBar->showMessage(QObject::tr(" Tx ") % opid % QObject::tr(" failed"), 15 * 1000);
// And then start monitoring the transaction
rpc->addNewTxToWatch(tx, opid);
if (!opid.isEmpty())
errStr = QObject::tr("The transaction with id ") % opid % QObject::tr(" failed. The error was") + ":\n\n" + errStr;
QMessageBox::critical(this, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok);
});
}
}
@ -731,7 +740,7 @@ void MainWindow::payZcashURI() {
if (kv[0].toLower() == "amt" || kv[0].toLower() == "amount") {
amount = kv[1].toDouble();
} else if (kv[0].toLower() == "memo") {
} else if (kv[0].toLower() == "memo" || kv[0].toLower() == "message" || kv[0].toLower() == "msg") {
memo = kv[1];
// Test if this is hex
@ -1200,7 +1209,16 @@ void MainWindow::setupRecieveTab() {
});
// zAddr toggle button, one for sprout and one for sapling
QObject::connect(ui->rdioZAddr, &QRadioButton::toggled, addZAddrsToComboList(false));
QObject::connect(ui->rdioZAddr, &QRadioButton::toggled, [=](bool checked) {
ui->btnRecieveNewAddr->setEnabled(!checked);
if (checked) {
ui->btnRecieveNewAddr->setToolTip(tr("Creation of new Sprout addresses is deprecated"));
}
else {
ui->btnRecieveNewAddr->setToolTip("");
}
addZAddrsToComboList(false)(checked);
});
QObject::connect(ui->rdioZSAddr, &QRadioButton::toggled, addZAddrsToComboList(true));
// Explicitly get new address button.
@ -1233,7 +1251,7 @@ void MainWindow::setupRecieveTab() {
if (Settings::getInstance()->isSaplingActive()) {
ui->rdioZSAddr->setVisible(true);
ui->rdioZSAddr->setChecked(true);
ui->rdioZAddr->setText("z-Addr(Sprout)");
ui->rdioZAddr->setText("z-Addr(Legacy Sprout)");
} else {
ui->rdioZSAddr->setVisible(false);
ui->rdioZAddr->setChecked(true);
@ -1260,7 +1278,6 @@ void MainWindow::setupRecieveTab() {
ui->rcvBal->clear();
ui->txtRecieve->clear();
ui->qrcodeDisplay->clear();
ui->lblUsed->clear();
return;
}
@ -1277,9 +1294,9 @@ void MainWindow::setupRecieveTab() {
ui->txtRecieve->setPlainText(addr);
ui->qrcodeDisplay->setQrcodeString(addr);
if (rpc->getUsedAddresses()->value(addr, false)) {
ui->lblUsed->setText(tr("Address has been previously used"));
ui->rcvBal->setToolTip(tr("Address has been previously used"));
} else {
ui->lblUsed->setText(tr("Address is unused"));
ui->rcvBal->setToolTip(tr("Address is unused"));
}
});

View File

@ -38,6 +38,7 @@ public:
~MainWindow();
void updateLabelsAutoComplete();
RPC* getRPC() { return rpc; }
QString doSendTxValidations(Tx tx);
void setDefaultPayFrom();
@ -78,6 +79,8 @@ private:
void addAddressSection();
void maxAmountChecked(int checked);
void editSchedule();
void addressChanged(int number, const QString& text);
void amountChanged (int number, const QString& text);

View File

@ -22,7 +22,7 @@
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>2</number>
<number>1</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
@ -346,8 +346,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>928</width>
<height>380</height>
<width>920</width>
<height>301</height>
</rect>
</property>
<layout class="QVBoxLayout" name="sendToLayout">
@ -530,6 +530,50 @@
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="layoutSendRecurring">
<item>
<widget class="QCheckBox" name="chkRecurring">
<property name="text">
<string>Recurring payment</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblRecurDesc">
<property name="text">
<string>Every month, starting 12-May-2012, for 6 payments</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnRecurSchedule">
<property name="text">
<string>Edit Schedule</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
@ -559,6 +603,13 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_17">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
@ -629,7 +680,7 @@
<item>
<widget class="QRadioButton" name="rdioZSAddr">
<property name="text">
<string>z-Addr(Sapling)</string>
<string>z-Addr</string>
</property>
</widget>
</item>
@ -649,7 +700,7 @@
<item>
<widget class="QRadioButton" name="rdioZAddr">
<property name="text">
<string>z-Addr(Sprout)</string>
<string>z-Addr(Legacy Sprout)</string>
</property>
</widget>
</item>
@ -739,17 +790,7 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Address</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<item row="3" column="1" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_16">
<item>
<widget class="QPushButton" name="exportKey">
@ -773,17 +814,13 @@
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_15">
<item row="0" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Address used</string>
<string>Address</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QLabel" name="lblUsed">
<property name="text">
<string notr="true"/>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>

245
src/newrecurring.ui Normal file
View File

@ -0,0 +1,245 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>newRecurringDialog</class>
<widget class="QDialog" name="newRecurringDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>740</width>
<height>403</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Schedule</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="5" column="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="cmbSchedule"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="10" column="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Schedule</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Payment Description</string>
</property>
</widget>
</item>
<item row="8" column="2">
<widget class="QLabel" name="lblNextPayment">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="7" column="1" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="txtAmt">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cmbCurrency"/>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="txtDesc"/>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>From</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Number of payments</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Amount</string>
</property>
</widget>
</item>
<item row="9" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="2">
<widget class="AddressCombo" name="cmbFromAddress"/>
</item>
<item row="6" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="txtNumPayments"/>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="8" column="1">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Next Payment</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="txtToAddr"/>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>To</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPlainTextEdit" name="txtMemo"/>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Memo</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AddressCombo</class>
<extends>QComboBox</extends>
<header>addresscombo.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>txtDesc</tabstop>
<tabstop>cmbFromAddress</tabstop>
<tabstop>txtToAddr</tabstop>
<tabstop>txtAmt</tabstop>
<tabstop>cmbCurrency</tabstop>
<tabstop>cmbSchedule</tabstop>
<tabstop>txtNumPayments</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>newRecurringDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>newRecurringDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

48
src/recurring.cpp Normal file
View File

@ -0,0 +1,48 @@
#include "recurring.h"
#include "mainwindow.h"
#include "rpc.h"
#include "settings.h"
#include "ui_newrecurring.h"
void Recurring::showEditDialog(QWidget* parent, MainWindow* main, Tx tx) {
Ui_newRecurringDialog ui;
QDialog d(parent);
ui.setupUi(&d);
Settings::saveRestore(&d);
// Add all the from addresses
auto allBalances = main->getRPC()->getAllBalances();
for (QString addr : allBalances->keys()) {
ui.cmbFromAddress->addItem(addr, allBalances->value(addr));
}
if (!tx.fromAddr.isEmpty()) {
ui.cmbFromAddress->setCurrentText(tx.fromAddr);
ui.cmbFromAddress->setEnabled(false);
}
ui.cmbCurrency->addItem(Settings::getTokenName());
ui.cmbCurrency->addItem("USD");
if (tx.toAddrs.length() > 0) {
ui.txtToAddr->setText(tx.toAddrs[0].addr);
ui.txtToAddr->setEnabled(false);
ui.txtAmt->setText(Settings::getDecimalString(tx.toAddrs[0].amount));
ui.txtAmt->setEnabled(false);
ui.txtMemo->setPlainText(tx.toAddrs[0].txtMemo);
ui.txtMemo->setEnabled(false);
}
ui.cmbSchedule->addItem("Every Day", QVariant(Schedule::DAY));
ui.cmbSchedule->addItem("Every Week", QVariant(Schedule::WEEK));
ui.cmbSchedule->addItem("Every Month", QVariant(Schedule::MONTH));
ui.cmbSchedule->addItem("Every Year", QVariant(Schedule::YEAR));
ui.txtNumPayments->setText("10");
ui.txtDesc->setFocus();
d.exec();
}

36
src/recurring.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef RECURRING_H
#define RECURRING_H
#include "precompiled.h"
#include "mainwindow.h"
enum Schedule {
DAY = 1,
WEEK,
MONTH,
YEAR
};
struct RecurringPaymentInfo {
QString desc;
QString fromAddr;
QString toAddr;
double amt;
QString currency;
Schedule schedule;
int numPayments;
long startBlock;
int completedPayments;
};
class Recurring
{
public:
Recurring();
static void showEditDialog(QWidget* parent, MainWindow* main, Tx tx);
};
#endif // RECURRING_H

92
src/recurringdialog.ui Normal file
View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RecurringDialog</class>
<widget class="QDialog" name="RecurringDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>601</width>
<height>438</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTableView" name="tableView"/>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>RecurringDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>RecurringDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -227,7 +227,8 @@ void RPC::getTransactions(const std::function<void(json)>& cb) {
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
void RPC::sendZTransaction(json params, const std::function<void(json)>& cb) {
void RPC::sendZTransaction(json params, const std::function<void(json)>& cb,
const std::function<void(QString)>& err) {
json payload = {
{"jsonrpc", "1.0"},
{"id", "someid"},
@ -235,7 +236,13 @@ void RPC::sendZTransaction(json params, const std::function<void(json)>& cb) {
{"params", params}
};
conn->doRPCWithDefaultErrorHandling(payload, cb);
conn->doRPC(payload, cb, [=] (auto reply, auto parsed) {
if (!parsed.is_discarded() && !parsed["error"]["message"].is_null()) {
err(QString::fromStdString(parsed["error"]["message"]));
} else {
err(reply->errorString());
}
});
}
/**
@ -537,11 +544,15 @@ void RPC::getInfoThenRefresh(bool force) {
static int lastBlock = 0;
int curBlock = reply["blocks"].get<json::number_integer_t>();
int version = reply["version"].get<json::number_integer_t>();
if ( force || (curBlock != lastBlock) ) {
// Something changed, so refresh everything.
lastBlock = curBlock;
// See if the turnstile migration has any steps that need to be done.
turnstile->executeMigrationStep();
refreshBalances();
refreshAddresses(); // This calls refreshZSentTransactions() and refreshReceivedZTrans()
refreshTransactions();
@ -602,7 +613,7 @@ void RPC::getInfoThenRefresh(bool force) {
// as the progress instead of verification progress.
progress = (double)blockNumber / (double)estimatedheight;
}
txt = txt % " ( " % QString::number(progress * 100, 'f', 0) % "% )";
txt = txt % " ( " % QString::number(progress * 100, 'f', 2) % "% )";
ui->blockheight->setText(txt);
ui->heightLabel->setText(QObject::tr("Downloading blocks"));
} else {
@ -617,7 +628,7 @@ void RPC::getInfoThenRefresh(bool force) {
" (" %
(Settings::getInstance()->isTestnet() ? QObject::tr("testnet:") : "") %
QString::number(blockNumber) %
(isSyncing ? ("/" % QString::number(progress*100, 'f', 0) % "%") : QString()) %
(isSyncing ? ("/" % QString::number(progress*100, 'f', 2) % "%") : QString()) %
")";
main->statusLabel->setText(statusText);
@ -629,6 +640,7 @@ void RPC::getInfoThenRefresh(bool force) {
else {
tooltip = QObject::tr("zcashd has no peer connections");
}
tooltip = tooltip % "(v " % QString::number(version) % ")";
if (!zecPrice.isEmpty()) {
tooltip = "1 ZEC = " % zecPrice % "\n" % tooltip;
@ -690,10 +702,7 @@ void RPC::refreshAddresses() {
}
// Function to create the data model and update the views, used below.
void RPC::updateUI(bool anyUnconfirmed) {
// See if the turnstile migration has any steps that need to be done.
turnstile->executeMigrationStep();
void RPC::updateUI(bool anyUnconfirmed) {
ui->unconfirmedWarning->setVisible(anyUnconfirmed);
// Update balances model data, which will update the table too
@ -863,12 +872,36 @@ void RPC::refreshSentZTrans() {
);
}
void RPC::addNewTxToWatch(Tx tx, const QString& newOpid) {
watchingOps.insert(newOpid, tx);
void RPC::addNewTxToWatch(const QString& newOpid, WatchedTx wtx) {
watchingOps.insert(newOpid, wtx);
watchTxStatus();
}
// Execute a transaction!
void RPC::executeTransaction(Tx tx,
const std::function<void(QString opid)> submitted,
const std::function<void(QString opid, QString txid)> computed,
const std::function<void(QString opid, QString errStr)> error) {
// First, create the json params
json params = json::array();
fillTxJsonParams(params, tx);
std::cout << std::setw(2) << params << std::endl;
sendZTransaction(params, [=](const json& reply) {
QString opid = QString::fromStdString(reply.get<json::string_t>());
// And then start monitoring the transaction
addNewTxToWatch( opid, WatchedTx { opid, tx, computed, error} );
submitted(opid);
},
[=](QString errStr) {
error("", errStr);
});
}
void RPC::watchTxStatus() {
if (conn == nullptr)
return noConnection();
@ -888,35 +921,26 @@ void RPC::watchTxStatus() {
if (watchingOps.contains(id)) {
// And if it ended up successful
QString status = QString::fromStdString(it["status"]);
main->loadingLabel->setVisible(false);
if (status == "success") {
auto txid = QString::fromStdString(it["result"]["txid"]);
SentTxStore::addToSentTx(watchingOps.value(id), txid);
main->ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
main->loadingLabel->setVisible(false);
SentTxStore::addToSentTx(watchingOps[id].tx, txid);
auto wtx = watchingOps[id];
watchingOps.remove(id);
wtx.completed(id, txid);
// Refresh balances to show unconfirmed balances
refresh(true);
refresh(true);
} else if (status == "failed") {
// If it failed, then we'll actually show a warning.
auto errorMsg = QString::fromStdString(it["error"]["message"]);
QMessageBox msg(
QMessageBox::Critical,
QObject::tr("Transaction Error"),
QObject::tr("The transaction with id ") % id % QObject::tr(" failed. The error was") + ":\n\n" + errorMsg,
QMessageBox::Ok,
main
);
watchingOps.remove(id);
main->ui->statusBar->showMessage(QObject::tr(" Tx ") % id % QObject::tr(" failed"), 15 * 1000);
main->loadingLabel->setVisible(false);
msg.exec();
auto wtx = watchingOps[id];
watchingOps.remove(id);
wtx.error(id, errorMsg);
}
}
@ -973,8 +997,14 @@ void RPC::checkForUpdate(bool silent) {
}
auto currentVersion = QVersionNumber::fromString(APP_VERSION);
// Get the max version that the user has hidden updates for
QSettings s;
auto maxHiddenVersion = QVersionNumber::fromString(s.value("update/lastversion", "0.0.0").toString());
qDebug() << "Version check: Current " << currentVersion << ", Available " << maxVersion;
if (maxVersion > currentVersion) {
if (maxVersion > currentVersion && maxVersion > maxHiddenVersion) {
auto ans = QMessageBox::information(main, QObject::tr("Update Available"),
QObject::tr("A new release v%1 is available! You have v%2.\n\nWould you like to visit the releases page?")
.arg(maxVersion.toString())
@ -982,6 +1012,15 @@ void RPC::checkForUpdate(bool silent) {
QMessageBox::Yes, QMessageBox::Cancel);
if (ans == QMessageBox::Yes) {
QDesktopServices::openUrl(QUrl("https://github.com/ZcashFoundation/zec-qt-wallet/releases"));
} else {
// If the user selects cancel, don't bother them again for this version
s.setValue("update/lastversion", maxVersion.toString());
}
} else {
if (!silent) {
QMessageBox::information(main, QObject::tr("No updates available"),
QObject::tr("You already have the latest release v%1")
.arg(currentVersion.toString()));
}
} else {
if (!silent) {

View File

@ -24,6 +24,13 @@ struct TransactionItem {
QString memo;
};
struct WatchedTx {
QString opid;
Tx tx;
std::function<void(QString, QString)> completed;
std::function<void(QString, QString)> error;
};
class RPC
{
public:
@ -42,11 +49,17 @@ public:
void refreshZECPrice();
void getZboardTopics(std::function<void(QMap<QString, QString>)> cb);
void executeTransaction(Tx tx,
const std::function<void(QString opid)> submitted,
const std::function<void(QString opid, QString txid)> computed,
const std::function<void(QString opid, QString errStr)> error);
void fillTxJsonParams(json& params, Tx tx);
void sendZTransaction (json params, const std::function<void(json)>& cb);
void sendZTransaction(json params, const std::function<void(json)>& cb, const std::function<void(QString)>& err);
void watchTxStatus();
void addNewTxToWatch(Tx tx, const QString& newOpid);
const QMap<QString, Tx> getWatchingTxns() { return watchingOps; }
void addNewTxToWatch(const QString& newOpid, WatchedTx wtx);
const TxTableModel* getTransactionsModel() { return transactionsTableModel; }
const QList<QString>* getAllZAddresses() { return zaddresses; }
@ -103,7 +116,7 @@ private:
QList<QString>* zaddresses = nullptr;
QList<QString>* taddresses = nullptr;
QMap<QString, Tx> watchingOps;
QMap<QString, WatchedTx> watchingOps;
TxTableModel* transactionsTableModel = nullptr;
BalancesTableModel* balancesTableModel = nullptr;

View File

@ -3,8 +3,10 @@
#include "addressbook.h"
#include "ui_confirm.h"
#include "ui_memodialog.h"
#include "ui_newrecurring.h"
#include "settings.h"
#include "rpc.h"
#include "recurring.h"
using json = nlohmann::json;
@ -80,6 +82,34 @@ void MainWindow::setupSendTab() {
QFont f = ui->Address1->font();
f.setPointSize(f.pointSize() - 1);
ui->MemoTxt1->setFont(f);
// Recurring button
QObject::connect(ui->chkRecurring, &QCheckBox::stateChanged, [=] (int checked) {
if (checked) {
ui->btnRecurSchedule->setEnabled(true);
} else {
ui->btnRecurSchedule->setEnabled(false);
ui->lblRecurDesc->setText("");
}
});
// Recurring schedule button
QObject::connect(ui->btnRecurSchedule, &QPushButton::clicked, this, &MainWindow::editSchedule);
// Hide the recurring section for now
ui->chkRecurring->setVisible(false);
ui->lblRecurDesc->setVisible(false);
ui->btnRecurSchedule->setVisible(false);
// Set the default state for the whole page
removeExtraAddresses();
}
void MainWindow::editSchedule() {
// Open the edit schedule dialog
Recurring::showEditDialog(this, this, createTxFromSendPage());
}
void MainWindow::updateLabelsAutoComplete() {
@ -354,6 +384,11 @@ void MainWindow::removeExtraAddresses() {
delete addressGroupBox;
}
// Reset the recurring button
ui->chkRecurring->setCheckState(Qt::Unchecked);
ui->btnRecurSchedule->setEnabled(false);
ui->lblRecurDesc->setText("");
}
void MainWindow::maxAmountChecked(int checked) {
@ -620,18 +655,23 @@ void MainWindow::sendButton() {
// Show a dialog to confirm the Tx
if (confirmTx(tx)) {
json params = json::array();
rpc->fillTxJsonParams(params, tx);
std::cout << std::setw(2) << params << std::endl;
// And send the Tx
rpc->sendZTransaction(params, [=](const json& reply) {
QString opid = QString::fromStdString(reply.get<json::string_t>());
ui->statusBar->showMessage(tr("Computing Tx: ") % opid);
rpc->executeTransaction(tx,
[=] (QString opid) {
ui->statusBar->showMessage(tr("Computing Tx: ") % opid);
},
[=] (QString, QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
},
[=] (QString opid, QString errStr) {
ui->statusBar->showMessage(QObject::tr(" Tx ") % opid % QObject::tr(" failed"), 15 * 1000);
// And then start monitoring the transaction
rpc->addNewTxToWatch(tx, opid);
});
if (!opid.isEmpty())
errStr = QObject::tr("The transaction with id ") % opid % QObject::tr(" failed. The error was") + ":\n\n" + errStr;
QMessageBox::critical(this, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok);
}
);
}
}

View File

@ -84,12 +84,22 @@ void SentTxStore::addToSentTx(Tx tx, QString txid) {
totalAmount += i.amount;
}
QString toAddresses;
if (tx.toAddrs.length() == 1) {
toAddresses = tx.toAddrs[0].addr;
} else {
// Concatenate all the toAddresses
for (auto a : tx.toAddrs) {
toAddresses += a.addr % "(" % Settings::getZECDisplayFormat(a.amount) % ") ";
}
}
auto list = jsonDoc.array();
QJsonObject txItem;
txItem["type"] = "sent";
txItem["from"] = tx.fromAddr;
txItem["datetime"] = QDateTime::currentMSecsSinceEpoch() / (qint64)1000;
txItem["address"] = QString(); // The sent address is blank, to be consistent with t-Addr sent behaviour
txItem["address"] = toAddresses;
txItem["txid"] = txid;
txItem["amount"] = -totalAmount;
txItem["fee"] = -tx.fee;

View File

@ -320,8 +320,10 @@ void Turnstile::executeMigrationStep() {
return;
}
// Sometimes, we check too quickly, and the unspent UTXO is not updated yet, so we'll
// double check to see if there is enough balance.
if (!rpc->getAllBalances()->keys().contains(nextStep->intTAddr)) {
qDebug() << QString("The intermediate t-address doesn't have balance, even though it is confirmed");
//qDebug() << QString("The intermediate t-address doesn't have balance, even though it seems to be confirmed");
return;
}
@ -351,17 +353,20 @@ void Turnstile::executeMigrationStep() {
}
void Turnstile::doSendTx(Tx tx, std::function<void(void)> cb) {
json params = json::array();
rpc->fillTxJsonParams(params, tx);
std::cout << std::setw(2) << params << std::endl;
rpc->sendZTransaction(params, [=] (const json& reply) {
QString opid = QString::fromStdString(reply.get<json::string_t>());
//qDebug() << opid;
mainwindow->ui->statusBar->showMessage(QObject::tr("Computing Tx: ") % opid);
rpc->executeTransaction(tx, [=] (QString opid) {
mainwindow->ui->statusBar->showMessage(QObject::tr("Computing Tx: ") % opid);
},
[=] (QString /*opid*/, QString txid) {
mainwindow->ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
cb();
},
[=] (QString opid, QString errStr) {
mainwindow->ui->statusBar->showMessage(QObject::tr(" Tx ") % opid % QObject::tr(" failed"), 15 * 1000);
// And then start monitoring the transaction
rpc->addNewTxToWatch(tx, opid);
if (!opid.isEmpty())
errStr = QObject::tr("The transaction with id ") % opid % QObject::tr(" failed. The error was") + ":\n\n" + errStr;
cb();
});
QMessageBox::critical(mainwindow, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok);
});
}

View File

@ -54,7 +54,8 @@ SOURCES += \
src/logger.cpp \
src/addresscombo.cpp \
src/websockets.cpp \
src/mobileappconnector.cpp
src/mobileappconnector.cpp \
src/recurring.cpp
HEADERS += \
src/mainwindow.h \
@ -76,7 +77,8 @@ HEADERS += \
src/logger.h \
src/addresscombo.h \
src/websockets.h \
src/mobileappconnector.h
src/mobileappconnector.h \
src/recurring.h
FORMS += \
src/mainwindow.ui \
@ -90,7 +92,10 @@ FORMS += \
src/connection.ui \
src/zboard.ui \
src/addressbook.ui \
src/mobileappconnector.ui
src/mobileappconnector.ui \
src/createzcashconfdialog.ui \
src/recurringdialog.ui \
src/newrecurring.ui
TRANSLATIONS = res/zec_qt_wallet_es.ts \