Compare commits

..
75 Commits
Author SHA1 Message Date
tobtoht eab5145f34 Merge pull request 'Fix: exchange tab hide/show' (#263) from tobtoht/feather:fix_exchange_tab into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/263
2020-12-25 22:19:46 +00:00
tobtoht a2c7245e46 Fix: exchange tab hide/show 2020-12-25 23:18:40 +01:00
tobtoht 4aa57f2684 Merge pull request 'Build: prepare beta-3' (#262) from tobtoht/feather:beta-3 into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/262
2020-12-25 21:37:49 +00:00
tobtoht d8625fdb29 Merge pull request 'Settings: allow changing default wallet directory' (#261) from tobtoht/feather:wizard_reset_restore_page into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/261
2020-12-25 21:37:26 +00:00
tobtoht 207180396f Build: prepare beta-3 2020-12-25 22:36:44 +01:00
tobtoht b544e6dfce Settings: allow changing default wallet directory 2020-12-25 22:07:48 +01:00
tobtoht bc2cac672a Merge pull request 'Wizard: reset restore page on initialize' (#260) from tobtoht/feather:wizard_reset_restore_page into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/260
2020-12-25 15:36:57 +00:00
tobtoht 6003ffebec Wizard: reset restore page on initialize 2020-12-25 16:28:50 +01:00
tobtoht 51b3781c09 Merge pull request 'Don't use unsigned ints' (#257) from tobtoht/feather:no_unsigned_ints into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/257
2020-12-25 14:44:48 +00:00
tobtoht 14e3422423 Don't use unsigned ints 2020-12-25 15:38:44 +01:00
tobtoht 0e5ab21205 Merge pull request 'BalanceDialog' (#259) from tobtoht/feather:balance_dialogg into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/259
2020-12-25 14:22:16 +00:00
tobtoht a11dc3f4b2 BalanceDialog 2020-12-25 15:20:39 +01:00
tobtoht f5255440e4 Merge pull request 'Status: constructing transaction' (#258) from tobtoht/feather:constructing_tx into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/258
2020-12-25 14:16:58 +00:00
tobtoht 0dc196e76d Status: constructing transaction 2020-12-24 15:46:56 +01:00
tobtoht 136f877778 Merge pull request 'TickerWidget: minor cleanup' (#256) from tobtoht/feather:ticker_clean into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/256
2020-12-24 12:52:13 +00:00
tobtoht 3b7731d44f Merge pull request 'Utils: improve bytes formatting' (#255) from tobtoht/feather:bytes_formatting into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/255
2020-12-24 12:52:01 +00:00
tobtoht b51e1f3b0c Merge pull request 'Contacts: disallow duplicates' (#254) from tobtoht/feather:contacts_duplicates into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/254
2020-12-24 12:51:46 +00:00
tobtoht ae3c214c3f TickerWidget: minor cleanup 2020-12-24 13:50:20 +01:00
tobtoht 200f16ecf2 Utils: improve bytes formatting 2020-12-24 13:21:06 +01:00
tobtoht ecd840c03a Contacts: disallow duplicates 2020-12-23 05:59:06 +01:00
tobtoht 7092116efd Merge pull request 'Wizard: autoselect open if wallets available & refresh wallets when wizard opened' (#253) from tobtoht/feather:wizard_open_refresh into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/253
2020-12-23 03:00:31 +00:00
tobtoht 68c493b736 Wizard: autoselect open if wallets available & refresh wallets when wizard opened 2020-12-23 03:57:01 +01:00
tobtoht 4f62d52e5e Merge pull request 'Nodes: double click to connect' (#251) from tobtoht/feather:nodes_doubleclick into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/251
2020-12-23 01:44:19 +00:00
tobtoht c8be1e4966 Merge pull request 'Status: fix negative blocks remaining' (#252) from tobtoht/feather:status_blocks_remaining into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/252
2020-12-23 01:44:00 +00:00
tobtoht c964cd5349 Status: fix negative blocks remaining 2020-12-23 02:42:22 +01:00
tobtoht 3fbf58f64d Nodes: double click to connect 2020-12-23 02:23:26 +01:00
tobtoht bd4400a7f2 Merge pull request 'Send: Show error when no connecting to daemon' (#250) from tobtoht/feather:send_no_daemon into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/250
2020-12-22 23:53:37 +00:00
tobtoht c0e4d48fae Send: Show error when no connecting to daemon 2020-12-23 00:46:42 +01:00
tobtoht 8668f2beb2 Merge pull request 'Send: re-enable send button on wallet close' (#248) from tobtoht/feather:send_reenable_wallet_close into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/248
2020-12-22 23:34:07 +00:00
tobtoht 628da207d0 Merge pull request 'History: don't allow rebroadcasting incoming transaction' (#249) from tobtoht/feather:send_rebroadcast_incoming into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/249
2020-12-22 23:33:54 +00:00
tobtoht 677f7df376 History: don't allow rebroadcasting incoming transaction 2020-12-23 00:33:24 +01:00
tobtoht 580675e8a0 Send: re-enable send button on wallet close 2020-12-23 00:24:10 +01:00
tobtoht 900a9b3fac Merge pull request 'Websocket: keep connection alive' (#247) from tobtoht/feather:websocket_keepalive into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/247
2020-12-22 23:11:56 +00:00
tobtoht f8f1e824a1 Merge pull request 'CSSWidget: don't filter funded proposals' (#246) from tobtoht/feather:ccs_no_filter into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/246
2020-12-22 23:11:27 +00:00
tobtoht 441c32bc30 Merge pull request 'Nodes: force reconnect when new wallet is opened' (#245) from tobtoht/feather:nodes_force_reconnect into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/245
2020-12-22 23:11:18 +00:00
tobtoht 41a0fb3b10 Websocket: keep connection alive 2020-12-23 00:08:17 +01:00
tobtoht a38912a348 CSSWidget: don't filter funded proposals 2020-12-22 20:54:30 +01:00
tobtoht d568e4e1b6 Nodes: force reconnect when new wallet is opened 2020-12-22 02:13:24 +01:00
tobtoht 12849c8571 Merge pull request 'Libwalletqt: don't inherit subaddress account from GUI' (#243) from tobtoht/feather:switch_to_primary into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/243
2020-12-22 00:10:13 +00:00
tobtoht f7fb051b67 Libwalletqt: don't inherit subaddress account from GUI 2020-12-22 01:07:39 +01:00
tobtoht 693a6bc0df Merge pull request 'DebugInfoDialog: Add entry to show if wallet only contains funds in primary account' (#242) from tobtoht/feather:balance_accounts into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/242
2020-12-20 14:25:46 +00:00
tobtoht ccd19edf2f Merge pull request 'Remove MorphToken, reason: Tor block' (#237) from tobtoht/feather:into_the_trash_it_goes into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/237
2020-12-20 14:25:11 +00:00
tobtoht f2f6bf30b3 DebugInfoDialog: Add entry to show if wallet only contains funds in primary account 2020-12-20 03:10:00 +01:00
tobtoht 45b3f1ebde Merge pull request 'Update banner' (#239) from tobtoht/feather:banner into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/239
2020-12-19 00:10:28 +00:00
tobtoht b4af2f75b1 Update banner 2020-12-19 01:09:03 +01:00
tobtoht fdabaf3374 Merge pull request 'Remove non-breaking space from currency strings' (#236) from tobtoht/feather:currencystring_nonbreaking into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/236
2020-12-18 23:32:07 +00:00
tobtoht 2e481507e8 Remove non-breaking space from currency strings 2020-12-19 00:29:22 +01:00
tobtoht 9b4e0e29e4 Merge pull request 'Wizard: Clean up open wallet UI' (#241) from tobtoht/feather:open_wallet_clean into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/241
2020-12-18 23:25:57 +00:00
tobtoht 80bd8e085d Merge pull request 'Store wallet immediately upon finishing sync' (#240) from tobtoht/feather:store_sync into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/240
2020-12-18 23:25:09 +00:00
tobtoht 7d2748c0bc Merge pull request 'ExternalLinkWarning: don't show Tor warning on Tails/Whonix' (#235) from tobtoht/feather:link_warning_tailswhonix into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/235
2020-12-18 23:24:41 +00:00
tobtoht 2c0b04559e Merge pull request 'TxConfDialog: Justify amounts' (#234) from tobtoht/feather:txconf_justify_amounts into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/234
2020-12-18 23:24:14 +00:00
tobtoht 4476741ace Wizard: Sort wallets by modified date 2020-12-19 00:13:33 +01:00
tobtoht 68f8be01ac Wizard: don't show stagenet wallet in mainnet mode 2020-12-18 23:39:27 +01:00
tobtoht d84ee4692d Store wallet immediately upon finishing sync 2020-12-18 23:07:27 +01:00
tobtoht a0fe51f318 Remove MorphToken, reason: Tor block 2020-12-17 03:57:43 +01:00
tobtoht 70810ce6a2 ExternalLinkWarning: don't show Tor warning on Tails/Whonix 2020-12-16 18:02:49 +01:00
tobtoht f75d1f546b TxConfDialog: Justify amounts 2020-12-16 17:31:25 +01:00
tobtoht 6c8255b12b Merge pull request 'Home: wrap text in tables' (#227) from tobtoht/feather:home_widget_wrap into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/227
2020-12-16 15:32:37 +00:00
tobtoht f39cdb393f Merge pull request 'TxInfoDialog: use frame to bundle destinations widgets' (#231) from tobtoht/feather:txinfo_destination_frame into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/231
2020-12-16 15:32:22 +00:00
tobtoht d4fda0f5f3 Merge pull request 'Statusbar: block remaining instead of absolute values' (#229) from tobtoht/feather:block_remaining into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/229
2020-12-16 15:32:11 +00:00
tobtoht 9afefa75a0 Merge pull request 'Wizard: improve title' (#228) from tobtoht/feather:welcome_to_feather into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/228
2020-12-16 15:31:56 +00:00
tobtoht 32c21ae81b Merge pull request 'DebugInfoDialog: cleanup ws status message' (#230) from tobtoht/feather:debuginfo_ws_status into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/230
2020-12-16 15:31:39 +00:00
tobtoht ca1c2fa5ba Merge pull request 'TxProofWidget: add help labels for proofs' (#232) from tobtoht/feather:txproofwidget_help_labels into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/232
2020-12-16 15:31:17 +00:00
tobtoht 6619927a7d Merge pull request 'Send: add help labels' (#233) from tobtoht/feather:send_help_labels into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/233
2020-12-16 15:31:00 +00:00
tobtoht e805dd04cb Send: add help labels 2020-12-16 16:29:52 +01:00
tobtoht dbc3bed669 TxProofWidget: add help labels for proofs 2020-12-16 15:55:04 +01:00
tobtoht 55aab1683f TxInfoDialog: use frame to bundle destinations widgets 2020-12-16 15:37:20 +01:00
tobtoht 33978bda4f Merge pull request 'Apply currency formatting to fiat column in transaction history' (#226) from blasty/feather:history_currencyformat into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/226
2020-12-16 14:13:51 +00:00
blasty 4c9ece5025 Apply currency formatting to fiat column in transaction history 2020-12-16 10:53:41 +01:00
tobtoht a984d64add Home: wrap text in tables 2020-12-16 04:40:32 +01:00
tobtoht cb95e89773 Wizard: improve title 2020-12-16 04:18:42 +01:00
tobtoht f8361fb31c Statusbar: block remaining instead of absolute values 2020-12-16 04:09:32 +01:00
tobtoht 5ceac87e6e DebugInfoDialog: cleanup ws status message 2020-12-15 21:02:53 +01:00
tobtoht e058f3bad1 Merge pull request 'CMakeLists.txt: only copy tor binary if it has been changed' (#225) from blasty/feather:update_cmakelists into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/225
2020-12-15 13:43:50 +00:00
blasty 460a44ba34 CMakeLists.txt: only copy tor binary if it has been changed 2020-12-15 14:31:49 +01:00
83 changed files with 965 additions and 1290 deletions
+2 -3
View File
@@ -7,11 +7,10 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
set(VERSION_MAJOR "0") set(VERSION_MAJOR "0")
set(VERSION_MINOR "1") set(VERSION_MINOR "1")
set(VERSION_REVISION "0") set(VERSION_REVISION "0")
set(VERSION "beta-2") set(VERSION "beta-3")
option(FETCH_DEPS "Download dependencies if they are not found" ON) option(FETCH_DEPS "Download dependencies if they are not found" ON)
option(XMRTO "Include Xmr.To module" ON) option(XMRTO "Include Xmr.To module" ON)
option(MORPHTOKEN "Include MorphToken module" ON)
option(XMRIG "Include XMRig module" ON) option(XMRIG "Include XMRig module" ON)
option(TOR_BIN "Path to Tor binary to embed inside Feather" OFF) option(TOR_BIN "Path to Tor binary to embed inside Feather" OFF)
@@ -191,7 +190,7 @@ if(TOR_BIN)
# - linux: See `Dockerfile` # - linux: See `Dockerfile`
# - windows: https://github.com/mxe/mxe/blob/1024dc7d2db5eb7d5d3c64a2c12b5f592572f1ce/plugins/apps/tor.mk # - windows: https://github.com/mxe/mxe/blob/1024dc7d2db5eb7d5d3c64a2c12b5f592572f1ce/plugins/apps/tor.mk
# - macos: taken from Tor Browser official release # - 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}") message(STATUS "${TOR_COPY_CMD}")
execute_process(COMMAND bash -c "${TOR_COPY_CMD}" RESULT_VARIABLE ret) execute_process(COMMAND bash -c "${TOR_COPY_CMD}" RESULT_VARIABLE ret)
if(ret EQUAL "1") if(ret EQUAL "1")
-1
View File
@@ -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: There are some Monero/Feather related options/definitions that you may pass:
- `-DXMRTO=OFF` - disable Xmr.To feature - `-DXMRTO=OFF` - disable Xmr.To feature
- `-DMORPHTOKEN=OFF` - disable MorphToken feature
- `-DXMRIG=OFF` - disable XMRig feature - `-DXMRIG=OFF` - disable XMRig feature
- `-DTOR_BIN=/path/to/tor` - Embed a Tor executable inside Feather - `-DTOR_BIN=/path/to/tor` - Embed a Tor executable inside Feather
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests - `-DDONATE_BEG=OFF` - disable the dreaded donate requests
-1
View File
@@ -31,7 +31,6 @@ CMAKEFLAGS = \
-DBUILD_64=On \ -DBUILD_64=On \
-DBUILD_TESTS=Off \ -DBUILD_TESTS=Off \
-DXMRTO=On \ -DXMRTO=On \
-DMORPHTOKEN=On \
-DXMRIG=On \ -DXMRIG=On \
-DTOR_BIN=Off \ -DTOR_BIN=Off \
-DCMAKE_CXX_STANDARD=11 \ -DCMAKE_CXX_STANDARD=11 \
-4
View File
@@ -124,10 +124,6 @@ if(XMRTO)
target_compile_definitions(feather PRIVATE HAS_XMRTO=1) target_compile_definitions(feather PRIVATE HAS_XMRTO=1)
endif() endif()
if(MORPHTOKEN)
target_compile_definitions(feather PRIVATE HAS_MORPHTOKEN=1)
endif()
if(TOR_BIN) if(TOR_BIN)
target_compile_definitions(feather PRIVATE HAS_TOR_BIN=1) target_compile_definitions(feather PRIVATE HAS_TOR_BIN=1)
endif() endif()
-230
View File
@@ -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;
}
-47
View File
@@ -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
-423
View File
@@ -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>
+23 -16
View File
@@ -80,6 +80,8 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
this->accountName = Utils::getUnixAccountName(); this->accountName = Utils::getUnixAccountName();
this->homeDir = QDir::homePath(); this->homeDir = QDir::homePath();
QString walletDir = config()->get(Config::walletDirectory).toString();
if (walletDir.isEmpty()) {
#if defined(Q_OS_LINUX) or defined(Q_OS_MAC) #if defined(Q_OS_LINUX) or defined(Q_OS_MAC)
this->defaultWalletDir = QString("%1/Monero/wallets").arg(this->configRoot); this->defaultWalletDir = QString("%1/Monero/wallets").arg(this->configRoot);
this->defaultWalletDirRoot = QString("%1/Monero").arg(this->configRoot); this->defaultWalletDirRoot = QString("%1/Monero").arg(this->configRoot);
@@ -87,6 +89,10 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
this->defaultWalletDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Monero"; this->defaultWalletDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Monero";
this->defaultWalletDirRoot = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); this->defaultWalletDirRoot = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
#endif #endif
} else {
this->defaultWalletDir = walletDir;
this->defaultWalletDirRoot = walletDir;
}
// Create wallet dirs // Create wallet dirs
if (!QDir().mkpath(defaultWalletDir)) if (!QDir().mkpath(defaultWalletDir))
@@ -134,11 +140,11 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
auto genesis_timestamp = this->restoreHeights[NetworkType::Type::MAINNET]->data.firstKey(); auto genesis_timestamp = this->restoreHeights[NetworkType::Type::MAINNET]->data.firstKey();
AppContext::txFiatHistory = new TxFiatHistory(genesis_timestamp, this->configDirectory); AppContext::txFiatHistory = new TxFiatHistory(genesis_timestamp, this->configDirectory);
connect(this->ws, &WSClient::connectionEstablished, AppContext::txFiatHistory, &TxFiatHistory::onUpdateDatabase); 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(); QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1}})").arg(year).toUtf8();
this->ws->sendMsg(data); 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(); QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1, "month": %2}})").arg(year).arg(month).toUtf8();
this->ws->sendMsg(data); this->ws->sendMsg(data);
}); });
@@ -378,22 +384,22 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
auto changed = false; auto changed = false;
if(!this->heights.contains("mainnet")) { if(!this->heights.contains("mainnet")) {
this->heights["mainnet"] = (unsigned int) mainnet; this->heights["mainnet"] = mainnet;
changed = true; changed = true;
} }
else { else {
if (mainnet > this->heights["mainnet"]) { if (mainnet > this->heights["mainnet"]) {
this->heights["mainnet"] = (unsigned int) mainnet; this->heights["mainnet"] = mainnet;
changed = true; changed = true;
} }
} }
if(!this->heights.contains("stagenet")) { if(!this->heights.contains("stagenet")) {
this->heights["stagenet"] = (unsigned int) stagenet; this->heights["stagenet"] = stagenet;
changed = true; changed = true;
} }
else { else {
if (stagenet > this->heights["stagenet"]) { if (stagenet > this->heights["stagenet"]) {
this->heights["stagenet"] = (unsigned int) stagenet; this->heights["stagenet"] = stagenet;
changed = true; changed = true;
} }
} }
@@ -605,7 +611,7 @@ void AppContext::initRestoreHeights() {
restoreHeights[NetworkType::MAINNET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_monero_mainnet.txt", NetworkType::MAINNET); 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"); auto seed = this->currentWallet->getCacheAttribute("feather.seed");
if(!seed.isEmpty()) { if(!seed.isEmpty()) {
const auto msg = "This wallet has a 14 word mnemonic seed which has the restore height embedded."; 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) { if (!this->refreshed) {
refreshModels(); refreshModels();
this->refreshed = true; this->refreshed = true;
this->storeWallet(); // store wallet immediately upon finishing synchronization
this->currentWallet->store();
} }
qDebug() << "Wallet refresh status: " << success; qDebug() << "Wallet refresh status: " << success;
@@ -757,9 +764,11 @@ void AppContext::onTransactionCreated(PendingTransaction *tx, const QString &add
if(address == this->donationAddress) if(address == this->donationAddress)
this->donationSending = true; this->donationSending = true;
// Let UI know that the transaction was constructed
emit endTransaction();
// tx created, but not sent yet. ask user to verify first. // tx created, but not sent yet. ask user to verify first.
emit createTransactionSuccess(tx, address, mixin); emit createTransactionSuccess(tx, address, mixin);
emit endTransaction();
} }
void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid){ void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid){
@@ -790,16 +799,14 @@ void AppContext::storeWallet() {
} }
void AppContext::updateBalance() { void AppContext::updateBalance() {
if(!this->currentWallet) if (!this->currentWallet)
return; return;
AppContext::balance = this->currentWallet->balance() / globals::cdiv; quint64 balance_u = this->currentWallet->balance();
auto balance_str = QString::number(balance, 'f'); AppContext::balance = balance_u / globals::cdiv;
double spendable = this->currentWallet->unlockedBalance();
double unlocked = this->currentWallet->unlockedBalance() / globals::cdiv; emit balanceUpdated(balance_u, spendable);
auto unlocked_str = QString::number(unlocked, 'f');
emit balanceUpdated(balance, unlocked, balance_str, unlocked_str);
} }
void AppContext::syncStatusUpdated(quint64 height, quint64 target) { void AppContext::syncStatusUpdated(quint64 height, quint64 target) {
+7 -8
View File
@@ -66,9 +66,9 @@ public:
static void createConfigDirectory(const QString &dir) ; static void createConfigDirectory(const QString &dir) ;
QMap<QString, unsigned int> heights; QMap<QString, int> heights;
QMap<NetworkType::Type, RestoreHeightLookup*> restoreHeights; QMap<NetworkType::Type, RestoreHeightLookup*> restoreHeights;
const unsigned int kdfRounds = 1; const quint64 kdfRounds = 1;
PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low; PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low;
quint32 tx_mixin = static_cast<const quint32 &>(10); quint32 tx_mixin = static_cast<const quint32 &>(10);
QString seedLanguage = "English"; // 14 word `monero-seed` only has English QString seedLanguage = "English"; // 14 word `monero-seed` only has English
@@ -90,7 +90,6 @@ public:
static TxFiatHistory *txFiatHistory; static TxFiatHistory *txFiatHistory;
// libwalletqt // libwalletqt
unsigned int blockHeight = 0;
bool refreshed = false; bool refreshed = false;
WalletManager *walletManager; WalletManager *walletManager;
Wallet *currentWallet = nullptr; Wallet *currentWallet = nullptr;
@@ -116,7 +115,7 @@ public slots:
void onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) const; void onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) const;
void onCreateTransactionError(const QString &msg); void onCreateTransactionError(const QString &msg);
void onOpenAliasResolve(const QString &openAlias); void onOpenAliasResolve(const QString &openAlias);
void onSetRestoreHeight(unsigned int height); void onSetRestoreHeight(quint64 height);
void onPreferredFiatCurrencyChanged(const QString &symbol); void onPreferredFiatCurrencyChanged(const QString &symbol);
private slots: private slots:
@@ -138,11 +137,11 @@ private slots:
void onConnectionStatusChanged(int status); void onConnectionStatusChanged(int status);
signals: 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 blockchainSync(int height, int target);
void refreshSync(int height, int target); void refreshSync(int height, int target);
void synchronized(); void synchronized();
void blockHeightWSUpdated(QMap<QString, unsigned int> heights); void blockHeightWSUpdated(QMap<QString, int> heights);
void walletSynchronized(); void walletSynchronized();
void walletOpened(); void walletOpened();
void walletClosed(); void walletClosed();
@@ -164,7 +163,7 @@ signals:
void openAliasResolveError(const QString &msg); void openAliasResolveError(const QString &msg);
void openAliasResolved(const QString &address, const QString &openAlias); void openAliasResolved(const QString &address, const QString &openAlias);
void setRestoreHeightError(const QString &msg); void setRestoreHeightError(const QString &msg);
void customRestoreHeightSet(unsigned int height); void customRestoreHeightSet(int height);
void closeApplication(); void closeApplication();
void donationNag(); void donationNag();
void initiateTransaction(); void initiateTransaction();
@@ -173,7 +172,7 @@ signals:
void setTitle(const QString &title); // set window title void setTitle(const QString &title); // set window title
private: private:
const unsigned int m_donationBoundary = 15; const int m_donationBoundary = 15;
UtilsNetworking *m_utilsNetworkingNodes; UtilsNetworking *m_utilsNetworkingNodes;
QTimer *m_storeTimer = new QTimer(this); QTimer *m_storeTimer = new QTimer(this);
QUrl m_wsUrl = QUrl(QStringLiteral("ws://7e6egbawekbkxzkv4244pqeqgoo4axko2imgjbedwnn6s5yb6b7oliqd.onion/ws")); QUrl m_wsUrl = QUrl(QStringLiteral("ws://7e6egbawekbkxzkv4244pqeqgoo4axko2imgjbedwnn6s5yb6b7oliqd.onion/ws"));
+3 -2
View File
@@ -11,6 +11,9 @@
<file>assets/images/appicons/128x128.png</file> <file>assets/images/appicons/128x128.png</file>
<file>assets/images/appicons/256x256.png</file> <file>assets/images/appicons/256x256.png</file>
<file>assets/images/arrow.svg</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/bitcoin.png</file>
<file>assets/images/camera_dark.png</file> <file>assets/images/camera_dark.png</file>
<file>assets/images/camera_white.png</file> <file>assets/images/camera_white.png</file>
@@ -48,11 +51,9 @@
<file>assets/images/lock.svg</file> <file>assets/images/lock.svg</file>
<file>assets/images/microphone.png</file> <file>assets/images/microphone.png</file>
<file>assets/images/mining.png</file> <file>assets/images/mining.png</file>
<file>assets/images/morphtoken.png</file>
<file>assets/images/network.png</file> <file>assets/images/network.png</file>
<file>assets/images/offline_tx.png</file> <file>assets/images/offline_tx.png</file>
<file>assets/images/person.svg</file> <file>assets/images/person.svg</file>
<file>assets/images/photos/1.png</file>
<file>assets/images/preferences.png</file> <file>assets/images/preferences.png</file>
<file>assets/images/preferences.svg</file> <file>assets/images/preferences.svg</file>
<file>assets/images/qrcode.png</file> <file>assets/images/qrcode.png</file>
+4 -2
View File
@@ -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. 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. 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
View File
@@ -5,17 +5,17 @@
#include "ui_contactswidget.h" #include "ui_contactswidget.h"
#include "dialog/contactsdialog.h" #include "dialog/contactsdialog.h"
#include "model/ModelUtils.h" #include "model/ModelUtils.h"
#include "utils/utils.h" #include "mainwindow.h"
#include "libwalletqt/AddressBook.h"
#include <QClipboard> #include <QMessageBox>
#include <QDebug>
#include <QKeyEvent>
ContactsWidget::ContactsWidget(QWidget *parent) : ContactsWidget::ContactsWidget(QWidget *parent) :
QWidget(parent), QWidget(parent),
ui(new Ui::ContactsWidget) ui(new Ui::ContactsWidget)
{ {
ui->setupUi(this); ui->setupUi(this);
m_ctx = MainWindow::getContext();
// header context menu // header context menu
ui->contacts->header()->setContextMenuPolicy(Qt::CustomContextMenu); ui->contacts->header()->setContextMenuPolicy(Qt::CustomContextMenu);
@@ -28,15 +28,16 @@ ContactsWidget::ContactsWidget(QWidget *parent) :
// context menu // context menu
ui->contacts->setContextMenuPolicy(Qt::CustomContextMenu); ui->contacts->setContextMenuPolicy(Qt::CustomContextMenu);
m_contextMenu = new QMenu(ui->contacts); 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 // row context menu
m_rowMenu = new QMenu(ui->contacts); 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 address", this, &ContactsWidget::copyAddress);
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy name", this, &ContactsWidget::copyName); 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_rowMenu->addAction("Pay to", this, &ContactsWidget::payTo);
m_deleteEntryAction = m_rowMenu->addAction("Delete", this, &ContactsWidget::deleteContact); m_rowMenu->addAction("Delete", this, &ContactsWidget::deleteContact);
connect(ui->contacts, &QTreeView::customContextMenuRequested, [=](const QPoint & point){ connect(ui->contacts, &QTreeView::customContextMenuRequested, [=](const QPoint & point){
QModelIndex index = ui->contacts->indexAt(point); QModelIndex index = ui->contacts->indexAt(point);
@@ -95,16 +96,44 @@ void ContactsWidget::showHeaderMenu(const QPoint& position)
m_headerMenu->exec(QCursor::pos()); 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(); int ret = dialog->exec();
if (!ret) return; if (!ret) return;
QString address = dialog->getAddress(); address = dialog->getAddress();
QString name = dialog->getName(); 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() void ContactsWidget::deleteContact()
+5 -5
View File
@@ -6,6 +6,7 @@
#include "model/AddressBookModel.h" #include "model/AddressBookModel.h"
#include "model/AddressBookProxyModel.h" #include "model/AddressBookProxyModel.h"
#include "appcontext.h"
#include <QWidget> #include <QWidget>
#include <QMenu> #include <QMenu>
@@ -21,29 +22,28 @@ class ContactsWidget : public QWidget
public: public:
explicit ContactsWidget(QWidget *parent = nullptr); explicit ContactsWidget(QWidget *parent = nullptr);
void setModel(AddressBookModel * model); void setModel(AddressBookModel * model);
~ContactsWidget(); ~ContactsWidget() override;
public slots: public slots:
void copyAddress(); void copyAddress();
void copyName(); void copyName();
void payTo(); void payTo();
void newContact(); void newContact(QString address = "", QString name = "");
void deleteContact(); void deleteContact();
void setShowFullAddresses(bool show); void setShowFullAddresses(bool show);
void setSearchFilter(const QString &filter); void setSearchFilter(const QString &filter);
signals: signals:
void addContact(QString &address, QString &name);
void fillAddress(QString &address); void fillAddress(QString &address);
private slots: private slots:
void showHeaderMenu(const QPoint& position); void showHeaderMenu(const QPoint &position);
private: private:
Ui::ContactsWidget *ui; Ui::ContactsWidget *ui;
AppContext *m_ctx;
QAction *m_showFullAddressesAction; QAction *m_showFullAddressesAction;
QAction *m_deleteEntryAction;
QMenu *m_rowMenu; QMenu *m_rowMenu;
QMenu *m_contextMenu; QMenu *m_contextMenu;
QMenu *m_headerMenu; QMenu *m_headerMenu;
+27
View File
@@ -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;
}
+27
View File
@@ -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
+142
View File
@@ -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>
+7 -1
View File
@@ -4,13 +4,19 @@
#include "ui_contactsdialog.h" #include "ui_contactsdialog.h"
#include "contactsdialog.h" #include "contactsdialog.h"
ContactsDialog::ContactsDialog(QWidget *parent) ContactsDialog::ContactsDialog(QWidget *parent, const QString &address, const QString &name)
: QDialog(parent) : QDialog(parent)
, ui(new Ui::ContactsDialog) , ui(new Ui::ContactsDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
setMinimumWidth(400); setMinimumWidth(400);
ui->lineEdit_address->setText(address);
ui->lineEdit_name->setText(name);
if (!name.isEmpty()) {
ui->lineEdit_name->setFocus();
}
connect(ui->buttonBox, &QDialogButtonBox::accepted, [&](){ connect(ui->buttonBox, &QDialogButtonBox::accepted, [&](){
m_address = ui->lineEdit_address->text(); m_address = ui->lineEdit_address->text();
m_name = ui->lineEdit_name->text(); m_name = ui->lineEdit_name->text();
+1 -1
View File
@@ -15,7 +15,7 @@ class ContactsDialog : public QDialog
Q_OBJECT Q_OBJECT
public: public:
explicit ContactsDialog(QWidget *parent = nullptr); explicit ContactsDialog(QWidget *parent = nullptr, const QString &address = "", const QString &name = "");
~ContactsDialog() override; ~ContactsDialog() override;
QString getAddress(); QString getAddress();
+2 -1
View File
@@ -57,11 +57,12 @@ void DebugInfoDialog::updateInfo() {
ui->label_remoteNode->setText(node.full); ui->label_remoteNode->setText(node.full);
ui->label_walletStatus->setText(this->statusToString(m_ctx->currentWallet->connectionStatus())); ui->label_walletStatus->setText(this->statusToString(m_ctx->currentWallet->connectionStatus()));
ui->label_torStatus->setText(torStatus); 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_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_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_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(); QString os = QSysInfo::prettyProductName();
if (m_ctx->isTails) { if (m_ctx->isTails) {
+19 -5
View File
@@ -258,14 +258,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="19" column="0"> <item row="20" column="0">
<widget class="QLabel" name="label_24"> <widget class="QLabel" name="label_24">
<property name="text"> <property name="text">
<string>Timestamp:</string> <string>Timestamp:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="19" column="1"> <item row="20" column="1">
<widget class="QLabel" name="label_timestamp"> <widget class="QLabel" name="label_timestamp">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@@ -275,14 +275,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="18" column="0"> <item row="19" column="0">
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
<property name="text"> <property name="text">
<string>Operating system:</string> <string>Operating system:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="18" column="1"> <item row="19" column="1">
<widget class="QLabel" name="label_OS"> <widget class="QLabel" name="label_OS">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@@ -292,7 +292,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="17" column="1"> <item row="18" column="1">
<widget class="Line" name="line_4"> <widget class="Line" name="line_4">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@@ -316,6 +316,20 @@
</property> </property>
</widget> </widget>
</item> </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> </layout>
</item> </item>
<item> <item>
+1 -1
View File
@@ -24,7 +24,7 @@ RestoreDialog::RestoreDialog(AppContext *ctx, QWidget *parent)
} }
} }
unsigned int RestoreDialog::getHeight() { int RestoreDialog::getHeight() {
return ui->restoreHeightWidget->getHeight(); return ui->restoreHeightWidget->getHeight();
} }
+1 -1
View File
@@ -24,7 +24,7 @@ Q_OBJECT
public: public:
explicit RestoreDialog(AppContext *ctx, QWidget *parent = nullptr); explicit RestoreDialog(AppContext *ctx, QWidget *parent = nullptr);
void initRestoreHeights(RestoreHeightLookup *lookup); void initRestoreHeights(RestoreHeightLookup *lookup);
unsigned int getHeight(); int getHeight();
~RestoreDialog() override; ~RestoreDialog() override;
signals: signals:
+1 -3
View File
@@ -49,9 +49,7 @@ TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *tx
QString destinations = txInfo->destinations_formatted(); QString destinations = txInfo->destinations_formatted();
if (destinations.isEmpty()) { if (destinations.isEmpty()) {
ui->destinations->setHidden(true); ui->frameDestinations->hide();
ui->label_destinations->setHidden(true);
ui->line_2->setHidden(true);
} else { } else {
ui->destinations->setText(destinations); ui->destinations->setText(destinations);
} }
+24
View File
@@ -131,6 +131,27 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QFrame" name="frameDestinations">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<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> <item>
<widget class="Line" name="line_2"> <widget class="Line" name="line_2">
<property name="orientation"> <property name="orientation">
@@ -158,6 +179,9 @@
</property> </property>
</widget> </widget>
</item> </item>
</layout>
</widget>
</item>
<item> <item>
<widget class="Line" name="line_3"> <widget class="Line" name="line_3">
<property name="orientation"> <property name="orientation">
+13 -7
View File
@@ -32,16 +32,22 @@ TxConfDialog::TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QStrin
}; };
QString amount = WalletManager::displayAmount(tx->amount()); 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 = 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()); 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); 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->setText(ModelUtils::displayAddress(address, 2));
ui->label_address->setFont(ModelUtils::getMonospaceFont()); ui->label_address->setFont(ModelUtils::getMonospaceFont());
+1 -1
View File
@@ -55,7 +55,7 @@ void HistoryWidget::showContextMenu(const QPoint &point) {
unconfirmed = tInfo.isFailed() || tInfo.isPending(); 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); menu.addAction(QIcon(":/assets/images/info.png"), "Resend transaction", this, &HistoryWidget::onResendTransaction);
} }
+1 -1
View File
@@ -39,7 +39,7 @@ namespace {
static const int DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS = 30; static const int DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS = 30;
static const int WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS = 5; 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) Wallet::Wallet(QObject * parent)
+65 -38
View File
@@ -21,12 +21,14 @@
#include "dialog/broadcasttxdialog.h" #include "dialog/broadcasttxdialog.h"
#include "dialog/tximportdialog.h" #include "dialog/tximportdialog.h"
#include "dialog/passworddialog.h" #include "dialog/passworddialog.h"
#include "dialog/balancedialog.h"
#include "utils/utils.h" #include "utils/utils.h"
#include "utils/config.h" #include "utils/config.h"
#include "utils/daemonrpc.h" #include "utils/daemonrpc.h"
#include "components.h" #include "components.h"
#include "calcwindow.h" #include "calcwindow.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "globals.h"
// libwalletqt // libwalletqt
#include "libwalletqt/WalletManager.h" #include "libwalletqt/WalletManager.h"
@@ -79,7 +81,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
connected ? m_statusBtnTor->setIcon(QIcon(":/assets/images/tor_logo.png")) connected ? m_statusBtnTor->setIcon(QIcon(":/assets/images/tor_logo.png"))
: m_statusBtnTor->setIcon(QIcon(":/assets/images/tor_logo_disabled.png"));}); : m_statusBtnTor->setIcon(QIcon(":/assets/images/tor_logo_disabled.png"));});
connect(m_ctx->nodes, &Nodes::updateStatus, [=](const QString &msg){ connect(m_ctx->nodes, &Nodes::updateStatus, [=](const QString &msg){
m_statusLabelStatus->setText(msg); this->setStatusText(msg);
}); });
// menu connects // menu connects
@@ -121,10 +123,6 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
ui->tabExchanges->setTabVisible(0, false); ui->tabExchanges->setTabVisible(0, false);
#endif #endif
#ifndef HAS_MORPHTOKEN
ui->tabExchanges->setTabVisible(1, false);
#endif
#if defined(Q_OS_LINUX) #if defined(Q_OS_LINUX)
// system tray // system tray
m_trayIcon = new QSystemTrayIcon(QIcon(":/assets/images/appicons/64x64.png")); 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->xmrToWidget, &XMRToWidget::onWalletClosed);
connect(this, &MainWindow::walletClosed, ui->sendWidget, &SendWidget::onWalletClosed); connect(this, &MainWindow::walletClosed, ui->sendWidget, &SendWidget::onWalletClosed);
connect(m_ctx, &AppContext::balanceUpdated, this, &MainWindow::onBalanceUpdated); 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::walletOpened, this, &MainWindow::onWalletOpened);
connect(m_ctx, &AppContext::walletClosed, this, QOverload<>::of(&MainWindow::onWalletClosed)); connect(m_ctx, &AppContext::walletClosed, this, QOverload<>::of(&MainWindow::onWalletClosed));
connect(m_ctx, &AppContext::walletOpenedError, this, &MainWindow::onWalletOpenedError); 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::initiateTransaction, ui->xmrToWidget, &XMRToWidget::onInitiateTransaction);
connect(m_ctx, &AppContext::endTransaction, ui->xmrToWidget, &XMRToWidget::onEndTransaction); 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 // testnet/stagenet warning
auto worthlessWarning = QString("Feather wallet is currently running in %1 mode. This is meant " auto worthlessWarning = QString("Feather wallet is currently running in %1 mode. This is meant "
"for developers only. Your coins are WORTHLESS."); "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); connect(ui->historyWidget, &HistoryWidget::resendTransaction, this, &MainWindow::onResendTransaction);
// Contacts // Contacts
connect(ui->contactWidget, &ContactsWidget::addContact, this, &MainWindow::onAddContact);
connect(ui->contactWidget, &ContactsWidget::fillAddress, ui->sendWidget, &SendWidget::fillAddress); connect(ui->contactWidget, &ContactsWidget::fillAddress, ui->sendWidget, &SendWidget::fillAddress);
// Open alias // Open alias
@@ -400,7 +412,7 @@ void MainWindow::initMenu() {
m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc); m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc);
m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc"); 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)); 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_tabShowHideMapper["Exchange"] = new ToggleTab(ui->tabExchange, "Exchange", "Exchange", ui->actionShow_Exchange, Config::showTabExchange);
m_tabShowHideSignalMapper->setMapping(ui->actionShow_Exchange, "Exchange"); m_tabShowHideSignalMapper->setMapping(ui->actionShow_Exchange, "Exchange");
@@ -447,7 +459,7 @@ void MainWindow::initMenu() {
// set restore height // set restore height
connect(ui->actionChange_restore_height, &QAction::triggered, this, &MainWindow::showRestoreHeightDialog); 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. " 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); "Please re-open the wallet. Feather will now quit.").arg(height);
QMessageBox::information(this, "Cannot set custom restore height", msg); QMessageBox::information(this, "Cannot set custom restore height", msg);
@@ -518,6 +530,7 @@ void MainWindow::initWidgets() {
WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){ WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){
auto *wizard = new WalletWizard(m_ctx, startPage, this); auto *wizard = new WalletWizard(m_ctx, startPage, this);
connect(wizard, &WalletWizard::openWallet, m_ctx, &AppContext::onOpenWallet); connect(wizard, &WalletWizard::openWallet, m_ctx, &AppContext::onOpenWallet);
connect(wizard, &WalletWizard::defaultWalletDirChanged, m_windowSettings, &Settings::updatePaths);
return wizard; return wizard;
} }
@@ -605,9 +618,9 @@ void MainWindow::onWalletOpened() {
this->activateWindow(); this->activateWindow();
this->setEnabled(true); this->setEnabled(true);
if(!m_ctx->tor->torConnected) 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 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); connect(m_ctx->currentWallet, &Wallet::connectionStatusChanged, this, &MainWindow::onConnectionStatusChanged);
@@ -649,40 +662,47 @@ void MainWindow::onWalletOpened() {
this->touchbarShowWallet(); this->touchbarShowWallet();
this->updatePasswordIcon(); 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; qDebug() << Q_FUNC_INFO;
bool hide = config()->get(Config::hideBalance).toBool(); bool hide = config()->get(Config::hideBalance).toBool();
auto label_str = QString("Balance: %1 XMR").arg(unlocked_str); QString label_str = QString("Balance: %1 XMR").arg(QString::number(spendable / globals::cdiv, 'f'));
if(balance > unlocked) if (balance > spendable)
label_str += QString(" (+%1 XMR unconfirmed)").arg(QString::number(balance - unlocked, 'f')); label_str += QString(" (+%1 XMR unconfirmed)").arg(QString::number((balance - spendable) / globals::cdiv, 'f'));
if (hide) { if (hide)
label_str = "Balance: HIDDEN"; label_str = "Balance: HIDDEN";
}
m_statusLabelBalance->setToolTip("Click for details");
m_statusLabelBalance->setText(label_str); m_statusLabelBalance->setText(label_str);
m_balanceWidget->setHidden(hide); m_balanceWidget->setHidden(hide);
} }
void MainWindow::setStatusText(const QString &text) {
m_statusText = text;
if (!m_constructingTransaction)
m_statusLabelStatus->setText(text);
}
void MainWindow::onSynchronized() { void MainWindow::onSynchronized() {
this->updateNetStats(); this->updateNetStats();
m_statusLabelStatus->setText("Synchronized"); this->setStatusText("Synchronized");
this->onConnectionStatusChanged(Wallet::ConnectionStatus_Connected); this->onConnectionStatusChanged(Wallet::ConnectionStatus_Connected);
} }
void MainWindow::onBlockchainSync(int height, int target) { void MainWindow::onBlockchainSync(int height, int target) {
QString heightText = QString("Blockchain sync: %1/%2").arg(height).arg(target); QString blocks = (target >= height) ? QString::number(target - height) : "?";
m_statusLabelStatus->setText(heightText); QString heightText = QString("Blockchain sync: %1 blocks remaining").arg(blocks);
this->setStatusText(heightText);
} }
void MainWindow::onRefreshSync(int height, int target) { void MainWindow::onRefreshSync(int height, int target) {
QString heightText = QString("Wallet refresh: %1/%2").arg(height).arg(target); QString blocks = (target >= height) ? QString::number(target - height) : "?";
m_statusLabelStatus->setText(heightText); QString heightText = QString("Wallet refresh: %1 blocks remaining").arg(blocks);
this->updateNetStats(); this->setStatusText(heightText);
} }
void MainWindow::onConnectionStatusChanged(int status) void MainWindow::onConnectionStatusChanged(int status)
@@ -696,7 +716,7 @@ void MainWindow::onConnectionStatusChanged(int status)
switch(status){ switch(status){
case Wallet::ConnectionStatus_Disconnected: case Wallet::ConnectionStatus_Disconnected:
statusIcon = ":/assets/images/status_disconnected.svg"; statusIcon = ":/assets/images/status_disconnected.svg";
m_statusLabelStatus->setText("Disconnected"); this->setStatusText("Disconnected");
break; break;
case Wallet::ConnectionStatus_Connected: case Wallet::ConnectionStatus_Connected:
if (m_ctx->currentWallet->synchronized()) { if (m_ctx->currentWallet->synchronized()) {
@@ -707,11 +727,11 @@ void MainWindow::onConnectionStatusChanged(int status)
break; break;
case Wallet::ConnectionStatus_Connecting: case Wallet::ConnectionStatus_Connecting:
statusIcon = ":/assets/images/status_lagging.svg"; statusIcon = ":/assets/images/status_lagging.svg";
m_statusLabelStatus->setText("Connecting to daemon"); this->setStatusText("Connecting to daemon");
break; break;
case Wallet::ConnectionStatus_WrongVersion: case Wallet::ConnectionStatus_WrongVersion:
statusIcon = ":/assets/images/status_disconnected.svg"; statusIcon = ":/assets/images/status_disconnected.svg";
m_statusLabelStatus->setText("Incompatible daemon"); this->setStatusText("Incompatible daemon");
break; break;
default: default:
statusIcon = ":/assets/images/status_disconnected.svg"; statusIcon = ":/assets/images/status_disconnected.svg";
@@ -813,9 +833,11 @@ void MainWindow::create_status_bar() {
m_statusLabelNetStats->setTextInteractionFlags(Qt::TextSelectableByMouse); m_statusLabelNetStats->setTextInteractionFlags(Qt::TextSelectableByMouse);
this->statusBar()->addWidget(m_statusLabelNetStats); 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); m_statusLabelBalance->setTextInteractionFlags(Qt::TextSelectableByMouse);
this->statusBar()->addPermanentWidget(m_statusLabelBalance); 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"); m_statusBtnConnectionStatusIndicator = new StatusBarButton(QIcon(":/assets/images/status_disconnected.svg"), "Connection status");
connect(m_statusBtnConnectionStatusIndicator, &StatusBarButton::clicked, this, &MainWindow::showConnectionStatusDialog); connect(m_statusBtnConnectionStatusIndicator, &StatusBarButton::clicked, this, &MainWindow::showConnectionStatusDialog);
@@ -1066,16 +1088,6 @@ void MainWindow::onResendTransaction(const QString &txid) {
dialog->deleteLater(); 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() { void MainWindow::importContacts() {
const QString targetFile = QFileDialog::getOpenFileName(this, "Import CSV file", QDir::homePath(), "CSV Files (*.csv)"); const QString targetFile = QFileDialog::getOpenFileName(this, "Import CSV file", QDir::homePath(), "CSV Files (*.csv)");
if(targetFile.isEmpty()) return; 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() { MainWindow::~MainWindow() {
delete ui; delete ui;
} }
+10 -6
View File
@@ -118,7 +118,6 @@ public slots:
void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path); void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
void onViewOnBlockExplorer(const QString &txid); void onViewOnBlockExplorer(const QString &txid);
void onResendTransaction(const QString &txid); void onResendTransaction(const QString &txid);
void onAddContact(const QString &address, const QString &name);
void importContacts(); void importContacts();
void showRestoreHeightDialog(); void showRestoreHeightDialog();
void importTransaction(); void importTransaction();
@@ -134,7 +133,7 @@ public slots:
void loadSignedTxFromText(); void loadSignedTxFromText();
// libwalletqt // libwalletqt
void onBalanceUpdated(double balance, double unlocked, const QString &balance_str, const QString &unlocked_str); void onBalanceUpdated(quint64 balance, quint64 spendable);
void onSynchronized(); void onSynchronized();
void onWalletOpened(); void onWalletOpened();
void onWalletClosed(); void onWalletClosed();
@@ -169,19 +168,19 @@ private:
void updatePasswordIcon(); void updatePasswordIcon();
void updateNetStats(); void updateNetStats();
void rescanSpent(); void rescanSpent();
void setStatusText(const QString &text);
void showBalanceDialog();
QString statusDots();
WalletWizard *createWizard(WalletWizard::Page startPage); WalletWizard *createWizard(WalletWizard::Page startPage);
Ui::MainWindow *ui; Ui::MainWindow *ui;
Settings *m_windowSettings = nullptr; Settings *m_windowSettings = nullptr;
CalcWindow *m_windowCalc = nullptr; CalcWindow *m_windowCalc = nullptr;
SignVerifyDialog *m_windowSignVerify = nullptr;
RestoreDialog *m_restoreDialog = nullptr; RestoreDialog *m_restoreDialog = nullptr;
AboutDialog *m_aboutDialog = nullptr; AboutDialog *m_aboutDialog = nullptr;
XMRigWidget *m_xmrig = nullptr; XMRigWidget *m_xmrig = nullptr;
bool m_windowSpawned = false;
QSystemTrayIcon *m_trayIcon; QSystemTrayIcon *m_trayIcon;
QMenu m_trayMenu; QMenu m_trayMenu;
QAction *m_trayActionCalc; QAction *m_trayActionCalc;
@@ -193,7 +192,7 @@ private:
TickerWidget *m_balanceWidget; TickerWidget *m_balanceWidget;
// lower status bar // lower status bar
QLabel *m_statusLabelBalance; ClickableLabel *m_statusLabelBalance;
QLabel *m_statusLabelStatus; QLabel *m_statusLabelStatus;
QLabel *m_statusLabelNetStats; QLabel *m_statusLabelNetStats;
StatusBarButton *m_statusBtnConnectionStatusIndicator; StatusBarButton *m_statusBtnConnectionStatusIndicator;
@@ -219,6 +218,11 @@ private:
QTimer m_updateBytes; QTimer m_updateBytes;
QString m_statusText;
int m_statusDots;
bool m_constructingTransaction = false;
QTimer m_txTimer;
private slots: private slots:
void menuToggleTabVisible(const QString &key); void menuToggleTabVisible(const QString &key);
}; };
-32
View File
@@ -324,32 +324,6 @@
</item> </item>
</layout> </layout>
</widget> </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> </widget>
</item> </item>
</layout> </layout>
@@ -784,12 +758,6 @@
<header>calcwidget.h</header> <header>calcwidget.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>MorphTokenWidget</class>
<extends>QWidget</extends>
<header>MorphTokenWidget.h</header>
<container>1</container>
</customwidget>
<customwidget> <customwidget>
<class>CCSWidget</class> <class>CCSWidget</class>
<extends>QWidget</extends> <extends>QWidget</extends>
-2
View File
@@ -22,8 +22,6 @@ void CCSModel::updateEntries(const QList<QSharedPointer<CCSEntry>>& entries) {
m_entries.clear(); m_entries.clear();
for (const auto& entry : entries) { for (const auto& entry : entries) {
if (entry->raised_amount > entry->target_amount)
continue;
m_entries.push_back(entry); m_entries.push_back(entry);
} }
+1 -1
View File
@@ -5,7 +5,7 @@
#include <utils/nodes.h> #include <utils/nodes.h>
#include "appcontext.h" #include "appcontext.h"
NodeModel::NodeModel(unsigned int nodeSource, QObject *parent) NodeModel::NodeModel(int nodeSource, QObject *parent)
: QAbstractTableModel(parent) : QAbstractTableModel(parent)
, m_nodeSource(nodeSource) , m_nodeSource(nodeSource)
, m_offline(QIcon(":/assets/images/expired_icon.png")) , m_offline(QIcon(":/assets/images/expired_icon.png"))
+2 -2
View File
@@ -19,7 +19,7 @@ public:
COUNT COUNT
}; };
explicit NodeModel(unsigned int nodeSource, QObject *parent = nullptr); explicit NodeModel(int nodeSource, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override;
@@ -34,7 +34,7 @@ private:
QList<FeatherNode> m_nodes; QList<FeatherNode> m_nodes;
QIcon m_offline; QIcon m_offline;
QIcon m_online; QIcon m_online;
unsigned int m_nodeSource; int m_nodeSource;
}; };
#endif //FEATHER_NODEMODEL_H #endif //FEATHER_NODEMODEL_H
+1 -1
View File
@@ -169,7 +169,7 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tI
usd_amount = AppContext::prices->convert("USD", this->preferredFiatSymbol, usd_amount); usd_amount = AppContext::prices->convert("USD", this->preferredFiatSymbol, usd_amount);
double fiat_rounded = ceil(Utils::roundSignificant(usd_amount, 3) * 100.0) / 100.0; 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: default:
{ {
+19
View File
@@ -12,6 +12,7 @@ SendWidget::SendWidget(QWidget *parent) :
ui(new Ui::SendWidget) ui(new Ui::SendWidget)
{ {
ui->setupUi(this); ui->setupUi(this);
m_ctx = MainWindow::getContext();
QString amount_rx = R"(^\d{0,8}[\.,]\d{0,12}|(all)$)"; QString amount_rx = R"(^\d{0,8}[\.,]\d{0,12}|(all)$)";
QRegExp rx; QRegExp rx;
@@ -30,6 +31,16 @@ SendWidget::SendWidget(QWidget *parent) :
ui->label_conversionAmount->hide(); ui->label_conversionAmount->hide();
ui->btn_openAlias->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(); this->setupComboBox();
} }
@@ -70,6 +81,13 @@ void SendWidget::fillAddress(const QString &address) {
} }
void SendWidget::sendClicked() { 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; double amount;
QString currency = ui->comboCurrencySelection->currentText(); QString currency = ui->comboCurrencySelection->currentText();
QString recipient = ui->lineAddress->text().simplified().remove(' '); QString recipient = ui->lineAddress->text().simplified().remove(' ');
@@ -173,6 +191,7 @@ void SendWidget::clearFields() {
void SendWidget::onWalletClosed() { void SendWidget::onWalletClosed() {
this->clearFields(); this->clearFields();
ui->btnSend->setEnabled(true);
} }
void SendWidget::onInitiateTransaction() { void SendWidget::onInitiateTransaction() {
+11 -4
View File
@@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>603</width> <width>647</width>
<height>175</height> <height>175</height>
</rect> </rect>
</property> </property>
@@ -39,7 +39,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_2"> <widget class="HelpLabel" name="label_PayTo">
<property name="text"> <property name="text">
<string>Pay to</string> <string>Pay to</string>
</property> </property>
@@ -53,7 +53,7 @@
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label"> <widget class="HelpLabel" name="label_Description">
<property name="text"> <property name="text">
<string>Description</string> <string>Description</string>
</property> </property>
@@ -63,7 +63,7 @@
<widget class="QLineEdit" name="lineDescription"/> <widget class="QLineEdit" name="lineDescription"/>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label_3"> <widget class="HelpLabel" name="label_Amount">
<property name="text"> <property name="text">
<string>Amount</string> <string>Amount</string>
</property> </property>
@@ -184,6 +184,13 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>HelpLabel</class>
<extends>QLabel</extends>
<header>components.h</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections/> <connections/>
<slots> <slots>
+18 -6
View File
@@ -7,6 +7,8 @@
#include "utils/config.h" #include "utils/config.h"
#include "mainwindow.h" #include "mainwindow.h"
#include <QFileDialog>
Settings::Settings(QWidget *parent) : Settings::Settings(QWidget *parent) :
QDialog(parent), QDialog(parent),
ui(new Ui::Settings) ui(new Ui::Settings)
@@ -58,16 +60,26 @@ Settings::Settings(QWidget *parent) :
connect(ui->comboBox_fiatCurrency, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::fiatCurrencySelected); connect(ui->comboBox_fiatCurrency, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::fiatCurrencySelected);
// setup path tab // setup paths tab
#if defined(Q_OS_LINUX) this->updatePaths();
ui->textEditPaths->appendPlainText(m_ctx->applicationPath); connect(ui->btn_browseDefaultWalletDir, &QPushButton::clicked, [this]{
#endif QString walletDir = QFileDialog::getExistingDirectory(this, "Select wallet directory ", m_ctx->defaultWalletDir, QFileDialog::ShowDirsOnly);
ui->textEditPaths->appendPlainText(m_ctx->configDirectory); if (walletDir.isEmpty()) return;
ui->textEditPaths->appendPlainText(m_ctx->defaultWalletDir); m_ctx->defaultWalletDir = walletDir;
m_ctx->defaultWalletDirRoot = walletDir;
config()->set(Config::walletDirectory, walletDir);
ui->lineEdit_defaultWalletDir->setText(m_ctx->defaultWalletDir);
});
this->adjustSize(); 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) { void Settings::fiatCurrencySelected(int index) {
QString selection = ui->comboBox_fiatCurrency->itemText(index); QString selection = ui->comboBox_fiatCurrency->itemText(index);
config()->set(Config::preferredFiatCurrency, selection); config()->set(Config::preferredFiatCurrency, selection);
+1
View File
@@ -31,6 +31,7 @@ signals:
void blockExplorerChanged(QString blockExplorer); void blockExplorerChanged(QString blockExplorer);
public slots: public slots:
void updatePaths();
void copyToClipboard(); void copyToClipboard();
void checkboxExternalLinkWarn(); void checkboxExternalLinkWarn();
void fiatCurrencySelected(int index); void fiatCurrencySelected(int index);
+68 -5
View File
@@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1019</width> <width>1123</width>
<height>336</height> <height>555</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -282,12 +282,75 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPlainTextEdit" name="textEditPaths"> <layout class="QFormLayout" name="formLayout_2">
<property name="readOnly"> <item row="0" column="1">
<bool>true</bool> <layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="lineEdit_defaultWalletDir">
<property name="enabled">
<bool>false</bool>
</property> </property>
</widget> </widget>
</item> </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>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab_console"> <widget class="QWidget" name="tab_console">
-93
View File
@@ -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));
}
-51
View File
@@ -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
View File
@@ -19,7 +19,7 @@
using namespace std::chrono; 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_fileName(info.fileName()),
m_modified(info.lastModified().toSecsSinceEpoch()), m_modified(info.lastModified().toSecsSinceEpoch()),
m_path(QDir::toNativeSeparators(info.absoluteFilePath())), m_path(QDir::toNativeSeparators(info.absoluteFilePath())),
@@ -42,23 +42,15 @@ QString WalletKeysFiles::path() const {
return m_path; return m_path;
} }
quint8 WalletKeysFiles::networkType() const { int WalletKeysFiles::networkType() const {
return m_networkType; return m_networkType;
} }
WalletKeysFilesModel::WalletKeysFilesModel(AppContext *ctx, QObject *parent) : WalletKeysFilesModel::WalletKeysFilesModel(AppContext *ctx, QObject *parent)
QAbstractTableModel(parent), : QAbstractTableModel(parent)
m_ctx(ctx) { , m_ctx(ctx)
this->walletDirectories << m_ctx->defaultWalletDirRoot; {
auto walletPath = config()->get(Config::walletPath).toString(); this->updateDirectories();
if(!walletPath.isEmpty() && Utils::fileExists(walletPath)) {
QDir d = QFileInfo(walletPath).absoluteDir();
this->walletDirectories << d.absolutePath();
}
this->walletDirectories << m_ctx->homeDir;
this->walletDirectories.removeDuplicates();
this->m_walletKeysFilesItemModel = qobject_cast<QAbstractItemModel *>(this); this->m_walletKeysFilesItemModel = qobject_cast<QAbstractItemModel *>(this);
} }
@@ -71,6 +63,20 @@ void WalletKeysFilesModel::clear() {
void WalletKeysFilesModel::refresh() { void WalletKeysFilesModel::refresh() {
this->clear(); this->clear();
this->findWallets(); 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() { void WalletKeysFilesModel::findWallets() {
@@ -133,7 +139,7 @@ int WalletKeysFilesModel::rowCount(const QModelIndex & parent) const {
} }
int WalletKeysFilesModel::columnCount(const QModelIndex &) const { int WalletKeysFilesModel::columnCount(const QModelIndex &) const {
return 3; return 4;
} }
QVariant WalletKeysFilesModel::data(const QModelIndex &index, int role) const { QVariant WalletKeysFilesModel::data(const QModelIndex &index, int role) const {
@@ -162,13 +168,15 @@ QVariant WalletKeysFilesModel::data(const QModelIndex &index, int role) const {
#endif #endif
return fp; return fp;
} }
case ModelColumns::Modified:
return walletKeyFile.modified();
default: default:
break; break;
} }
} else if(role == Qt::UserRole) { } else if(role == Qt::UserRole) {
switch(index.column()) { switch(index.column()) {
case ModelColumns::NetworkType: case ModelColumns::NetworkType:
return static_cast<uint>(walletKeyFile.networkType()); return static_cast<int>(walletKeyFile.networkType());
case ModelColumns::FileName: case ModelColumns::FileName:
return walletKeyFile.fileName(); return walletKeyFile.fileName();
case ModelColumns::Modified: case ModelColumns::Modified:
@@ -201,9 +209,15 @@ QVariant WalletKeysFilesModel::headerData(int section, Qt::Orientation orientati
return QVariant(); return QVariant();
} }
WalletKeysFilesProxyModel::WalletKeysFilesProxyModel(QObject *parent) : WalletKeysFilesProxyModel::WalletKeysFilesProxyModel(QObject *parent, NetworkType::Type nettype)
QSortFilterProxyModel(parent) {} : QSortFilterProxyModel(parent)
, m_nettype(nettype)
{
}
bool WalletKeysFilesProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { 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);
} }
+9 -4
View File
@@ -12,19 +12,19 @@
class WalletKeysFiles class WalletKeysFiles
{ {
public: public:
WalletKeysFiles(const QFileInfo &info, quint8 networkType, QString address); WalletKeysFiles(const QFileInfo &info, int networkType, QString address);
QString fileName() const; QString fileName() const;
qint64 modified() const; qint64 modified() const;
QString path() const; QString path() const;
quint8 networkType() const; int networkType() const;
QString address() const; QString address() const;
private: private:
QString m_fileName; QString m_fileName;
qint64 m_modified; qint64 m_modified;
QString m_path; QString m_path;
quint8 m_networkType; int m_networkType;
QString m_address; QString m_address;
}; };
@@ -54,6 +54,8 @@ public:
QStringList walletDirectories; QStringList walletDirectories;
private: private:
void updateDirectories();
AppContext *m_ctx; AppContext *m_ctx;
QList<WalletKeysFiles> m_walletKeyFiles; QList<WalletKeysFiles> m_walletKeyFiles;
QAbstractItemModel *m_walletKeysFilesItemModel; QAbstractItemModel *m_walletKeysFilesItemModel;
@@ -64,8 +66,11 @@ class WalletKeysFilesProxyModel : public QSortFilterProxyModel
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit WalletKeysFilesProxyModel(QObject *parent = nullptr); explicit WalletKeysFilesProxyModel(QObject *parent, NetworkType::Type nettype);
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
private:
NetworkType::Type m_nettype;
}; };
#endif // KEYSFILES_H #endif // KEYSFILES_H
+1 -1
View File
@@ -101,7 +101,7 @@ void Nodes::connectToNode() {
// auto connect // auto connect
m_wsExhaustedWarningEmitted = false; m_wsExhaustedWarningEmitted = false;
m_customExhaustedWarningEmitted = false; m_customExhaustedWarningEmitted = false;
this->autoConnect(); this->autoConnect(true);
} }
void Nodes::connectToNode(const FeatherNode &node) { void Nodes::connectToNode(const FeatherNode &node) {
+25 -25
View File
@@ -19,44 +19,44 @@
struct RestoreHeightLookup { struct RestoreHeightLookup {
NetworkType::Type type; NetworkType::Type type;
QMap<unsigned int, unsigned int> data; QMap<int, int> data;
explicit RestoreHeightLookup(NetworkType::Type type) : type(type) {} 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 // restore height based on a given timestamp using a lookup
// table. If it cannot find the date in the lookup table, it // table. If it cannot find the date in the lookup table, it
// will calculate the blockheight based off the last known // will calculate the blockheight based off the last known
// date: ((now - lastKnownDate) / blockTime) - clearance // date: ((now - lastKnownDate) / blockTime) - clearance
if(this->type == NetworkType::TESTNET) return 1; if(this->type == NetworkType::TESTNET) return 1;
unsigned int blockTime = 120; int blockTime = 120;
unsigned int blocksPerDay = 86400 / blockTime; int blocksPerDay = 86400 / blockTime;
unsigned int blockCalcClearance = blocksPerDay * 5; int blockCalcClearance = blocksPerDay * 5;
QList<unsigned int> values = this->data.keys(); QList<int> values = this->data.keys();
if(date <= values.at(0)) if(date <= values.at(0))
return this->data[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) { if(values[i] > date) {
return i - 1 < 0 ? this->data[values[i]] : this->data[values[i-1]] - blockCalcClearance; return i - 1 < 0 ? this->data[values[i]] : this->data[values[i-1]] - blockCalcClearance;
} }
} }
// lookup failed, calculate blockheight from last known checkpoint // lookup failed, calculate blockheight from last known checkpoint
unsigned int lastBlockHeightTime = values.at(values.count() - 1); int lastBlockHeightTime = values.at(values.count() - 1);
unsigned int lastBlockHeight = this->data[lastBlockHeightTime]; int lastBlockHeight = this->data[lastBlockHeightTime];
unsigned int deltaTime = date - lastBlockHeightTime; int deltaTime = date - lastBlockHeightTime;
unsigned int deltaBlocks = deltaTime / blockTime; int deltaBlocks = deltaTime / blockTime;
unsigned int blockHeight = (lastBlockHeight + deltaBlocks) - blockCalcClearance; int blockHeight = (lastBlockHeight + deltaBlocks) - blockCalcClearance;
qDebug() << "Calculated blockheight: " << blockHeight << " from epoch " << date; qDebug() << "Calculated blockheight: " << blockHeight << " from epoch " << date;
return blockHeight; return blockHeight;
} }
unsigned int restoreHeightToDate(unsigned int height) { int restoreHeightToDate(int height) {
// @TODO: most likely inefficient, refactor // @TODO: most likely inefficient, refactor
QMap<unsigned int, unsigned int>::iterator i; QMap<int, int>::iterator i;
unsigned int timestamp = 0; int timestamp = 0;
for (i = this->data.begin(); i != this->data.end(); ++i) { for (i = this->data.begin(); i != this->data.end(); ++i) {
unsigned int ts = i.key(); int ts = i.key();
if (i.value() > height) if (i.value() > height)
return timestamp; return timestamp;
timestamp = ts; timestamp = ts;
@@ -68,7 +68,7 @@ struct RestoreHeightLookup {
// initialize this class using a lookup table, e.g `:/assets/restore_heights_monero_mainnet.txt`/ // initialize this class using a lookup table, e.g `:/assets/restore_heights_monero_mainnet.txt`/
auto rtn = new RestoreHeightLookup(type); auto rtn = new RestoreHeightLookup(type);
auto data = Utils::barrayToString(Utils::fileOpen(fn)); auto data = Utils::barrayToString(Utils::fileOpen(fn));
QMap<unsigned int, unsigned int> _data; QMap<int, int> _data;
for(const auto &line: data.split('\n')) { for(const auto &line: data.split('\n')) {
if(line.trimmed().isEmpty()) continue; if(line.trimmed().isEmpty()) continue;
auto spl = line.trimmed().split(':'); auto spl = line.trimmed().split(':');
@@ -82,7 +82,7 @@ struct FeatherSeed {
QString mnemonicSeed; QString mnemonicSeed;
QString spendKey; QString spendKey;
time_t time = 0; time_t time = 0;
unsigned int restoreHeight = 0; int restoreHeight = 0;
RestoreHeightLookup *lookup = nullptr; RestoreHeightLookup *lookup = nullptr;
QString language; QString language;
std::string coinName; std::string coinName;
@@ -124,7 +124,7 @@ struct FeatherSeed {
return rtn; 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. // writes both 14/25 word mnemonic seeds.
Wallet *wallet = nullptr; Wallet *wallet = nullptr;
if(this->lookup == nullptr) return wallet; if(this->lookup == nullptr) return wallet;
@@ -136,25 +136,25 @@ struct FeatherSeed {
this->restoreHeight = _seed.restoreHeight; this->restoreHeight = _seed.restoreHeight;
this->spendKey = _seed.spendKey; 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); wallet->setCacheAttribute("feather.seed", this->mnemonicSeed);
} else { } 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); wallet->setPassword(password);
return wallet; return wallet;
} }
unsigned int setRestoreHeight() { int setRestoreHeight() {
if(this->lookup == nullptr) return 1; if(this->lookup == nullptr) return 1;
if(this->time == 0) 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; return this->restoreHeight;
} }
unsigned int setRestoreHeight(unsigned int height) { int setRestoreHeight(int height) {
auto now = (unsigned int)std::time(nullptr); auto now = std::time(nullptr);
auto nowClearance = 3600 * 24; auto nowClearance = 3600 * 24;
auto currentBlockHeight = this->lookup->dateToRestoreHeight(now - nowClearance); auto currentBlockHeight = this->lookup->dateToRestoreHeight(now - nowClearance);
if(height >= currentBlockHeight + nowClearance) { if(height >= currentBlockHeight + nowClearance) {
+4 -4
View File
@@ -7,7 +7,7 @@
#include "txfiathistory.h" #include "txfiathistory.h"
#include "utils/utils.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), QObject(parent),
m_genesis_timestamp(genesis_timestamp), m_genesis_timestamp(genesis_timestamp),
m_configDirectory(configDirectory) { m_configDirectory(configDirectory) {
@@ -15,7 +15,7 @@ TxFiatHistory::TxFiatHistory(unsigned int genesis_timestamp, const QString &conf
this->loadDatabase(); this->loadDatabase();
} }
double TxFiatHistory::get(unsigned int timestamp) { double TxFiatHistory::get(int timestamp) {
QDateTime ts; QDateTime ts;
ts.setTime_t(timestamp); ts.setTime_t(timestamp);
auto key = ts.toString("yyyyMMdd"); auto key = ts.toString("yyyyMMdd");
@@ -59,7 +59,7 @@ void TxFiatHistory::onUpdateDatabase() {
auto now = QDate::currentDate(); auto now = QDate::currentDate();
auto nowKey = now.toString("yyyyMMdd"); auto nowKey = now.toString("yyyyMMdd");
unsigned int year = genesis.toString("yyyy").toUInt(); int year = genesis.toString("yyyy").toInt();
auto yearCurrent = now.year(); auto yearCurrent = now.year();
// if current year is genesis year we'll refresh regardless. // 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 // keep local fiatTxHistory database up to date, loop for missing dates
for(year; year != yearCurrent + 1; year += 1){ 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; if(year == yearCurrent && month == now.month() && now.day() == 1) break;
QDateTime _now; QDateTime _now;
_now.setDate(QDate(year, month, 1)); _now.setDate(QDate(year, month, 1));
+5 -5
View File
@@ -6,17 +6,17 @@ class TxFiatHistory : public QObject {
Q_OBJECT Q_OBJECT
public: 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(const QString &date);
double get(unsigned int timestamp); double get(int timestamp);
public slots: public slots:
void onUpdateDatabase(); void onUpdateDatabase();
void onWSData(const QJsonObject &data); void onWSData(const QJsonObject &data);
signals: signals:
void requestYear(unsigned int year); void requestYear(int year);
void requestYearMonth(unsigned int year, unsigned int month); void requestYearMonth(int year, int month);
private: private:
void loadDatabase(); void loadDatabase();
@@ -25,7 +25,7 @@ private:
QString m_configDirectory; QString m_configDirectory;
bool m_initialized = false; bool m_initialized = false;
QMap<QString, double> m_database; QMap<QString, double> m_database;
unsigned int m_genesis_timestamp; int m_genesis_timestamp;
}; };
#endif //FEATHER_TXFIATHISTORY_H #endif //FEATHER_TXFIATHISTORY_H
+36 -8
View File
@@ -22,6 +22,7 @@
#include "utils.h" #include "utils.h"
#include "utils/config.h" #include "utils/config.h"
#include "utils/tails.h" #include "utils/tails.h"
#include "utils/whonix.h"
// Application log for current session // Application log for current session
QVector<logMessage> applicationLog = QVector<logMessage>(); // todo: replace with ring buffer QVector<logMessage> applicationLog = QVector<logMessage>(); // todo: replace with ring buffer
@@ -284,6 +285,8 @@ void Utils::externalLinkWarning(QWidget *parent, const QString &url){
QString body = "You are about to open the following link:\n\n"; QString body = "You are about to open the following link:\n\n";
body += QString("%1\n\n").arg(url); body += QString("%1\n\n").arg(url);
if (!(TailsOS::detect() || WhonixOS::detect()))
body += "You will NOT be using Tor."; body += "You will NOT be using Tor.";
switch (QMessageBox::warning(parent, "External link warning", body, QMessageBox::Cancel|QMessageBox::Ok)) { switch (QMessageBox::warning(parent, "External link warning", body, QMessageBox::Cancel|QMessageBox::Ok)) {
@@ -550,30 +553,55 @@ QString Utils::formatBytes(quint64 bytes)
int i; int i;
double _data; 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; _data = bytes / 1000.0;
if (_data < 0) if (_data < 0)
_data = 0; _data = 0;
// unrealistic // unrealistic
if (_data > 1000) if (_data > 10000)
_data = 0; _data = 0;
return QString("%1 %2").arg(QString::number(_data, 'f', 1), sizes[i]); return QString("%1 %2").arg(QString::number(_data, 'f', 1), sizes[i]);
} }
QString Utils::amountToCurrencyString(double amount, const QString &currencyCode) {
QMap<QString, QLocale> Utils::localeCache = {};
QLocale Utils::getCurrencyLocale(const QString &currencyCode) {
QLocale locale; QLocale locale;
if (localeCache.contains(currencyCode)) {
locale = localeCache[currencyCode];
} else {
QList<QLocale> allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); QList<QLocale> allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
for (const auto& locale_: allLocales) { for (const auto& locale_: allLocales) {
if (locale_.currencySymbol(QLocale::CurrencyIsoCode) == currencyCode) { if (locale_.currencySymbol(QLocale::CurrencyIsoCode) == currencyCode) {
locale = locale_; locale = locale_;
} }
} }
localeCache[currencyCode] = locale;
if (currencyCode == "USD") }
return locale.toCurrencyString(amount, "$"); return locale;
}
return locale.toCurrencyString(amount);
QString Utils::amountToCurrencyString(double amount, const QString &currencyCode) {
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;
}
}
return maxLength;
} }
+3
View File
@@ -95,7 +95,10 @@ public:
static QFont relativeFont(int delta); static QFont relativeFont(int delta);
static double roundSignificant(double N, double n); static double roundSignificant(double N, double n);
static QString formatBytes(quint64 bytes); static QString formatBytes(quint64 bytes);
static QLocale getCurrencyLocale(const QString &currencyCode);
static QString amountToCurrencyString(double amount, const QString &currencyCode); static QString amountToCurrencyString(double amount, const QString &currencyCode);
static int maxLength(const QVector<QString> &array);
static QMap<QString, QLocale> localeCache;
template<typename QEnum> template<typename QEnum>
static QString QtEnumToString (const QEnum value) static QString QtEnumToString (const QEnum value)
+8
View File
@@ -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); connect(&this->webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &WSClient::onError);
m_tor = url.host().endsWith(".onion"); 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) { void WSClient::sendMsg(const QByteArray &data) {
+1
View File
@@ -33,6 +33,7 @@ private slots:
private: private:
QTimer m_connectionTimer; QTimer m_connectionTimer;
QTimer m_pingTimer;
AppContext *m_ctx; AppContext *m_ctx;
bool m_tor = true; bool m_tor = true;
}; };
+1 -1
View File
@@ -33,7 +33,7 @@ void XmRig::stop() {
} }
void XmRig::start(const QString &path, void XmRig::start(const QString &path,
unsigned int threads, int threads,
const QString &address, const QString &address,
const QString &username, const QString &username,
const QString &password, const QString &password,
+1 -1
View File
@@ -23,7 +23,7 @@ public:
explicit XmRig(const QString &configDir, QObject *parent = nullptr); explicit XmRig(const QString &configDir, QObject *parent = nullptr);
void prepare(); 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(); void stop();
bool unpackBins(); bool unpackBins();
+2
View File
@@ -31,6 +31,8 @@ CCSWidget::CCSWidget(QWidget *parent) :
connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &CCSWidget::showContextMenu); connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &CCSWidget::showContextMenu);
connect(ui->tableView, &QTableView::doubleClicked, this, &CCSWidget::linkClicked); connect(ui->tableView, &QTableView::doubleClicked, this, &CCSWidget::linkClicked);
ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
} }
CCSModel* CCSWidget::model() { CCSModel* CCSWidget::model() {
+60 -81
View File
@@ -18,10 +18,10 @@
#include "utils/nodes.h" #include "utils/nodes.h"
#include "mainwindow.h" #include "mainwindow.h"
NodeWidget::NodeWidget(QWidget *parent) : NodeWidget::NodeWidget(QWidget *parent)
QWidget(parent), : QWidget(parent)
ui(new Ui::NodeWidget), , ui(new Ui::NodeWidget)
m_contextMenu(new QMenu(this)) { {
ui->setupUi(this); ui->setupUi(this);
connect(ui->btn_add_custom, &QPushButton::clicked, this, &NodeWidget::onCustomAddClicked); 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->wsView->setContextMenuPolicy(Qt::CustomContextMenu);
ui->customView->setContextMenuPolicy(Qt::CustomContextMenu); ui->customView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->wsView, &QTreeView::customContextMenuRequested, this, &NodeWidget::onShowWSContextMenu); connect(ui->wsView, &QTreeView::customContextMenuRequested, this, &NodeWidget::onShowWSContextMenu);
connect(ui->customView, &QTreeView::customContextMenuRequested, this, &NodeWidget::onShowCustomContextMenu); 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) { void NodeWidget::onShowWSContextMenu(const QPoint &pos) {
QModelIndex index = ui->wsView->indexAt(pos); m_activeView = ui->wsView;
if (!index.isValid()) return; FeatherNode node = this->selectedNode();
if (node.full.isEmpty()) return;
FeatherNode node = m_wsModel->node(index.row());
this->showContextMenu(pos, node); this->showContextMenu(pos, node);
} }
void NodeWidget::onShowCustomContextMenu(const QPoint &pos) { void NodeWidget::onShowCustomContextMenu(const QPoint &pos) {
QModelIndex index = ui->customView->indexAt(pos); m_activeView = ui->customView;
if (!index.isValid()) return; FeatherNode node = this->selectedNode();
if (node.full.isEmpty()) return;
FeatherNode node = m_customModel->node(index.row());
this->showContextMenu(pos, node); this->showContextMenu(pos, node);
} }
void NodeWidget::showContextMenu(const QPoint &pos, const FeatherNode &node) { void NodeWidget::showContextMenu(const QPoint &pos, const FeatherNode &node) {
bool custom = node.custom; QMenu menu(this);
m_activeView = custom ? ui->customView : ui->wsView;
m_contextActionRemove = new QAction("Remove"); if (!node.isActive) {
m_contextActionConnect = new QAction("Connect to node"); menu.addAction(m_contextActionConnect);
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);
} }
m_contextMenu->addAction(m_contextActionOpenStatusURL); menu.addAction(m_contextActionOpenStatusURL);
m_contextMenu->addAction(m_contextActionCopy); menu.addAction(m_contextActionCopy);
if(custom) { if (m_activeView == ui->customView)
connect(m_contextActionRemove, &QAction::triggered, this, &NodeWidget::onContextCustomNodeRemove); menu.addAction(m_contextActionRemove);
m_contextMenu->addAction(m_contextActionRemove);
connect(m_contextActionOpenStatusURL, &QAction::triggered, this, &NodeWidget::onContextCustomStatusURL); menu.exec(m_activeView->viewport()->mapToGlobal(pos));
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();
} }
void NodeWidget::onContextConnect() { 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(); QModelIndex index = m_activeView->currentIndex();
if (!index.isValid()) return; if (!index.isValid()) return FeatherNode();
if(m_activeView->objectName() == "wsView"){ FeatherNode node;
FeatherNode node = m_wsModel->node(index.row()); if (m_activeView == ui->customView) {
emit connectToNode(node); node = m_customModel->node(index.row());
} else { } else {
FeatherNode node = m_customModel->node(index.row()); node = m_wsModel->node(index.row());
emit connectToNode(node);
} }
} return node;
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() {
} }
void NodeWidget::onContextCustomNodeRemove() { void NodeWidget::onContextCustomNodeRemove() {
+3 -7
View File
@@ -35,12 +35,9 @@ public slots:
private slots: private slots:
void onContextConnect(); void onContextConnect();
void onContextDisconnect();
void onContextCustomNodeRemove(); void onContextCustomNodeRemove();
void onContextWSStatusURL(); void onContextStatusURL();
void onContextCustomStatusURL(); void onContextNodeCopy();
void onContextWSNodeCopy();
void onContextCustomNodeCopy();
signals: signals:
void connectToNode(FeatherNode node); void connectToNode(FeatherNode node);
@@ -54,14 +51,13 @@ private:
QTreeView *m_activeView; QTreeView *m_activeView;
QMenu *m_contextMenu;
QAction *m_contextActionConnect; QAction *m_contextActionConnect;
QAction *m_contextActionDisconnect;
QAction *m_contextActionRemove; QAction *m_contextActionRemove;
QAction *m_contextActionOpenStatusURL; QAction *m_contextActionOpenStatusURL;
QAction *m_contextActionCopy; QAction *m_contextActionCopy;
void showContextMenu(const QPoint &pos, const FeatherNode &node); void showContextMenu(const QPoint &pos, const FeatherNode &node);
FeatherNode selectedNode();
}; };
#endif // NODEWIDGET_H #endif // NODEWIDGET_H
+2
View File
@@ -26,6 +26,8 @@ RedditWidget::RedditWidget(QWidget *parent) :
connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &RedditWidget::showContextMenu); connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &RedditWidget::showContextMenu);
connect(ui->tableView, &QTableView::doubleClicked, this, &RedditWidget::linkClicked); connect(ui->tableView, &QTableView::doubleClicked, this, &RedditWidget::linkClicked);
ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
} }
RedditModel* RedditWidget::model() { RedditModel* RedditWidget::model() {
+34 -20
View File
@@ -17,15 +17,10 @@ RestoreHeightWidget::RestoreHeightWidget(QWidget *parent) :
{ {
ui->setupUi(this); ui->setupUi(this);
ui->lineEdit_restoreHeight->setValidator(new QIntValidator(0, 2147483647, this)); ui->lineEdit_restoreHeight->setValidator(new QIntValidator(0, 2147483647, this));
connect(ui->lineEdit_restoreHeight, &QLineEdit::textEdited, [=](const QString &val){
// update slider on lineEdit change connect(ui->lineEdit_restoreHeight, &QLineEdit::textEdited, [this](const QString &val){
if(val.isEmpty()) return; if (val.isEmpty()) return;
auto height = val.toUInt(); this->setHeight(val.toInt());
if(height <= 1) return;
auto timestamp = m_restoreHeightLookup->restoreHeightToDate(height);
ui->restoreSlider->blockSignals(true);
ui->restoreSlider->setValue(timestamp);
ui->restoreSlider->blockSignals(false);
}); });
} }
@@ -33,26 +28,45 @@ void RestoreHeightWidget::hideSlider(){
ui->restoreGrid->hide(); 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) { void RestoreHeightWidget::initRestoreHeights(RestoreHeightLookup *lookup) {
// init slider // init slider
m_restoreHeightLookup = lookup; m_restoreHeightLookup = lookup;
auto now = (unsigned int)std::time(nullptr); int now = std::time(nullptr);
QList<unsigned int> blockDates = m_restoreHeightLookup->data.keys(); QList<int> blockDates = m_restoreHeightLookup->data.keys();
ui->restoreSlider->setMinimum(blockDates[0]); ui->restoreSlider->setMinimum(blockDates[0]);
ui->restoreSlider->setMaximum(now); ui->restoreSlider->setMaximum(now);
connect(ui->restoreSlider, &QSlider::valueChanged, this, &RestoreHeightWidget::onValueChanged);
}
void RestoreHeightWidget::onValueChanged(int date) { connect(ui->restoreSlider, &QSlider::sliderMoved, [this](int date){
QDateTime timestamp; // Update lineEdit
timestamp.setTime_t((unsigned int) date); int blockHeight = m_restoreHeightLookup->dateToRestoreHeight(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)); ui->lineEdit_restoreHeight->setText(QString::number(blockHeight));
this->updateTimestamp(date);
});
} }
unsigned int RestoreHeightWidget::getHeight() { void RestoreHeightWidget::updateTimestamp(int date) {
return ui->lineEdit_restoreHeight->text().toUInt(); QDateTime timestamp;
timestamp.setTime_t(date);
ui->label_restoreHeightDate->setText(timestamp.toString("yyyy-MM-dd"));
}
int RestoreHeightWidget::getHeight() {
return ui->lineEdit_restoreHeight->text().toInt();
} }
RestoreHeightWidget::~RestoreHeightWidget() { RestoreHeightWidget::~RestoreHeightWidget() {
+5 -5
View File
@@ -20,14 +20,14 @@ class RestoreHeightWidget : public QWidget
public: public:
explicit RestoreHeightWidget(QWidget *parent = nullptr); explicit RestoreHeightWidget(QWidget *parent = nullptr);
void initRestoreHeights(RestoreHeightLookup *lookup); void initRestoreHeights(RestoreHeightLookup *lookup);
unsigned int getHeight(); void setHeight(int height);
int getHeight();
void hideSlider(); void hideSlider();
~RestoreHeightWidget(); ~RestoreHeightWidget() override;
private slots:
void onValueChanged(int date);
private: private:
void updateTimestamp(int date);
RestoreHeightLookup *m_restoreHeightLookup = nullptr; RestoreHeightLookup *m_restoreHeightLookup = nullptr;
Ui::RestoreHeightWidget *ui; Ui::RestoreHeightWidget *ui;
}; };
+6 -6
View File
@@ -50,15 +50,19 @@ void TickerWidget::init() {
double amount = m_convertBalance ? AppContext::balance : 1.0; double amount = m_convertBalance ? AppContext::balance : 1.0;
double conversion = AppContext::prices->convert(m_symbol, fiatCurrency, amount); double conversion = AppContext::prices->convert(m_symbol, fiatCurrency, amount);
if (conversion < 0) return; if (conversion < 0) return;
ui->tickerPct->setHidden(conversion == 0 || m_hidePercent);
auto markets = AppContext::prices->markets; auto markets = AppContext::prices->markets;
if(!markets.contains(m_symbol)) return; if(!markets.contains(m_symbol)) return;
bool hidePercent = (conversion == 0 || m_hidePercent);
if (hidePercent) {
ui->tickerPct->hide();
} else {
auto pct24h = markets[m_symbol].price_usd_change_pct_24h; auto pct24h = markets[m_symbol].price_usd_change_pct_24h;
auto pct24hText = QString::number(pct24h, 'f', 2); auto pct24hText = QString::number(pct24h, 'f', 2);
this->setPctText(pct24hText, pct24h >= 0.0); this->setPctText(pct24hText, pct24h >= 0.0);
}
this->setFiatText(fiatCurrency, conversion); this->setFiatText(fiatCurrency, conversion);
} }
@@ -84,10 +88,6 @@ void TickerWidget::setFontSizes() {
ui->tickerFiat->setFont(Utils::relativeFont(0)); ui->tickerFiat->setFont(Utils::relativeFont(0));
} }
void TickerWidget::removePctContainer() {
ui->tickerPct->deleteLater();
}
TickerWidget::~TickerWidget() { TickerWidget::~TickerWidget() {
delete ui; delete ui;
} }
-1
View File
@@ -18,7 +18,6 @@ class TickerWidget : public QWidget
public: public:
explicit TickerWidget(QWidget *parent, QString symbol, QString title = "", bool convertBalance = false, bool hidePercent = false); explicit TickerWidget(QWidget *parent, QString symbol, QString title = "", bool convertBalance = false, bool hidePercent = false);
void removePctContainer();
void setFiatText(QString &fiatCurrency, double amount); void setFiatText(QString &fiatCurrency, double amount);
void setPctText(QString &text, bool positive); void setPctText(QString &text, bool positive);
void setFontSizes(); void setFontSizes();
+5
View File
@@ -32,6 +32,11 @@ TxProofWidget::TxProofWidget(QWidget *parent, Wallet *wallet, TransactionInfo *t
connect(ui->btn_copySpendProof, &QPushButton::clicked, this, &TxProofWidget::copySpendProof); connect(ui->btn_copySpendProof, &QPushButton::clicked, this, &TxProofWidget::copySpendProof);
connect(ui->btn_copyTxProof, &QPushButton::clicked, this, &TxProofWidget::copyTxProof); 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() { void TxProofWidget::copySpendProof() {
+9 -2
View File
@@ -48,7 +48,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="label"> <widget class="HelpLabel" name="label_SpendProof">
<property name="text"> <property name="text">
<string>SpendProof</string> <string>SpendProof</string>
</property> </property>
@@ -72,7 +72,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="label_2"> <widget class="HelpLabel" name="label_InOutProof">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@@ -98,6 +98,13 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>HelpLabel</class>
<extends>QLabel</extends>
<header>components.h</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>
+8
View File
@@ -34,7 +34,11 @@ CreateWalletPage::CreateWalletPage(AppContext *ctx, QWidget *parent) :
connect(ui->btnChange, &QPushButton::clicked, [=] { connect(ui->btnChange, &QPushButton::clicked, [=] {
QString walletDir = QFileDialog::getExistingDirectory(this, "Select wallet directory ", m_ctx->defaultWalletDir, QFileDialog::ShowDirsOnly); QString walletDir = QFileDialog::getExistingDirectory(this, "Select wallet directory ", m_ctx->defaultWalletDir, QFileDialog::ShowDirsOnly);
if(walletDir.isEmpty()) return; if(walletDir.isEmpty()) return;
m_ctx->defaultWalletDir = walletDir;
m_ctx->defaultWalletDirRoot = walletDir;
ui->directory->setText(walletDir); ui->directory->setText(walletDir);
config()->set(Config::walletDirectory, walletDir);
emit defaultWalletDirChanged(walletDir);
}); });
connect(ui->directory, &QLineEdit::textChanged, [=](const QString &data) { 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(){ bool CreateWalletPage::validateWidgets(){
ui->walletName->setStyleSheet(""); ui->walletName->setStyleSheet("");
ui->directory->setStyleSheet(""); ui->directory->setStyleSheet("");
+2 -1
View File
@@ -20,15 +20,16 @@ class CreateWalletPage : public QWizardPage
public: public:
explicit CreateWalletPage(AppContext *ctx, QWidget *parent = nullptr); explicit CreateWalletPage(AppContext *ctx, QWidget *parent = nullptr);
void initializePage() override;
bool validatePage() override; bool validatePage() override;
int nextId() const override; int nextId() const override;
signals: signals:
void createWallet(); void createWallet();
void defaultWalletDirChanged(QString walletDir);
private: private:
AppContext *m_ctx; AppContext *m_ctx;
QLabel *topLabel;
Ui::CreateWalletPage *ui; Ui::CreateWalletPage *ui;
QString m_walletDir; QString m_walletDir;
bool validateWidgets(); bool validateWidgets();
+1 -2
View File
@@ -23,7 +23,6 @@ public:
explicit CreateWalletSeedPage(AppContext *ctx, QWidget *parent = nullptr); explicit CreateWalletSeedPage(AppContext *ctx, QWidget *parent = nullptr);
bool validatePage() override; bool validatePage() override;
int nextId() const override; int nextId() const override;
// bool isFinalPage() const;
public slots: public slots:
void displaySeed(const QString &seed); void displaySeed(const QString &seed);
@@ -40,7 +39,7 @@ private:
Ui::CreateWalletSeedPage *ui; Ui::CreateWalletSeedPage *ui;
QString m_mnemonic; QString m_mnemonic;
unsigned int m_restoreHeight; int m_restoreHeight;
bool m_roulette = false; bool m_roulette = false;
int m_rouletteSpin = 15; int m_rouletteSpin = 15;
+13 -5
View File
@@ -11,14 +11,22 @@
#include "libwalletqt/WalletManager.h" #include "libwalletqt/WalletManager.h"
MenuPage::MenuPage(AppContext *ctx, QWidget *parent) : MenuPage::MenuPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent)
QWizardPage(parent), : QWizardPage(parent)
ui(new Ui::MenuPage), , ui(new Ui::MenuPage)
m_ctx(ctx) { , m_ctx(ctx)
, m_walletKeysFilesModel(wallets)
{
ui->setupUi(this); ui->setupUi(this);
this->setTitle("Welcome to feather");
this->setButtonText(QWizard::FinishButton, "Open recent wallet"); this->setButtonText(QWizard::FinishButton, "Open recent wallet");
}
void MenuPage::initializePage() {
if (m_walletKeysFilesModel->rowCount() > 0) {
ui->radioOpen->setChecked(true);
} else {
ui->radioCreate->setChecked(true); ui->radioCreate->setChecked(true);
}
} }
int MenuPage::nextId() const { int MenuPage::nextId() const {
+3 -2
View File
@@ -19,13 +19,14 @@ class MenuPage : public QWizardPage
Q_OBJECT Q_OBJECT
public: public:
explicit MenuPage(AppContext *ctx, QWidget *parent = nullptr); explicit MenuPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent = nullptr);
void initializePage() override;
bool validatePage() override; bool validatePage() override;
int nextId() const override; int nextId() const override;
private: private:
AppContext *m_ctx; AppContext *m_ctx;
QLabel *topLabel; WalletKeysFilesModel *m_walletKeysFilesModel;
Ui::MenuPage *ui; Ui::MenuPage *ui;
}; };
+24 -2
View File
@@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>424</width> <width>617</width>
<height>245</height> <height>463</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -29,6 +29,9 @@
</item> </item>
<item> <item>
<widget class="QRadioButton" name="radioCreate"> <widget class="QRadioButton" name="radioCreate">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="text"> <property name="text">
<string>Create new wallet</string> <string>Create new wallet</string>
</property> </property>
@@ -36,6 +39,9 @@
</item> </item>
<item> <item>
<widget class="QRadioButton" name="radioOpen"> <widget class="QRadioButton" name="radioOpen">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="text"> <property name="text">
<string>Open wallet file</string> <string>Open wallet file</string>
</property> </property>
@@ -43,6 +49,9 @@
</item> </item>
<item> <item>
<widget class="QRadioButton" name="radioSeed"> <widget class="QRadioButton" name="radioSeed">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="text"> <property name="text">
<string>Restore wallet from seed</string> <string>Restore wallet from seed</string>
</property> </property>
@@ -50,6 +59,9 @@
</item> </item>
<item> <item>
<widget class="QRadioButton" name="radioViewOnly"> <widget class="QRadioButton" name="radioViewOnly">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="text"> <property name="text">
<string>Import from keys</string> <string>Import from keys</string>
</property> </property>
@@ -78,6 +90,16 @@
</property> </property>
</widget> </widget>
</item> </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> </layout>
</widget> </widget>
<resources/> <resources/>
+32 -14
View File
@@ -10,12 +10,12 @@
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
// @TODO: rescan wallet dir on wizard open OpenWalletPage::OpenWalletPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent)
: QWizardPage(parent)
OpenWalletPage::OpenWalletPage(AppContext *ctx, QWidget *parent) : , ui(new Ui::OpenWalletPage)
QWizardPage(parent), , m_ctx(ctx)
ui(new Ui::OpenWalletPage), , m_walletKeysFilesModel(wallets)
m_ctx(ctx) { {
ui->setupUi(this); ui->setupUi(this);
connect(ui->btnBrowse, &QPushButton::clicked, [=]{ connect(ui->btnBrowse, &QPushButton::clicked, [=]{
@@ -46,19 +46,37 @@ OpenWalletPage::OpenWalletPage(AppContext *ctx, QWidget *parent) :
ui->walletTable->setSelectionBehavior(QAbstractItemView::SelectRows); ui->walletTable->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->walletTable->setContextMenuPolicy(Qt::CustomContextMenu); ui->walletTable->setContextMenuPolicy(Qt::CustomContextMenu);
this->walletKeysFilesModel = new WalletKeysFilesModel(m_ctx); m_keysProxy = new WalletKeysFilesProxyModel(this, m_ctx->networkType);
this->walletKeysFilesModel->refresh(); m_keysProxy->setSourceModel(m_walletKeysFilesModel);
m_keysProxy = new WalletKeysFilesProxyModel();
m_keysProxy->setSourceModel(this->walletKeysFilesModel);
m_keysProxy->setSortRole(Qt::UserRole); m_keysProxy->setSortRole(Qt::UserRole);
ui->walletTable->setModel(m_keysProxy); ui->walletTable->setModel(m_keysProxy);
ui->walletTable->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->walletTable->hideColumn(WalletKeysFilesModel::NetworkType);
ui->walletTable->header()->setSectionResizeMode(WalletKeysFilesModel::Path, QHeaderView::Stretch); ui->walletTable->hideColumn(WalletKeysFilesModel::Path);
ui->walletTable->hideColumn(WalletKeysFilesModel::Modified);
ui->walletTable->header()->setSectionResizeMode(WalletKeysFilesModel::FileName, QHeaderView::Stretch);
ui->walletTable->setSortingEnabled(true); 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(); 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 { int OpenWalletPage::nextId() const {
+5 -4
View File
@@ -20,7 +20,8 @@ class OpenWalletPage : public QWizardPage
Q_OBJECT Q_OBJECT
public: public:
explicit OpenWalletPage(AppContext *ctx, QWidget *parent = nullptr); explicit OpenWalletPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent = nullptr);
void initializePage() override;
bool validatePage() override; bool validatePage() override;
int nextId() const override; int nextId() const override;
@@ -28,11 +29,11 @@ signals:
void openWallet(QString path); void openWallet(QString path);
private: private:
void updatePath();
AppContext *m_ctx; AppContext *m_ctx;
WalletKeysFilesModel *walletKeysFilesModel; WalletKeysFilesModel *m_walletKeysFilesModel;
WalletKeysFilesProxyModel *m_keysProxy; WalletKeysFilesProxyModel *m_keysProxy;
QSortFilterProxyModel *ll;
QLabel *topLabel;
Ui::OpenWalletPage *ui; Ui::OpenWalletPage *ui;
QStandardItemModel *m_model; QStandardItemModel *m_model;
}; };
+7
View File
@@ -55,6 +55,13 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="1" column="0">
<widget class="QLabel" name="labelPath">
<property name="text">
<string>path</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>
+6 -3
View File
@@ -31,7 +31,7 @@ RestorePage::RestorePage(AppContext *ctx, QWidget *parent) :
auto data = Utils::fileOpen(":/assets/mnemonic_25_english.txt"); auto data = Utils::fileOpen(":/assets/mnemonic_25_english.txt");
for(const auto &seed_word: data.split('\n')) for(const auto &seed_word: data.split('\n'))
m_words25 << seed_word; 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)); m_words14 << QString::fromStdString(wordlist::english.get_word(i));
// //
@@ -104,12 +104,15 @@ int RestorePage::nextId() const {
return WalletWizard::Page_CreateWallet; return WalletWizard::Page_CreateWallet;
} }
void RestorePage::cleanupPage() const {} void RestorePage::initializePage() {
ui->seedEdit->setText("");
ui->restoreHeightWidget->setHeight(1);
}
bool RestorePage::validatePage() { bool RestorePage::validatePage() {
ui->label_errorString->hide(); ui->label_errorString->hide();
auto errStyle = "QTextEdit{border: 1px solid red;}"; 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 seed = ui->seedEdit->toPlainText().replace("\n", "").replace("\r", "").trimmed();
auto seedSplit = seed.split(" "); auto seedSplit = seed.split(" ");
+2 -3
View File
@@ -25,15 +25,14 @@ class RestorePage : public QWizardPage
public: public:
explicit RestorePage(AppContext *ctx, QWidget *parent = nullptr); explicit RestorePage(AppContext *ctx, QWidget *parent = nullptr);
bool validatePage() override; bool validatePage() override;
void initializePage() override;
int nextId() const override; int nextId() const override;
void cleanupPage() const;
private: private:
AppContext *m_ctx; AppContext *m_ctx;
QLabel *topLabel;
Ui::RestorePage *ui; Ui::RestorePage *ui;
unsigned int m_mode = 14; int m_mode = 14;
QStringList m_words14; QStringList m_words14;
QStringList m_words25; QStringList m_words25;
QStringListModel *m_completer14Model = nullptr; QStringListModel *m_completer14Model = nullptr;
+1 -1
View File
@@ -72,7 +72,7 @@ bool ViewOnlyPage::validatePage() {
ui->lineEdit_viewkey->setStyleSheet(""); ui->lineEdit_viewkey->setStyleSheet("");
ui->label_errorString->hide(); ui->label_errorString->hide();
unsigned int restoreHeight = ui->restoreHeightWidget->getHeight(); int restoreHeight = ui->restoreHeightWidget->getHeight();
auto spendkey = ui->lineEdit_spendkey->text().trimmed(); auto spendkey = ui->lineEdit_spendkey->text().trimmed();
auto viewkey = ui->lineEdit_viewkey->text().trimmed(); auto viewkey = ui->lineEdit_viewkey->text().trimmed();
auto address = ui->lineEdit_address->text().trimmed(); auto address = ui->lineEdit_address->text().trimmed();
+15 -6
View File
@@ -18,14 +18,20 @@
#include <QScreen> #include <QScreen>
#include <QApplication> #include <QApplication>
WalletWizard::WalletWizard(AppContext *ctx, WalletWizard::Page startPage, QWidget *parent) : QWizard(parent), WalletWizard::WalletWizard(AppContext *ctx, WalletWizard::Page startPage, QWidget *parent)
m_ctx(ctx) { : QWizard(parent)
this->setWindowTitle("Welcome to feather"); , m_ctx(ctx)
{
this->setWindowTitle("Welcome to Feather Wallet");
this->setWindowIcon(QIcon(":/assets/images/appicons/64x64.png")); 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 createWallet = new CreateWalletPage(m_ctx, this);
auto createWalletSeed = new CreateWalletSeedPage(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_CreateWallet, createWallet);
setPage(Page_OpenWallet, openWalletPage); setPage(Page_OpenWallet, openWalletPage);
setPage(Page_CreateWalletSeed, createWalletSeed); setPage(Page_CreateWalletSeed, createWalletSeed);
@@ -38,7 +44,7 @@ WalletWizard::WalletWizard(AppContext *ctx, WalletWizard::Page startPage, QWidge
else else
setStartId(Page_Menu); setStartId(Page_Menu);
setPixmap(QWizard::WatermarkPixmap, QPixmap(":/assets/images/photos/1.png")); setPixmap(QWizard::WatermarkPixmap, QPixmap(":/assets/images/banners/3.png"));
setWizardStyle(WizardStyle::ModernStyle); setWizardStyle(WizardStyle::ModernStyle);
setOption(QWizard::NoBackButtonOnStartPage); setOption(QWizard::NoBackButtonOnStartPage);
@@ -48,6 +54,9 @@ WalletWizard::WalletWizard(AppContext *ctx, WalletWizard::Page startPage, QWidge
connect(createWalletSeed, &CreateWalletSeedPage::createWallet, this, &WalletWizard::createWallet); connect(createWalletSeed, &CreateWalletSeedPage::createWallet, this, &WalletWizard::createWallet);
connect(createWallet, &CreateWalletPage::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){ connect(openWalletPage, &OpenWalletPage::openWallet, [=](const QString &path){
const auto walletPasswd = this->field("walletPasswd").toString(); const auto walletPasswd = this->field("walletPasswd").toString();
+4
View File
@@ -20,11 +20,15 @@ public:
enum Page { Page_Menu, Page_CreateWallet, Page_CreateWalletSeed, Page_OpenWallet, Page_Network, Page_Restore, Page_ViewOnly }; 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); explicit WalletWizard(AppContext *ctx, WalletWizard::Page startPage = WalletWizard::Page::Page_Menu, QWidget *parent = nullptr);
signals: signals:
void openWallet(QString path, QString password); void openWallet(QString path, QString password);
void defaultWalletDirChanged(QString walletDir);
private: private:
AppContext *m_ctx; AppContext *m_ctx;
WalletKeysFilesModel *m_walletKeysFilesModel;
void createWallet(); void createWallet();
}; };
+3 -2
View File
@@ -6,6 +6,7 @@
#include "dialog/xmrtoinfodialog.h" #include "dialog/xmrtoinfodialog.h"
#include "libwalletqt/WalletManager.h" #include "libwalletqt/WalletManager.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "globals.h"
#include <QMenu> #include <QMenu>
#include <QMessageBox> #include <QMessageBox>
@@ -71,8 +72,8 @@ void XMRToWidget::setHistoryModel(XmrToModel *model) {
this->ui->historyTable->setModel(model); this->ui->historyTable->setModel(model);
} }
void XMRToWidget::onBalanceUpdated(double balance, double unlocked, const QString &balance_str, const QString &unlocked_str) { void XMRToWidget::onBalanceUpdated(quint64 balance, quint64 spendable) {
this->m_unlockedBalance = unlocked; this->m_unlockedBalance = spendable / globals::cdiv;
} }
void XMRToWidget::onWalletClosed() { void XMRToWidget::onWalletClosed() {
+1 -1
View File
@@ -30,7 +30,7 @@ public slots:
void onRatesUpdated(XmrToRates rates); void onRatesUpdated(XmrToRates rates);
void onTorCheckBoxToggled(int state); void onTorCheckBoxToggled(int state);
void onCreateOrder(); 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 updateConversionLabel();
void onInitiateTransaction(); void onInitiateTransaction();