mirror of
https://github.com/wownero/wowlet.git
synced 2026-10-29 20:28:53 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eab5145f34 | ||
|
|
a2c7245e46 | ||
|
|
4aa57f2684 | ||
|
|
d8625fdb29 | ||
|
|
207180396f | ||
|
|
b544e6dfce | ||
|
|
bc2cac672a | ||
|
|
6003ffebec | ||
|
|
51b3781c09 | ||
|
|
14e3422423 | ||
|
|
0e5ab21205 | ||
|
|
a11dc3f4b2 | ||
|
|
f5255440e4 | ||
|
|
0dc196e76d | ||
|
|
136f877778 | ||
|
|
3b7731d44f | ||
|
|
b51e1f3b0c | ||
|
|
ae3c214c3f | ||
|
|
200f16ecf2 | ||
|
|
ecd840c03a | ||
|
|
7092116efd | ||
|
|
68c493b736 | ||
|
|
4f62d52e5e | ||
|
|
c8be1e4966 | ||
|
|
c964cd5349 | ||
|
|
3fbf58f64d | ||
|
|
bd4400a7f2 | ||
|
|
c0e4d48fae | ||
|
|
8668f2beb2 | ||
|
|
628da207d0 | ||
|
|
677f7df376 | ||
|
|
580675e8a0 | ||
|
|
900a9b3fac | ||
|
|
f8f1e824a1 | ||
|
|
441c32bc30 | ||
|
|
41a0fb3b10 | ||
|
|
a38912a348 | ||
|
|
d568e4e1b6 | ||
|
|
12849c8571 | ||
|
|
f7fb051b67 | ||
|
|
693a6bc0df | ||
|
|
ccd19edf2f | ||
|
|
f2f6bf30b3 | ||
|
|
45b3f1ebde | ||
|
|
b4af2f75b1 | ||
|
|
fdabaf3374 | ||
|
|
2e481507e8 | ||
|
|
9b4e0e29e4 | ||
|
|
80bd8e085d | ||
|
|
7d2748c0bc | ||
|
|
2c0b04559e | ||
|
|
4476741ace | ||
|
|
68f8be01ac | ||
|
|
d84ee4692d | ||
|
|
a0fe51f318 | ||
|
|
70810ce6a2 | ||
|
|
f75d1f546b | ||
|
|
6c8255b12b | ||
|
|
f39cdb393f | ||
|
|
d4fda0f5f3 | ||
|
|
9afefa75a0 | ||
|
|
32c21ae81b | ||
|
|
ca1c2fa5ba | ||
|
|
6619927a7d | ||
|
|
e805dd04cb | ||
|
|
dbc3bed669 | ||
|
|
55aab1683f | ||
|
|
33978bda4f | ||
|
|
4c9ece5025 | ||
|
|
a984d64add | ||
|
|
cb95e89773 | ||
|
|
f8361fb31c | ||
|
|
5ceac87e6e | ||
|
|
e058f3bad1 | ||
|
|
460a44ba34 |
+2
-3
@@ -7,11 +7,10 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
set(VERSION_MAJOR "0")
|
||||
set(VERSION_MINOR "1")
|
||||
set(VERSION_REVISION "0")
|
||||
set(VERSION "beta-2")
|
||||
set(VERSION "beta-3")
|
||||
|
||||
option(FETCH_DEPS "Download dependencies if they are not found" ON)
|
||||
option(XMRTO "Include Xmr.To module" ON)
|
||||
option(MORPHTOKEN "Include MorphToken module" ON)
|
||||
option(XMRIG "Include XMRig module" ON)
|
||||
option(TOR_BIN "Path to Tor binary to embed inside Feather" OFF)
|
||||
|
||||
@@ -191,7 +190,7 @@ if(TOR_BIN)
|
||||
# - linux: See `Dockerfile`
|
||||
# - windows: https://github.com/mxe/mxe/blob/1024dc7d2db5eb7d5d3c64a2c12b5f592572f1ce/plugins/apps/tor.mk
|
||||
# - macos: taken from Tor Browser official release
|
||||
set(TOR_COPY_CMD "cp ${TOR_BIN} ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/exec/tor")
|
||||
set(TOR_COPY_CMD "cp -u ${TOR_BIN} ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/exec/tor")
|
||||
message(STATUS "${TOR_COPY_CMD}")
|
||||
execute_process(COMMAND bash -c "${TOR_COPY_CMD}" RESULT_VARIABLE ret)
|
||||
if(ret EQUAL "1")
|
||||
|
||||
@@ -42,7 +42,6 @@ via the `CMAKE_PREFIX_PATH` definition. For me this is:
|
||||
There are some Monero/Feather related options/definitions that you may pass:
|
||||
|
||||
- `-DXMRTO=OFF` - disable Xmr.To feature
|
||||
- `-DMORPHTOKEN=OFF` - disable MorphToken feature
|
||||
- `-DXMRIG=OFF` - disable XMRig feature
|
||||
- `-DTOR_BIN=/path/to/tor` - Embed a Tor executable inside Feather
|
||||
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
|
||||
|
||||
@@ -31,7 +31,6 @@ CMAKEFLAGS = \
|
||||
-DBUILD_64=On \
|
||||
-DBUILD_TESTS=Off \
|
||||
-DXMRTO=On \
|
||||
-DMORPHTOKEN=On \
|
||||
-DXMRIG=On \
|
||||
-DTOR_BIN=Off \
|
||||
-DCMAKE_CXX_STANDARD=11 \
|
||||
|
||||
@@ -124,10 +124,6 @@ if(XMRTO)
|
||||
target_compile_definitions(feather PRIVATE HAS_XMRTO=1)
|
||||
endif()
|
||||
|
||||
if(MORPHTOKEN)
|
||||
target_compile_definitions(feather PRIVATE HAS_MORPHTOKEN=1)
|
||||
endif()
|
||||
|
||||
if(TOR_BIN)
|
||||
target_compile_definitions(feather PRIVATE HAS_TOR_BIN=1)
|
||||
endif()
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#include "MorphTokenWidget.h"
|
||||
#include "ui_MorphTokenWidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "qrcode/QrCode.h"
|
||||
#include "dialog/qrcodedialog.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
MorphTokenWidget::MorphTokenWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::MorphTokenWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_ctx = MainWindow::getContext();
|
||||
|
||||
m_network = new UtilsNetworking(this->m_ctx->network);
|
||||
m_api = new MorphTokenApi(this, m_network);
|
||||
|
||||
connect(ui->btnCreateTrade, &QPushButton::clicked, this, &MorphTokenWidget::createTrade);
|
||||
connect(ui->btn_lookupTrade, &QPushButton::clicked, this, &MorphTokenWidget::lookupTrade);
|
||||
connect(ui->btn_getRates, &QPushButton::clicked, this, &MorphTokenWidget::getRates);
|
||||
|
||||
connect(m_api, &MorphTokenApi::ApiResponse, this, &MorphTokenWidget::onApiResponse);
|
||||
|
||||
connect(ui->combo_From, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index){
|
||||
this->displayRate();
|
||||
ui->label_refundAddress->setText(QString("Refund address (%1):").arg(ui->combo_From->currentText()));
|
||||
});
|
||||
connect(ui->combo_To, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index){
|
||||
this->displayRate();
|
||||
ui->label_destinationAddress->setText(QString("Destination address (%1):").arg(ui->combo_To->currentText()));
|
||||
});
|
||||
|
||||
connect(ui->check_autorefresh, &QCheckBox::toggled, [this](bool toggled){
|
||||
m_countdown = 30;
|
||||
toggled ? m_countdownTimer.start(1000) : m_countdownTimer.stop();
|
||||
ui->check_autorefresh->setText("Autorefresh");
|
||||
});
|
||||
connect(&m_countdownTimer, &QTimer::timeout, this, &MorphTokenWidget::onCountdown);
|
||||
|
||||
connect(ui->line_Id, &QLineEdit::textChanged, [this](const QString &text){
|
||||
ui->btn_lookupTrade->setEnabled(!text.isEmpty());
|
||||
ui->check_autorefresh->setEnabled(!text.isEmpty());
|
||||
});
|
||||
|
||||
// Default to BTC -> XMR
|
||||
ui->combo_From->setCurrentIndex(1);
|
||||
ui->combo_To->setCurrentIndex(0);
|
||||
|
||||
ui->label_rate->setVisible(false);
|
||||
m_ratesTimer.setSingleShot(true);
|
||||
connect(&m_ratesTimer, &QTimer::timeout, [this]{
|
||||
ui->label_rate->setVisible(false);
|
||||
});
|
||||
|
||||
ui->qrCode->setVisible(false);
|
||||
ui->label_depositAddress->setVisible(false);
|
||||
connect(ui->qrCode, &ClickableLabel::clicked, this, &MorphTokenWidget::showQrCodeDialog);
|
||||
|
||||
ui->tabWidget->setTabVisible(2, false);
|
||||
}
|
||||
|
||||
void MorphTokenWidget::createTrade() {
|
||||
QString inputAsset = ui->combo_From->currentText();
|
||||
QString outputAsset = ui->combo_To->currentText();
|
||||
QString refundAddress = ui->line_refundAddress->text();
|
||||
QString destinationAddress = ui->line_destinationAddress->text();
|
||||
|
||||
m_api->createTrade(inputAsset, outputAsset, refundAddress, destinationAddress);
|
||||
}
|
||||
|
||||
void MorphTokenWidget::lookupTrade() {
|
||||
QString morphId = ui->line_Id->text();
|
||||
|
||||
if (!morphId.isEmpty())
|
||||
m_api->getTrade(morphId);
|
||||
}
|
||||
|
||||
void MorphTokenWidget::getRates() {
|
||||
m_api->getRates();
|
||||
}
|
||||
|
||||
void MorphTokenWidget::onApiResponse(const MorphTokenApi::MorphTokenResponse &resp) {
|
||||
if (!resp.ok) {
|
||||
ui->check_autorefresh->setChecked(false);
|
||||
QMessageBox::warning(this, "MorphToken error", QString("Request failed:\n\n%1").arg(resp.message));
|
||||
return;
|
||||
}
|
||||
|
||||
ui->debugInfo->setPlainText(QJsonDocument(resp.obj).toJson(QJsonDocument::Indented));
|
||||
|
||||
bool shouldShowQr = (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE || resp.endpoint == MorphTokenApi::Endpoint::GET_TRADE);
|
||||
ui->qrCode->setVisible(shouldShowQr);
|
||||
ui->label_depositAddress->setVisible(shouldShowQr);
|
||||
|
||||
if (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE || resp.endpoint == MorphTokenApi::Endpoint::GET_TRADE) {
|
||||
ui->tabWidget->setCurrentIndex(1);
|
||||
ui->line_Id->setText(resp.obj.value("id").toString());
|
||||
|
||||
auto obj = resp.obj;
|
||||
auto input = obj["input"].toObject();
|
||||
auto output = obj["output"].toArray()[0].toObject();
|
||||
QString state = obj.value("state").toString();
|
||||
QString statusText;
|
||||
|
||||
ui->trade->setTitle(QString("Trade (%1)").arg(state));
|
||||
|
||||
statusText += QString("Morph ID: %1\n\n").arg(obj["id"].toString());
|
||||
|
||||
if (state == "PENDING") {
|
||||
statusText += QString("Waiting for a deposit, send %1 to %2\n").arg(input["asset"].toString(),
|
||||
input["deposit_address"].toString());
|
||||
statusText += QString("Rate: 1 %1 -> %2 %3\n\n").arg(input["asset"].toString(),
|
||||
output["seen_rate"].toString(),
|
||||
output["asset"].toString());
|
||||
statusText += "Limits:\n";
|
||||
statusText += QString(" Minimum amount accepted: %1 %2\n").arg(formatAmount(input["asset"].toString(), input["limits"].toObject()["min"].toDouble()),
|
||||
input["asset"].toString());
|
||||
statusText += QString(" Maximum amount accepted: %1 %2\n").arg(formatAmount(input["asset"].toString(), input["limits"].toObject()["max"].toDouble()),
|
||||
input["asset"].toString());
|
||||
statusText += QString("\nSend a single deposit. If the amount is outside the limits, a refund will happen.");
|
||||
|
||||
m_depositAddress = input["deposit_address"].toString();
|
||||
|
||||
const QrCode qrc(m_depositAddress, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::MEDIUM);
|
||||
int width = ui->qrCode->width();
|
||||
if (qrc.isValid()) {
|
||||
ui->qrCode->setPixmap(qrc.toPixmap(1).scaled(width, width, Qt::KeepAspectRatio));
|
||||
}
|
||||
} else if (state == "PROCESSING" || state == "TRADING" || state == "CONFIRMING") {
|
||||
if (state == "CONFIRMING") {
|
||||
statusText += QString("Waiting for confirmations\n");
|
||||
} else if (state == "TRADING") {
|
||||
statusText += QString("Your transaction has been received and is confirmed. MorphToken is now executing your trade.\n"
|
||||
"Usually this step takes no longer than a minute, "
|
||||
"but in rare cases it can take a couple hours.\n"
|
||||
"Wait a bit before contacting support.\n");
|
||||
}
|
||||
statusText += QString("Converting %1 to %2\n").arg(input["asset"].toString(), output["asset"].toString());
|
||||
statusText += QString("Sending to %1\n").arg(output["address"].toString());
|
||||
statusText += QString("Stuck? Contact support at contact@morphtoken.com");
|
||||
} else if (state == "COMPLETE") {
|
||||
if (output["txid"].toString().isEmpty()) {
|
||||
statusText += QString("MorphToken is sending your transaction.\n");
|
||||
statusText += QString("MorphToken will send %1 %2 to %2").arg(this->formatAmount(output["asset"].toString(), output["converted_amount"].toDouble() - output["network_fee"].toObject()["fee"].toDouble()),
|
||||
output["asset"].toString(),
|
||||
output["address"].toString());
|
||||
} else {
|
||||
statusText += QString("Sent %1 %2 to %3\ntxid: {}").arg(this->formatAmount(output["asset"].toString(), output["converted_amount"].toDouble() - output["network_fee"].toObject()["fee"].toDouble()),
|
||||
output["asset"].toString(),
|
||||
output["address"].toString(),
|
||||
output["txid"].toString());
|
||||
}
|
||||
} else if (state == "PROCESSING_REFUND" || state == "COMPLETE_WITH_REFUND") {
|
||||
statusText += QString("MorphToken will refund %1 %2\nReason: %3\n").arg(obj["final_amount"].toString(),
|
||||
obj["asset"].toString(),
|
||||
obj["reason"].toString());
|
||||
|
||||
if (obj.contains("txid")) {
|
||||
statusText += QString("txid: %1").arg(obj["txid"].toString());
|
||||
}
|
||||
} else if (state == "COMPLETE_WITHOUT_REFUND") {
|
||||
statusText += "Deposit amount below network fee, too small to refund.";
|
||||
} else if (state == "PROCESSING_DELAY") {
|
||||
statusText += "Please enter in contact with support.\n\ncontact@morphtoken.com";
|
||||
} else if (state == "CANCELLED") {
|
||||
statusText += "The trade was cancelled. If this is unexpected, please contact support at contact@morphtoken.com";
|
||||
}
|
||||
|
||||
ui->label_status->setText(statusText);
|
||||
} else if (resp.endpoint == MorphTokenApi::Endpoint::GET_RATES) {
|
||||
m_rates = resp.obj.value("data").toObject();
|
||||
this->displayRate();
|
||||
ui->label_rate->setVisible(true);
|
||||
m_ratesTimer.start(120 * 1000);
|
||||
}
|
||||
|
||||
if (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE) {
|
||||
QMessageBox::information(this, "MorphToken", "Trade created!\n\nMake sure to save your Morph ID. You may need it in case something goes wrong.");
|
||||
}
|
||||
}
|
||||
|
||||
void MorphTokenWidget::onCountdown() {
|
||||
if (m_countdown > 0) {
|
||||
m_countdown -= 1;
|
||||
} else {
|
||||
this->lookupTrade();
|
||||
m_countdown = 30;
|
||||
}
|
||||
ui->check_autorefresh->setText(QString("Autorefresh (%1)").arg(m_countdown));
|
||||
}
|
||||
|
||||
void MorphTokenWidget::displayRate() {
|
||||
QString inputAsset = ui->combo_From->currentText();
|
||||
QString outputAsset = ui->combo_To->currentText();
|
||||
QString outputRate = m_rates.value(inputAsset).toObject().value(outputAsset).toString("1");
|
||||
|
||||
QString rateStr = QString("1 %1 -> %2 %3").arg(inputAsset, outputRate, outputAsset);
|
||||
ui->label_rate->setText(rateStr);
|
||||
}
|
||||
|
||||
void MorphTokenWidget::showQrCodeDialog() {
|
||||
QrCode qr(m_depositAddress, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::HIGH);
|
||||
auto *dialog = new QrCodeDialog(this, qr, "Deposit address");
|
||||
dialog->exec();
|
||||
dialog->deleteLater();
|
||||
}
|
||||
|
||||
QString MorphTokenWidget::formatAmount(const QString &asset, double amount) {
|
||||
double displayAmount;
|
||||
double div;
|
||||
|
||||
if (asset == "ETH")
|
||||
div = 1e18;
|
||||
else if (asset == "XMR")
|
||||
div = 1e12;
|
||||
else
|
||||
div = 1e8;
|
||||
|
||||
displayAmount = amount / div;
|
||||
|
||||
return QString::number(displayAmount, 'f', 8);
|
||||
}
|
||||
|
||||
MorphTokenWidget::~MorphTokenWidget() {
|
||||
delete ui;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_MORPHTOKENWIDGET_H
|
||||
#define FEATHER_MORPHTOKENWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "appcontext.h"
|
||||
#include "utils/MorphTokenApi.h"
|
||||
|
||||
namespace Ui {
|
||||
class MorphTokenWidget;
|
||||
}
|
||||
|
||||
class MorphTokenWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MorphTokenWidget(QWidget *parent = nullptr);
|
||||
~MorphTokenWidget() override;
|
||||
|
||||
private:
|
||||
void createTrade();
|
||||
void lookupTrade();
|
||||
void getRates();
|
||||
void onApiResponse(const MorphTokenApi::MorphTokenResponse &resp);
|
||||
|
||||
void onCountdown();
|
||||
void displayRate();
|
||||
void showQrCodeDialog();
|
||||
|
||||
QString formatAmount(const QString &asset, double amount);
|
||||
|
||||
Ui::MorphTokenWidget *ui;
|
||||
|
||||
AppContext *m_ctx;
|
||||
MorphTokenApi *m_api;
|
||||
UtilsNetworking *m_network;
|
||||
QTimer m_countdownTimer;
|
||||
int m_countdown = 30;
|
||||
QJsonObject m_rates;
|
||||
QTimer m_ratesTimer;
|
||||
QString m_depositAddress;
|
||||
};
|
||||
|
||||
#endif //FEATHER_MORPHTOKENWIDGET_H
|
||||
@@ -1,423 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MorphTokenWidget</class>
|
||||
<widget class="QWidget" name="MorphTokenWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1036</width>
|
||||
<height>614</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tabCreateTrade">
|
||||
<attribute name="title">
|
||||
<string>Create trade</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>From:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_From">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>XMR</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BTC</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ETH</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BCH</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>LTC</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>DASH</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>To:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_To">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>XMR</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BTC</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ETH</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BCH</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>LTC</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>DASH</string>
|
||||
</property>
|
||||
</item>
|
||||
</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>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_rate">
|
||||
<property name="text">
|
||||
<string>rate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_refundAddress">
|
||||
<property name="text">
|
||||
<string>Refund address (XMR):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="line_refundAddress"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_destinationAddress">
|
||||
<property name="text">
|
||||
<string>Destination address (XMR):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="line_destinationAddress"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Powered by MorphToken.com</string>
|
||||
</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>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_getRates">
|
||||
<property name="text">
|
||||
<string>Get Rates</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnCreateTrade">
|
||||
<property name="text">
|
||||
<string>Create Trade</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabLookupTrade">
|
||||
<attribute name="title">
|
||||
<string>Lookup trade</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Morph ID or MorphToken deposit address:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="line_Id"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="check_autorefresh">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Autorefresh</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_lookupTrade">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lookup trade</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="trade">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Trade</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_status">
|
||||
<property name="text">
|
||||
<string>No trade loaded.</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="ClickableLabel" name="qrCode">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>qrcode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_depositAddress">
|
||||
<property name="text">
|
||||
<string>Deposit address</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<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>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabDebug">
|
||||
<attribute name="title">
|
||||
<string>Debug</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="debugInfo">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ClickableLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>components.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
+27
-20
@@ -80,13 +80,19 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||
this->accountName = Utils::getUnixAccountName();
|
||||
this->homeDir = QDir::homePath();
|
||||
|
||||
QString walletDir = config()->get(Config::walletDirectory).toString();
|
||||
if (walletDir.isEmpty()) {
|
||||
#if defined(Q_OS_LINUX) or defined(Q_OS_MAC)
|
||||
this->defaultWalletDir = QString("%1/Monero/wallets").arg(this->configRoot);
|
||||
this->defaultWalletDirRoot = QString("%1/Monero").arg(this->configRoot);
|
||||
this->defaultWalletDir = QString("%1/Monero/wallets").arg(this->configRoot);
|
||||
this->defaultWalletDirRoot = QString("%1/Monero").arg(this->configRoot);
|
||||
#elif defined(Q_OS_WIN)
|
||||
this->defaultWalletDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Monero";
|
||||
this->defaultWalletDirRoot = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||
this->defaultWalletDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Monero";
|
||||
this->defaultWalletDirRoot = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||
#endif
|
||||
} else {
|
||||
this->defaultWalletDir = walletDir;
|
||||
this->defaultWalletDirRoot = walletDir;
|
||||
}
|
||||
|
||||
// Create wallet dirs
|
||||
if (!QDir().mkpath(defaultWalletDir))
|
||||
@@ -134,11 +140,11 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||
auto genesis_timestamp = this->restoreHeights[NetworkType::Type::MAINNET]->data.firstKey();
|
||||
AppContext::txFiatHistory = new TxFiatHistory(genesis_timestamp, this->configDirectory);
|
||||
connect(this->ws, &WSClient::connectionEstablished, AppContext::txFiatHistory, &TxFiatHistory::onUpdateDatabase);
|
||||
connect(AppContext::txFiatHistory, &TxFiatHistory::requestYear, [=](unsigned int year){
|
||||
connect(AppContext::txFiatHistory, &TxFiatHistory::requestYear, [=](int year){
|
||||
QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1}})").arg(year).toUtf8();
|
||||
this->ws->sendMsg(data);
|
||||
});
|
||||
connect(AppContext::txFiatHistory, &TxFiatHistory::requestYearMonth, [=](unsigned int year, unsigned int month) {
|
||||
connect(AppContext::txFiatHistory, &TxFiatHistory::requestYearMonth, [=](int year, int month) {
|
||||
QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1, "month": %2}})").arg(year).arg(month).toUtf8();
|
||||
this->ws->sendMsg(data);
|
||||
});
|
||||
@@ -378,22 +384,22 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
|
||||
auto changed = false;
|
||||
|
||||
if(!this->heights.contains("mainnet")) {
|
||||
this->heights["mainnet"] = (unsigned int) mainnet;
|
||||
this->heights["mainnet"] = mainnet;
|
||||
changed = true;
|
||||
}
|
||||
else {
|
||||
if (mainnet > this->heights["mainnet"]) {
|
||||
this->heights["mainnet"] = (unsigned int) mainnet;
|
||||
this->heights["mainnet"] = mainnet;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if(!this->heights.contains("stagenet")) {
|
||||
this->heights["stagenet"] = (unsigned int) stagenet;
|
||||
this->heights["stagenet"] = stagenet;
|
||||
changed = true;
|
||||
}
|
||||
else {
|
||||
if (stagenet > this->heights["stagenet"]) {
|
||||
this->heights["stagenet"] = (unsigned int) stagenet;
|
||||
this->heights["stagenet"] = stagenet;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
@@ -605,7 +611,7 @@ void AppContext::initRestoreHeights() {
|
||||
restoreHeights[NetworkType::MAINNET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_monero_mainnet.txt", NetworkType::MAINNET);
|
||||
}
|
||||
|
||||
void AppContext::onSetRestoreHeight(unsigned int height){
|
||||
void AppContext::onSetRestoreHeight(quint64 height){
|
||||
auto seed = this->currentWallet->getCacheAttribute("feather.seed");
|
||||
if(!seed.isEmpty()) {
|
||||
const auto msg = "This wallet has a 14 word mnemonic seed which has the restore height embedded.";
|
||||
@@ -721,7 +727,8 @@ void AppContext::onWalletRefreshed(bool success) {
|
||||
if (!this->refreshed) {
|
||||
refreshModels();
|
||||
this->refreshed = true;
|
||||
this->storeWallet();
|
||||
// store wallet immediately upon finishing synchronization
|
||||
this->currentWallet->store();
|
||||
}
|
||||
|
||||
qDebug() << "Wallet refresh status: " << success;
|
||||
@@ -757,9 +764,11 @@ void AppContext::onTransactionCreated(PendingTransaction *tx, const QString &add
|
||||
if(address == this->donationAddress)
|
||||
this->donationSending = true;
|
||||
|
||||
// Let UI know that the transaction was constructed
|
||||
emit endTransaction();
|
||||
|
||||
// tx created, but not sent yet. ask user to verify first.
|
||||
emit createTransactionSuccess(tx, address, mixin);
|
||||
emit endTransaction();
|
||||
}
|
||||
|
||||
void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid){
|
||||
@@ -790,16 +799,14 @@ void AppContext::storeWallet() {
|
||||
}
|
||||
|
||||
void AppContext::updateBalance() {
|
||||
if(!this->currentWallet)
|
||||
if (!this->currentWallet)
|
||||
return;
|
||||
|
||||
AppContext::balance = this->currentWallet->balance() / globals::cdiv;
|
||||
auto balance_str = QString::number(balance, 'f');
|
||||
quint64 balance_u = this->currentWallet->balance();
|
||||
AppContext::balance = balance_u / globals::cdiv;
|
||||
double spendable = this->currentWallet->unlockedBalance();
|
||||
|
||||
double unlocked = this->currentWallet->unlockedBalance() / globals::cdiv;
|
||||
auto unlocked_str = QString::number(unlocked, 'f');
|
||||
|
||||
emit balanceUpdated(balance, unlocked, balance_str, unlocked_str);
|
||||
emit balanceUpdated(balance_u, spendable);
|
||||
}
|
||||
|
||||
void AppContext::syncStatusUpdated(quint64 height, quint64 target) {
|
||||
|
||||
+7
-8
@@ -66,9 +66,9 @@ public:
|
||||
|
||||
static void createConfigDirectory(const QString &dir) ;
|
||||
|
||||
QMap<QString, unsigned int> heights;
|
||||
QMap<QString, int> heights;
|
||||
QMap<NetworkType::Type, RestoreHeightLookup*> restoreHeights;
|
||||
const unsigned int kdfRounds = 1;
|
||||
const quint64 kdfRounds = 1;
|
||||
PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low;
|
||||
quint32 tx_mixin = static_cast<const quint32 &>(10);
|
||||
QString seedLanguage = "English"; // 14 word `monero-seed` only has English
|
||||
@@ -90,7 +90,6 @@ public:
|
||||
static TxFiatHistory *txFiatHistory;
|
||||
|
||||
// libwalletqt
|
||||
unsigned int blockHeight = 0;
|
||||
bool refreshed = false;
|
||||
WalletManager *walletManager;
|
||||
Wallet *currentWallet = nullptr;
|
||||
@@ -116,7 +115,7 @@ public slots:
|
||||
void onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) const;
|
||||
void onCreateTransactionError(const QString &msg);
|
||||
void onOpenAliasResolve(const QString &openAlias);
|
||||
void onSetRestoreHeight(unsigned int height);
|
||||
void onSetRestoreHeight(quint64 height);
|
||||
void onPreferredFiatCurrencyChanged(const QString &symbol);
|
||||
|
||||
private slots:
|
||||
@@ -138,11 +137,11 @@ private slots:
|
||||
void onConnectionStatusChanged(int status);
|
||||
|
||||
signals:
|
||||
void balanceUpdated(double balance, double unlocked, QString balance_str, QString unlocked_str);
|
||||
void balanceUpdated(quint64 balance, quint64 spendable);
|
||||
void blockchainSync(int height, int target);
|
||||
void refreshSync(int height, int target);
|
||||
void synchronized();
|
||||
void blockHeightWSUpdated(QMap<QString, unsigned int> heights);
|
||||
void blockHeightWSUpdated(QMap<QString, int> heights);
|
||||
void walletSynchronized();
|
||||
void walletOpened();
|
||||
void walletClosed();
|
||||
@@ -164,7 +163,7 @@ signals:
|
||||
void openAliasResolveError(const QString &msg);
|
||||
void openAliasResolved(const QString &address, const QString &openAlias);
|
||||
void setRestoreHeightError(const QString &msg);
|
||||
void customRestoreHeightSet(unsigned int height);
|
||||
void customRestoreHeightSet(int height);
|
||||
void closeApplication();
|
||||
void donationNag();
|
||||
void initiateTransaction();
|
||||
@@ -173,7 +172,7 @@ signals:
|
||||
void setTitle(const QString &title); // set window title
|
||||
|
||||
private:
|
||||
const unsigned int m_donationBoundary = 15;
|
||||
const int m_donationBoundary = 15;
|
||||
UtilsNetworking *m_utilsNetworkingNodes;
|
||||
QTimer *m_storeTimer = new QTimer(this);
|
||||
QUrl m_wsUrl = QUrl(QStringLiteral("ws://7e6egbawekbkxzkv4244pqeqgoo4axko2imgjbedwnn6s5yb6b7oliqd.onion/ws"));
|
||||
|
||||
+3
-2
@@ -11,6 +11,9 @@
|
||||
<file>assets/images/appicons/128x128.png</file>
|
||||
<file>assets/images/appicons/256x256.png</file>
|
||||
<file>assets/images/arrow.svg</file>
|
||||
<file>assets/images/banners/1.png</file>
|
||||
<file>assets/images/banners/2.png</file>
|
||||
<file>assets/images/banners/3.png</file>
|
||||
<file>assets/images/bitcoin.png</file>
|
||||
<file>assets/images/camera_dark.png</file>
|
||||
<file>assets/images/camera_white.png</file>
|
||||
@@ -48,11 +51,9 @@
|
||||
<file>assets/images/lock.svg</file>
|
||||
<file>assets/images/microphone.png</file>
|
||||
<file>assets/images/mining.png</file>
|
||||
<file>assets/images/morphtoken.png</file>
|
||||
<file>assets/images/network.png</file>
|
||||
<file>assets/images/offline_tx.png</file>
|
||||
<file>assets/images/person.svg</file>
|
||||
<file>assets/images/photos/1.png</file>
|
||||
<file>assets/images/preferences.png</file>
|
||||
<file>assets/images/preferences.svg</file>
|
||||
<file>assets/images/qrcode.png</file>
|
||||
|
||||
+4
-2
@@ -1,8 +1,10 @@
|
||||
The wallet UI is heavily inspired by Electrum. We would like to recognize Thomas Voegtlin for his pioneering work on Bitcoin.
|
||||
|
||||
Feather uses monero-seed written by Tevador, for 14 word mnemonic seeds.
|
||||
|
||||
Initial CMake support for the Monero GUI was coded by TheCharlatan/xiphon.
|
||||
Wizard banner art was adapted from a paper wallet design by the themonera (themonera.art).
|
||||
|
||||
The wallet UI is heavily inspired by Electrum. We would like to recognize Thomas Voegtlin for his pioneering work on Bitcoin.
|
||||
Initial CMake support for the Monero GUI was coded by TheCharlatan/xiphon.
|
||||
|
||||
Huge thanks to nioc, fluffypony, wowario, thrmo for help during development.
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.6 KiB |
+42
-13
@@ -5,17 +5,17 @@
|
||||
#include "ui_contactswidget.h"
|
||||
#include "dialog/contactsdialog.h"
|
||||
#include "model/ModelUtils.h"
|
||||
#include "utils/utils.h"
|
||||
#include "mainwindow.h"
|
||||
#include "libwalletqt/AddressBook.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QDebug>
|
||||
#include <QKeyEvent>
|
||||
#include <QMessageBox>
|
||||
|
||||
ContactsWidget::ContactsWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::ContactsWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_ctx = MainWindow::getContext();
|
||||
|
||||
// header context menu
|
||||
ui->contacts->header()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
@@ -28,15 +28,16 @@ ContactsWidget::ContactsWidget(QWidget *parent) :
|
||||
// context menu
|
||||
ui->contacts->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_contextMenu = new QMenu(ui->contacts);
|
||||
m_contextMenu->addAction(QIcon(":/assets/images/person.svg"), "New contact", this, &ContactsWidget::newContact);
|
||||
|
||||
m_contextMenu->addAction(QIcon(":/assets/images/person.svg"), "New contact", [this]{
|
||||
this->newContact();
|
||||
});
|
||||
|
||||
// row context menu
|
||||
m_rowMenu = new QMenu(ui->contacts);
|
||||
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy address", this, &ContactsWidget::copyAddress);
|
||||
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy name", this, &ContactsWidget::copyName);
|
||||
m_rowMenu->addAction(QIcon(":/assets/images/appicons/128x128.png"), "Pay to", this, &ContactsWidget::payTo);
|
||||
m_deleteEntryAction = m_rowMenu->addAction("Delete", this, &ContactsWidget::deleteContact);
|
||||
m_rowMenu->addAction("Pay to", this, &ContactsWidget::payTo);
|
||||
m_rowMenu->addAction("Delete", this, &ContactsWidget::deleteContact);
|
||||
|
||||
connect(ui->contacts, &QTreeView::customContextMenuRequested, [=](const QPoint & point){
|
||||
QModelIndex index = ui->contacts->indexAt(point);
|
||||
@@ -95,16 +96,44 @@ void ContactsWidget::showHeaderMenu(const QPoint& position)
|
||||
m_headerMenu->exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void ContactsWidget::newContact()
|
||||
void ContactsWidget::newContact(QString address, QString name)
|
||||
{
|
||||
auto * dialog = new ContactsDialog(this);
|
||||
auto * dialog = new ContactsDialog(this, address, name);
|
||||
int ret = dialog->exec();
|
||||
if (!ret) return;
|
||||
|
||||
QString address = dialog->getAddress();
|
||||
QString name = dialog->getName();
|
||||
address = dialog->getAddress();
|
||||
name = dialog->getName();
|
||||
|
||||
emit addContact(address, name);
|
||||
bool addressValid = WalletManager::addressValid(address, m_ctx->currentWallet->nettype());
|
||||
if (!addressValid) {
|
||||
QMessageBox::warning(this, "Invalid address", "Invalid address");
|
||||
return;
|
||||
}
|
||||
|
||||
int num_addresses = m_ctx->currentWallet->addressBook()->count();
|
||||
QString address_entry;
|
||||
QString name_entry;
|
||||
for (int i=0; i<num_addresses; i++) {
|
||||
m_ctx->currentWallet->addressBook()->getRow(i, [&address_entry, &name_entry](const AddressBookInfo &entry){
|
||||
address_entry = entry.address();
|
||||
name_entry = entry.description();
|
||||
});
|
||||
|
||||
if (address == address_entry) {
|
||||
QMessageBox::warning(this, "Unable to add contact", "Duplicate address");
|
||||
ui->contacts->setCurrentIndex(m_model->index(i,0)); // Highlight duplicate address
|
||||
return;
|
||||
}
|
||||
if (name == name_entry) {
|
||||
QMessageBox::warning(this, "Unable to add contact", "Duplicate label");
|
||||
this->newContact(address, name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_ctx->currentWallet->addressBook()->addRow(address, "", name);
|
||||
m_ctx->storeWallet();
|
||||
}
|
||||
|
||||
void ContactsWidget::deleteContact()
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "model/AddressBookModel.h"
|
||||
#include "model/AddressBookProxyModel.h"
|
||||
#include "appcontext.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QMenu>
|
||||
@@ -21,29 +22,28 @@ class ContactsWidget : public QWidget
|
||||
public:
|
||||
explicit ContactsWidget(QWidget *parent = nullptr);
|
||||
void setModel(AddressBookModel * model);
|
||||
~ContactsWidget();
|
||||
~ContactsWidget() override;
|
||||
|
||||
public slots:
|
||||
void copyAddress();
|
||||
void copyName();
|
||||
void payTo();
|
||||
void newContact();
|
||||
void newContact(QString address = "", QString name = "");
|
||||
void deleteContact();
|
||||
void setShowFullAddresses(bool show);
|
||||
void setSearchFilter(const QString &filter);
|
||||
|
||||
signals:
|
||||
void addContact(QString &address, QString &name);
|
||||
void fillAddress(QString &address);
|
||||
|
||||
private slots:
|
||||
void showHeaderMenu(const QPoint& position);
|
||||
void showHeaderMenu(const QPoint &position);
|
||||
|
||||
private:
|
||||
Ui::ContactsWidget *ui;
|
||||
AppContext *m_ctx;
|
||||
|
||||
QAction *m_showFullAddressesAction;
|
||||
QAction *m_deleteEntryAction;
|
||||
QMenu *m_rowMenu;
|
||||
QMenu *m_contextMenu;
|
||||
QMenu *m_headerMenu;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#include "balancedialog.h"
|
||||
#include "ui_balancedialog.h"
|
||||
|
||||
#include "libwalletqt/WalletManager.h"
|
||||
|
||||
BalanceDialog::BalanceDialog(QWidget *parent, Wallet *wallet)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::BalanceDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->label_unconfirmed_help->setHelpText("Outputs require 10 confirmations before they become spendable. "
|
||||
"This will take 20 minutes on average.");
|
||||
|
||||
ui->label_unconfirmed->setText(WalletManager::displayAmount(wallet->balance() - wallet->unlockedBalance()));
|
||||
ui->label_spendable->setText(WalletManager::displayAmount(wallet->unlockedBalance()));
|
||||
ui->label_total->setText(WalletManager::displayAmount(wallet->balance()));
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
BalanceDialog::~BalanceDialog() {
|
||||
delete ui;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_BALANCEDIALOG_H
|
||||
#define FEATHER_BALANCEDIALOG_H
|
||||
|
||||
#include "libwalletqt/Wallet.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class BalanceDialog;
|
||||
}
|
||||
|
||||
class BalanceDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BalanceDialog(QWidget *parent, Wallet *wallet);
|
||||
~BalanceDialog() override;
|
||||
|
||||
private:
|
||||
Ui::BalanceDialog *ui;
|
||||
};
|
||||
|
||||
#endif //FEATHER_BALANCEDIALOG_H
|
||||
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>BalanceDialog</class>
|
||||
<widget class="QDialog" name="BalanceDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>405</width>
|
||||
<height>162</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Balance</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="HelpLabel" name="label_unconfirmed_help">
|
||||
<property name="text">
|
||||
<string>Unconfirmed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_unconfirmed">
|
||||
<property name="text">
|
||||
<string>0.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_spendable_help">
|
||||
<property name="text">
|
||||
<string>Spendable</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_spendable">
|
||||
<property name="text">
|
||||
<string>0.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Total</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_total">
|
||||
<property name="text">
|
||||
<string>0.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>HelpLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>components.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>BalanceDialog</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>BalanceDialog</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>
|
||||
@@ -4,13 +4,19 @@
|
||||
#include "ui_contactsdialog.h"
|
||||
#include "contactsdialog.h"
|
||||
|
||||
ContactsDialog::ContactsDialog(QWidget *parent)
|
||||
ContactsDialog::ContactsDialog(QWidget *parent, const QString &address, const QString &name)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::ContactsDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setMinimumWidth(400);
|
||||
|
||||
ui->lineEdit_address->setText(address);
|
||||
ui->lineEdit_name->setText(name);
|
||||
if (!name.isEmpty()) {
|
||||
ui->lineEdit_name->setFocus();
|
||||
}
|
||||
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, [&](){
|
||||
m_address = ui->lineEdit_address->text();
|
||||
m_name = ui->lineEdit_name->text();
|
||||
|
||||
@@ -15,7 +15,7 @@ class ContactsDialog : public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ContactsDialog(QWidget *parent = nullptr);
|
||||
explicit ContactsDialog(QWidget *parent = nullptr, const QString &address = "", const QString &name = "");
|
||||
~ContactsDialog() override;
|
||||
|
||||
QString getAddress();
|
||||
|
||||
@@ -57,11 +57,12 @@ void DebugInfoDialog::updateInfo() {
|
||||
ui->label_remoteNode->setText(node.full);
|
||||
ui->label_walletStatus->setText(this->statusToString(m_ctx->currentWallet->connectionStatus()));
|
||||
ui->label_torStatus->setText(torStatus);
|
||||
ui->label_websocketStatus->setText(Utils::QtEnumToString(m_ctx->ws->webSocket.state()));
|
||||
ui->label_websocketStatus->setText(Utils::QtEnumToString(m_ctx->ws->webSocket.state()).remove("State"));
|
||||
|
||||
ui->label_netType->setText(Utils::QtEnumToString(m_ctx->currentWallet->nettype()));
|
||||
ui->label_seedType->setText(m_ctx->currentWallet->getCacheAttribute("feather.seed").isEmpty() ? "25 word" : "14 word");
|
||||
ui->label_viewOnly->setText(m_ctx->currentWallet->viewOnly() ? "True" : "False");
|
||||
ui->label_primaryOnly->setText(m_ctx->currentWallet->balance(0) == m_ctx->currentWallet->balanceAll() ? "True" : "False");
|
||||
|
||||
QString os = QSysInfo::prettyProductName();
|
||||
if (m_ctx->isTails) {
|
||||
|
||||
@@ -258,14 +258,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="19" column="0">
|
||||
<item row="20" column="0">
|
||||
<widget class="QLabel" name="label_24">
|
||||
<property name="text">
|
||||
<string>Timestamp:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="19" column="1">
|
||||
<item row="20" column="1">
|
||||
<widget class="QLabel" name="label_timestamp">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
@@ -275,14 +275,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="0">
|
||||
<item row="19" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Operating system:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="1">
|
||||
<item row="19" column="1">
|
||||
<widget class="QLabel" name="label_OS">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
@@ -292,7 +292,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="1">
|
||||
<item row="18" column="1">
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
@@ -316,6 +316,20 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="1">
|
||||
<widget class="QLabel" name="label_primaryOnly">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Primary only:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -24,7 +24,7 @@ RestoreDialog::RestoreDialog(AppContext *ctx, QWidget *parent)
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int RestoreDialog::getHeight() {
|
||||
int RestoreDialog::getHeight() {
|
||||
return ui->restoreHeightWidget->getHeight();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ Q_OBJECT
|
||||
public:
|
||||
explicit RestoreDialog(AppContext *ctx, QWidget *parent = nullptr);
|
||||
void initRestoreHeights(RestoreHeightLookup *lookup);
|
||||
unsigned int getHeight();
|
||||
int getHeight();
|
||||
~RestoreDialog() override;
|
||||
|
||||
signals:
|
||||
|
||||
@@ -49,9 +49,7 @@ TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *tx
|
||||
|
||||
QString destinations = txInfo->destinations_formatted();
|
||||
if (destinations.isEmpty()) {
|
||||
ui->destinations->setHidden(true);
|
||||
ui->label_destinations->setHidden(true);
|
||||
ui->line_2->setHidden(true);
|
||||
ui->frameDestinations->hide();
|
||||
} else {
|
||||
ui->destinations->setText(destinations);
|
||||
}
|
||||
|
||||
@@ -132,30 +132,54 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<widget class="QFrame" name="frameDestinations">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_destinations">
|
||||
<property name="text">
|
||||
<string>Destinations:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="destinations">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_destinations">
|
||||
<property name="text">
|
||||
<string>Destinations:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="destinations">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -32,16 +32,22 @@ TxConfDialog::TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QStrin
|
||||
};
|
||||
|
||||
QString amount = WalletManager::displayAmount(tx->amount());
|
||||
QString amount_fiat = convert(tx->amount() / globals::cdiv);
|
||||
ui->label_amount->setText(QString("%1 (%2 %3)").arg(amount, amount_fiat, preferredCur));
|
||||
|
||||
QString fee = WalletManager::displayAmount(tx->fee());
|
||||
QString fee_fiat = convert(tx->fee() / globals::cdiv);
|
||||
ui->label_fee->setText(QString("%1 (%2 %3)").arg(fee, fee_fiat, preferredCur));
|
||||
|
||||
QString total = WalletManager::displayAmount(tx->amount() + tx->fee());
|
||||
QVector<QString> amounts = {amount, fee, total};
|
||||
int maxLength = Utils::maxLength(amounts);
|
||||
std::for_each(amounts.begin(), amounts.end(), [maxLength](QString& amount){amount = amount.rightJustified(maxLength, ' ');});
|
||||
|
||||
QString amount_fiat = convert(tx->amount() / globals::cdiv);
|
||||
QString fee_fiat = convert(tx->fee() / globals::cdiv);
|
||||
QString total_fiat = convert((tx->amount() + tx->fee()) / globals::cdiv);
|
||||
ui->label_total->setText(QString("%1 (%2 %3)").arg(total, total_fiat, preferredCur));
|
||||
QVector<QString> amounts_fiat = {amount_fiat, fee_fiat, total_fiat};
|
||||
int maxLengthFiat = Utils::maxLength(amounts_fiat);
|
||||
std::for_each(amounts_fiat.begin(), amounts_fiat.end(), [maxLengthFiat](QString& amount){amount = amount.rightJustified(maxLengthFiat, ' ');});
|
||||
|
||||
ui->label_amount->setText(QString("%1 (%2 %3)").arg(amounts[0], amounts_fiat[0], preferredCur));
|
||||
ui->label_fee->setText(QString("%1 (%2 %3)").arg(amounts[1], amounts_fiat[1], preferredCur));
|
||||
ui->label_total->setText(QString("%1 (%2 %3)").arg(amounts[2], amounts_fiat[2], preferredCur));
|
||||
|
||||
ui->label_address->setText(ModelUtils::displayAddress(address, 2));
|
||||
ui->label_address->setFont(ModelUtils::getMonospaceFont());
|
||||
|
||||
@@ -55,7 +55,7 @@ void HistoryWidget::showContextMenu(const QPoint &point) {
|
||||
unconfirmed = tInfo.isFailed() || tInfo.isPending();
|
||||
});
|
||||
|
||||
if (AppContext::txCache.contains(txid) && unconfirmed) {
|
||||
if (AppContext::txCache.contains(txid) && unconfirmed && direction != TransactionInfo::Direction_In) {
|
||||
menu.addAction(QIcon(":/assets/images/info.png"), "Resend transaction", this, &HistoryWidget::onResendTransaction);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace {
|
||||
static const int DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS = 30;
|
||||
static const int WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS = 5;
|
||||
|
||||
static constexpr char ATTRIBUTE_SUBADDRESS_ACCOUNT[] ="gui.subaddress_account";
|
||||
static constexpr char ATTRIBUTE_SUBADDRESS_ACCOUNT[] = "feather.subaddress_account";
|
||||
}
|
||||
|
||||
Wallet::Wallet(QObject * parent)
|
||||
|
||||
+65
-38
@@ -21,12 +21,14 @@
|
||||
#include "dialog/broadcasttxdialog.h"
|
||||
#include "dialog/tximportdialog.h"
|
||||
#include "dialog/passworddialog.h"
|
||||
#include "dialog/balancedialog.h"
|
||||
#include "utils/utils.h"
|
||||
#include "utils/config.h"
|
||||
#include "utils/daemonrpc.h"
|
||||
#include "components.h"
|
||||
#include "calcwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "globals.h"
|
||||
|
||||
// libwalletqt
|
||||
#include "libwalletqt/WalletManager.h"
|
||||
@@ -79,7 +81,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
||||
connected ? m_statusBtnTor->setIcon(QIcon(":/assets/images/tor_logo.png"))
|
||||
: m_statusBtnTor->setIcon(QIcon(":/assets/images/tor_logo_disabled.png"));});
|
||||
connect(m_ctx->nodes, &Nodes::updateStatus, [=](const QString &msg){
|
||||
m_statusLabelStatus->setText(msg);
|
||||
this->setStatusText(msg);
|
||||
});
|
||||
|
||||
// menu connects
|
||||
@@ -121,10 +123,6 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
||||
ui->tabExchanges->setTabVisible(0, false);
|
||||
#endif
|
||||
|
||||
#ifndef HAS_MORPHTOKEN
|
||||
ui->tabExchanges->setTabVisible(1, false);
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
// system tray
|
||||
m_trayIcon = new QSystemTrayIcon(QIcon(":/assets/images/appicons/64x64.png"));
|
||||
@@ -216,7 +214,6 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
||||
connect(this, &MainWindow::walletClosed, ui->xmrToWidget, &XMRToWidget::onWalletClosed);
|
||||
connect(this, &MainWindow::walletClosed, ui->sendWidget, &SendWidget::onWalletClosed);
|
||||
connect(m_ctx, &AppContext::balanceUpdated, this, &MainWindow::onBalanceUpdated);
|
||||
connect(m_ctx, &AppContext::balanceUpdated, ui->xmrToWidget, &XMRToWidget::onBalanceUpdated);
|
||||
connect(m_ctx, &AppContext::walletOpened, this, &MainWindow::onWalletOpened);
|
||||
connect(m_ctx, &AppContext::walletClosed, this, QOverload<>::of(&MainWindow::onWalletClosed));
|
||||
connect(m_ctx, &AppContext::walletOpenedError, this, &MainWindow::onWalletOpenedError);
|
||||
@@ -238,6 +235,22 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
||||
connect(m_ctx, &AppContext::initiateTransaction, ui->xmrToWidget, &XMRToWidget::onInitiateTransaction);
|
||||
connect(m_ctx, &AppContext::endTransaction, ui->xmrToWidget, &XMRToWidget::onEndTransaction);
|
||||
|
||||
connect(m_ctx, &AppContext::initiateTransaction, [this]{
|
||||
m_statusDots = 0;
|
||||
m_constructingTransaction = true;
|
||||
m_txTimer.start(1000);
|
||||
});
|
||||
connect(m_ctx, &AppContext::endTransaction, [this]{
|
||||
// Todo: endTransaction can fail to fire when the node is switched during tx creation
|
||||
m_constructingTransaction = false;
|
||||
m_txTimer.stop();
|
||||
this->setStatusText(m_statusText);
|
||||
});
|
||||
connect(&m_txTimer, &QTimer::timeout, [this]{
|
||||
m_statusLabelStatus->setText("Constructing transaction" + this->statusDots());
|
||||
});
|
||||
|
||||
|
||||
// testnet/stagenet warning
|
||||
auto worthlessWarning = QString("Feather wallet is currently running in %1 mode. This is meant "
|
||||
"for developers only. Your coins are WORTHLESS.");
|
||||
@@ -293,7 +306,6 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
||||
connect(ui->historyWidget, &HistoryWidget::resendTransaction, this, &MainWindow::onResendTransaction);
|
||||
|
||||
// Contacts
|
||||
connect(ui->contactWidget, &ContactsWidget::addContact, this, &MainWindow::onAddContact);
|
||||
connect(ui->contactWidget, &ContactsWidget::fillAddress, ui->sendWidget, &SendWidget::fillAddress);
|
||||
|
||||
// Open alias
|
||||
@@ -400,7 +412,7 @@ void MainWindow::initMenu() {
|
||||
m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc);
|
||||
m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc");
|
||||
|
||||
#if defined(HAS_XMRTO) || defined(HAS_MORPHTOKEN)
|
||||
#if defined(HAS_XMRTO)
|
||||
connect(ui->actionShow_Exchange, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
m_tabShowHideMapper["Exchange"] = new ToggleTab(ui->tabExchange, "Exchange", "Exchange", ui->actionShow_Exchange, Config::showTabExchange);
|
||||
m_tabShowHideSignalMapper->setMapping(ui->actionShow_Exchange, "Exchange");
|
||||
@@ -447,7 +459,7 @@ void MainWindow::initMenu() {
|
||||
|
||||
// set restore height
|
||||
connect(ui->actionChange_restore_height, &QAction::triggered, this, &MainWindow::showRestoreHeightDialog);
|
||||
connect(m_ctx, &AppContext::customRestoreHeightSet, [=](unsigned int height){
|
||||
connect(m_ctx, &AppContext::customRestoreHeightSet, [=](int height){
|
||||
auto msg = QString("The restore height for this wallet has been set to %1. "
|
||||
"Please re-open the wallet. Feather will now quit.").arg(height);
|
||||
QMessageBox::information(this, "Cannot set custom restore height", msg);
|
||||
@@ -518,6 +530,7 @@ void MainWindow::initWidgets() {
|
||||
WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){
|
||||
auto *wizard = new WalletWizard(m_ctx, startPage, this);
|
||||
connect(wizard, &WalletWizard::openWallet, m_ctx, &AppContext::onOpenWallet);
|
||||
connect(wizard, &WalletWizard::defaultWalletDirChanged, m_windowSettings, &Settings::updatePaths);
|
||||
return wizard;
|
||||
}
|
||||
|
||||
@@ -605,9 +618,9 @@ void MainWindow::onWalletOpened() {
|
||||
this->activateWindow();
|
||||
this->setEnabled(true);
|
||||
if(!m_ctx->tor->torConnected)
|
||||
m_statusLabelStatus->setText("Wallet opened - Starting Tor (may take a while)");
|
||||
this->setStatusText("Wallet opened - Starting Tor (may take a while)");
|
||||
else
|
||||
m_statusLabelStatus->setText("Wallet opened - Searching for node");
|
||||
this->setStatusText("Wallet opened - Searching for node");
|
||||
|
||||
connect(m_ctx->currentWallet, &Wallet::connectionStatusChanged, this, &MainWindow::onConnectionStatusChanged);
|
||||
|
||||
@@ -649,40 +662,47 @@ void MainWindow::onWalletOpened() {
|
||||
this->touchbarShowWallet();
|
||||
this->updatePasswordIcon();
|
||||
|
||||
m_updateBytes.start(1000);
|
||||
m_updateBytes.start(100);
|
||||
}
|
||||
|
||||
void MainWindow::onBalanceUpdated(double balance, double unlocked, const QString &balance_str, const QString &unlocked_str) {
|
||||
void MainWindow::onBalanceUpdated(quint64 balance, quint64 spendable) {
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
bool hide = config()->get(Config::hideBalance).toBool();
|
||||
|
||||
auto label_str = QString("Balance: %1 XMR").arg(unlocked_str);
|
||||
if(balance > unlocked)
|
||||
label_str += QString(" (+%1 XMR unconfirmed)").arg(QString::number(balance - unlocked, 'f'));
|
||||
QString label_str = QString("Balance: %1 XMR").arg(QString::number(spendable / globals::cdiv, 'f'));
|
||||
if (balance > spendable)
|
||||
label_str += QString(" (+%1 XMR unconfirmed)").arg(QString::number((balance - spendable) / globals::cdiv, 'f'));
|
||||
|
||||
if (hide) {
|
||||
if (hide)
|
||||
label_str = "Balance: HIDDEN";
|
||||
}
|
||||
|
||||
m_statusLabelBalance->setToolTip("Click for details");
|
||||
m_statusLabelBalance->setText(label_str);
|
||||
m_balanceWidget->setHidden(hide);
|
||||
}
|
||||
|
||||
void MainWindow::setStatusText(const QString &text) {
|
||||
m_statusText = text;
|
||||
if (!m_constructingTransaction)
|
||||
m_statusLabelStatus->setText(text);
|
||||
}
|
||||
|
||||
void MainWindow::onSynchronized() {
|
||||
this->updateNetStats();
|
||||
m_statusLabelStatus->setText("Synchronized");
|
||||
this->setStatusText("Synchronized");
|
||||
this->onConnectionStatusChanged(Wallet::ConnectionStatus_Connected);
|
||||
}
|
||||
|
||||
void MainWindow::onBlockchainSync(int height, int target) {
|
||||
QString heightText = QString("Blockchain sync: %1/%2").arg(height).arg(target);
|
||||
m_statusLabelStatus->setText(heightText);
|
||||
QString blocks = (target >= height) ? QString::number(target - height) : "?";
|
||||
QString heightText = QString("Blockchain sync: %1 blocks remaining").arg(blocks);
|
||||
this->setStatusText(heightText);
|
||||
}
|
||||
|
||||
void MainWindow::onRefreshSync(int height, int target) {
|
||||
QString heightText = QString("Wallet refresh: %1/%2").arg(height).arg(target);
|
||||
m_statusLabelStatus->setText(heightText);
|
||||
this->updateNetStats();
|
||||
QString blocks = (target >= height) ? QString::number(target - height) : "?";
|
||||
QString heightText = QString("Wallet refresh: %1 blocks remaining").arg(blocks);
|
||||
this->setStatusText(heightText);
|
||||
}
|
||||
|
||||
void MainWindow::onConnectionStatusChanged(int status)
|
||||
@@ -696,7 +716,7 @@ void MainWindow::onConnectionStatusChanged(int status)
|
||||
switch(status){
|
||||
case Wallet::ConnectionStatus_Disconnected:
|
||||
statusIcon = ":/assets/images/status_disconnected.svg";
|
||||
m_statusLabelStatus->setText("Disconnected");
|
||||
this->setStatusText("Disconnected");
|
||||
break;
|
||||
case Wallet::ConnectionStatus_Connected:
|
||||
if (m_ctx->currentWallet->synchronized()) {
|
||||
@@ -707,11 +727,11 @@ void MainWindow::onConnectionStatusChanged(int status)
|
||||
break;
|
||||
case Wallet::ConnectionStatus_Connecting:
|
||||
statusIcon = ":/assets/images/status_lagging.svg";
|
||||
m_statusLabelStatus->setText("Connecting to daemon");
|
||||
this->setStatusText("Connecting to daemon");
|
||||
break;
|
||||
case Wallet::ConnectionStatus_WrongVersion:
|
||||
statusIcon = ":/assets/images/status_disconnected.svg";
|
||||
m_statusLabelStatus->setText("Incompatible daemon");
|
||||
this->setStatusText("Incompatible daemon");
|
||||
break;
|
||||
default:
|
||||
statusIcon = ":/assets/images/status_disconnected.svg";
|
||||
@@ -813,9 +833,11 @@ void MainWindow::create_status_bar() {
|
||||
m_statusLabelNetStats->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
this->statusBar()->addWidget(m_statusLabelNetStats);
|
||||
|
||||
m_statusLabelBalance = new QLabel("Balance: 0.00 XMR", this);
|
||||
m_statusLabelBalance = new ClickableLabel(this);
|
||||
m_statusLabelBalance->setText("Balance: 0.00 XMR");
|
||||
m_statusLabelBalance->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
this->statusBar()->addPermanentWidget(m_statusLabelBalance);
|
||||
connect(m_statusLabelBalance, &ClickableLabel::clicked, this, &MainWindow::showBalanceDialog);
|
||||
|
||||
m_statusBtnConnectionStatusIndicator = new StatusBarButton(QIcon(":/assets/images/status_disconnected.svg"), "Connection status");
|
||||
connect(m_statusBtnConnectionStatusIndicator, &StatusBarButton::clicked, this, &MainWindow::showConnectionStatusDialog);
|
||||
@@ -1066,16 +1088,6 @@ void MainWindow::onResendTransaction(const QString &txid) {
|
||||
dialog->deleteLater();
|
||||
}
|
||||
|
||||
void MainWindow::onAddContact(const QString &address, const QString &name) {
|
||||
bool addressValid = WalletManager::addressValid(address, m_ctx->currentWallet->nettype());
|
||||
if (!addressValid)
|
||||
QMessageBox::warning(this, "Invalid address", "Invalid address");
|
||||
else {
|
||||
m_ctx->currentWallet->addressBook()->addRow(address, "", name);
|
||||
m_ctx->storeWallet();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::importContacts() {
|
||||
const QString targetFile = QFileDialog::getOpenFileName(this, "Import CSV file", QDir::homePath(), "CSV Files (*.csv)");
|
||||
if(targetFile.isEmpty()) return;
|
||||
@@ -1322,6 +1334,21 @@ void MainWindow::rescanSpent() {
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::showBalanceDialog() {
|
||||
if (!m_ctx->currentWallet) {
|
||||
return;
|
||||
}
|
||||
auto *dialog = new BalanceDialog(this, m_ctx->currentWallet);
|
||||
dialog->exec();
|
||||
dialog->deleteLater();
|
||||
}
|
||||
|
||||
QString MainWindow::statusDots() {
|
||||
m_statusDots++;
|
||||
m_statusDots = m_statusDots % 4;
|
||||
return QString(".").repeated(m_statusDots);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
+10
-6
@@ -118,7 +118,6 @@ public slots:
|
||||
void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
|
||||
void onViewOnBlockExplorer(const QString &txid);
|
||||
void onResendTransaction(const QString &txid);
|
||||
void onAddContact(const QString &address, const QString &name);
|
||||
void importContacts();
|
||||
void showRestoreHeightDialog();
|
||||
void importTransaction();
|
||||
@@ -134,7 +133,7 @@ public slots:
|
||||
void loadSignedTxFromText();
|
||||
|
||||
// libwalletqt
|
||||
void onBalanceUpdated(double balance, double unlocked, const QString &balance_str, const QString &unlocked_str);
|
||||
void onBalanceUpdated(quint64 balance, quint64 spendable);
|
||||
void onSynchronized();
|
||||
void onWalletOpened();
|
||||
void onWalletClosed();
|
||||
@@ -169,19 +168,19 @@ private:
|
||||
void updatePasswordIcon();
|
||||
void updateNetStats();
|
||||
void rescanSpent();
|
||||
void setStatusText(const QString &text);
|
||||
void showBalanceDialog();
|
||||
QString statusDots();
|
||||
|
||||
WalletWizard *createWizard(WalletWizard::Page startPage);
|
||||
|
||||
Ui::MainWindow *ui;
|
||||
Settings *m_windowSettings = nullptr;
|
||||
CalcWindow *m_windowCalc = nullptr;
|
||||
SignVerifyDialog *m_windowSignVerify = nullptr;
|
||||
RestoreDialog *m_restoreDialog = nullptr;
|
||||
AboutDialog *m_aboutDialog = nullptr;
|
||||
XMRigWidget *m_xmrig = nullptr;
|
||||
|
||||
bool m_windowSpawned = false;
|
||||
|
||||
QSystemTrayIcon *m_trayIcon;
|
||||
QMenu m_trayMenu;
|
||||
QAction *m_trayActionCalc;
|
||||
@@ -193,7 +192,7 @@ private:
|
||||
TickerWidget *m_balanceWidget;
|
||||
|
||||
// lower status bar
|
||||
QLabel *m_statusLabelBalance;
|
||||
ClickableLabel *m_statusLabelBalance;
|
||||
QLabel *m_statusLabelStatus;
|
||||
QLabel *m_statusLabelNetStats;
|
||||
StatusBarButton *m_statusBtnConnectionStatusIndicator;
|
||||
@@ -219,6 +218,11 @@ private:
|
||||
|
||||
QTimer m_updateBytes;
|
||||
|
||||
QString m_statusText;
|
||||
int m_statusDots;
|
||||
bool m_constructingTransaction = false;
|
||||
QTimer m_txTimer;
|
||||
|
||||
private slots:
|
||||
void menuToggleTabVisible(const QString &key);
|
||||
};
|
||||
|
||||
@@ -324,32 +324,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabMorphToken">
|
||||
<attribute name="icon">
|
||||
<iconset resource="assets.qrc">
|
||||
<normaloff>:/assets/images/morphtoken.png</normaloff>:/assets/images/morphtoken.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>MorphToken</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="MorphTokenWidget" name="morphtokenWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -784,12 +758,6 @@
|
||||
<header>calcwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>MorphTokenWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>MorphTokenWidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>CCSWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
|
||||
@@ -22,8 +22,6 @@ void CCSModel::updateEntries(const QList<QSharedPointer<CCSEntry>>& entries) {
|
||||
|
||||
m_entries.clear();
|
||||
for (const auto& entry : entries) {
|
||||
if (entry->raised_amount > entry->target_amount)
|
||||
continue;
|
||||
m_entries.push_back(entry);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <utils/nodes.h>
|
||||
#include "appcontext.h"
|
||||
|
||||
NodeModel::NodeModel(unsigned int nodeSource, QObject *parent)
|
||||
NodeModel::NodeModel(int nodeSource, QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
, m_nodeSource(nodeSource)
|
||||
, m_offline(QIcon(":/assets/images/expired_icon.png"))
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
COUNT
|
||||
};
|
||||
|
||||
explicit NodeModel(unsigned int nodeSource, QObject *parent = nullptr);
|
||||
explicit NodeModel(int nodeSource, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
int columnCount(const QModelIndex &parent) const override;
|
||||
@@ -34,7 +34,7 @@ private:
|
||||
QList<FeatherNode> m_nodes;
|
||||
QIcon m_offline;
|
||||
QIcon m_online;
|
||||
unsigned int m_nodeSource;
|
||||
int m_nodeSource;
|
||||
};
|
||||
|
||||
#endif //FEATHER_NODEMODEL_H
|
||||
|
||||
@@ -169,7 +169,7 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tI
|
||||
usd_amount = AppContext::prices->convert("USD", this->preferredFiatSymbol, usd_amount);
|
||||
double fiat_rounded = ceil(Utils::roundSignificant(usd_amount, 3) * 100.0) / 100.0;
|
||||
|
||||
return QString("%1%2").arg(this->preferredFiatSign).arg(QString::number(fiat_rounded));
|
||||
return QString("%1").arg(Utils::amountToCurrencyString(fiat_rounded, this->preferredFiatSymbol));
|
||||
}
|
||||
default:
|
||||
{
|
||||
@@ -239,4 +239,4 @@ Qt::ItemFlags TransactionHistoryModel::flags(const QModelIndex &index) const {
|
||||
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
|
||||
|
||||
return QAbstractTableModel::flags(index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ SendWidget::SendWidget(QWidget *parent) :
|
||||
ui(new Ui::SendWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_ctx = MainWindow::getContext();
|
||||
|
||||
QString amount_rx = R"(^\d{0,8}[\.,]\d{0,12}|(all)$)";
|
||||
QRegExp rx;
|
||||
@@ -30,6 +31,16 @@ SendWidget::SendWidget(QWidget *parent) :
|
||||
ui->label_conversionAmount->hide();
|
||||
ui->btn_openAlias->hide();
|
||||
|
||||
ui->label_PayTo->setHelpText("Recipient of the funds.\n\n"
|
||||
"You may enter a Monero address, or an alias (email-like address that forwards to a Monero address)");
|
||||
ui->label_Description->setHelpText("Description of the transaction (optional).\n\n"
|
||||
"The description is not sent to the recipient of the funds. It is stored in your wallet cache, "
|
||||
"and displayed in the 'History' tab.");
|
||||
ui->label_Amount->setHelpText("Amount to be sent.\n\nThis is the exact amount the recipient will receive. "
|
||||
"In addition to this amount a transaction fee will be subtracted from your balance. "
|
||||
"You will be able to review the transaction fee before the transaction is broadcast.\n\n"
|
||||
"To send all your balance, click the Max button to the right.");
|
||||
|
||||
this->setupComboBox();
|
||||
}
|
||||
|
||||
@@ -70,6 +81,13 @@ void SendWidget::fillAddress(const QString &address) {
|
||||
}
|
||||
|
||||
void SendWidget::sendClicked() {
|
||||
if (m_ctx->currentWallet->connectionStatus() != Wallet::ConnectionStatus_Connected) {
|
||||
QMessageBox::warning(this, "Error", "Unable to create transaction:\n\n"
|
||||
"Wallet is not connected to a node.\n"
|
||||
"Go to File -> Settings -> Node to manually connect to a node.");
|
||||
return;
|
||||
}
|
||||
|
||||
double amount;
|
||||
QString currency = ui->comboCurrencySelection->currentText();
|
||||
QString recipient = ui->lineAddress->text().simplified().remove(' ');
|
||||
@@ -173,6 +191,7 @@ void SendWidget::clearFields() {
|
||||
|
||||
void SendWidget::onWalletClosed() {
|
||||
this->clearFields();
|
||||
ui->btnSend->setEnabled(true);
|
||||
}
|
||||
|
||||
void SendWidget::onInitiateTransaction() {
|
||||
|
||||
+11
-4
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>603</width>
|
||||
<width>647</width>
|
||||
<height>175</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -39,7 +39,7 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<widget class="HelpLabel" name="label_PayTo">
|
||||
<property name="text">
|
||||
<string>Pay to</string>
|
||||
</property>
|
||||
@@ -53,7 +53,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="HelpLabel" name="label_Description">
|
||||
<property name="text">
|
||||
<string>Description</string>
|
||||
</property>
|
||||
@@ -63,7 +63,7 @@
|
||||
<widget class="QLineEdit" name="lineDescription"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<widget class="HelpLabel" name="label_Amount">
|
||||
<property name="text">
|
||||
<string>Amount</string>
|
||||
</property>
|
||||
@@ -184,6 +184,13 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>HelpLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>components.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<slots>
|
||||
|
||||
+18
-6
@@ -7,6 +7,8 @@
|
||||
#include "utils/config.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
Settings::Settings(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::Settings)
|
||||
@@ -58,16 +60,26 @@ Settings::Settings(QWidget *parent) :
|
||||
|
||||
connect(ui->comboBox_fiatCurrency, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::fiatCurrencySelected);
|
||||
|
||||
// setup path tab
|
||||
#if defined(Q_OS_LINUX)
|
||||
ui->textEditPaths->appendPlainText(m_ctx->applicationPath);
|
||||
#endif
|
||||
ui->textEditPaths->appendPlainText(m_ctx->configDirectory);
|
||||
ui->textEditPaths->appendPlainText(m_ctx->defaultWalletDir);
|
||||
// setup paths tab
|
||||
this->updatePaths();
|
||||
connect(ui->btn_browseDefaultWalletDir, &QPushButton::clicked, [this]{
|
||||
QString walletDir = QFileDialog::getExistingDirectory(this, "Select wallet directory ", m_ctx->defaultWalletDir, QFileDialog::ShowDirsOnly);
|
||||
if (walletDir.isEmpty()) return;
|
||||
m_ctx->defaultWalletDir = walletDir;
|
||||
m_ctx->defaultWalletDirRoot = walletDir;
|
||||
config()->set(Config::walletDirectory, walletDir);
|
||||
ui->lineEdit_defaultWalletDir->setText(m_ctx->defaultWalletDir);
|
||||
});
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
void Settings::updatePaths() {
|
||||
ui->lineEdit_defaultWalletDir->setText(m_ctx->defaultWalletDir);
|
||||
ui->lineEdit_configDir->setText(m_ctx->configDirectory);
|
||||
ui->lineEdit_applicationDir->setText(m_ctx->applicationPath);
|
||||
}
|
||||
|
||||
void Settings::fiatCurrencySelected(int index) {
|
||||
QString selection = ui->comboBox_fiatCurrency->itemText(index);
|
||||
config()->set(Config::preferredFiatCurrency, selection);
|
||||
|
||||
@@ -31,6 +31,7 @@ signals:
|
||||
void blockExplorerChanged(QString blockExplorer);
|
||||
|
||||
public slots:
|
||||
void updatePaths();
|
||||
void copyToClipboard();
|
||||
void checkboxExternalLinkWarn();
|
||||
void fiatCurrencySelected(int index);
|
||||
|
||||
+69
-6
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1019</width>
|
||||
<height>336</height>
|
||||
<width>1123</width>
|
||||
<height>555</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -282,11 +282,74 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="textEditPaths">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit_defaultWalletDir">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_browseDefaultWalletDir">
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Wallet directory:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Config directory:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_configDir">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Application directory:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_applicationDir">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#include "MorphTokenApi.h"
|
||||
|
||||
MorphTokenApi::MorphTokenApi(QObject *parent, UtilsNetworking *network, QString baseUrl)
|
||||
: QObject(parent)
|
||||
, m_network(network)
|
||||
, m_baseUrl(std::move(baseUrl))
|
||||
{
|
||||
}
|
||||
|
||||
void MorphTokenApi::createTrade(const QString &inputAsset, const QString &outputAsset, const QString &refundAddress, const QString &outputAddress) {
|
||||
QJsonObject trade;
|
||||
|
||||
QJsonObject input;
|
||||
input["asset"] = inputAsset;
|
||||
input["refund"] = refundAddress;
|
||||
|
||||
QJsonArray output;
|
||||
QJsonObject outputObj;
|
||||
outputObj["asset"] = outputAsset;
|
||||
outputObj["weight"] = 10000;
|
||||
outputObj["address"] = outputAddress;
|
||||
output.append(outputObj);
|
||||
|
||||
trade["input"] = input;
|
||||
trade["output"] = output;
|
||||
|
||||
QString url = QString("%1/morph").arg(m_baseUrl);
|
||||
QNetworkReply *reply = m_network->postJson(url, trade);
|
||||
connect(reply, &QNetworkReply::finished, std::bind(&MorphTokenApi::onResponse, this, reply, Endpoint::CREATE_TRADE));
|
||||
}
|
||||
|
||||
void MorphTokenApi::getTrade(const QString &morphId) {
|
||||
QString url = QString("%1/morph/%2").arg(m_baseUrl, morphId);
|
||||
QNetworkReply *reply = m_network->getJson(url);
|
||||
connect(reply, &QNetworkReply::finished, std::bind(&MorphTokenApi::onResponse, this, reply, Endpoint::GET_TRADE));
|
||||
}
|
||||
|
||||
void MorphTokenApi::getRates() {
|
||||
QString url = QString("%1/rates").arg(m_baseUrl);
|
||||
QNetworkReply *reply = m_network->getJson(url);
|
||||
connect(reply, &QNetworkReply::finished, std::bind(&MorphTokenApi::onResponse, this, reply, Endpoint::GET_RATES));
|
||||
}
|
||||
|
||||
void MorphTokenApi::getLimits(const QString &inputAsset, const QString &outputAsset) {
|
||||
QJsonObject limits;
|
||||
|
||||
QJsonObject input;
|
||||
input["asset"] = inputAsset;
|
||||
|
||||
QJsonArray output;
|
||||
QJsonObject outputObj;
|
||||
outputObj["asset"] = outputAsset;
|
||||
outputObj["weight"] = 10000;
|
||||
output.append(outputObj);
|
||||
|
||||
limits["input"] = input;
|
||||
limits["output"] = output;
|
||||
|
||||
QString url = QString("%1/limits").arg(m_baseUrl);
|
||||
QNetworkReply *reply = m_network->postJson(url, limits);
|
||||
connect(reply, &QNetworkReply::finished, std::bind(&MorphTokenApi::onResponse, this, reply, Endpoint::GET_LIMITS));
|
||||
}
|
||||
|
||||
void MorphTokenApi::onResponse(QNetworkReply *reply, Endpoint endpoint) {
|
||||
const auto ok = reply->error() == QNetworkReply::NoError;
|
||||
const auto err = reply->errorString();
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
QJsonObject obj;
|
||||
if (!data.isEmpty() && Utils::validateJSON(data)) {
|
||||
auto doc = QJsonDocument::fromJson(data);
|
||||
obj = doc.object();
|
||||
}
|
||||
else if (!ok) {
|
||||
emit ApiResponse(MorphTokenResponse(false, endpoint, err, {}));
|
||||
return;
|
||||
}
|
||||
else {
|
||||
emit ApiResponse(MorphTokenResponse(false, endpoint, "Invalid response from MorphToken", {}));
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj.contains("success")) {
|
||||
emit ApiResponse(MorphTokenResponse(false, endpoint, obj.value("description").toString(), obj));
|
||||
return;
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
emit ApiResponse(MorphTokenResponse(true, endpoint, "", obj));
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_MORPHTOKENAPI_H
|
||||
#define FEATHER_MORPHTOKENAPI_H
|
||||
|
||||
#include <QObject>
|
||||
#include <utility>
|
||||
#include "utils/networking.h"
|
||||
|
||||
class MorphTokenApi : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Endpoint {
|
||||
CREATE_TRADE = 0,
|
||||
GET_TRADE,
|
||||
GET_RATES,
|
||||
GET_LIMITS
|
||||
};
|
||||
|
||||
struct MorphTokenResponse {
|
||||
explicit MorphTokenResponse(bool ok, Endpoint endpoint, QString message, QJsonObject obj)
|
||||
: ok(ok), endpoint(endpoint), message(std::move(message)), obj(std::move(obj)) {};
|
||||
|
||||
bool ok;
|
||||
Endpoint endpoint;
|
||||
QString message;
|
||||
QJsonObject obj;
|
||||
};
|
||||
|
||||
explicit MorphTokenApi(QObject *parent, UtilsNetworking *network, QString baseUrl = "https://api.morphtoken.com");
|
||||
|
||||
void createTrade(const QString &inputAsset, const QString &outputAsset, const QString &refundAddress, const QString &outputAddress);
|
||||
void getTrade(const QString &morphId);
|
||||
void getRates();
|
||||
void getLimits(const QString &inputAsset, const QString &outputAsset);
|
||||
|
||||
signals:
|
||||
void ApiResponse(MorphTokenResponse resp);
|
||||
|
||||
private slots:
|
||||
void onResponse(QNetworkReply *reply, Endpoint endpoint);
|
||||
|
||||
private:
|
||||
QString m_baseUrl;
|
||||
UtilsNetworking *m_network;
|
||||
};
|
||||
|
||||
|
||||
#endif //FEATHER_MORPHTOKENAPI_H
|
||||
+34
-20
@@ -19,7 +19,7 @@
|
||||
using namespace std::chrono;
|
||||
|
||||
|
||||
WalletKeysFiles::WalletKeysFiles(const QFileInfo &info, quint8 networkType, QString address) :
|
||||
WalletKeysFiles::WalletKeysFiles(const QFileInfo &info, int networkType, QString address) :
|
||||
m_fileName(info.fileName()),
|
||||
m_modified(info.lastModified().toSecsSinceEpoch()),
|
||||
m_path(QDir::toNativeSeparators(info.absoluteFilePath())),
|
||||
@@ -42,23 +42,15 @@ QString WalletKeysFiles::path() const {
|
||||
return m_path;
|
||||
}
|
||||
|
||||
quint8 WalletKeysFiles::networkType() const {
|
||||
int WalletKeysFiles::networkType() const {
|
||||
return m_networkType;
|
||||
}
|
||||
|
||||
WalletKeysFilesModel::WalletKeysFilesModel(AppContext *ctx, QObject *parent) :
|
||||
QAbstractTableModel(parent),
|
||||
m_ctx(ctx) {
|
||||
this->walletDirectories << m_ctx->defaultWalletDirRoot;
|
||||
auto walletPath = config()->get(Config::walletPath).toString();
|
||||
if(!walletPath.isEmpty() && Utils::fileExists(walletPath)) {
|
||||
QDir d = QFileInfo(walletPath).absoluteDir();
|
||||
this->walletDirectories << d.absolutePath();
|
||||
}
|
||||
|
||||
this->walletDirectories << m_ctx->homeDir;
|
||||
this->walletDirectories.removeDuplicates();
|
||||
|
||||
WalletKeysFilesModel::WalletKeysFilesModel(AppContext *ctx, QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
, m_ctx(ctx)
|
||||
{
|
||||
this->updateDirectories();
|
||||
this->m_walletKeysFilesItemModel = qobject_cast<QAbstractItemModel *>(this);
|
||||
}
|
||||
|
||||
@@ -71,6 +63,20 @@ void WalletKeysFilesModel::clear() {
|
||||
void WalletKeysFilesModel::refresh() {
|
||||
this->clear();
|
||||
this->findWallets();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void WalletKeysFilesModel::updateDirectories() {
|
||||
this->walletDirectories.clear();
|
||||
this->walletDirectories << m_ctx->defaultWalletDirRoot;
|
||||
auto walletPath = config()->get(Config::walletPath).toString();
|
||||
if(!walletPath.isEmpty() && Utils::fileExists(walletPath)) {
|
||||
QDir d = QFileInfo(walletPath).absoluteDir();
|
||||
this->walletDirectories << d.absolutePath();
|
||||
}
|
||||
|
||||
this->walletDirectories << m_ctx->homeDir;
|
||||
this->walletDirectories.removeDuplicates();
|
||||
}
|
||||
|
||||
void WalletKeysFilesModel::findWallets() {
|
||||
@@ -133,7 +139,7 @@ int WalletKeysFilesModel::rowCount(const QModelIndex & parent) const {
|
||||
}
|
||||
|
||||
int WalletKeysFilesModel::columnCount(const QModelIndex &) const {
|
||||
return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
QVariant WalletKeysFilesModel::data(const QModelIndex &index, int role) const {
|
||||
@@ -162,13 +168,15 @@ QVariant WalletKeysFilesModel::data(const QModelIndex &index, int role) const {
|
||||
#endif
|
||||
return fp;
|
||||
}
|
||||
case ModelColumns::Modified:
|
||||
return walletKeyFile.modified();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if(role == Qt::UserRole) {
|
||||
switch(index.column()) {
|
||||
case ModelColumns::NetworkType:
|
||||
return static_cast<uint>(walletKeyFile.networkType());
|
||||
return static_cast<int>(walletKeyFile.networkType());
|
||||
case ModelColumns::FileName:
|
||||
return walletKeyFile.fileName();
|
||||
case ModelColumns::Modified:
|
||||
@@ -201,9 +209,15 @@ QVariant WalletKeysFilesModel::headerData(int section, Qt::Orientation orientati
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
WalletKeysFilesProxyModel::WalletKeysFilesProxyModel(QObject *parent) :
|
||||
QSortFilterProxyModel(parent) {}
|
||||
WalletKeysFilesProxyModel::WalletKeysFilesProxyModel(QObject *parent, NetworkType::Type nettype)
|
||||
: QSortFilterProxyModel(parent)
|
||||
, m_nettype(nettype)
|
||||
{
|
||||
}
|
||||
|
||||
bool WalletKeysFilesProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||
return true;
|
||||
QModelIndex nettypeIndex = sourceModel()->index(sourceRow, WalletKeysFilesModel::NetworkType, sourceParent);
|
||||
NetworkType::Type nettype = static_cast<NetworkType::Type>(sourceModel()->data(nettypeIndex, Qt::UserRole).toInt());
|
||||
|
||||
return (m_nettype == nettype);
|
||||
}
|
||||
@@ -12,19 +12,19 @@
|
||||
class WalletKeysFiles
|
||||
{
|
||||
public:
|
||||
WalletKeysFiles(const QFileInfo &info, quint8 networkType, QString address);
|
||||
WalletKeysFiles(const QFileInfo &info, int networkType, QString address);
|
||||
|
||||
QString fileName() const;
|
||||
qint64 modified() const;
|
||||
QString path() const;
|
||||
quint8 networkType() const;
|
||||
int networkType() const;
|
||||
QString address() const;
|
||||
|
||||
private:
|
||||
QString m_fileName;
|
||||
qint64 m_modified;
|
||||
QString m_path;
|
||||
quint8 m_networkType;
|
||||
int m_networkType;
|
||||
QString m_address;
|
||||
};
|
||||
|
||||
@@ -54,6 +54,8 @@ public:
|
||||
QStringList walletDirectories;
|
||||
|
||||
private:
|
||||
void updateDirectories();
|
||||
|
||||
AppContext *m_ctx;
|
||||
QList<WalletKeysFiles> m_walletKeyFiles;
|
||||
QAbstractItemModel *m_walletKeysFilesItemModel;
|
||||
@@ -64,8 +66,11 @@ class WalletKeysFilesProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WalletKeysFilesProxyModel(QObject *parent = nullptr);
|
||||
explicit WalletKeysFilesProxyModel(QObject *parent, NetworkType::Type nettype);
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
|
||||
private:
|
||||
NetworkType::Type m_nettype;
|
||||
};
|
||||
|
||||
#endif // KEYSFILES_H
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ void Nodes::connectToNode() {
|
||||
// auto connect
|
||||
m_wsExhaustedWarningEmitted = false;
|
||||
m_customExhaustedWarningEmitted = false;
|
||||
this->autoConnect();
|
||||
this->autoConnect(true);
|
||||
}
|
||||
|
||||
void Nodes::connectToNode(const FeatherNode &node) {
|
||||
|
||||
+25
-25
@@ -19,44 +19,44 @@
|
||||
|
||||
struct RestoreHeightLookup {
|
||||
NetworkType::Type type;
|
||||
QMap<unsigned int, unsigned int> data;
|
||||
QMap<int, int> data;
|
||||
explicit RestoreHeightLookup(NetworkType::Type type) : type(type) {}
|
||||
|
||||
unsigned int dateToRestoreHeight(unsigned int date) {
|
||||
int dateToRestoreHeight(int date) {
|
||||
// restore height based on a given timestamp using a lookup
|
||||
// table. If it cannot find the date in the lookup table, it
|
||||
// will calculate the blockheight based off the last known
|
||||
// date: ((now - lastKnownDate) / blockTime) - clearance
|
||||
|
||||
if(this->type == NetworkType::TESTNET) return 1;
|
||||
unsigned int blockTime = 120;
|
||||
unsigned int blocksPerDay = 86400 / blockTime;
|
||||
unsigned int blockCalcClearance = blocksPerDay * 5;
|
||||
QList<unsigned int> values = this->data.keys();
|
||||
int blockTime = 120;
|
||||
int blocksPerDay = 86400 / blockTime;
|
||||
int blockCalcClearance = blocksPerDay * 5;
|
||||
QList<int> values = this->data.keys();
|
||||
if(date <= values.at(0))
|
||||
return this->data[values.at(0)];
|
||||
for(unsigned int i = 0; i != values.count(); i++) {
|
||||
for(int i = 0; i != values.count(); i++) {
|
||||
if(values[i] > date) {
|
||||
return i - 1 < 0 ? this->data[values[i]] : this->data[values[i-1]] - blockCalcClearance;
|
||||
}
|
||||
}
|
||||
|
||||
// lookup failed, calculate blockheight from last known checkpoint
|
||||
unsigned int lastBlockHeightTime = values.at(values.count() - 1);
|
||||
unsigned int lastBlockHeight = this->data[lastBlockHeightTime];
|
||||
unsigned int deltaTime = date - lastBlockHeightTime;
|
||||
unsigned int deltaBlocks = deltaTime / blockTime;
|
||||
unsigned int blockHeight = (lastBlockHeight + deltaBlocks) - blockCalcClearance;
|
||||
int lastBlockHeightTime = values.at(values.count() - 1);
|
||||
int lastBlockHeight = this->data[lastBlockHeightTime];
|
||||
int deltaTime = date - lastBlockHeightTime;
|
||||
int deltaBlocks = deltaTime / blockTime;
|
||||
int blockHeight = (lastBlockHeight + deltaBlocks) - blockCalcClearance;
|
||||
qDebug() << "Calculated blockheight: " << blockHeight << " from epoch " << date;
|
||||
return blockHeight;
|
||||
}
|
||||
|
||||
unsigned int restoreHeightToDate(unsigned int height) {
|
||||
int restoreHeightToDate(int height) {
|
||||
// @TODO: most likely inefficient, refactor
|
||||
QMap<unsigned int, unsigned int>::iterator i;
|
||||
unsigned int timestamp = 0;
|
||||
QMap<int, int>::iterator i;
|
||||
int timestamp = 0;
|
||||
for (i = this->data.begin(); i != this->data.end(); ++i) {
|
||||
unsigned int ts = i.key();
|
||||
int ts = i.key();
|
||||
if (i.value() > height)
|
||||
return timestamp;
|
||||
timestamp = ts;
|
||||
@@ -68,7 +68,7 @@ struct RestoreHeightLookup {
|
||||
// initialize this class using a lookup table, e.g `:/assets/restore_heights_monero_mainnet.txt`/
|
||||
auto rtn = new RestoreHeightLookup(type);
|
||||
auto data = Utils::barrayToString(Utils::fileOpen(fn));
|
||||
QMap<unsigned int, unsigned int> _data;
|
||||
QMap<int, int> _data;
|
||||
for(const auto &line: data.split('\n')) {
|
||||
if(line.trimmed().isEmpty()) continue;
|
||||
auto spl = line.trimmed().split(':');
|
||||
@@ -82,7 +82,7 @@ struct FeatherSeed {
|
||||
QString mnemonicSeed;
|
||||
QString spendKey;
|
||||
time_t time = 0;
|
||||
unsigned int restoreHeight = 0;
|
||||
int restoreHeight = 0;
|
||||
RestoreHeightLookup *lookup = nullptr;
|
||||
QString language;
|
||||
std::string coinName;
|
||||
@@ -124,7 +124,7 @@ struct FeatherSeed {
|
||||
return rtn;
|
||||
}
|
||||
|
||||
Wallet *writeWallet(WalletManager *manager, NetworkType::Type type, const QString &path, const QString &password, unsigned int kdfRounds) {
|
||||
Wallet *writeWallet(WalletManager *manager, NetworkType::Type type, const QString &path, const QString &password, quint64 kdfRounds) {
|
||||
// writes both 14/25 word mnemonic seeds.
|
||||
Wallet *wallet = nullptr;
|
||||
if(this->lookup == nullptr) return wallet;
|
||||
@@ -136,25 +136,25 @@ struct FeatherSeed {
|
||||
this->restoreHeight = _seed.restoreHeight;
|
||||
this->spendKey = _seed.spendKey;
|
||||
}
|
||||
wallet = manager->createDeterministicWalletFromSpendKey(path, password, this->language, type, this->spendKey, this->restoreHeight, (quint64)kdfRounds);
|
||||
wallet = manager->createDeterministicWalletFromSpendKey(path, password, this->language, type, this->spendKey, this->restoreHeight, kdfRounds);
|
||||
wallet->setCacheAttribute("feather.seed", this->mnemonicSeed);
|
||||
} else {
|
||||
wallet = manager->recoveryWallet(path, password, this->mnemonicSeed, "", type, this->restoreHeight, (quint64) kdfRounds);
|
||||
wallet = manager->recoveryWallet(path, password, this->mnemonicSeed, "", type, this->restoreHeight, kdfRounds);
|
||||
}
|
||||
|
||||
wallet->setPassword(password);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
unsigned int setRestoreHeight() {
|
||||
int setRestoreHeight() {
|
||||
if(this->lookup == nullptr) return 1;
|
||||
if(this->time == 0) return 1;
|
||||
this->restoreHeight = this->lookup->dateToRestoreHeight((unsigned int)this->time);
|
||||
this->restoreHeight = this->lookup->dateToRestoreHeight(this->time);
|
||||
return this->restoreHeight;
|
||||
}
|
||||
|
||||
unsigned int setRestoreHeight(unsigned int height) {
|
||||
auto now = (unsigned int)std::time(nullptr);
|
||||
int setRestoreHeight(int height) {
|
||||
auto now = std::time(nullptr);
|
||||
auto nowClearance = 3600 * 24;
|
||||
auto currentBlockHeight = this->lookup->dateToRestoreHeight(now - nowClearance);
|
||||
if(height >= currentBlockHeight + nowClearance) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "txfiathistory.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
TxFiatHistory::TxFiatHistory(unsigned int genesis_timestamp, const QString &configDirectory, QObject *parent) :
|
||||
TxFiatHistory::TxFiatHistory(int genesis_timestamp, const QString &configDirectory, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_genesis_timestamp(genesis_timestamp),
|
||||
m_configDirectory(configDirectory) {
|
||||
@@ -15,7 +15,7 @@ TxFiatHistory::TxFiatHistory(unsigned int genesis_timestamp, const QString &conf
|
||||
this->loadDatabase();
|
||||
}
|
||||
|
||||
double TxFiatHistory::get(unsigned int timestamp) {
|
||||
double TxFiatHistory::get(int timestamp) {
|
||||
QDateTime ts;
|
||||
ts.setTime_t(timestamp);
|
||||
auto key = ts.toString("yyyyMMdd");
|
||||
@@ -59,7 +59,7 @@ void TxFiatHistory::onUpdateDatabase() {
|
||||
|
||||
auto now = QDate::currentDate();
|
||||
auto nowKey = now.toString("yyyyMMdd");
|
||||
unsigned int year = genesis.toString("yyyy").toUInt();
|
||||
int year = genesis.toString("yyyy").toInt();
|
||||
auto yearCurrent = now.year();
|
||||
|
||||
// if current year is genesis year we'll refresh regardless.
|
||||
@@ -71,7 +71,7 @@ void TxFiatHistory::onUpdateDatabase() {
|
||||
|
||||
// keep local fiatTxHistory database up to date, loop for missing dates
|
||||
for(year; year != yearCurrent + 1; year += 1){
|
||||
for(unsigned int month = 1; month != 13; month++) {
|
||||
for(int month = 1; month != 13; month++) {
|
||||
if(year == yearCurrent && month == now.month() && now.day() == 1) break;
|
||||
QDateTime _now;
|
||||
_now.setDate(QDate(year, month, 1));
|
||||
|
||||
@@ -6,17 +6,17 @@ class TxFiatHistory : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TxFiatHistory(unsigned int genesis_timestamp, const QString &configDirectory, QObject *parent = nullptr);
|
||||
explicit TxFiatHistory(int genesis_timestamp, const QString &configDirectory, QObject *parent = nullptr);
|
||||
double get(const QString &date);
|
||||
double get(unsigned int timestamp);
|
||||
double get(int timestamp);
|
||||
|
||||
public slots:
|
||||
void onUpdateDatabase();
|
||||
void onWSData(const QJsonObject &data);
|
||||
|
||||
signals:
|
||||
void requestYear(unsigned int year);
|
||||
void requestYearMonth(unsigned int year, unsigned int month);
|
||||
void requestYear(int year);
|
||||
void requestYearMonth(int year, int month);
|
||||
|
||||
private:
|
||||
void loadDatabase();
|
||||
@@ -25,7 +25,7 @@ private:
|
||||
QString m_configDirectory;
|
||||
bool m_initialized = false;
|
||||
QMap<QString, double> m_database;
|
||||
unsigned int m_genesis_timestamp;
|
||||
int m_genesis_timestamp;
|
||||
};
|
||||
|
||||
#endif //FEATHER_TXFIATHISTORY_H
|
||||
|
||||
+42
-14
@@ -22,6 +22,7 @@
|
||||
#include "utils.h"
|
||||
#include "utils/config.h"
|
||||
#include "utils/tails.h"
|
||||
#include "utils/whonix.h"
|
||||
|
||||
// Application log for current session
|
||||
QVector<logMessage> applicationLog = QVector<logMessage>(); // todo: replace with ring buffer
|
||||
@@ -284,7 +285,9 @@ void Utils::externalLinkWarning(QWidget *parent, const QString &url){
|
||||
|
||||
QString body = "You are about to open the following link:\n\n";
|
||||
body += QString("%1\n\n").arg(url);
|
||||
body += "You will NOT be using Tor.";
|
||||
|
||||
if (!(TailsOS::detect() || WhonixOS::detect()))
|
||||
body += "You will NOT be using Tor.";
|
||||
|
||||
switch (QMessageBox::warning(parent, "External link warning", body, QMessageBox::Cancel|QMessageBox::Ok)) {
|
||||
case QMessageBox::Cancel:
|
||||
@@ -550,30 +553,55 @@ QString Utils::formatBytes(quint64 bytes)
|
||||
|
||||
int i;
|
||||
double _data;
|
||||
for (i = 0; i < sizes.count() && bytes >= 1000; i++, bytes /= 1000)
|
||||
for (i = 0; i < sizes.count() && bytes >= 10000; i++, bytes /= 1000)
|
||||
_data = bytes / 1000.0;
|
||||
|
||||
if (_data < 0)
|
||||
_data = 0;
|
||||
|
||||
// unrealistic
|
||||
if (_data > 1000)
|
||||
if (_data > 10000)
|
||||
_data = 0;
|
||||
|
||||
return QString("%1 %2").arg(QString::number(_data, 'f', 1), sizes[i]);
|
||||
}
|
||||
|
||||
QString Utils::amountToCurrencyString(double amount, const QString ¤cyCode) {
|
||||
|
||||
QMap<QString, QLocale> Utils::localeCache = {};
|
||||
|
||||
QLocale Utils::getCurrencyLocale(const QString ¤cyCode) {
|
||||
QLocale locale;
|
||||
QList<QLocale> allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
|
||||
for (const auto& locale_: allLocales) {
|
||||
if (locale_.currencySymbol(QLocale::CurrencyIsoCode) == currencyCode) {
|
||||
locale = locale_;
|
||||
if (localeCache.contains(currencyCode)) {
|
||||
locale = localeCache[currencyCode];
|
||||
} else {
|
||||
QList<QLocale> allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
|
||||
for (const auto& locale_: allLocales) {
|
||||
if (locale_.currencySymbol(QLocale::CurrencyIsoCode) == currencyCode) {
|
||||
locale = locale_;
|
||||
}
|
||||
}
|
||||
localeCache[currencyCode] = locale;
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
|
||||
QString Utils::amountToCurrencyString(double amount, const QString ¤cyCode) {
|
||||
QLocale locale = getCurrencyLocale(currencyCode);
|
||||
|
||||
// \xC2\xA0 = UTF-8 non-breaking space, it looks off.
|
||||
if (currencyCode == "USD")
|
||||
return locale.toCurrencyString(amount, "$").remove("\xC2\xA0");
|
||||
|
||||
return locale.toCurrencyString(amount).remove("\xC2\xA0");
|
||||
}
|
||||
|
||||
int Utils::maxLength(const QVector<QString> &array) {
|
||||
int maxLength = 0;
|
||||
for (const auto &str: array) {
|
||||
auto length = str.length();
|
||||
if (length > maxLength) {
|
||||
maxLength = length;
|
||||
}
|
||||
}
|
||||
|
||||
if (currencyCode == "USD")
|
||||
return locale.toCurrencyString(amount, "$");
|
||||
|
||||
return locale.toCurrencyString(amount);
|
||||
}
|
||||
return maxLength;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,10 @@ public:
|
||||
static QFont relativeFont(int delta);
|
||||
static double roundSignificant(double N, double n);
|
||||
static QString formatBytes(quint64 bytes);
|
||||
static QLocale getCurrencyLocale(const QString ¤cyCode);
|
||||
static QString amountToCurrencyString(double amount, const QString ¤cyCode);
|
||||
static int maxLength(const QVector<QString> &array);
|
||||
static QMap<QString, QLocale> localeCache;
|
||||
|
||||
template<typename QEnum>
|
||||
static QString QtEnumToString (const QEnum value)
|
||||
|
||||
@@ -21,6 +21,14 @@ WSClient::WSClient(AppContext *ctx, const QUrl &url, QObject *parent) :
|
||||
connect(&this->webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &WSClient::onError);
|
||||
|
||||
m_tor = url.host().endsWith(".onion");
|
||||
|
||||
// Keep websocket connection alive
|
||||
connect(&m_pingTimer, &QTimer::timeout, [this]{
|
||||
if (this->webSocket.state() == QAbstractSocket::ConnectedState)
|
||||
this->webSocket.ping();
|
||||
});
|
||||
m_pingTimer.setInterval(30 * 1000);
|
||||
m_pingTimer.start();
|
||||
}
|
||||
|
||||
void WSClient::sendMsg(const QByteArray &data) {
|
||||
|
||||
@@ -33,6 +33,7 @@ private slots:
|
||||
|
||||
private:
|
||||
QTimer m_connectionTimer;
|
||||
QTimer m_pingTimer;
|
||||
AppContext *m_ctx;
|
||||
bool m_tor = true;
|
||||
};
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ void XmRig::stop() {
|
||||
}
|
||||
|
||||
void XmRig::start(const QString &path,
|
||||
unsigned int threads,
|
||||
int threads,
|
||||
const QString &address,
|
||||
const QString &username,
|
||||
const QString &password,
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ public:
|
||||
explicit XmRig(const QString &configDir, QObject *parent = nullptr);
|
||||
void prepare();
|
||||
|
||||
void start(const QString &path, unsigned int threads, const QString &address, const QString &username, const QString &password, bool tor = false, bool tls = true);
|
||||
void start(const QString &path, int threads, const QString &address, const QString &username, const QString &password, bool tor = false, bool tls = true);
|
||||
void stop();
|
||||
bool unpackBins();
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ CCSWidget::CCSWidget(QWidget *parent) :
|
||||
connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &CCSWidget::showContextMenu);
|
||||
|
||||
connect(ui->tableView, &QTableView::doubleClicked, this, &CCSWidget::linkClicked);
|
||||
|
||||
ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
}
|
||||
|
||||
CCSModel* CCSWidget::model() {
|
||||
|
||||
+60
-81
@@ -18,10 +18,10 @@
|
||||
#include "utils/nodes.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
NodeWidget::NodeWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::NodeWidget),
|
||||
m_contextMenu(new QMenu(this)) {
|
||||
NodeWidget::NodeWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, ui(new Ui::NodeWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->btn_add_custom, &QPushButton::clicked, this, &NodeWidget::onCustomAddClicked);
|
||||
@@ -35,112 +35,91 @@ NodeWidget::NodeWidget(QWidget *parent) :
|
||||
}
|
||||
});
|
||||
|
||||
m_contextActionRemove = new QAction("Remove", this);
|
||||
m_contextActionConnect = new QAction(QIcon(":/assets/images/connect.svg"), "Connect to node", this);
|
||||
m_contextActionOpenStatusURL = new QAction(QIcon(":/assets/images/network.png"), "Visit status page", this);
|
||||
m_contextActionCopy = new QAction(QIcon(":/assets/images/copy.png"), "Copy", this);
|
||||
connect(m_contextActionConnect, &QAction::triggered, this, &NodeWidget::onContextConnect);
|
||||
connect(m_contextActionRemove, &QAction::triggered, this, &NodeWidget::onContextCustomNodeRemove);
|
||||
connect(m_contextActionOpenStatusURL, &QAction::triggered, this, &NodeWidget::onContextStatusURL);
|
||||
connect(m_contextActionCopy, &QAction::triggered, this, &NodeWidget::onContextNodeCopy);
|
||||
connect(m_contextActionRemove, &QAction::triggered, this, &NodeWidget::onContextCustomNodeRemove);
|
||||
|
||||
ui->wsView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
ui->customView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->wsView, &QTreeView::customContextMenuRequested, this, &NodeWidget::onShowWSContextMenu);
|
||||
connect(ui->customView, &QTreeView::customContextMenuRequested, this, &NodeWidget::onShowCustomContextMenu);
|
||||
|
||||
connect(ui->customView, &QTreeView::doubleClicked, this, &NodeWidget::onContextConnect);
|
||||
connect(ui->wsView, &QTreeView::doubleClicked, this, &NodeWidget::onContextConnect);
|
||||
}
|
||||
|
||||
void NodeWidget::onShowWSContextMenu(const QPoint &pos) {
|
||||
QModelIndex index = ui->wsView->indexAt(pos);
|
||||
if (!index.isValid()) return;
|
||||
m_activeView = ui->wsView;
|
||||
FeatherNode node = this->selectedNode();
|
||||
if (node.full.isEmpty()) return;
|
||||
|
||||
FeatherNode node = m_wsModel->node(index.row());
|
||||
this->showContextMenu(pos, node);
|
||||
}
|
||||
|
||||
void NodeWidget::onShowCustomContextMenu(const QPoint &pos) {
|
||||
QModelIndex index = ui->customView->indexAt(pos);
|
||||
if (!index.isValid()) return;
|
||||
m_activeView = ui->customView;
|
||||
FeatherNode node = this->selectedNode();
|
||||
if (node.full.isEmpty()) return;
|
||||
|
||||
FeatherNode node = m_customModel->node(index.row());
|
||||
this->showContextMenu(pos, node);
|
||||
}
|
||||
|
||||
|
||||
void NodeWidget::showContextMenu(const QPoint &pos, const FeatherNode &node) {
|
||||
bool custom = node.custom;
|
||||
m_activeView = custom ? ui->customView : ui->wsView;
|
||||
QMenu menu(this);
|
||||
|
||||
m_contextActionRemove = new QAction("Remove");
|
||||
m_contextActionConnect = new QAction("Connect to node");
|
||||
m_contextActionConnect->setIcon(QIcon(":/assets/images/connect.svg"));
|
||||
m_contextActionOpenStatusURL = new QAction("Visit status page");
|
||||
m_contextActionOpenStatusURL->setIcon(QIcon(":/assets/images/network.png"));
|
||||
m_contextActionCopy = new QAction("Copy");
|
||||
m_contextActionCopy->setIcon(QIcon(":/assets/images/copy.png"));
|
||||
|
||||
if(!node.isActive) {
|
||||
connect(m_contextActionConnect, &QAction::triggered, this, &NodeWidget::onContextConnect);
|
||||
m_contextMenu->addAction(m_contextActionConnect);
|
||||
if (!node.isActive) {
|
||||
menu.addAction(m_contextActionConnect);
|
||||
}
|
||||
|
||||
m_contextMenu->addAction(m_contextActionOpenStatusURL);
|
||||
m_contextMenu->addAction(m_contextActionCopy);
|
||||
menu.addAction(m_contextActionOpenStatusURL);
|
||||
menu.addAction(m_contextActionCopy);
|
||||
|
||||
if(custom) {
|
||||
connect(m_contextActionRemove, &QAction::triggered, this, &NodeWidget::onContextCustomNodeRemove);
|
||||
m_contextMenu->addAction(m_contextActionRemove);
|
||||
if (m_activeView == ui->customView)
|
||||
menu.addAction(m_contextActionRemove);
|
||||
|
||||
connect(m_contextActionOpenStatusURL, &QAction::triggered, this, &NodeWidget::onContextCustomStatusURL);
|
||||
connect(m_contextActionCopy, &QAction::triggered, this, &NodeWidget::onContextCustomNodeCopy);
|
||||
} else {
|
||||
connect(m_contextActionOpenStatusURL, &QAction::triggered, this, &NodeWidget::onContextWSStatusURL);
|
||||
connect(m_contextActionCopy, &QAction::triggered, this, &NodeWidget::onContextWSNodeCopy);
|
||||
}
|
||||
|
||||
m_contextMenu->exec(m_activeView->viewport()->mapToGlobal(pos));
|
||||
m_contextActionRemove->deleteLater();
|
||||
m_contextActionConnect->deleteLater();
|
||||
m_contextActionOpenStatusURL->deleteLater();
|
||||
m_contextActionCopy->deleteLater();
|
||||
menu.exec(m_activeView->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void NodeWidget::onContextConnect() {
|
||||
QObject *obj = sender();
|
||||
if (obj == ui->customView)
|
||||
m_activeView = ui->customView;
|
||||
else
|
||||
m_activeView = ui->wsView;
|
||||
|
||||
FeatherNode node = this->selectedNode();
|
||||
if (!node.full.isEmpty())
|
||||
emit connectToNode(node);
|
||||
}
|
||||
|
||||
void NodeWidget::onContextStatusURL() {
|
||||
FeatherNode node = this->selectedNode();
|
||||
if (!node.full.isEmpty())
|
||||
Utils::externalLinkWarning(this, node.as_url());
|
||||
}
|
||||
|
||||
void NodeWidget::onContextNodeCopy() {
|
||||
FeatherNode node = this->selectedNode();
|
||||
Utils::copyToClipboard(node.full);
|
||||
}
|
||||
|
||||
FeatherNode NodeWidget::selectedNode() {
|
||||
QModelIndex index = m_activeView->currentIndex();
|
||||
if (!index.isValid()) return;
|
||||
if (!index.isValid()) return FeatherNode();
|
||||
|
||||
if(m_activeView->objectName() == "wsView"){
|
||||
FeatherNode node = m_wsModel->node(index.row());
|
||||
emit connectToNode(node);
|
||||
FeatherNode node;
|
||||
if (m_activeView == ui->customView) {
|
||||
node = m_customModel->node(index.row());
|
||||
} else {
|
||||
FeatherNode node = m_customModel->node(index.row());
|
||||
emit connectToNode(node);
|
||||
node = m_wsModel->node(index.row());
|
||||
}
|
||||
}
|
||||
|
||||
void NodeWidget::onContextWSStatusURL() {
|
||||
QModelIndex index = ui->wsView->currentIndex();
|
||||
if (!index.isValid()) return;
|
||||
FeatherNode node = m_wsModel->node(index.row());
|
||||
Utils::externalLinkWarning(this, node.as_url());
|
||||
}
|
||||
|
||||
void NodeWidget::onContextCustomStatusURL() {
|
||||
QModelIndex index = ui->customView->currentIndex();
|
||||
if (!index.isValid()) return;
|
||||
FeatherNode node = m_customModel->node(index.row());
|
||||
Utils::externalLinkWarning(this, node.as_url());
|
||||
}
|
||||
|
||||
void NodeWidget::onContextDisconnect() {
|
||||
QModelIndex index = ui->customView->currentIndex();
|
||||
if (!index.isValid()) return;
|
||||
FeatherNode node = m_customModel->node(index.row());
|
||||
|
||||
Utils::copyToClipboard(node.full);
|
||||
}
|
||||
|
||||
void NodeWidget::onContextWSNodeCopy() {
|
||||
QModelIndex index = ui->wsView->currentIndex();
|
||||
if (!index.isValid()) return;
|
||||
FeatherNode node = m_wsModel->node(index.row());
|
||||
|
||||
Utils::copyToClipboard(node.full);
|
||||
}
|
||||
|
||||
void NodeWidget::onContextCustomNodeCopy() {
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void NodeWidget::onContextCustomNodeRemove() {
|
||||
|
||||
@@ -35,12 +35,9 @@ public slots:
|
||||
|
||||
private slots:
|
||||
void onContextConnect();
|
||||
void onContextDisconnect();
|
||||
void onContextCustomNodeRemove();
|
||||
void onContextWSStatusURL();
|
||||
void onContextCustomStatusURL();
|
||||
void onContextWSNodeCopy();
|
||||
void onContextCustomNodeCopy();
|
||||
void onContextStatusURL();
|
||||
void onContextNodeCopy();
|
||||
|
||||
signals:
|
||||
void connectToNode(FeatherNode node);
|
||||
@@ -54,14 +51,13 @@ private:
|
||||
|
||||
QTreeView *m_activeView;
|
||||
|
||||
QMenu *m_contextMenu;
|
||||
QAction *m_contextActionConnect;
|
||||
QAction *m_contextActionDisconnect;
|
||||
QAction *m_contextActionRemove;
|
||||
QAction *m_contextActionOpenStatusURL;
|
||||
QAction *m_contextActionCopy;
|
||||
|
||||
void showContextMenu(const QPoint &pos, const FeatherNode &node);
|
||||
FeatherNode selectedNode();
|
||||
};
|
||||
|
||||
#endif // NODEWIDGET_H
|
||||
|
||||
@@ -26,6 +26,8 @@ RedditWidget::RedditWidget(QWidget *parent) :
|
||||
connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &RedditWidget::showContextMenu);
|
||||
|
||||
connect(ui->tableView, &QTableView::doubleClicked, this, &RedditWidget::linkClicked);
|
||||
|
||||
ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
}
|
||||
|
||||
RedditModel* RedditWidget::model() {
|
||||
|
||||
@@ -17,15 +17,10 @@ RestoreHeightWidget::RestoreHeightWidget(QWidget *parent) :
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->lineEdit_restoreHeight->setValidator(new QIntValidator(0, 2147483647, this));
|
||||
connect(ui->lineEdit_restoreHeight, &QLineEdit::textEdited, [=](const QString &val){
|
||||
// update slider on lineEdit change
|
||||
if(val.isEmpty()) return;
|
||||
auto height = val.toUInt();
|
||||
if(height <= 1) return;
|
||||
auto timestamp = m_restoreHeightLookup->restoreHeightToDate(height);
|
||||
ui->restoreSlider->blockSignals(true);
|
||||
ui->restoreSlider->setValue(timestamp);
|
||||
ui->restoreSlider->blockSignals(false);
|
||||
|
||||
connect(ui->lineEdit_restoreHeight, &QLineEdit::textEdited, [this](const QString &val){
|
||||
if (val.isEmpty()) return;
|
||||
this->setHeight(val.toInt());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -33,26 +28,45 @@ void RestoreHeightWidget::hideSlider(){
|
||||
ui->restoreGrid->hide();
|
||||
}
|
||||
|
||||
void RestoreHeightWidget::setHeight(int height) {
|
||||
if (height < 0)
|
||||
height = 0;
|
||||
|
||||
// Update lineEdit
|
||||
ui->lineEdit_restoreHeight->setText(QString::number(height));
|
||||
|
||||
// Update slider
|
||||
int date = m_restoreHeightLookup->restoreHeightToDate(height);
|
||||
ui->restoreSlider->setValue(date);
|
||||
|
||||
this->updateTimestamp(date);
|
||||
}
|
||||
|
||||
void RestoreHeightWidget::initRestoreHeights(RestoreHeightLookup *lookup) {
|
||||
// init slider
|
||||
m_restoreHeightLookup = lookup;
|
||||
auto now = (unsigned int)std::time(nullptr);
|
||||
QList<unsigned int> blockDates = m_restoreHeightLookup->data.keys();
|
||||
int now = std::time(nullptr);
|
||||
QList<int> blockDates = m_restoreHeightLookup->data.keys();
|
||||
ui->restoreSlider->setMinimum(blockDates[0]);
|
||||
ui->restoreSlider->setMaximum(now);
|
||||
connect(ui->restoreSlider, &QSlider::valueChanged, this, &RestoreHeightWidget::onValueChanged);
|
||||
|
||||
connect(ui->restoreSlider, &QSlider::sliderMoved, [this](int date){
|
||||
// Update lineEdit
|
||||
int blockHeight = m_restoreHeightLookup->dateToRestoreHeight(date);
|
||||
ui->lineEdit_restoreHeight->setText(QString::number(blockHeight));
|
||||
|
||||
this->updateTimestamp(date);
|
||||
});
|
||||
}
|
||||
|
||||
void RestoreHeightWidget::onValueChanged(int date) {
|
||||
void RestoreHeightWidget::updateTimestamp(int date) {
|
||||
QDateTime timestamp;
|
||||
timestamp.setTime_t((unsigned int) date);
|
||||
timestamp.setTime_t(date);
|
||||
ui->label_restoreHeightDate->setText(timestamp.toString("yyyy-MM-dd"));
|
||||
auto blockHeight = m_restoreHeightLookup->dateToRestoreHeight((unsigned int) date);
|
||||
ui->lineEdit_restoreHeight->setText(QString::number(blockHeight));
|
||||
}
|
||||
|
||||
unsigned int RestoreHeightWidget::getHeight() {
|
||||
return ui->lineEdit_restoreHeight->text().toUInt();
|
||||
int RestoreHeightWidget::getHeight() {
|
||||
return ui->lineEdit_restoreHeight->text().toInt();
|
||||
}
|
||||
|
||||
RestoreHeightWidget::~RestoreHeightWidget() {
|
||||
|
||||
@@ -20,14 +20,14 @@ class RestoreHeightWidget : public QWidget
|
||||
public:
|
||||
explicit RestoreHeightWidget(QWidget *parent = nullptr);
|
||||
void initRestoreHeights(RestoreHeightLookup *lookup);
|
||||
unsigned int getHeight();
|
||||
void setHeight(int height);
|
||||
int getHeight();
|
||||
void hideSlider();
|
||||
~RestoreHeightWidget();
|
||||
|
||||
private slots:
|
||||
void onValueChanged(int date);
|
||||
~RestoreHeightWidget() override;
|
||||
|
||||
private:
|
||||
void updateTimestamp(int date);
|
||||
|
||||
RestoreHeightLookup *m_restoreHeightLookup = nullptr;
|
||||
Ui::RestoreHeightWidget *ui;
|
||||
};
|
||||
|
||||
@@ -50,15 +50,19 @@ void TickerWidget::init() {
|
||||
double amount = m_convertBalance ? AppContext::balance : 1.0;
|
||||
double conversion = AppContext::prices->convert(m_symbol, fiatCurrency, amount);
|
||||
if (conversion < 0) return;
|
||||
ui->tickerPct->setHidden(conversion == 0 || m_hidePercent);
|
||||
|
||||
auto markets = AppContext::prices->markets;
|
||||
if(!markets.contains(m_symbol)) return;
|
||||
|
||||
auto pct24h = markets[m_symbol].price_usd_change_pct_24h;
|
||||
auto pct24hText = QString::number(pct24h, 'f', 2);
|
||||
bool hidePercent = (conversion == 0 || m_hidePercent);
|
||||
if (hidePercent) {
|
||||
ui->tickerPct->hide();
|
||||
} else {
|
||||
auto pct24h = markets[m_symbol].price_usd_change_pct_24h;
|
||||
auto pct24hText = QString::number(pct24h, 'f', 2);
|
||||
this->setPctText(pct24hText, pct24h >= 0.0);
|
||||
}
|
||||
|
||||
this->setPctText(pct24hText, pct24h >= 0.0);
|
||||
this->setFiatText(fiatCurrency, conversion);
|
||||
}
|
||||
|
||||
@@ -84,10 +88,6 @@ void TickerWidget::setFontSizes() {
|
||||
ui->tickerFiat->setFont(Utils::relativeFont(0));
|
||||
}
|
||||
|
||||
void TickerWidget::removePctContainer() {
|
||||
ui->tickerPct->deleteLater();
|
||||
}
|
||||
|
||||
TickerWidget::~TickerWidget() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ class TickerWidget : public QWidget
|
||||
|
||||
public:
|
||||
explicit TickerWidget(QWidget *parent, QString symbol, QString title = "", bool convertBalance = false, bool hidePercent = false);
|
||||
void removePctContainer();
|
||||
void setFiatText(QString &fiatCurrency, double amount);
|
||||
void setPctText(QString &text, bool positive);
|
||||
void setFontSizes();
|
||||
|
||||
@@ -32,6 +32,11 @@ TxProofWidget::TxProofWidget(QWidget *parent, Wallet *wallet, TransactionInfo *t
|
||||
|
||||
connect(ui->btn_copySpendProof, &QPushButton::clicked, this, &TxProofWidget::copySpendProof);
|
||||
connect(ui->btn_copyTxProof, &QPushButton::clicked, this, &TxProofWidget::copyTxProof);
|
||||
|
||||
ui->label_SpendProof->setHelpText("A SpendProof proves authorship of a transaction.\n\n"
|
||||
"SpendProofs do not prove that a particular amount was sent to an address, for that use OutProofs.");
|
||||
ui->label_InOutProof->setHelpText("An InProof proves ownership of an output.\n"
|
||||
"An OutProof shows the prover sent an output to an address.");
|
||||
}
|
||||
|
||||
void TxProofWidget::copySpendProof() {
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="HelpLabel" name="label_SpendProof">
|
||||
<property name="text">
|
||||
<string>SpendProof</string>
|
||||
</property>
|
||||
@@ -72,7 +72,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<widget class="HelpLabel" name="label_InOutProof">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -98,6 +98,13 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>HelpLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>components.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -34,7 +34,11 @@ CreateWalletPage::CreateWalletPage(AppContext *ctx, QWidget *parent) :
|
||||
connect(ui->btnChange, &QPushButton::clicked, [=] {
|
||||
QString walletDir = QFileDialog::getExistingDirectory(this, "Select wallet directory ", m_ctx->defaultWalletDir, QFileDialog::ShowDirsOnly);
|
||||
if(walletDir.isEmpty()) return;
|
||||
m_ctx->defaultWalletDir = walletDir;
|
||||
m_ctx->defaultWalletDirRoot = walletDir;
|
||||
ui->directory->setText(walletDir);
|
||||
config()->set(Config::walletDirectory, walletDir);
|
||||
emit defaultWalletDirChanged(walletDir);
|
||||
});
|
||||
|
||||
connect(ui->directory, &QLineEdit::textChanged, [=](const QString &data) {
|
||||
@@ -47,6 +51,10 @@ CreateWalletPage::CreateWalletPage(AppContext *ctx, QWidget *parent) :
|
||||
});
|
||||
}
|
||||
|
||||
void CreateWalletPage::initializePage() {
|
||||
ui->directory->setText(m_ctx->defaultWalletDir);
|
||||
}
|
||||
|
||||
bool CreateWalletPage::validateWidgets(){
|
||||
ui->walletName->setStyleSheet("");
|
||||
ui->directory->setStyleSheet("");
|
||||
|
||||
@@ -20,15 +20,16 @@ class CreateWalletPage : public QWizardPage
|
||||
|
||||
public:
|
||||
explicit CreateWalletPage(AppContext *ctx, QWidget *parent = nullptr);
|
||||
void initializePage() override;
|
||||
bool validatePage() override;
|
||||
int nextId() const override;
|
||||
|
||||
signals:
|
||||
void createWallet();
|
||||
void defaultWalletDirChanged(QString walletDir);
|
||||
|
||||
private:
|
||||
AppContext *m_ctx;
|
||||
QLabel *topLabel;
|
||||
Ui::CreateWalletPage *ui;
|
||||
QString m_walletDir;
|
||||
bool validateWidgets();
|
||||
|
||||
@@ -23,7 +23,6 @@ public:
|
||||
explicit CreateWalletSeedPage(AppContext *ctx, QWidget *parent = nullptr);
|
||||
bool validatePage() override;
|
||||
int nextId() const override;
|
||||
// bool isFinalPage() const;
|
||||
|
||||
public slots:
|
||||
void displaySeed(const QString &seed);
|
||||
@@ -40,7 +39,7 @@ private:
|
||||
Ui::CreateWalletSeedPage *ui;
|
||||
|
||||
QString m_mnemonic;
|
||||
unsigned int m_restoreHeight;
|
||||
int m_restoreHeight;
|
||||
|
||||
bool m_roulette = false;
|
||||
int m_rouletteSpin = 15;
|
||||
|
||||
+14
-6
@@ -11,14 +11,22 @@
|
||||
|
||||
#include "libwalletqt/WalletManager.h"
|
||||
|
||||
MenuPage::MenuPage(AppContext *ctx, QWidget *parent) :
|
||||
QWizardPage(parent),
|
||||
ui(new Ui::MenuPage),
|
||||
m_ctx(ctx) {
|
||||
MenuPage::MenuPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent)
|
||||
: QWizardPage(parent)
|
||||
, ui(new Ui::MenuPage)
|
||||
, m_ctx(ctx)
|
||||
, m_walletKeysFilesModel(wallets)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
this->setTitle("Welcome to feather");
|
||||
this->setButtonText(QWizard::FinishButton, "Open recent wallet");
|
||||
ui->radioCreate->setChecked(true);
|
||||
}
|
||||
|
||||
void MenuPage::initializePage() {
|
||||
if (m_walletKeysFilesModel->rowCount() > 0) {
|
||||
ui->radioOpen->setChecked(true);
|
||||
} else {
|
||||
ui->radioCreate->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
int MenuPage::nextId() const {
|
||||
|
||||
+3
-2
@@ -19,13 +19,14 @@ class MenuPage : public QWizardPage
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MenuPage(AppContext *ctx, QWidget *parent = nullptr);
|
||||
explicit MenuPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent = nullptr);
|
||||
void initializePage() override;
|
||||
bool validatePage() override;
|
||||
int nextId() const override;
|
||||
|
||||
private:
|
||||
AppContext *m_ctx;
|
||||
QLabel *topLabel;
|
||||
WalletKeysFilesModel *m_walletKeysFilesModel;
|
||||
Ui::MenuPage *ui;
|
||||
};
|
||||
|
||||
|
||||
+24
-2
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>424</width>
|
||||
<height>245</height>
|
||||
<width>617</width>
|
||||
<height>463</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -29,6 +29,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioCreate">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create new wallet</string>
|
||||
</property>
|
||||
@@ -36,6 +39,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioOpen">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open wallet file</string>
|
||||
</property>
|
||||
@@ -43,6 +49,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioSeed">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Restore wallet from seed</string>
|
||||
</property>
|
||||
@@ -50,6 +59,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioViewOnly">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Import from keys</string>
|
||||
</property>
|
||||
@@ -78,6 +90,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>banner: themonera.art</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
+32
-14
@@ -10,12 +10,12 @@
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
// @TODO: rescan wallet dir on wizard open
|
||||
|
||||
OpenWalletPage::OpenWalletPage(AppContext *ctx, QWidget *parent) :
|
||||
QWizardPage(parent),
|
||||
ui(new Ui::OpenWalletPage),
|
||||
m_ctx(ctx) {
|
||||
OpenWalletPage::OpenWalletPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent)
|
||||
: QWizardPage(parent)
|
||||
, ui(new Ui::OpenWalletPage)
|
||||
, m_ctx(ctx)
|
||||
, m_walletKeysFilesModel(wallets)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->btnBrowse, &QPushButton::clicked, [=]{
|
||||
@@ -46,19 +46,37 @@ OpenWalletPage::OpenWalletPage(AppContext *ctx, QWidget *parent) :
|
||||
ui->walletTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
ui->walletTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
this->walletKeysFilesModel = new WalletKeysFilesModel(m_ctx);
|
||||
this->walletKeysFilesModel->refresh();
|
||||
|
||||
m_keysProxy = new WalletKeysFilesProxyModel();
|
||||
m_keysProxy->setSourceModel(this->walletKeysFilesModel);
|
||||
m_keysProxy = new WalletKeysFilesProxyModel(this, m_ctx->networkType);
|
||||
m_keysProxy->setSourceModel(m_walletKeysFilesModel);
|
||||
m_keysProxy->setSortRole(Qt::UserRole);
|
||||
|
||||
ui->walletTable->setModel(m_keysProxy);
|
||||
ui->walletTable->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
ui->walletTable->header()->setSectionResizeMode(WalletKeysFilesModel::Path, QHeaderView::Stretch);
|
||||
ui->walletTable->hideColumn(WalletKeysFilesModel::NetworkType);
|
||||
ui->walletTable->hideColumn(WalletKeysFilesModel::Path);
|
||||
ui->walletTable->hideColumn(WalletKeysFilesModel::Modified);
|
||||
ui->walletTable->header()->setSectionResizeMode(WalletKeysFilesModel::FileName, QHeaderView::Stretch);
|
||||
ui->walletTable->setSortingEnabled(true);
|
||||
ui->walletTable->sortByColumn(WalletKeysFilesModel::Modified, Qt::AscendingOrder); // @TODO: this does not work
|
||||
ui->walletTable->sortByColumn(WalletKeysFilesModel::Modified, Qt::DescendingOrder);
|
||||
ui->walletTable->show();
|
||||
|
||||
connect(ui->walletTable->selectionModel(), &QItemSelectionModel::currentRowChanged, [this](QModelIndex current, QModelIndex prev){
|
||||
this->updatePath();
|
||||
});
|
||||
}
|
||||
|
||||
void OpenWalletPage::initializePage() {
|
||||
m_walletKeysFilesModel->refresh();
|
||||
}
|
||||
|
||||
void OpenWalletPage::updatePath() {
|
||||
QModelIndex index = ui->walletTable->currentIndex();
|
||||
if (!index.isValid()) {
|
||||
ui->labelPath->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = index.model()->data(index.siblingAtColumn(WalletKeysFilesModel::Path), Qt::DisplayRole).toString();
|
||||
ui->labelPath->setText(path);
|
||||
}
|
||||
|
||||
int OpenWalletPage::nextId() const {
|
||||
|
||||
@@ -20,7 +20,8 @@ class OpenWalletPage : public QWizardPage
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OpenWalletPage(AppContext *ctx, QWidget *parent = nullptr);
|
||||
explicit OpenWalletPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent = nullptr);
|
||||
void initializePage() override;
|
||||
bool validatePage() override;
|
||||
int nextId() const override;
|
||||
|
||||
@@ -28,11 +29,11 @@ signals:
|
||||
void openWallet(QString path);
|
||||
|
||||
private:
|
||||
void updatePath();
|
||||
|
||||
AppContext *m_ctx;
|
||||
WalletKeysFilesModel *walletKeysFilesModel;
|
||||
WalletKeysFilesModel *m_walletKeysFilesModel;
|
||||
WalletKeysFilesProxyModel *m_keysProxy;
|
||||
QSortFilterProxyModel *ll;
|
||||
QLabel *topLabel;
|
||||
Ui::OpenWalletPage *ui;
|
||||
QStandardItemModel *m_model;
|
||||
};
|
||||
|
||||
@@ -55,6 +55,13 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelPath">
|
||||
<property name="text">
|
||||
<string>path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
@@ -31,7 +31,7 @@ RestorePage::RestorePage(AppContext *ctx, QWidget *parent) :
|
||||
auto data = Utils::fileOpen(":/assets/mnemonic_25_english.txt");
|
||||
for(const auto &seed_word: data.split('\n'))
|
||||
m_words25 << seed_word;
|
||||
for(unsigned int i = 0; i != 2048; i++)
|
||||
for(int i = 0; i != 2048; i++)
|
||||
m_words14 << QString::fromStdString(wordlist::english.get_word(i));
|
||||
|
||||
//
|
||||
@@ -104,12 +104,15 @@ int RestorePage::nextId() const {
|
||||
return WalletWizard::Page_CreateWallet;
|
||||
}
|
||||
|
||||
void RestorePage::cleanupPage() const {}
|
||||
void RestorePage::initializePage() {
|
||||
ui->seedEdit->setText("");
|
||||
ui->restoreHeightWidget->setHeight(1);
|
||||
}
|
||||
|
||||
bool RestorePage::validatePage() {
|
||||
ui->label_errorString->hide();
|
||||
auto errStyle = "QTextEdit{border: 1px solid red;}";
|
||||
unsigned int restoreHeight = ui->restoreHeightWidget->getHeight();
|
||||
int restoreHeight = ui->restoreHeightWidget->getHeight();
|
||||
auto seed = ui->seedEdit->toPlainText().replace("\n", "").replace("\r", "").trimmed();
|
||||
auto seedSplit = seed.split(" ");
|
||||
|
||||
|
||||
@@ -25,15 +25,14 @@ class RestorePage : public QWizardPage
|
||||
public:
|
||||
explicit RestorePage(AppContext *ctx, QWidget *parent = nullptr);
|
||||
bool validatePage() override;
|
||||
void initializePage() override;
|
||||
int nextId() const override;
|
||||
void cleanupPage() const;
|
||||
|
||||
private:
|
||||
AppContext *m_ctx;
|
||||
QLabel *topLabel;
|
||||
Ui::RestorePage *ui;
|
||||
|
||||
unsigned int m_mode = 14;
|
||||
int m_mode = 14;
|
||||
QStringList m_words14;
|
||||
QStringList m_words25;
|
||||
QStringListModel *m_completer14Model = nullptr;
|
||||
|
||||
@@ -72,7 +72,7 @@ bool ViewOnlyPage::validatePage() {
|
||||
ui->lineEdit_viewkey->setStyleSheet("");
|
||||
ui->label_errorString->hide();
|
||||
|
||||
unsigned int restoreHeight = ui->restoreHeightWidget->getHeight();
|
||||
int restoreHeight = ui->restoreHeightWidget->getHeight();
|
||||
auto spendkey = ui->lineEdit_spendkey->text().trimmed();
|
||||
auto viewkey = ui->lineEdit_viewkey->text().trimmed();
|
||||
auto address = ui->lineEdit_address->text().trimmed();
|
||||
|
||||
@@ -18,14 +18,20 @@
|
||||
#include <QScreen>
|
||||
#include <QApplication>
|
||||
|
||||
WalletWizard::WalletWizard(AppContext *ctx, WalletWizard::Page startPage, QWidget *parent) : QWizard(parent),
|
||||
m_ctx(ctx) {
|
||||
this->setWindowTitle("Welcome to feather");
|
||||
WalletWizard::WalletWizard(AppContext *ctx, WalletWizard::Page startPage, QWidget *parent)
|
||||
: QWizard(parent)
|
||||
, m_ctx(ctx)
|
||||
{
|
||||
this->setWindowTitle("Welcome to Feather Wallet");
|
||||
this->setWindowIcon(QIcon(":/assets/images/appicons/64x64.png"));
|
||||
auto openWalletPage = new OpenWalletPage(m_ctx, this);
|
||||
|
||||
m_walletKeysFilesModel = new WalletKeysFilesModel(m_ctx, this);
|
||||
m_walletKeysFilesModel->refresh();
|
||||
|
||||
auto openWalletPage = new OpenWalletPage(m_ctx, m_walletKeysFilesModel, this);
|
||||
auto createWallet = new CreateWalletPage(m_ctx, this);
|
||||
auto createWalletSeed = new CreateWalletSeedPage(m_ctx, this);
|
||||
setPage(Page_Menu, new MenuPage(m_ctx, this));
|
||||
setPage(Page_Menu, new MenuPage(m_ctx, m_walletKeysFilesModel, this));
|
||||
setPage(Page_CreateWallet, createWallet);
|
||||
setPage(Page_OpenWallet, openWalletPage);
|
||||
setPage(Page_CreateWalletSeed, createWalletSeed);
|
||||
@@ -38,7 +44,7 @@ WalletWizard::WalletWizard(AppContext *ctx, WalletWizard::Page startPage, QWidge
|
||||
else
|
||||
setStartId(Page_Menu);
|
||||
|
||||
setPixmap(QWizard::WatermarkPixmap, QPixmap(":/assets/images/photos/1.png"));
|
||||
setPixmap(QWizard::WatermarkPixmap, QPixmap(":/assets/images/banners/3.png"));
|
||||
setWizardStyle(WizardStyle::ModernStyle);
|
||||
setOption(QWizard::NoBackButtonOnStartPage);
|
||||
|
||||
@@ -48,6 +54,9 @@ WalletWizard::WalletWizard(AppContext *ctx, WalletWizard::Page startPage, QWidge
|
||||
|
||||
connect(createWalletSeed, &CreateWalletSeedPage::createWallet, this, &WalletWizard::createWallet);
|
||||
connect(createWallet, &CreateWalletPage::createWallet, this, &WalletWizard::createWallet);
|
||||
connect(createWallet, &CreateWalletPage::defaultWalletDirChanged, [this](const QString &walletDir){
|
||||
emit defaultWalletDirChanged(walletDir);
|
||||
});
|
||||
|
||||
connect(openWalletPage, &OpenWalletPage::openWallet, [=](const QString &path){
|
||||
const auto walletPasswd = this->field("walletPasswd").toString();
|
||||
|
||||
@@ -20,11 +20,15 @@ public:
|
||||
enum Page { Page_Menu, Page_CreateWallet, Page_CreateWalletSeed, Page_OpenWallet, Page_Network, Page_Restore, Page_ViewOnly };
|
||||
|
||||
explicit WalletWizard(AppContext *ctx, WalletWizard::Page startPage = WalletWizard::Page::Page_Menu, QWidget *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void openWallet(QString path, QString password);
|
||||
void defaultWalletDirChanged(QString walletDir);
|
||||
|
||||
private:
|
||||
AppContext *m_ctx;
|
||||
WalletKeysFilesModel *m_walletKeysFilesModel;
|
||||
|
||||
void createWallet();
|
||||
};
|
||||
|
||||
|
||||
+3
-2
@@ -6,6 +6,7 @@
|
||||
#include "dialog/xmrtoinfodialog.h"
|
||||
#include "libwalletqt/WalletManager.h"
|
||||
#include "mainwindow.h"
|
||||
#include "globals.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
@@ -71,8 +72,8 @@ void XMRToWidget::setHistoryModel(XmrToModel *model) {
|
||||
this->ui->historyTable->setModel(model);
|
||||
}
|
||||
|
||||
void XMRToWidget::onBalanceUpdated(double balance, double unlocked, const QString &balance_str, const QString &unlocked_str) {
|
||||
this->m_unlockedBalance = unlocked;
|
||||
void XMRToWidget::onBalanceUpdated(quint64 balance, quint64 spendable) {
|
||||
this->m_unlockedBalance = spendable / globals::cdiv;
|
||||
}
|
||||
|
||||
void XMRToWidget::onWalletClosed() {
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ public slots:
|
||||
void onRatesUpdated(XmrToRates rates);
|
||||
void onTorCheckBoxToggled(int state);
|
||||
void onCreateOrder();
|
||||
void onBalanceUpdated(double balance, double unlocked, const QString &balance_str, const QString &unlocked_str);
|
||||
void onBalanceUpdated(quint64 balance, quint64 spendable);
|
||||
void updateConversionLabel();
|
||||
|
||||
void onInitiateTransaction();
|
||||
|
||||
Reference in New Issue
Block a user