Merge pull request '1.0.0' (#394) from tobtoht/feather:1.0.0 into master

Reviewed-on: https://git.featherwallet.org/feather/feather/pulls/394
This commit is contained in:
tobtoht 2021-10-28 18:04:39 +00:00
commit 465a10b983
135 changed files with 2014 additions and 1195 deletions

View file

@ -3,10 +3,10 @@ project(feather)
message(STATUS "Initiating compile using CMake ${CMAKE_VERSION}")
set(VERSION_MAJOR "0")
set(VERSION_MINOR "1")
set(VERSION_MAJOR "1")
set(VERSION_MINOR "0")
set(VERSION_REVISION "0")
set(VERSION "beta-9")
set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
option(STATIC "Link libraries statically, requires static Qt")
@ -32,7 +32,7 @@ if(DEBUG)
set(CMAKE_VERBOSE_MAKEFILE ON)
endif()
set(MONERO_HEAD "d4257af2e7503fc6dc09fc704606230d353a0a02")
set(MONERO_HEAD "bdd284b35d2e2c9c6ac733b4bc5ce8bd3b1162dd")
set(BUILD_GUI_DEPS ON)
option(ARCH "Target architecture" "x86-64")
set(BUILD_64 ON)

View file

@ -11,7 +11,12 @@ CMAKEFLAGS = \
release:
mkdir -p build/release && \
cd build/release && \
cmake -D CMAKE_BUILD_TYPE=Release $(CMAKEFLAGS) ../.. && \
cmake \
-DARCH=x86-64 \
-D BUILD_TAG="linux-x64" \
-D CMAKE_BUILD_TYPE=Release \
$(CMAKEFLAGS) \
../.. && \
$(MAKE)
release-static:

View file

@ -2,7 +2,7 @@
# Contributor: wowario <wowario[at]protonmail[dot]com>
pkgname='monero-feather-git'
pkgver=0.8.0.d3791cbd26
pkgver=1.0.0
pkgrel=1
pkgdesc='A free Monero desktop wallet'
license=('BSD')

View file

@ -0,0 +1,28 @@
Copyright (c) 2020-2021, The Monero Project
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

View file

@ -0,0 +1,67 @@
; Feather Wallet Installer for Windows
; Copyright (c) 2021-2021, The Monero Project
#define AppName "Feather Wallet"
#define AppVersion "1.0.2"
#define AppPublisher "Feather Wallet"
#define AppURL "https://featherwallet.org"
#define AppExeName "feather.exe"
[Setup]
AppId={{E3C599C7-4DF1-49F2-9C35-918A288677A4}
AppName={#AppName}
AppVersion={#AppVersion}
;AppVerName={#AppName} {#AppVersion}
AppPublisher={#AppPublisher}
AppPublisherURL={#AppURL}
AppSupportURL={#AppURL}
AppUpdatesURL={#AppURL}
DefaultDirName={autopf}\{#AppName}
DisableDirPage=yes
DisableProgramGroupPage=yes
; Uncomment the following line to run in non administrative install mode (install for current user only.)
;PrivilegesRequired=lowest
OutputBaseFilename=FeatherWalletSetup
SetupIconFile=appicon.ico
Compression=lzma
SolidCompression=yes
WizardStyle=modern
ArchitecturesInstallIn64BitMode=x64
ArchitecturesAllowed=x64
DisableReadyPage=yes
WizardSmallImageFile=compiler:WizClassicSmallImage.bmp
WizardImageFile=compiler:WizClassicImage.bmp
UninstallDisplayIcon={app}\{#AppExeName}
[Messages]
SetupWindowTitle=Setup - Feather Wallet {#AppVersion}
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}";
[Files]
Source: "bin\{#AppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion
;Source: "C:\Users\dev\Desktop\feather setup\finishbanner.bmp"; Flags: dontcopy
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Code]
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectTasks then
WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall)
end;
[Icons]
Name: "{autoprograms}\{#AppName}"; Filename: "{app}\{#AppExeName}"
Name: "{autodesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\{#AppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

2
monero

@ -1 +1 @@
Subproject commit d4257af2e7503fc6dc09fc704606230d353a0a02
Subproject commit bdd284b35d2e2c9c6ac733b4bc5ce8bd3b1162dd

View file

@ -23,13 +23,12 @@ CalcWidget::CalcWidget(QWidget *parent)
ui->imageExchange->setFixedSize(26, 26);
// validator/locale for input
QLocale lo(QLocale::C);
lo.setNumberOptions(QLocale::RejectGroupSeparator);
auto dv = new QDoubleValidator(0.0, 2147483647, 10, this); // [0, 32bit max], 10 decimals of precision
dv->setNotation(QDoubleValidator::StandardNotation);
dv->setLocale(lo);
ui->lineFrom->setValidator(dv);
ui->lineTo->setValidator(dv);
QString amount_rx = R"(^\d{0,8}[\.]\d{0,12}$)";
QRegExp rx;
rx.setPattern(amount_rx);
QValidator *validator = new QRegExpValidator(rx, this);
ui->lineFrom->setValidator(validator);
ui->lineTo->setValidator(validator);
connect(&appData()->prices, &Prices::fiatPricesUpdated, this, &CalcWidget::onPricesReceived);
connect(&appData()->prices, &Prices::cryptoPricesUpdated, this, &CalcWidget::onPricesReceived);

View file

@ -26,8 +26,7 @@ CoinsWidget::CoinsWidget(QSharedPointer<AppContext> ctx, QWidget *parent)
connect(ui->coins->header(), &QHeaderView::customContextMenuRequested, this, &CoinsWidget::showHeaderMenu);
// copy menu
m_copyMenu->setIcon(icons()->icon("copy.png"));
m_copyMenu->addAction("Public key", this, [this]{copy(copyField::PubKey);});
m_copyMenu->addAction("Public Key", this, [this]{copy(copyField::PubKey);});
m_copyMenu->addAction("Key Image", this, [this]{copy(copyField::KeyImage);});
m_copyMenu->addAction("Transaction ID", this, [this]{copy(copyField::TxID);});
m_copyMenu->addAction("Address", this, [this]{copy(copyField::Address);});
@ -47,12 +46,12 @@ CoinsWidget::CoinsWidget(QSharedPointer<AppContext> ctx, QWidget *parent)
m_freezeAllSelectedAction = new QAction("Freeze selected", this);
m_thawAllSelectedAction = new QAction("Thaw selected", this);
m_viewOutputAction = new QAction(icons()->icon("info2.svg"), "Details", this);
m_viewOutputAction = new QAction("Details", this);
m_sweepOutputAction = new QAction("Sweep output", this);
m_sweepOutputsAction = new QAction("Sweep selected outputs", this);
connect(m_freezeOutputAction, &QAction::triggered, this, &CoinsWidget::freezeOutput);
connect(m_thawOutputAction, &QAction::triggered, this, &CoinsWidget::thawOutput);
connect(m_freezeOutputAction, &QAction::triggered, this, &CoinsWidget::freezeAllSelected);
connect(m_thawOutputAction, &QAction::triggered, this, &CoinsWidget::thawAllSelected);
connect(m_viewOutputAction, &QAction::triggered, this, &CoinsWidget::viewOutput);
connect(m_sweepOutputAction, &QAction::triggered, this, &CoinsWidget::onSweepOutputs);
connect(m_sweepOutputsAction, &QAction::triggered, this, &CoinsWidget::onSweepOutputs);
@ -155,36 +154,24 @@ void CoinsWidget::setSearchFilter(const QString &filter) {
m_proxyModel->setSearchFilter(filter);
}
void CoinsWidget::freezeOutput() {
QModelIndex index = ui->coins->currentIndex();
QVector<int> indexes = {m_proxyModel->mapToSource(index).row()};
this->freezeCoins(indexes);
QStringList CoinsWidget::selectedPubkeys() {
QModelIndexList list = ui->coins->selectionModel()->selectedRows();
QStringList pubkeys;
for (QModelIndex index: list) {
pubkeys << m_model->entryFromIndex(m_proxyModel->mapToSource(index))->pubKey();
}
return pubkeys;
}
void CoinsWidget::freezeAllSelected() {
QModelIndexList list = ui->coins->selectionModel()->selectedRows();
QVector<int> indexes;
for (QModelIndex index: list) {
indexes.push_back(m_proxyModel->mapToSource(index).row()); // todo: will segfault if index get invalidated
}
this->freezeCoins(indexes);
}
void CoinsWidget::thawOutput() {
QModelIndex index = ui->coins->currentIndex();
QVector<int> indexes = {m_proxyModel->mapToSource(index).row()};
this->thawCoins(indexes);
QStringList pubkeys = this->selectedPubkeys();
this->freezeCoins(pubkeys);
}
void CoinsWidget::thawAllSelected() {
QModelIndexList list = ui->coins->selectionModel()->selectedRows();
QVector<int> indexes;
for (QModelIndex index: list) {
indexes.push_back(m_proxyModel->mapToSource(index).row());
}
this->thawCoins(indexes);
QStringList pubkeys = this->selectedPubkeys();
this->thawCoins(pubkeys);
}
void CoinsWidget::viewOutput() {
@ -291,17 +278,17 @@ QVector<CoinsInfo*> CoinsWidget::currentEntries() {
return selectedCoins;
}
void CoinsWidget::freezeCoins(const QVector<int>& indexes) {
for (int i : indexes) {
m_ctx->wallet->coins()->freeze(i);
void CoinsWidget::freezeCoins(QStringList &pubkeys) {
for (auto &pubkey : pubkeys) {
m_ctx->wallet->coins()->freeze(pubkey);
}
m_ctx->wallet->coins()->refresh(m_ctx->wallet->currentSubaddressAccount());
m_ctx->updateBalance();
}
void CoinsWidget::thawCoins(const QVector<int> &indexes) {
for (int i : indexes) {
m_ctx->wallet->coins()->thaw(i);
void CoinsWidget::thawCoins(QStringList &pubkeys) {
for (auto &pubkey : pubkeys) {
m_ctx->wallet->coins()->thaw(pubkey);
}
m_ctx->wallet->coins()->refresh(m_ctx->wallet->currentSubaddressAccount());
m_ctx->updateBalance();

View file

@ -33,9 +33,7 @@ public slots:
private slots:
void showHeaderMenu(const QPoint& position);
void setShowSpent(bool show);
void freezeOutput();
void freezeAllSelected();
void thawOutput();
void thawAllSelected();
void viewOutput();
void onSweepOutputs();
@ -43,8 +41,8 @@ private slots:
void editLabel();
private:
void freezeCoins(const QVector<int>& indexes);
void thawCoins(const QVector<int>& indexes);
void freezeCoins(QStringList &pubkeys);
void thawCoins(QStringList &pubkeys);
enum copyField {
PubKey = 0,
@ -79,6 +77,7 @@ private:
void copy(copyField field);
CoinsInfo* currentEntry();
QVector<CoinsInfo*> currentEntries();
QStringList selectedPubkeys();
};

View file

@ -18,6 +18,11 @@ ContactsWidget::ContactsWidget(QSharedPointer<AppContext> ctx, QWidget *parent)
{
ui->setupUi(this);
m_btn_addContact = new QPushButton(this);
m_btn_addContact->setIcon(icons()->icon("localMonero_register.svg"));
ui->searchLayout->addWidget(m_btn_addContact, 0, Qt::AlignRight);
connect(m_btn_addContact, &QPushButton::clicked, [this]{this->newContact();});
m_model = m_ctx->wallet->addressBookModel();
m_proxyModel = new AddressBookProxyModel;
m_proxyModel->setSourceModel(m_model);
@ -45,7 +50,7 @@ ContactsWidget::ContactsWidget(QSharedPointer<AppContext> ctx, QWidget *parent)
// context menu
ui->contacts->setContextMenuPolicy(Qt::CustomContextMenu);
m_contextMenu = new QMenu(ui->contacts);
m_contextMenu->addAction(icons()->icon("person.svg"), "New contact", [this]{
m_contextMenu->addAction("New contact", [this]{
this->newContact();
});

View file

@ -4,6 +4,7 @@
#ifndef FEATHER_CONTACTSWIDGET_H
#define FEATHER_CONTACTSWIDGET_H
#include <QPushButton>
#include <QWidget>
#include <QMenu>
@ -51,6 +52,7 @@ private:
QMenu *m_headerMenu;
AddressBookModel * m_model;
AddressBookProxyModel * m_proxyModel;
QPushButton *m_btn_addContact;
};
#endif // FEATHER_CONTACTSWIDGET_H

View file

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>589</width>
<height>416</height>
<width>914</width>
<height>763</height>
</rect>
</property>
<property name="windowTitle">
@ -30,14 +30,24 @@
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="search">
<property name="placeholderText">
<string>Search contacts...</string>
</property>
</widget>
<layout class="QHBoxLayout" name="searchLayout">
<item>
<widget class="QLineEdit" name="search">
<property name="placeholderText">
<string>Search contacts...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTreeView" name="contacts">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>

View file

@ -11,6 +11,7 @@
#include "dialog/TxProofDialog.h"
#include "utils/config.h"
#include "utils/Icons.h"
#include "WebsocketNotifier.h"
HistoryWidget::HistoryWidget(QSharedPointer<AppContext> ctx, QWidget *parent)
: QWidget(parent)
@ -26,10 +27,9 @@ HistoryWidget::HistoryWidget(QSharedPointer<AppContext> ctx, QWidget *parent)
m_contextMenu->addAction("View on block explorer", this, &HistoryWidget::onViewOnBlockExplorer);
// copy menu
m_copyMenu->setIcon(icons()->icon("copy.png"));
m_copyMenu->addAction("Transaction ID", this, [this]{copy(copyField::TxID);});
m_copyMenu->addAction("Description", this, [this]{copy(copyField::Description);});
m_copyMenu->addAction("Date", this, [this]{copy(copyField::Date);});
m_copyMenu->addAction("Description", this, [this]{copy(copyField::Description);});
m_copyMenu->addAction("Amount", this, [this]{copy(copyField::Amount);});
ui->history->setContextMenuPolicy(Qt::CustomContextMenu);
@ -53,7 +53,10 @@ HistoryWidget::HistoryWidget(QSharedPointer<AppContext> ctx, QWidget *parent)
ui->syncNotice->setVisible(config()->get(Config::showHistorySyncNotice).toBool());
ui->history->setHistoryModel(m_model);
m_ctx->wallet->transactionHistoryModel()->amountPrecision = config()->get(Config::amountPrecision).toInt();
connect(websocketNotifier(), &WebsocketNotifier::FiatRatesReceived, [this]{
ui->history->update();
});
// Load view state
QByteArray historyViewState = QByteArray::fromBase64(config()->get(Config::GUI_HistoryViewState).toByteArray());
@ -88,9 +91,9 @@ void HistoryWidget::showContextMenu(const QPoint &point) {
}
menu.addMenu(m_copyMenu);
menu.addAction(icons()->icon("info2.svg"), "Show details", this, &HistoryWidget::showTxDetails);
menu.addAction(icons()->icon("network.png"), "View on block explorer", this, &HistoryWidget::onViewOnBlockExplorer);
menu.addAction("Create tx proof", this, &HistoryWidget::createTxProof);
menu.addAction("Show details", this, &HistoryWidget::showTxDetails);
menu.addAction("View on block explorer", this, &HistoryWidget::onViewOnBlockExplorer);
menu.addAction("Create Tx Proof", this, &HistoryWidget::createTxProof);
menu.exec(ui->history->viewport()->mapToGlobal(point));
}
@ -121,6 +124,7 @@ void HistoryWidget::showTxDetails() {
emit resendTransaction(txid);
});
dialog->show();
dialog->setAttribute(Qt::WA_DeleteOnClose);
}
void HistoryWidget::onViewOnBlockExplorer() {
@ -157,10 +161,13 @@ void HistoryWidget::copy(copyField field) {
switch(field) {
case copyField::TxID:
return tx->hash();
case copyField::Description:
return tx->description();
case copyField::Date:
return tx->timestamp().toString("yyyy-MM-dd HH:mm");
return tx->timestamp().toString(QString("%1 %2").arg(config()->get(Config::dateFormat).toString(),
config()->get(Config::timeFormat).toString()));
case copyField::Amount:
return tx->displayAmount();
return WalletManager::displayAmount(tx->balanceDelta());
default:
return QString("");
}

View file

@ -47,7 +47,6 @@ MainWindow::MainWindow(WindowManager *windowManager, Wallet *wallet, QWidget *pa
// Ensure the destructor is called after closeEvent()
setAttribute(Qt::WA_DeleteOnClose);
m_windowSettings = new Settings(m_ctx, this);
m_windowCalc = new CalcWindow(this);
m_splashDialog = new SplashDialog(this);
@ -68,21 +67,12 @@ MainWindow::MainWindow(WindowManager *windowManager, Wallet *wallet, QWidget *pa
#endif
websocketNotifier()->emitCache(); // Get cached data
// Settings
for (const auto &widget: m_priceTickerWidgets)
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, widget, &PriceTickerWidget::updateDisplay);
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, m_balanceTickerWidget, &BalanceTickerWidget::updateDisplay);
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, m_ctx.get(), &AppContext::onPreferredFiatCurrencyChanged);
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, m_sendWidget, QOverload<>::of(&SendWidget::onPreferredFiatCurrencyChanged));
connect(m_windowSettings, &Settings::amountPrecisionChanged, m_ctx.get(), &AppContext::onAmountPrecisionChanged);
connect(m_windowSettings, &Settings::skinChanged, this, &MainWindow::skinChanged);
QTimer::singleShot(1, [this]{this->updateWidgetIcons();});
connect(m_windowManager, &WindowManager::torSettingsChanged, m_ctx.get(), &AppContext::onTorSettingsChanged);
connect(torManager(), &TorManager::connectionStateChanged, this, &MainWindow::onTorConnectionStateChanged);
this->onTorConnectionStateChanged(torManager()->torConnected);
ColorScheme::updateFromWidget(this);
QTimer::singleShot(1, [this]{this->updateWidgetIcons();});
// Timers
connect(&m_updateBytes, &QTimer::timeout, this, &MainWindow::updateNetStats);
@ -105,6 +95,10 @@ void MainWindow::initStatusBar() {
this->statusBar()->setStyleSheet("QStatusBar::item {border: None;}");
#endif
#if defined(Q_OS_MACOS)
this->patchStylesheetMac();
#endif
this->statusBar()->setFixedHeight(30);
m_statusLabelStatus = new QLabel("Idle", this);
@ -327,6 +321,7 @@ void MainWindow::initMenu() {
connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::menuAboutClicked);
connect(ui->actionOfficialWebsite, &QAction::triggered, [this](){Utils::externalLinkWarning(this, "https://featherwallet.org");});
connect(ui->actionDonate_to_Feather, &QAction::triggered, this, &MainWindow::donateButtonClicked);
connect(ui->actionDocumentation, &QAction::triggered, this, &MainWindow::onShowDocumentaton);
connect(ui->actionReport_bug, &QAction::triggered, this, &MainWindow::onReportBug);
connect(ui->actionShow_debug_info, &QAction::triggered, this, &MainWindow::showDebugInfo);
@ -341,6 +336,7 @@ void MainWindow::initMenu() {
ui->actionSettings->setShortcut(QKeySequence("Ctrl+Alt+S"));
ui->actionUpdate_balance->setShortcut(QKeySequence("Ctrl+U"));
ui->actionShow_Searchbar->setShortcut(QKeySequence("Ctrl+F"));
ui->actionDocumentation->setShortcut(QKeySequence("F1"));
}
void MainWindow::initHome() {
@ -626,9 +622,9 @@ void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QVecto
m_ctx->addCacheTransaction(tx->txid()[0], tx->signedTxToHex(0));
// Show advanced dialog on multi-destination transactions
if (address.size() > 1) {
if (address.size() > 1 || m_ctx->wallet->viewOnly()) {
TxConfAdvDialog dialog_adv{m_ctx, m_ctx->tmpTxDescription, this};
dialog_adv.setTransaction(tx);
dialog_adv.setTransaction(tx, !m_ctx->wallet->viewOnly());
dialog_adv.exec();
return;
}
@ -642,7 +638,7 @@ void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QVecto
break;
}
case QDialog::Accepted:
m_ctx->commitTransaction(tx);
m_ctx->commitTransaction(tx, m_ctx->tmpTxDescription);
break;
}
@ -666,9 +662,10 @@ void MainWindow::onTransactionCommitted(bool status, PendingTransaction *tx, con
if (msgBox.clickedButton() == showDetailsButton) {
this->showHistoryTab();
TransactionInfo *txInfo = m_ctx->wallet->history()->transaction(txid.first());
TxInfoDialog dialog{m_ctx, txInfo, this};
connect(&dialog, &TxInfoDialog::resendTranscation, this, &MainWindow::onResendTransaction);
dialog.exec();
auto *dialog = new TxInfoDialog(m_ctx, txInfo, this);
connect(dialog, &TxInfoDialog::resendTranscation, this, &MainWindow::onResendTransaction);
dialog->show();
dialog->setAttribute(Qt::WA_DeleteOnClose);
}
m_sendWidget->clearFields();
@ -832,9 +829,14 @@ void MainWindow::menuAboutClicked() {
}
void MainWindow::menuSettingsClicked() {
m_windowSettings->raise();
m_windowSettings->show();
m_windowSettings->activateWindow();
Settings settings{m_ctx, this};
for (const auto &widget: m_priceTickerWidgets) {
connect(&settings, &Settings::preferredFiatCurrencyChanged, widget, &PriceTickerWidget::updateDisplay);
}
connect(&settings, &Settings::preferredFiatCurrencyChanged, m_balanceTickerWidget, &BalanceTickerWidget::updateDisplay);
connect(&settings, &Settings::preferredFiatCurrencyChanged, m_sendWidget, QOverload<>::of(&SendWidget::onPreferredFiatCurrencyChanged));
connect(&settings, &Settings::skinChanged, this, &MainWindow::skinChanged);
settings.exec();
}
void MainWindow::menuSignVerifyClicked() {
@ -851,6 +853,10 @@ void MainWindow::skinChanged(const QString &skinName) {
m_windowManager->changeSkin(skinName);
ColorScheme::updateFromWidget(this);
this->updateWidgetIcons();
#if defined(Q_OS_MACOS)
this->patchStylesheetMac();
#endif
}
void MainWindow::updateWidgetIcons() {
@ -1042,7 +1048,7 @@ void MainWindow::showWSNodeExhaustedMessage() {
}
void MainWindow::exportKeyImages() {
QString fn = QFileDialog::getSaveFileName(this, "Save key images to file", QDir::homePath(), "Key Images (*_keyImages)");
QString fn = QFileDialog::getSaveFileName(this, "Save key images to file", QString("%1/%2_%3").arg(QDir::homePath(), this->walletName(), QString::number(QDateTime::currentSecsSinceEpoch())), "Key Images (*_keyImages)");
if (fn.isEmpty()) return;
if (!fn.endsWith("_keyImages")) fn += "_keyImages";
m_ctx->wallet->exportKeyImages(fn, true);
@ -1068,7 +1074,7 @@ void MainWindow::importKeyImages() {
}
void MainWindow::exportOutputs() {
QString fn = QFileDialog::getSaveFileName(this, "Save outputs to file", QDir::homePath(), "Outputs (*_outputs)");
QString fn = QFileDialog::getSaveFileName(this, "Save outputs to file", QString("%1/%2_%3").arg(QDir::homePath(), this->walletName(), QString::number(QDateTime::currentSecsSinceEpoch())), "Outputs (*_outputs)");
if (fn.isEmpty()) return;
if (!fn.endsWith("_outputs")) fn += "_outputs";
m_ctx->wallet->exportOutputs(fn, true);
@ -1464,14 +1470,12 @@ void MainWindow::onCreateDesktopEntry(bool checked) {
QMessageBox::information(this, "Desktop entry", msg);
}
void MainWindow::onShowDocumentaton() {
Utils::externalLinkWarning(this, "https://docs.featherwallet.org");
}
void MainWindow::onReportBug(bool checked) {
QMessageBox::information(this, "Reporting Bugs",
"<body>Please report any bugs as issues on our git repo:<br>\n"
"<a href=\"https://git.featherwallet.org/feather/feather/issues\" style=\"color: #33A4DF\">https://git.featherwallet.org/feather/feather/issues</a><br/><br/>"
"\n"
"Before reporting a bug, upgrade to the most recent version of Feather "
"(latest release or git HEAD), and include the version number in your report. "
"Try to explain not only what the bug is, but how it occurs.</body>");
Utils::externalLinkWarning(this, "https://docs.featherwallet.org/guides/report-an-issue");
}
QString MainWindow::getPlatformTag() {
@ -1590,6 +1594,14 @@ bool MainWindow::verifyPassword() {
return true;
}
void MainWindow::patchStylesheetMac() {
auto patch = Utils::fileOpenQRC(":assets/macStylesheet.patch");
auto patch_text = Utils::barrayToString(patch);
QString styleSheet = qApp->styleSheet() + patch_text;
qApp->setStyleSheet(styleSheet);
}
void MainWindow::toggleSearchbar(bool visible) {
config()->set(Config::showSearchbar, visible);

View file

@ -117,6 +117,7 @@ private slots:
void onExportHistoryCSV(bool checked);
void onExportContactsCSV(bool checked);
void onCreateDesktopEntry(bool checked);
void onShowDocumentaton();
void onReportBug(bool checked);
// offline tx signing
@ -214,6 +215,7 @@ private:
void updateRecentlyOpenedMenu();
void updateWidgetIcons();
bool verifyPassword();
void patchStylesheetMac();
QIcon hardwareDevicePairedIcon();
QIcon hardwareDeviceUnpairedIcon();
@ -222,7 +224,6 @@ private:
WindowManager *m_windowManager;
QSharedPointer<AppContext> m_ctx;
Settings *m_windowSettings = nullptr;
CalcWindow *m_windowCalc = nullptr;
SplashDialog *m_splashDialog = nullptr;

View file

@ -465,10 +465,12 @@
</property>
<addaction name="actionAbout"/>
<addaction name="actionOfficialWebsite"/>
<addaction name="actionDonate_to_Feather"/>
<addaction name="separator"/>
<addaction name="actionDocumentation"/>
<addaction name="actionReport_bug"/>
<addaction name="actionShow_debug_info"/>
<addaction name="separator"/>
<addaction name="actionDonate_to_Feather"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
@ -803,6 +805,11 @@
<string>Address checker</string>
</property>
</action>
<action name="actionDocumentation">
<property name="text">
<string>Documentation</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

View file

@ -7,6 +7,7 @@
#include <QMenu>
#include <QMessageBox>
#include "dialog/PaymentRequestDialog.h"
#include "dialog/QrCodeDialog.h"
#include "model/ModelUtils.h"
#include "utils/Icons.h"
@ -43,8 +44,6 @@ ReceiveWidget::ReceiveWidget(QSharedPointer<AppContext> ctx, QWidget *parent)
m_headerMenu = new QMenu(this);
m_showFullAddressesAction = m_headerMenu->addAction("Show full addresses", this, &ReceiveWidget::setShowFullAddresses);
m_showFullAddressesAction->setCheckable(true);
m_showUsedAddressesAction = m_headerMenu->addAction("Show used addresses", this, &ReceiveWidget::setShowUsedAddresses);
m_showUsedAddressesAction->setCheckable(true);
connect(ui->addresses->header(), &QHeaderView::customContextMenuRequested, this, &ReceiveWidget::showHeaderMenu);
// context menu
@ -60,6 +59,8 @@ ReceiveWidget::ReceiveWidget(QSharedPointer<AppContext> ctx, QWidget *parent)
connect(ui->check_showUsed, &QCheckBox::clicked, this, &ReceiveWidget::setShowUsedAddresses);
connect(ui->check_showHidden, &QCheckBox::clicked, this, &ReceiveWidget::setShowHiddenAddresses);
connect(ui->btn_createPaymentRequest, &QPushButton::clicked, this, &ReceiveWidget::createPaymentRequest);
}
void ReceiveWidget::setSearchbarVisible(bool visible) {
@ -96,9 +97,9 @@ void ReceiveWidget::showContextMenu(const QPoint &point) {
auto *menu = new QMenu(ui->addresses);
menu->addAction(icons()->icon("copy.png"), "Copy address", this, &ReceiveWidget::copyAddress);
menu->addAction(icons()->icon("copy.png"), "Copy label", this, &ReceiveWidget::copyLabel);
menu->addAction(icons()->icon("edit.png"), "Edit label", this, &ReceiveWidget::editLabel);
menu->addAction("Copy address", this, &ReceiveWidget::copyAddress);
menu->addAction("Copy label", this, &ReceiveWidget::copyLabel);
menu->addAction("Edit label", this, &ReceiveWidget::editLabel);
if (isUsed) {
menu->addAction(m_showTransactionsAction);
@ -106,7 +107,7 @@ void ReceiveWidget::showContextMenu(const QPoint &point) {
QStringList hiddenAddresses = this->getHiddenAddresses();
if (hiddenAddresses.contains(address)) {
menu->addAction("Show address", this, &ReceiveWidget::showAddress);
menu->addAction("Unhide address", this, &ReceiveWidget::showAddress);
} else {
menu->addAction("Hide address", this, &ReceiveWidget::hideAddress);
}
@ -118,6 +119,18 @@ void ReceiveWidget::showContextMenu(const QPoint &point) {
menu->popup(ui->addresses->viewport()->mapToGlobal(point));
}
void ReceiveWidget::createPaymentRequest() {
QModelIndex index = ui->addresses->currentIndex();
if (!index.isValid()) {
return;
}
QString address = index.model()->data(index.siblingAtColumn(SubaddressModel::Address), Qt::UserRole).toString();
PaymentRequestDialog dialog{this, m_ctx, address};
dialog.exec();
}
void ReceiveWidget::onShowTransactions() {
QModelIndex index = ui->addresses->currentIndex();
if (!index.isValid()) {
@ -190,6 +203,7 @@ void ReceiveWidget::updateQrCode(){
QModelIndex index = ui->addresses->currentIndex();
if (!index.isValid()) {
ui->qrCode->clear();
ui->btn_createPaymentRequest->hide();
return;
}
@ -199,6 +213,7 @@ void ReceiveWidget::updateQrCode(){
int width = ui->qrCode->width() - 4;
if (qrc.isValid()) {
ui->qrCode->setPixmap(qrc.toPixmap(1).scaled(width, width, Qt::KeepAspectRatio));
ui->btn_createPaymentRequest->show();
}
}

View file

@ -39,6 +39,7 @@ public slots:
void setShowHiddenAddresses(bool show);
void setSearchFilter(const QString &filter);
void onShowTransactions();
void createPaymentRequest();
signals:
void showTransactions(const QString& address);

View file

@ -69,6 +69,13 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_createPaymentRequest">
<property name="text">
<string>Payment Request</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">

View file

@ -26,13 +26,13 @@ SendWidget::SendWidget(QSharedPointer<AppContext> ctx, QWidget *parent)
QString amount_rx = R"(^\d{0,8}[\.,]\d{0,12}|(all)$)";
QRegExp rx;
rx.setPattern(amount_rx);
QValidator *validator = new QRegExpValidator(rx, this);
QValidator *validator = new QRegExpValidator(rx, this);
ui->lineAmount->setValidator(validator);
connect(m_ctx.get(), &AppContext::initiateTransaction, this, &SendWidget::onInitiateTransaction);
connect(m_ctx.get(), &AppContext::endTransaction, this, &SendWidget::onEndTransaction);
connect(m_ctx.get(), &AppContext::openAliasResolved, this, &SendWidget::onOpenAliasResolved);
connect(m_ctx.get(), &AppContext::openAliasResolveError, this, &SendWidget::onOpenAliasResolveError);
connect(WalletManager::instance(), &WalletManager::openAliasResolved, this, &SendWidget::onOpenAliasResolved);
connect(ui->btnScan, &QPushButton::clicked, this, &SendWidget::scanClicked);
connect(ui->btnSend, &QPushButton::clicked, this, &SendWidget::sendClicked);
@ -139,8 +139,8 @@ void SendWidget::sendClicked() {
QString currency = ui->comboCurrencySelection->currentText();
QString recipient = ui->lineAddress->text().simplified().remove(' ');
QString description = ui->lineDescription->text();
if(recipient.isEmpty()) {
QMessageBox::warning(this, "Malformed recipient", "The recipient address was not correct");
if (recipient.isEmpty()) {
QMessageBox::warning(this, "Malformed recipient", "No destination address was entered.");
return;
}
@ -178,7 +178,7 @@ void SendWidget::sendClicked() {
amount = this->amount();
bool sendAll = (ui->lineAmount->text() == "all");
if (amount == 0 && !sendAll) {
QMessageBox::warning(this, "Amount error", "Invalid amount specified.");
QMessageBox::warning(this, "Amount error", "No amount was entered.");
return;
}
m_ctx->onCreateTransaction(recipient, amount, description, sendAll);
@ -193,8 +193,9 @@ void SendWidget::sendClicked() {
}
void SendWidget::aliasClicked() {
auto address = ui->lineAddress->text();
m_ctx->onOpenAliasResolve(address);
ui->btn_openAlias->setEnabled(false);
auto alias = ui->lineAddress->text();
WalletManager::instance()->resolveOpenAliasAsync(alias);
}
void SendWidget::clearClicked() {
@ -254,7 +255,26 @@ double SendWidget::amountDouble() {
return amount / constants::cdiv;
}
void SendWidget::onOpenAliasResolved(const QString &address, const QString &openAlias) {
void SendWidget::onOpenAliasResolved(const QString &openAlias, const QString &address, bool dnssecValid) {
ui->btn_openAlias->setEnabled(true);
if (address.isEmpty()) {
this->onOpenAliasResolveError("Could not resolve OpenAlias.");
return;
}
if (!dnssecValid) {
this->onOpenAliasResolveError("Address found, but the DNSSEC signatures could not be verified, so this address may be spoofed.");
return;
}
bool valid = WalletManager::addressValid(address, constants::networkType);
if (!valid) {
this->onOpenAliasResolveError(QString("Address validation error. Perhaps it is of the wrong network type.\n\n"
"OpenAlias: %1\nAddress: %2").arg(openAlias, address));
return;
}
this->fill(address, openAlias);
ui->btn_openAlias->hide();
}

View file

@ -38,7 +38,7 @@ public slots:
void fillAddress(const QString &address);
void updateConversionLabel();
void onOpenAliasResolveError(const QString &err);
void onOpenAliasResolved(const QString &address, const QString &openAlias);
void onOpenAliasResolved(const QString &openAlias, const QString &address, bool dnssecValid);
void onPreferredFiatCurrencyChanged();
void onInitiateTransaction();

View file

@ -79,8 +79,9 @@ Settings::Settings(QSharedPointer<AppContext> ctx, QWidget *parent)
// Preferred fiat currency combobox
QStringList fiatCurrencies;
for (int index = 0; index < ui->comboBox_fiatCurrency->count(); index++)
for (int index = 0; index < ui->comboBox_fiatCurrency->count(); index++) {
fiatCurrencies << ui->comboBox_fiatCurrency->itemText(index);
}
auto preferredFiatCurrency = config()->get(Config::preferredFiatCurrency).toString();
if(!preferredFiatCurrency.isEmpty())

View file

@ -102,14 +102,6 @@ void WindowManager::startupWarning() {
this->showWarningMessageBox("Warning", worthlessWarning.arg("testnet"));
config()->set(Config::warnOnTestnet, false);
}
// Beta
if (config()->get(Config::warnOnAlpha).toBool()) {
QString warning = "Feather Wallet is currently in beta.\n\nPlease report any bugs "
"you encounter on our Git repository, IRC or on /r/FeatherWallet.";
this->showWarningMessageBox("Beta warning", warning);
config()->set(Config::warnOnAlpha, false);
}
}
void WindowManager::showWarningMessageBox(const QString &title, const QString &message) {
@ -460,8 +452,6 @@ void WindowManager::initTor() {
torManager()->init();
torManager()->start();
connect(torManager(), &TorManager::connectionStateChanged, &websocketNotifier()->websocketClient, &WebsocketClient::onToggleConnect);
this->onTorSettingsChanged();
}

View file

@ -55,9 +55,6 @@ AppContext::AppContext(Wallet *wallet)
this->updateBalance();
// force trigger preferredFiat signal for history model
this->onPreferredFiatCurrencyChanged(config()->get(Config::preferredFiatCurrency).toString());
connect(this->wallet->history(), &TransactionHistory::txNoteChanged, [this]{
this->wallet->history()->refresh(this->wallet->currentSubaddressAccount());
});
@ -75,7 +72,8 @@ void AppContext::onCreateTransaction(const QString &address, quint64 amount, con
quint64 unlocked_balance = this->wallet->unlockedBalance();
if (!all && amount > unlocked_balance) {
emit createTransactionError("Not enough money to spend");
emit createTransactionError(QString("Not enough money to spend.\n\n"
"Spendable balance: %1").arg(WalletManager::displayAmount(unlocked_balance)));
return;
} else if (unlocked_balance == 0) {
emit createTransactionError("No money to spend");
@ -132,14 +130,14 @@ void AppContext::onCancelTransaction(PendingTransaction *tx, const QVector<QStri
this->wallet->disposeTransaction(tx);
}
void AppContext::commitTransaction(PendingTransaction *tx) {
void AppContext::commitTransaction(PendingTransaction *tx, const QString &description) {
// Nodes - even well-connected, properly configured ones - consistently fail to relay transactions
// To mitigate transactions failing we just send the transaction to every node we know about over Tor
if (config()->get(Config::multiBroadcast).toBool()) {
this->onMultiBroadcast(tx);
}
this->wallet->commitTransactionAsync(tx);
this->wallet->commitTransactionAsync(tx, description);
}
void AppContext::onMultiBroadcast(PendingTransaction *tx) {
@ -167,21 +165,6 @@ QString AppContext::getCacheTransaction(const QString &txid) const {
return txHex;
}
// ################## Models ##################
void AppContext::onPreferredFiatCurrencyChanged(const QString &symbol) {
auto *model = this->wallet->transactionHistoryModel();
if (model != nullptr) {
model->preferredFiatSymbol = symbol;
}
}
void AppContext::onAmountPrecisionChanged(int precision) {
auto *model = this->wallet->transactionHistoryModel();
if (!model) return;
model->amountPrecision = precision;
}
// ################## Device ##################
void AppContext::onDeviceButtonRequest(quint64 code) {
@ -228,48 +211,6 @@ void AppContext::onSetRestoreHeight(quint64 height){
emit customRestoreHeightSet(height);
}
void AppContext::onOpenAliasResolve(const QString &openAlias) {
// @TODO: calling this freezes for about 1-2 seconds :/
const auto result = WalletManager::instance()->resolveOpenAlias(openAlias);; // TODO: async call
const auto spl = result.split("|");
auto msg = QString("");
if(spl.count() != 2) {
msg = "Internal error";
emit openAliasResolveError(msg);
return;
}
const auto &status = spl.at(0);
const auto &address = spl.at(1);
const auto valid = WalletManager::addressValid(address, constants::networkType);
if(status == "false"){
if(valid){
msg = "Address found, but the DNSSEC signatures could not be verified, so this address may be spoofed";
emit openAliasResolveError(msg);
return;
} else {
msg = "No valid address found at this OpenAlias address, but the DNSSEC signatures could not be verified, so this may be spoofed";
emit openAliasResolveError(msg);
return;
}
} else if(status != "true") {
msg = "Internal error";
emit openAliasResolveError(msg);
return;
}
if(valid){
emit openAliasResolved(address, openAlias);
return;
}
msg = QString("Address validation error.");
if(!address.isEmpty())
msg += QString(" Perhaps it is of the wrong network type."
"\n\nOpenAlias: %1\nAddress: %2").arg(openAlias).arg(address);
emit openAliasResolveError(msg);
}
void AppContext::stopTimers() {
m_storeTimer.stop();
}
@ -365,13 +306,6 @@ void AppContext::onTransactionCreated(PendingTransaction *tx, const QVector<QStr
}
void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid){
if (status) {
for (const auto &entry: txid) {
this->wallet->setUserNote(entry, this->tmpTxDescription);
}
this->tmpTxDescription = "";
}
// Store wallet immediately so we don't risk losing tx key if wallet crashes
this->wallet->store();

View file

@ -9,7 +9,6 @@
#include "utils/os/whonix.h"
#include "utils/networking.h"
#include "utils/wsclient.h"
#include "utils/FeatherSeed.h"
#include "utils/daemonrpc.h"
#include "utils/RestoreHeightLookup.h"
@ -38,7 +37,7 @@ public:
// libwalletqt
bool refreshed = false;
void commitTransaction(PendingTransaction *tx);
void commitTransaction(PendingTransaction *tx, const QString &description="");
void syncStatusUpdated(quint64 height, quint64 target);
void updateBalance();
void refreshModels();
@ -56,10 +55,7 @@ public slots:
void onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address);
void onSweepOutputs(const QVector<QString> &keyImages, QString address, bool churn, int outputs);
void onCreateTransactionError(const QString &msg);
void onOpenAliasResolve(const QString &openAlias);
void onSetRestoreHeight(quint64 height);
void onPreferredFiatCurrencyChanged(const QString &symbol);
void onAmountPrecisionChanged(int precision);
void onMultiBroadcast(PendingTransaction *tx);
void onDeviceButtonRequest(quint64 code);
void onDeviceButtonPressed();
@ -89,8 +85,6 @@ signals:
void createTransactionError(QString message);
void createTransactionCancelled(const QVector<QString> &address, quint64 amount);
void createTransactionSuccess(PendingTransaction *tx, const QVector<QString> &address);
void openAliasResolveError(const QString &msg);
void openAliasResolved(const QString &address, const QString &openAlias);
void setRestoreHeightError(const QString &msg);
void customRestoreHeightSet(int height);
void initiateTransaction();

View file

@ -2,8 +2,8 @@
<qresource prefix="/">
<file>assets/about.txt</file>
<file>assets/ack.txt</file>
<file>assets/contributors.txt</file>
<file>assets/feather.desktop</file>
<file>assets/macStylesheet.patch</file>
<file>assets/nodes.json</file>
<file>assets/gpg_keys/featherwallet.asc</file>
<file>assets/images/appicons/32x32.png</file>
@ -35,6 +35,7 @@
<file>assets/images/copy.png</file>
<file>assets/images/cutexmrfox.png</file>
<file>assets/images/edit.png</file>
<file>assets/images/external-link.svg</file>
<file>assets/images/exchange.png</file>
<file>assets/images/exchange_white.png</file>
<file>assets/images/expired.png</file>

View file

@ -1,12 +1,5 @@
Feather <feather_version> (<feather_git_head>)
Website: https://featherwallet.org
E-mail: dev@featherwallet.org
Created by dsc, tobtoht, and contributors.
Uses icons from the Icons8 icon pack (icons8.com).
Copyright (c) 2020-<current_year>, The Monero Project
All rights reserved.

View file

@ -1,12 +1,82 @@
The wallet UI is heavily inspired by Electrum. We would like to recognize Thomas Voegtlin for his pioneering work on Bitcoin.
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 the monero-seed libary written by Tevador for 14 word mnemonic seeds.
Wizard banner art was adapted from a paper wallet design by the themonera (themonera.art).
Wizard banner art was adapted from a paper wallet design by the themonera (themonera.art).
Initial CMake support for the Monero GUI was coded by TheCharlatan/xiphon.
• Uses icons from the Icons8 icon pack (icons8.com).
Huge thanks to nioc, fluffypony, wowario, thrmo for help during development.
• Initial CMake support for the Monero GUI was coded by TheCharlatan/xiphon.
Some more shoutouts for people for hosting nodes and/or having good ideas:
dnale0r, dEBRUYNE, binaryFate, lza_menace, jwinterm, kico, wowario
Shoutouts:
Alex_LocalMonero
anhdres
authentic
BigNastyHammer
binaryFate
bits-of-change
blasty
boldsuck
boogerlad
bruh
btsfav
csd
CzarekNakamoto
deanguss
dEBRUYNE
dnale0r
fluffypony
geonic
GhostintheQubes
GriftKilla
jberman
john_r365
jwinterm
kayabaNerve
kico
kinghat
lozbek
Lyza
lza_menace
Matt Smith
MoneroArbo
moneromooo
mrdeveloper
Nekun
netrik182
nikg83
nioc
noobmaximus
n-peugnet
onionltd
Paris
phoenix1213
qvqc
rating89us
rbrunner
rehrar
rottenwheel
samsunggalaxyplayer
samwhiskey
scoobybejesus
selsta
sethsimmons
Shakybeats
SmashTR
SovereignStreetArt
spoontechtips
stickyf00t
strace
rottenwheel
tevador
thrmo
tinkerwithtor
Vespco
viperperidot
witchman05
wowario
xiphon
xmrscott
xubuntu
yanmaani

View file

@ -1,5 +0,0 @@
dsc
tobtoht
selsta
Diego Salazar
Matt Smith

View file

@ -0,0 +1 @@
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px" height="24px"><path d="M 5 3 C 3.9069372 3 3 3.9069372 3 5 L 3 19 C 3 20.093063 3.9069372 21 5 21 L 19 21 C 20.093063 21 21 20.093063 21 19 L 21 12 L 19 12 L 19 19 L 5 19 L 5 5 L 12 5 L 12 3 L 5 3 z M 14 3 L 14 5 L 17.585938 5 L 8.2929688 14.292969 L 9.7070312 15.707031 L 19 6.4140625 L 19 10 L 21 10 L 21 3 L 14 3 z"/></svg>

After

Width:  |  Height:  |  Size: 415 B

View file

@ -0,0 +1,31 @@
/* From electrum/gui/qt/stylesheet_patcher.py */
StatusBarButton {
background-color: transparent;
border: 1px solid transparent;
border-radius: 4px;
margin: 0px;
padding: 2px;
}
StatusBarButton:checked {
background-color: transparent;
border: 1px solid #1464A0;
}
StatusBarButton:checked:disabled {
border: 1px solid #14506E;
}
StatusBarButton:pressed {
margin: 1px;
background-color: transparent;
border: 1px solid #1464A0;
}
StatusBarButton:disabled {
border: none;
}
StatusBarButton:hover {
border: 1px solid #148CD2;
}

View file

@ -1,16 +1,22 @@
{
"mainnet": {
"tor": [
"sfprpc5klzs5vyitq2mrooicgk2wcs5ho2nm3niqduvzn5o6ylaslaqd.onion:18089",
"sfprpc2fws6ltnq4hyr7lvpul3nank5layd7q7tyc5h4gy4h77gtabad.onion:18089",
"mxcd4577fldb3ppzy7obmmhnu3tf57gbcbd4qhwr2kxyjj2qi3dnbfqd.onion:18081",
"moneroxmrxw44lku6qniyarpwgznpcwml4drq7vb24ppatlcg4kmxpqd.onion:18089",
"rbpgdckle3h3vi4wwwrh75usqtoc5r3alohy7yyx57isynvay63nacyd.onion:18089",
"6dsdenp6vjkvqzy4wzsnzn6wixkdzihx3khiumyzieauxuxslmcaeiad.onion:18081",
"56wl7y2ebhamkkiza4b7il4mrzwtyvpdym7bm2bkg3jrei2je646k3qd.onion:18089",
"melo7jwjspngus3xhbt5kxeqc4njhokyvh55jfmehplglgmb7a6rb6yd.onion:18081"
"melo7jwjspngus3xhbt5kxeqc4njhokyvh55jfmehplglgmb7a6rb6yd.onion:18081",
"ip4zpbps7unk6xhlanqtw24f75akfbl3upeckfjqjks7ftfnk4i73oid.onion:18081",
"ghziyspoobhmp5oun2xcomrmetqiwbvuaegmte3s47nnqv7hkaa64sid.onion:18089",
"xmrnodesarnt4w35aqmu66aart3o324yw6qbnv6pglpof6uqaydzk5id.onion:18081",
"usexmr2eeexmlwpuvsfe6tyjmdqliplb2b7uxju6yrrziq3n7fksnxyd.onion:18081"
],
"clearnet": [
"node.melo.tools:18081",
"node-1.sethsimmons.me:18089",
"node.sethforprivacy.com:18089",
"node2.sethforprivacy.com:18089",
"selsta1.featherwallet.net:18081",
"selsta2.featherwallet.net:18081",
"node.monerooutreach.org:18081",

View file

@ -113,4 +113,12 @@ ClickableLabel::~ClickableLabel() = default;
void ClickableLabel::mousePressEvent(QMouseEvent* event) {
emit clicked();
}
WindowModalDialog::WindowModalDialog(QWidget *parent)
: QDialog(parent)
{
#ifndef Q_OS_MACOS
this->setWindowModality(Qt::WindowModal);
#endif
}

View file

@ -121,4 +121,11 @@ protected:
};
class WindowModalDialog : public QDialog {
Q_OBJECT
public:
explicit WindowModalDialog(QWidget *parent);
};
#endif //FEATHER_COMPONENTS_H

View file

@ -8,12 +8,11 @@
#include "utils/Utils.h"
AboutDialog::AboutDialog(QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::AboutDialog)
, m_model(new QStringListModel(this))
{
ui->setupUi(this);
this->setWindowIcon(QIcon("://assets/images/appicons/64x64.png"));
// cute fox (c) Diego "rehrar" Salazar :-D
QPixmap p(":assets/images/cutexmrfox.png");
ui->aboutImage->setPixmap(p.scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation));
@ -28,14 +27,6 @@ AboutDialog::AboutDialog(QWidget *parent)
auto ack_text = Utils::barrayToString(ack);
ui->ackText->setText(ack_text);
QString contributors = Utils::barrayToString(Utils::fileOpenQRC(":assets/contributors.txt"));
QStringList contributor_list = contributors.split("\n");
m_model->setStringList(contributor_list);
ui->authorView->setHeaderHidden(true);
ui->authorView->setModel(this->m_model);
ui->authorView->header()->setSectionResizeMode(QHeaderView::Stretch);
this->adjustSize();
}

View file

@ -7,11 +7,13 @@
#include <QDialog>
#include <QStringListModel>
#include "components.h"
namespace Ui {
class AboutDialog;
}
class AboutDialog : public QDialog
class AboutDialog : public WindowModalDialog
{
Q_OBJECT
@ -21,7 +23,6 @@ public:
private:
QScopedPointer<Ui::AboutDialog> ui;
QStringListModel *m_model;
};
#endif // FEATHER_ABOUT_H

View file

@ -43,7 +43,7 @@
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>a free, open-source Monero wallet</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Feather&lt;/span&gt;: a free, open-source Monero wallet&lt;/p&gt;&lt;p&gt;Created by dsc, tobtoht and contributors&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@ -71,18 +71,144 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="Authors">
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Authors</string>
<string>Info</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTreeView" name="authorView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Website:</string>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Docs:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Git:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Reddit:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>IRC:</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Matrix:</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Email:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_8">
<property name="text">
<string>featherwallet.org</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_10">
<property name="text">
<string>docs.featherwallet.org</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_11">
<property name="text">
<string>git.featherwallet.org</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_12">
<property name="text">
<string>/r/FeatherWallet</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_13">
<property name="text">
<string>#feather on OFTC</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_14">
<property name="text">
<string>#_oftc_#feather:matrix.org</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_15">
<property name="text">
<string>dev@featherwallet.org</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Twitter:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_17">
<property name="text">
<string>@featherwallet</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>

View file

@ -11,7 +11,7 @@
#include "utils/Icons.h"
AccountSwitcherDialog::AccountSwitcherDialog(QSharedPointer<AppContext> ctx, QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::AccountSwitcherDialog)
, m_ctx(std::move(ctx))
, m_model(m_ctx->wallet->subaddressAccountModel())
@ -25,6 +25,8 @@ AccountSwitcherDialog::AccountSwitcherDialog(QSharedPointer<AppContext> ctx, QWi
ui->label_totalBalance->setFont(ModelUtils::getMonospaceFont());
ui->label_totalBalance->setText(WalletManager::displayAmount(m_ctx->wallet->balanceAll()));
this->setWindowModality(Qt::WindowModal);
ui->accounts->setModel(m_proxyModel);
ui->accounts->setContextMenuPolicy(Qt::CustomContextMenu);
ui->accounts->setSelectionMode(QAbstractItemView::SingleSelection);

View file

@ -7,13 +7,14 @@
#include <QDialog>
#include "appcontext.h"
#include "components.h"
#include "model/SubaddressAccountModel.h"
namespace Ui {
class AccountSwitcherDialog;
}
class AccountSwitcherDialog : public QDialog
class AccountSwitcherDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -8,7 +8,7 @@
#include "model/ModelUtils.h"
BalanceDialog::BalanceDialog(QWidget *parent, Wallet *wallet)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::BalanceDialog)
{
ui->setupUi(this);

View file

@ -6,13 +6,14 @@
#include <QDialog>
#include "components.h"
#include "libwalletqt/Wallet.h"
namespace Ui {
class BalanceDialog;
}
class BalanceDialog : public QDialog
class BalanceDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -8,7 +8,7 @@
#include "utils/config.h"
CalcConfigDialog::CalcConfigDialog(QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::CalcConfigDialog)
{
ui->setupUi(this);

View file

@ -7,11 +7,13 @@
#include <QDialog>
#include <QListWidget>
#include "components.h"
namespace Ui {
class CalcConfigDialog;
}
class CalcConfigDialog : public QDialog
class CalcConfigDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -5,7 +5,7 @@
#include "ContactsDialog.h"
ContactsDialog::ContactsDialog(QWidget *parent, const QString &address, const QString &name)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::ContactsDialog)
{
ui->setupUi(this);

View file

@ -6,11 +6,13 @@
#include <QDialog>
#include "components.h"
namespace Ui {
class ContactsDialog;
}
class ContactsDialog : public QDialog
class ContactsDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -11,7 +11,7 @@
#include "utils/WebsocketNotifier.h"
DebugInfoDialog::DebugInfoDialog(QSharedPointer<AppContext> ctx, QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::DebugInfoDialog)
, m_ctx(std::move(ctx))
{
@ -139,7 +139,7 @@ void DebugInfoDialog::copyToClipboad() {
text += QString("Seed type: %1 \n").arg(ui->label_seedType->text());
text += QString("Device type: %1 \n").arg(ui->label_deviceType->text());
text += QString("View only: %1 \n").arg(ui->label_viewOnly->text());
text += QString("Primary only: %1 \n").arg(ui->label_primaryOnly->text());
text += QString("Primary only: %1 \n").arg(ui->label_primaryOnly->text());
text += QString("Operating system: %1 \n").arg(ui->label_OS->text());
text += QString("Timestamp: %1 \n").arg(ui->label_timestamp->text());

View file

@ -7,13 +7,14 @@
#include <QDialog>
#include "appcontext.h"
#include "components.h"
#include "libwalletqt/Wallet.h"
namespace Ui {
class DebugInfoDialog;
}
class DebugInfoDialog : public QDialog
class DebugInfoDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -5,7 +5,7 @@
#include "ui_InfoDialog.h"
InfoDialog::InfoDialog(QWidget *parent, const QString &title, const QString &infoData)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::InfoDialog)
{
ui->setupUi(this);

View file

@ -6,11 +6,13 @@
#include <QDialog>
#include "components.h"
namespace Ui {
class InfoDialog;
}
class InfoDialog : public QDialog
class InfoDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -5,7 +5,7 @@
#include "ui_KeysDialog.h"
KeysDialog::KeysDialog(QSharedPointer<AppContext> ctx, QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::KeysDialog)
{
ui->setupUi(this);

View file

@ -7,12 +7,13 @@
#include <QDialog>
#include "appcontext.h"
#include "components.h"
namespace Ui {
class KeysDialog;
}
class KeysDialog : public QDialog
class KeysDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -8,7 +8,7 @@
#include "utils/Utils.h"
LocalMoneroInfoDialog::LocalMoneroInfoDialog(QWidget *parent, LocalMoneroModel *model, int row)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::LocalMoneroInfoDialog)
, m_model(model)
, m_row(row)

View file

@ -7,13 +7,14 @@
#include <QDialog>
#include <QLabel>
#include "components.h"
#include "model/LocalMoneroModel.h"
namespace Ui {
class LocalMoneroInfoDialog;
}
class LocalMoneroInfoDialog : public QDialog
class LocalMoneroInfoDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -8,7 +8,7 @@
#include "utils/Utils.h"
OutputInfoDialog::OutputInfoDialog(CoinsInfo *cInfo, QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::OutputInfoDialog)
{
ui->setupUi(this);

View file

@ -6,6 +6,7 @@
#include <QDialog>
#include "components.h"
#include "libwalletqt/Coins.h"
#include "libwalletqt/CoinsInfo.h"
@ -13,7 +14,7 @@ namespace Ui {
class OutputInfoDialog;
}
class OutputInfoDialog : public QDialog
class OutputInfoDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -7,7 +7,7 @@
#include "libwalletqt/WalletManager.h"
OutputSweepDialog::OutputSweepDialog(QWidget *parent, quint64 amount)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::OutputSweepDialog)
, m_amount(amount)
{

View file

@ -6,13 +6,14 @@
#include <QDialog>
#include "components.h"
#include "libwalletqt/CoinsInfo.h"
namespace Ui {
class OutputSweepDialog;
}
class OutputSweepDialog : public QDialog
class OutputSweepDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -7,7 +7,7 @@
#include <QMessageBox>
PasswordChangeDialog::PasswordChangeDialog(QWidget *parent, Wallet *wallet)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::PasswordChangeDialog)
, m_wallet(wallet)
{

View file

@ -6,13 +6,14 @@
#include <QDialog>
#include "components.h"
#include "libwalletqt/Wallet.h"
namespace Ui {
class PasswordChangeDialog;
}
class PasswordChangeDialog : public QDialog
class PasswordChangeDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -7,7 +7,7 @@
#include "utils/Icons.h"
PasswordDialog::PasswordDialog(const QString &walletName, bool incorrectPassword, QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::PasswordDialog)
{
ui->setupUi(this);

View file

@ -6,11 +6,13 @@
#include <QDialog>
#include "components.h"
namespace Ui {
class PasswordDialog;
}
class PasswordDialog : public QDialog
class PasswordDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -0,0 +1,82 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project.
#include "PaymentRequestDialog.h"
#include "ui_PaymentRequestDialog.h"
#include <QClipboard>
#include <QFileDialog>
#include <QMessageBox>
#include <QRegExpValidator>
#include "WalletManager.h"
PaymentRequestDialog::PaymentRequestDialog(QWidget *parent, QSharedPointer<AppContext> ctx, QString address)
: WindowModalDialog(parent)
, ui(new Ui::PaymentRequestDialog)
, m_ctx(std::move(ctx))
, m_address(std::move(address))
{
ui->setupUi(this);
QString amount_rx = R"(^\d{0,8}[\.]\d{0,12}|(all)$)";
QRegExp rx;
rx.setPattern(amount_rx);
QValidator *validator = new QRegExpValidator(rx, this);
ui->line_amountXMR->setValidator(validator);
connect(ui->line_amountXMR, &QLineEdit::textChanged, this, &PaymentRequestDialog::updatePaymentRequest);
connect(ui->line_description, &QLineEdit::textChanged, this, &PaymentRequestDialog::updatePaymentRequest);
connect(ui->line_recipient, &QLineEdit::textChanged, this, &PaymentRequestDialog::updatePaymentRequest);
connect(ui->btn_copyLink, &QPushButton::clicked, this, &PaymentRequestDialog::copyLink);
connect(ui->btn_copyImage, &QPushButton::clicked, this, &PaymentRequestDialog::copyImage);
connect(ui->btn_saveImage, &QPushButton::clicked, this, &PaymentRequestDialog::saveImage);
this->updatePaymentRequest();
ui->line_amountXMR->setFocus();
this->adjustSize();
}
void PaymentRequestDialog::updatePaymentRequest() {
QString description = ui->line_description->text();
QString recipient = ui->line_recipient->text();
quint64 amount = WalletManager::amountFromString(ui->line_amountXMR->text());
QString uri = m_ctx->wallet->make_uri(m_address, amount, description, recipient);
ui->line_paymentRequestUri->setText(uri);
ui->line_paymentRequestUri->setCursorPosition(0);
// TODO: memory leak, cba to refactor now
m_qrCode = new QrCode(uri, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::MEDIUM);
if (m_qrCode->isValid()) {
ui->qrWidget->setQrCode(m_qrCode);
}
}
void PaymentRequestDialog::copyLink() {
Utils::copyToClipboard(ui->line_paymentRequestUri->text());
QMessageBox::information(this, "Information", "Payment request link copied to clipboard.");
}
void PaymentRequestDialog::copyImage() {
QApplication::clipboard()->setPixmap(m_qrCode->toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio));
QMessageBox::information(this, "Information", "QR code copied to clipboard.");
}
void PaymentRequestDialog::saveImage() {
QString filename = QFileDialog::getSaveFileName(this, "Select where to save file", QDir::current().filePath("qrcode.png"));
if (filename.isEmpty()) {
return;
}
QFile file(filename);
file.open(QIODevice::WriteOnly);
m_qrCode->toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio).save(&file, "PNG");
QMessageBox::information(this, "Information", "QR code saved to file");
}
PaymentRequestDialog::~PaymentRequestDialog() = default;

View file

@ -0,0 +1,38 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project.
#ifndef FEATHER_PAYMENTREQUESTDIALOG_H
#define FEATHER_PAYMENTREQUESTDIALOG_H
#include <QDialog>
#include "appcontext.h"
#include "components.h"
#include "qrcode/QrCode.h"
namespace Ui {
class PaymentRequestDialog;
}
class PaymentRequestDialog : public WindowModalDialog
{
Q_OBJECT
public:
explicit PaymentRequestDialog(QWidget *parent, QSharedPointer<AppContext> ctx, QString address);
~PaymentRequestDialog() override;
private slots:
void updatePaymentRequest();
void copyLink();
void copyImage();
void saveImage();
private:
QScopedPointer<Ui::PaymentRequestDialog> ui;
QSharedPointer<AppContext> m_ctx;
QString m_address;
QrCode *m_qrCode;
};
#endif //FEATHER_PAYMENTREQUESTDIALOG_H

View file

@ -0,0 +1,203 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PaymentRequestDialog</class>
<widget class="QDialog" name="PaymentRequestDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>658</width>
<height>761</height>
</rect>
</property>
<property name="windowTitle">
<string>Create Payment Request</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QrCodeWidget" name="qrWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="line_paymentRequestUri">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Amount:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="line_amountXMR">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>XMR</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Description:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="line_description"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Your name:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="line_recipient"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btn_copyLink">
<property name="text">
<string>Copy Link</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_copyImage">
<property name="text">
<string>Copy Image</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_saveImage">
<property name="text">
<string>Save Image</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="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QrCodeWidget</class>
<extends>QWidget</extends>
<header>widgets/QrCodeWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PaymentRequestDialog</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>PaymentRequestDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -9,7 +9,7 @@
#include <QMessageBox>
QrCodeDialog::QrCodeDialog(QWidget *parent, QrCode *qrCode, const QString &title)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::QrCodeDialog)
{
ui->setupUi(this);

View file

@ -6,6 +6,7 @@
#include <QDialog>
#include "components.h"
#include "qrcode/QrCode.h"
#include "widgets/QrCodeWidget.h"
@ -13,7 +14,7 @@ namespace Ui {
class QrCodeDialog;
}
class QrCodeDialog : public QDialog
class QrCodeDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -8,7 +8,7 @@
#include <QDialogButtonBox>
RestoreHeightDialog::RestoreHeightDialog(QWidget *parent, quint64 currentRestoreHeight)
: QDialog(parent)
: WindowModalDialog(parent)
, m_restoreHeightWidget(new RestoreHeightWidget(this))
{
auto *layout = new QVBoxLayout(this);

View file

@ -6,9 +6,10 @@
#include <QDialog>
#include "components.h"
#include "widgets/RestoreHeightWidget.h"
class RestoreHeightDialog : public QDialog
class RestoreHeightDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -7,7 +7,7 @@
#include "constants.h"
SeedDialog::SeedDialog(QSharedPointer<AppContext> ctx, QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::SeedDialog)
, m_ctx(std::move(ctx))
{

View file

@ -7,12 +7,13 @@
#include <QDialog>
#include "appcontext.h"
#include "components.h"
namespace Ui {
class SeedDialog;
}
class SeedDialog : public QDialog
class SeedDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -9,7 +9,7 @@
#include "utils/Utils.h"
SignVerifyDialog::SignVerifyDialog(Wallet *wallet, QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::SignVerifyDialog)
, m_wallet(wallet)
{

View file

@ -6,13 +6,14 @@
#include <QDialog>
#include "components.h"
#include "libwalletqt/Wallet.h"
namespace Ui {
class SignVerifyDialog;
}
class SignVerifyDialog : public QDialog
class SignVerifyDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -7,7 +7,7 @@
#include "utils/Icons.h"
SplashDialog::SplashDialog(QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::SplashDialog)
{
ui->setupUi(this);

View file

@ -6,11 +6,13 @@
#include <QDialog>
#include "components.h"
namespace Ui {
class SplashDialog;
}
class SplashDialog : public QDialog
class SplashDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -9,7 +9,7 @@
#include "utils/NetworkManager.h"
TxBroadcastDialog::TxBroadcastDialog(QWidget *parent, QSharedPointer<AppContext> ctx, const QString &transactionHex)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::TxBroadcastDialog)
, m_ctx(std::move(ctx))
{

View file

@ -7,13 +7,14 @@
#include <QDialog>
#include "appcontext.h"
#include "components.h"
#include "utils/daemonrpc.h"
namespace Ui {
class TxBroadcastDialog;
}
class TxBroadcastDialog : public QDialog
class TxBroadcastDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -7,6 +7,7 @@
#include <QFileDialog>
#include <QMessageBox>
#include "constants.h"
#include "dialog/QrCodeDialog.h"
#include "libwalletqt/Input.h"
#include "libwalletqt/Transfer.h"
@ -14,7 +15,7 @@
#include "qrcode/QrCode.h"
TxConfAdvDialog::TxConfAdvDialog(QSharedPointer<AppContext> ctx, const QString &description, QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::TxConfAdvDialog)
, m_ctx(std::move(ctx))
, m_exportUnsignedMenu(new QMenu(this))
@ -35,42 +36,51 @@ TxConfAdvDialog::TxConfAdvDialog(QSharedPointer<AppContext> ctx, const QString &
m_exportTxKeyMenu->addAction("Copy to clipboard", this, &TxConfAdvDialog::txKeyCopy);
ui->btn_exportTxKey->setMenu(m_exportTxKeyMenu);
if (m_ctx->wallet->viewOnly()) {
ui->btn_exportSigned->hide();
ui->btn_send->hide();
}
ui->label_description->setText(QString("Description: %1").arg(description));
ui->line_description->setText(description);
connect(ui->btn_sign, &QPushButton::clicked, this, &TxConfAdvDialog::signTransaction);
connect(ui->btn_send, &QPushButton::clicked, this, &TxConfAdvDialog::broadcastTransaction);
connect(ui->btn_close, &QPushButton::clicked, this, &TxConfAdvDialog::closeDialog);
ui->amount->setFont(ModelUtils::getMonospaceFont());
ui->fee->setFont(ModelUtils::getMonospaceFont());
ui->total->setFont(ModelUtils::getMonospaceFont());
ui->inputs->setFont(ModelUtils::getMonospaceFont());
ui->outputs->setFont(ModelUtils::getMonospaceFont());
this->adjustSize();
}
void TxConfAdvDialog::setTransaction(PendingTransaction *tx) {
void TxConfAdvDialog::setTransaction(PendingTransaction *tx, bool isSigned) {
ui->btn_sign->hide();
if (!isSigned) {
ui->btn_exportSigned->hide();
ui->btn_send->hide();
}
m_tx = tx;
m_tx->refresh();
PendingTransactionInfo *ptx = m_tx->transaction(0); //Todo: support split transactions
ui->txid->setText(tx->txid().first());
// TODO: implement hasTxKey()
if (!m_ctx->wallet->isHwBacked() && m_tx->transaction(0)->txKey() == "0100000000000000000000000000000000000000000000000000000000000000") {
ui->btn_exportTxKey->hide();
}
ui->amount->setText(WalletManager::displayAmount(tx->amount()));
ui->fee->setText(WalletManager::displayAmount(ptx->fee()));
ui->total->setText(WalletManager::displayAmount(tx->amount() + ptx->fee()));
m_txid = tx->txid().first();
ui->txid->setText(m_txid);
auto size_str = [this]{
if (m_ctx->wallet->viewOnly()) {
return QString("Size: %1 bytes (unsigned)").arg(QString::number(m_tx->unsignedTxToBin().size()));
} else {
this->setAmounts(tx->amount(), tx->fee());
auto size_str = [this, isSigned]{
if (isSigned) {
auto size = m_tx->signedTxToHex(0).size() / 2;
return QString("Size: %1 bytes (%2 bytes unsigned)").arg(QString::number(size), QString::number(m_tx->unsignedTxToBin().size()));
} else {
return QString("Size: %1 bytes (unsigned)").arg(QString::number(m_tx->unsignedTxToBin().size()));
}
}();
ui->label_size->setText(size_str);
@ -91,14 +101,38 @@ void TxConfAdvDialog::setUnsignedTransaction(UnsignedTransaction *utx) {
ui->txid->setText("n/a");
ui->label_size->setText("Size: n/a");
ui->amount->setText(WalletManager::displayAmount(utx->amount(0)));
ui->fee->setText(WalletManager::displayAmount(utx->fee(0)));
ui->total->setText(WalletManager::displayAmount(utx->amount(0) + utx->fee(0)));
this->setAmounts(utx->amount(0), utx->fee(0));
ConstructionInfo *ci = m_utx->constructionInfo(0);
this->setupConstructionData(ci);
}
void TxConfAdvDialog::setAmounts(quint64 amount, quint64 fee) {
QString preferredCur = config()->get(Config::preferredFiatCurrency).toString();
auto convert = [preferredCur](double amount){
return QString::number(appData()->prices.convert("XMR", preferredCur, amount), 'f', 2);
};
QString amount_str = WalletManager::displayAmount(amount);
QString fee_str = WalletManager::displayAmount(fee);
QString total = WalletManager::displayAmount(amount + fee);
QVector<QString> amounts = {amount_str, fee_str, total};
int maxLength = Utils::maxLength(amounts);
std::for_each(amounts.begin(), amounts.end(), [maxLength](QString& amount){amount = amount.rightJustified(maxLength, ' ');});
QString amount_fiat = convert(amount / constants::cdiv);
QString fee_fiat = convert(fee / constants::cdiv);
QString total_fiat = convert((amount + fee) / constants::cdiv);
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->amount->setText(QString("%1 (%2 %3)").arg(amounts[0], amounts_fiat[0], preferredCur));
ui->fee->setText(QString("%1 (%2 %3)").arg(amounts[1], amounts_fiat[1], preferredCur));
ui->total->setText(QString("%1 (%2 %3)").arg(amounts[2], amounts_fiat[2], preferredCur));
}
void TxConfAdvDialog::setupConstructionData(ConstructionInfo *ci) {
QString inputs_str;
auto inputs = ci->inputs();
@ -122,7 +156,6 @@ void TxConfAdvDialog::setupConstructionData(ConstructionInfo *ci) {
ui->label_outputs->setText(QString("Outputs (%1)").arg(QString::number(outputs.size())));
ui->label_ringSize->setText(QString("Ring size: %1").arg(QString::number(ci->minMixinCount() + 1)));
ui->label_unlockTime->setText(QString("Unlock time: %1 (height)").arg(QString::number(ci->unlockTime())));
}
void TxConfAdvDialog::signTransaction() {
@ -180,7 +213,7 @@ void TxConfAdvDialog::signedQrCode() {
void TxConfAdvDialog::broadcastTransaction() {
if (m_tx == nullptr) return;
m_ctx->commitTransaction(m_tx);
m_ctx->commitTransaction(m_tx, ui->line_description->text());
QDialog::accept();
}

View file

@ -10,13 +10,14 @@
#include <QTextCharFormat>
#include "appcontext.h"
#include "components.h"
#include "libwalletqt/PendingTransaction.h"
namespace Ui {
class TxConfAdvDialog;
}
class TxConfAdvDialog : public QDialog
class TxConfAdvDialog : public WindowModalDialog
{
Q_OBJECT
@ -24,7 +25,7 @@ public:
explicit TxConfAdvDialog(QSharedPointer<AppContext> ctx, const QString &description, QWidget *parent = nullptr);
~TxConfAdvDialog() override;
void setTransaction(PendingTransaction *tx);
void setTransaction(PendingTransaction *tx, bool isSigned = true); // #TODO: have libwallet return a UnsignedTransaction, this is just dumb
void setUnsignedTransaction(UnsignedTransaction *utx);
private:
@ -32,6 +33,7 @@ private:
void signTransaction();
void broadcastTransaction();
void closeDialog();
void setAmounts(quint64 amount, quint64 fee);
void unsignedCopy();
void unsignedQrCode();
@ -50,6 +52,7 @@ private:
QMenu *m_exportUnsignedMenu;
QMenu *m_exportSignedMenu;
QMenu *m_exportTxKeyMenu;
QString m_txid;
};
#endif //FEATHER_TXCONFADVDIALOG_H

View file

@ -21,15 +21,15 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Transaction ID:</string>
</property>
</widget>
</item>
<item>
<item row="0" column="1">
<widget class="QLineEdit" name="txid">
<property name="text">
<string>txid</string>
@ -41,15 +41,56 @@
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_description">
<property name="text">
<string>Description:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="line_description"/>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_size">
<property name="text">
<string>Size: </string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_ringSize">
<property name="text">
<string>Ringsize:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
@ -112,57 +153,6 @@
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_description">
<property name="text">
<string>Description:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_size">
<property name="text">
<string>Size: </string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_unlockTime">
<property name="text">
<string>Unlock time: </string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_ringSize">
<property name="text">
<string>Ringsize:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>

View file

@ -13,7 +13,7 @@
#include "utils/ColorScheme.h"
TxConfDialog::TxConfDialog(QSharedPointer<AppContext> ctx, PendingTransaction *tx, const QString &address, const QString &description, QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::TxConfDialog)
, m_ctx(std::move(ctx))
, m_tx(tx)

View file

@ -7,6 +7,7 @@
#include <QDialog>
#include "appcontext.h"
#include "components.h"
#include "libwalletqt/PendingTransaction.h"
#include "libwalletqt/WalletManager.h"
@ -14,7 +15,7 @@ namespace Ui {
class TxConfDialog;
}
class TxConfDialog : public QDialog
class TxConfDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -9,7 +9,7 @@
#include "utils/NetworkManager.h"
TxImportDialog::TxImportDialog(QWidget *parent, QSharedPointer<AppContext> ctx)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::TxImportDialog)
, m_ctx(std::move(ctx))
, m_loadTimer(new QTimer(this))

View file

@ -7,13 +7,14 @@
#include <QDialog>
#include "appcontext.h"
#include "components.h"
#include "utils/daemonrpc.h"
namespace Ui {
class TxImportDialog;
}
class TxImportDialog : public QDialog
class TxImportDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -9,6 +9,7 @@
#include "appcontext.h"
#include "config.h"
#include "constants.h"
#include "libwalletqt/Coins.h"
#include "libwalletqt/CoinsInfo.h"
#include "libwalletqt/TransactionHistory.h"
@ -16,6 +17,7 @@
#include "libwalletqt/WalletManager.h"
#include "model/ModelUtils.h"
#include "Utils.h"
#include "utils/Icons.h"
TxInfoDialog::TxInfoDialog(QSharedPointer<AppContext> ctx, TransactionInfo *txInfo, QWidget *parent)
: QDialog(parent)
@ -26,11 +28,16 @@ TxInfoDialog::TxInfoDialog(QSharedPointer<AppContext> ctx, TransactionInfo *txIn
{
ui->setupUi(this);
ui->btn_viewOnBlockExplorer->setIcon(icons()->icon("external-link.svg"));
ui->btn_viewOnBlockExplorer->setToolTip("View on block explorer");
connect(ui->btn_viewOnBlockExplorer, &QPushButton::clicked, this, &TxInfoDialog::viewOnBlockExplorer);
m_txid = txInfo->hash();
ui->label_txid->setText(m_txid);
connect(ui->btn_CopyTxKey, &QPushButton::pressed, this, &TxInfoDialog::copyTxKey);
connect(ui->btn_createTxProof, &QPushButton::pressed, this, &TxInfoDialog::createTxProof);
connect(ui->btn_copyTxID, &QPushButton::clicked, this, &TxInfoDialog::copyTxID);
connect(ui->btn_CopyTxKey, &QPushButton::clicked, this, &TxInfoDialog::copyTxKey);
connect(ui->btn_createTxProof, &QPushButton::clicked, this, &TxInfoDialog::createTxProof);
connect(m_ctx->wallet, &Wallet::newBlock, this, &TxInfoDialog::updateData);
@ -43,6 +50,12 @@ TxInfoDialog::TxInfoDialog(QSharedPointer<AppContext> ctx, TransactionInfo *txIn
} else {
ui->btn_rebroadcastTx->hide();
}
if (txInfo->direction() == TransactionInfo::Direction_In) {
ui->btn_CopyTxKey->setDisabled(true);
ui->btn_CopyTxKey->setToolTip("No tx secret key available for incoming transactions.");
}
//
// if (txInfo->direction() == TransactionInfo::Direction_Out) {
// // TODO: this will not properly represent coinjoin-like transactions.
@ -78,6 +91,9 @@ TxInfoDialog::TxInfoDialog(QSharedPointer<AppContext> ctx, TransactionInfo *txIn
}
this->adjustSize();
// Don't autofocus any of the buttons. There is probably a better way for this.
ui->label_txid->setFocus();
}
void TxInfoDialog::adjustHeight(QTextEdit *textEdit, qreal docHeight) {
@ -140,6 +156,10 @@ void TxInfoDialog::updateData() {
this->setData(tx);
}
void TxInfoDialog::copyTxID() {
Utils::copyToClipboard(m_txid);
}
void TxInfoDialog::copyTxKey() {
m_ctx->wallet->getTxKeyAsync(m_txid, [this](QVariantMap map){
QString txKey = map.value("tx_key").toString();
@ -157,4 +177,8 @@ void TxInfoDialog::createTxProof() {
m_txProofDialog->getTxKey();
}
void TxInfoDialog::viewOnBlockExplorer() {
Utils::externalLinkWarning(this, Utils::blockExplorerLink(config()->get(Config::blockExplorer).toString(), constants::networkType, m_txid));
}
TxInfoDialog::~TxInfoDialog() = default;

View file

@ -28,11 +28,13 @@ signals:
void resendTranscation(const QString &txid);
private:
void copyTxID();
void copyTxKey();
void createTxProof();
void setData(TransactionInfo *tx);
void updateData();
void adjustHeight(QTextEdit *textEdit, qreal docHeight);
void viewOnBlockExplorer();
QScopedPointer<Ui::TxInfoDialog> ui;
QSharedPointer<AppContext> m_ctx;

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>929</width>
<height>631</height>
<height>715</height>
</rect>
</property>
<property name="windowTitle">
@ -19,7 +19,7 @@
<property name="title">
<string>Transaction ID:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_txid">
<property name="text">
@ -30,6 +30,19 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_viewOnBlockExplorer">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -205,17 +218,24 @@
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="btn_copyTxID">
<property name="text">
<string>Copy Tx ID</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_CopyTxKey">
<property name="text">
<string>Copy Transaction Key</string>
<string>Copy Tx Secret Key</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_createTxProof">
<property name="text">
<string>Create Transaction Proof</string>
<string>Create Tx Proof</string>
</property>
</widget>
</item>

View file

@ -11,7 +11,7 @@
#include "utils/Utils.h"
TxProofDialog::TxProofDialog(QWidget *parent, QSharedPointer<AppContext> ctx, TransactionInfo *txInfo)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::TxProofDialog)
, m_ctx(std::move(ctx))
{
@ -88,13 +88,13 @@ void TxProofDialog::selectOutProof() {
m_mode = Mode::OutProof;
this->resetFrames();
if (m_OutDestinations.empty()) {
this->showWarning("This transaction did not spend any outputs owned by this wallet. Creating an OutProof is not possible.");
if (m_txKey.isEmpty()) {
this->showWarning("No transaction key stored for this transaction. Creating an OutProof is not possible.");
return;
}
if (m_txKey.isEmpty()) {
this->showWarning("No transaction key stored for this transaction. Creating an OutProof is not possible.");
if (m_OutDestinations.empty()) {
this->showWarning("This transaction did not send funds to any known addresses. Creating an OutProof is not possible.");
return;
}
@ -107,6 +107,11 @@ void TxProofDialog::selectInProof() {
m_mode = Mode::InProof;
this->resetFrames();
if (m_direction == TransactionInfo::Direction_Out) {
this->showWarning("Can't create InProofs for outgoing transactions.");
return;
}
if (m_InDestinations.empty()) {
this->showWarning("Your wallet did not receive any outputs in this transaction.");
return;

View file

@ -7,13 +7,14 @@
#include <QDialog>
#include "appcontext.h"
#include "components.h"
#include "libwalletqt/TransactionInfo.h"
namespace Ui {
class TxProofDialog;
}
class TxProofDialog : public QDialog
class TxProofDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -117,6 +117,11 @@ void UpdateDialog::onInstallUpdate() {
ui->btn_installUpdate->hide();
this->setStatus("Unzipping archive...");
#ifdef Q_OS_MACOS
this->installUpdateMac();
return;
#endif
zip_error_t err;
zip_error_init(&err);
@ -172,14 +177,7 @@ void UpdateDialog::onInstallUpdate() {
zip_fclose(zf);
zip_close(zip_archive);
QString applicationPath = qgetenv("APPIMAGE");
if (!applicationPath.isEmpty()) {
applicationPath = QFileInfo(applicationPath).absoluteDir().path();
} else {
applicationPath = QCoreApplication::applicationDirPath();
}
QDir applicationDir(applicationPath);
QDir applicationDir(Utils::applicationPath());
QString filePath = applicationDir.filePath(name);
m_updatePath = filePath;
@ -206,6 +204,41 @@ void UpdateDialog::onInstallUpdate() {
ui->btn_restart->show();
}
void UpdateDialog::installUpdateMac() {
QString appPath = Utils::applicationPath();
QDir appDir(appPath);
if (appPath.endsWith("Contents/MacOS")) {
appDir.cd("../../..");
}
QString appName = QString("feather-%1").arg(m_version);
QString zipName = QString("%1.zip").arg(appName);
QString fPath = appDir.filePath(zipName);
QFile file(fPath);
qDebug() << "Writing zip file to " << fPath;
if (!file.open(QIODevice::WriteOnly))
{
this->onInstallError(QString("Error: Could not write to application path: %1").arg(fPath));
return;
}
if (static_cast<size_t>(file.write(&m_updateZipArchive[0], m_updateZipArchive.size())) != m_updateZipArchive.size()) {
this->onInstallError("Error: Unable to write file");
return;
}
QProcess unzip;
unzip.start("/usr/bin/unzip", {"-n", fPath, "-d", appDir.path()});
unzip.waitForFinished();
m_updatePath = QString("%1.app/Contents/MacOS/feather").arg(appDir.filePath(appName));
file.remove();
this->setStatus("Installation successful. Do you want to restart Feather now?");
ui->btn_restart->show();
}
void UpdateDialog::onInstallError(const QString &errMsg) {
this->setStatus(errMsg);
}

View file

@ -33,6 +33,7 @@ signals:
private:
void setStatus(const QString &msg, bool success = false);
void installUpdateMac();
QScopedPointer<Ui::UpdateDialog> ui;

View file

@ -11,7 +11,7 @@
#include "utils/Utils.h"
VerifyProofDialog::VerifyProofDialog(Wallet *wallet, QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::VerifyProofDialog)
, m_wallet(wallet)
{
@ -50,6 +50,9 @@ VerifyProofDialog::VerifyProofDialog(Wallet *wallet, QWidget *parent)
}
});
connect(m_wallet, &Wallet::transactionProofVerified, this, &VerifyProofDialog::onTxProofVerified);
connect(m_wallet, &Wallet::spendProofVerified, this, &VerifyProofDialog::onSpendProofVerified);
ui->input_formattedProof->setFont(ModelUtils::getMonospaceFont());
}
@ -69,31 +72,15 @@ void VerifyProofDialog::checkProof() {
void VerifyProofDialog::checkTxProof(const QString &txId, const QString &address, const QString &message,
const QString &signature) {
TxProofResult r = m_wallet->checkTxProof(txId, address, message, signature);
if (!r.success) {
this->proofStatus(false, m_wallet->errorString());
return;
}
if (!r.good) {
this->proofStatus(false, "Proof is invalid");
return;
}
this->proofStatus(true, QString("Proof is valid.\n\nThis address received %1 XMR, with %2 confirmation(s)").arg(WalletManager::displayAmount(r.received), QString::number(r.confirmations)));
ui->btn_verifyFormattedProof->setEnabled(false);
ui->btn_verify->setEnabled(false);
m_wallet->checkTxProofAsync(txId, address, message, signature);
}
void VerifyProofDialog::checkSpendProof(const QString &txId, const QString &message, const QString &signature) {
auto r = m_wallet->checkSpendProof(txId, message, signature);
if (!r.first) {
this->proofStatus(false, m_wallet->errorString());
return;
}
r.second ? this->proofStatus(true, "Proof is valid")
: this->proofStatus(false, "Proof is invalid");
ui->btn_verifyFormattedProof->setEnabled(false);
ui->btn_verify->setEnabled(false);
m_wallet->checkSpendProofAsync(txId, message, signature);
}
void VerifyProofDialog::checkOutProof() {
@ -163,11 +150,37 @@ void VerifyProofDialog::proofStatus(bool success, const QString &message) {
ui->frame_status->show();
ui->label_icon->setPixmap(success ? m_success : m_failure);
ui->label_status->setText(message);
ui->btn_verifyFormattedProof->setEnabled(true);
}
else {
ui->btn_verify->setEnabled(true);
success ? QMessageBox::information(this, "Information", message)
: QMessageBox::warning(this, "Warning", message);
}
}
void VerifyProofDialog::onTxProofVerified(TxProofResult r) {
if (!r.success) {
this->proofStatus(false, m_wallet->errorString());
return;
}
if (!r.good) {
this->proofStatus(false, "Proof is invalid");
return;
}
this->proofStatus(true, QString("Proof is valid.\n\nThis address received %1 XMR, with %2 confirmation(s)").arg(WalletManager::displayAmount(r.received), QString::number(r.confirmations)));
}
void VerifyProofDialog::onSpendProofVerified(QPair<bool, bool> r) {
if (!r.first) {
this->proofStatus(false, m_wallet->errorString());
return;
}
r.second ? this->proofStatus(true, "Proof is valid")
: this->proofStatus(false, "Proof is invalid");
}
VerifyProofDialog::~VerifyProofDialog() = default;

View file

@ -7,13 +7,14 @@
#include <QDialog>
#include <QIcon>
#include "components.h"
#include "libwalletqt/Wallet.h"
namespace Ui {
class VerifyProofDialog;
}
class VerifyProofDialog : public QDialog
class VerifyProofDialog : public WindowModalDialog
{
Q_OBJECT
@ -31,6 +32,8 @@ private:
void checkInProof();
void checkFormattedProof();
void proofStatus(bool success, const QString &message);
void onTxProofVerified(TxProofResult result);
void onSpendProofVerified(QPair<bool, bool> result);
QScopedPointer<Ui::VerifyProofDialog> ui;
Wallet *m_wallet;

View file

@ -9,7 +9,7 @@
#include <QMessageBox>
ViewOnlyDialog::ViewOnlyDialog(QSharedPointer<AppContext> ctx, QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::ViewOnlyDialog)
, m_ctx(std::move(ctx))
{

View file

@ -7,12 +7,13 @@
#include <QDialog>
#include "appcontext.h"
#include "components.h"
namespace Ui {
class ViewOnlyDialog;
}
class ViewOnlyDialog : public QDialog
class ViewOnlyDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -9,7 +9,7 @@
#include "model/ModelUtils.h"
WalletCacheDebugDialog::WalletCacheDebugDialog(QSharedPointer<AppContext> ctx, QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::WalletCacheDebugDialog)
, m_ctx(std::move(ctx))
{

View file

@ -7,12 +7,13 @@
#include <QDialog>
#include "appcontext.h"
#include "components.h"
namespace Ui {
class WalletCacheDebugDialog;
}
class WalletCacheDebugDialog : public QDialog
class WalletCacheDebugDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -7,7 +7,7 @@
#include <QDesktopServices>
WalletInfoDialog::WalletInfoDialog(QSharedPointer<AppContext> ctx, QWidget *parent)
: QDialog(parent)
: WindowModalDialog(parent)
, ui(new Ui::WalletInfoDialog)
, m_ctx(std::move(ctx))
{

View file

@ -7,12 +7,13 @@
#include <QDialog>
#include "appcontext.h"
#include "components.h"
namespace Ui {
class WalletInfoDialog;
}
class WalletInfoDialog : public QDialog
class WalletInfoDialog : public WindowModalDialog
{
Q_OBJECT

View file

@ -71,15 +71,15 @@ quint64 Coins::count() const
return m_tinfo.count();
}
void Coins::freeze(int index) const
void Coins::freeze(QString &publicKey) const
{
m_pimpl->setFrozen(index);
m_pimpl->setFrozen(publicKey.toStdString());
emit coinFrozen();
}
void Coins::thaw(int index) const
void Coins::thaw(QString &publicKey) const
{
m_pimpl->thaw(index);
m_pimpl->thaw(publicKey.toStdString());
emit coinThawed();
}
@ -96,9 +96,9 @@ QVector<CoinsInfo*> Coins::coins_from_txid(const QString &txid)
return coins;
}
void Coins::setDescription(int index, quint32 accountIndex, const QString &description)
void Coins::setDescription(const QString &publicKey, quint32 accountIndex, const QString &description)
{
m_pimpl->setDescription(index, description.toStdString());
m_pimpl->setDescription(publicKey.toStdString(), description.toStdString());
this->refresh(accountIndex);
emit descriptionChanged();
}

Some files were not shown because too many files have changed in this diff Show more