Beta-6
1
.gitignore
vendored
|
@ -14,3 +14,4 @@ src/tor/*
|
|||
!src/tor/.gitkeep
|
||||
src/config-feather.h
|
||||
src/assets/exec/*
|
||||
feather.AppDir/*
|
||||
|
|
|
@ -22,7 +22,7 @@ git clone --branch master --recursive https://git.featherwallet.org/feather/feat
|
|||
cd feather
|
||||
```
|
||||
|
||||
Replace `master` with the desired version tag (e.g. `beta-4`) to build the release binary.
|
||||
Replace `master` with the desired version tag (e.g. `beta-6`) to build the release binary.
|
||||
|
||||
#### 2. Base image
|
||||
|
||||
|
@ -37,7 +37,7 @@ Building the base image takes a while. You only need to build the base image onc
|
|||
##### Standalone binary
|
||||
|
||||
```bash
|
||||
docker run --rm -it -v $PWD:/feather -w /feather feather:linux sh -c 'make release-static -j4'
|
||||
docker run --rm -it -v $PWD:/feather -w /feather feather:linux sh -c 'CHECK_UPDATES=On make release-static -j4'
|
||||
```
|
||||
|
||||
If you're re-running a build make sure to `rm -rf build/` first.
|
||||
|
@ -75,7 +75,7 @@ Building the base image takes a while. You only need to build the base image onc
|
|||
#### 3. Build
|
||||
|
||||
```bash
|
||||
docker run --rm -it -v $PWD:/feather -w /feather feather:win sh -c 'make depends root=/depends target=x86_64-w64-mingw32 tag=win-x64 -j4'
|
||||
docker run --rm -it -v $PWD:/feather -w /feather feather:win sh -c 'CHECK_UPDATES=On make depends root=/depends target=x86_64-w64-mingw32 tag=win-x64 -j4'
|
||||
```
|
||||
|
||||
If you're re-running a build make sure to `rm -rf build/` first.
|
||||
|
|
|
@ -7,11 +7,13 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
|
|||
set(VERSION_MAJOR "0")
|
||||
set(VERSION_MINOR "1")
|
||||
set(VERSION_REVISION "0")
|
||||
set(VERSION "beta-5")
|
||||
set(VERSION "beta-6")
|
||||
|
||||
option(FETCH_DEPS "Download dependencies if they are not found" ON)
|
||||
option(LOCALMONERO "Include LocalMonero module" ON)
|
||||
option(XMRIG "Include XMRig module" ON)
|
||||
option(TOR_BIN "Path to Tor binary to embed inside Feather" OFF)
|
||||
option(CHECK_UPDATES "Enable checking for application updates" OFF)
|
||||
|
||||
option(STATIC "Link libraries statically, requires static Qt")
|
||||
option(USE_DEVICE_TREZOR "Trezor support compilation" OFF)
|
||||
|
@ -29,7 +31,7 @@ if(DEBUG)
|
|||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||
endif()
|
||||
|
||||
set(MONERO_HEAD "41327974116dedccc2f9709d8ad3a8a1f591faed")
|
||||
set(MONERO_HEAD "626f1b4ba1c1a490ca97fe265230aea84eac0ed2")
|
||||
set(BUILD_GUI_DEPS ON)
|
||||
set(ARCH "x86-64")
|
||||
set(BUILD_64 ON)
|
||||
|
@ -139,6 +141,11 @@ if(NOT monero-seed_FOUND)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# libzip
|
||||
find_package(zlib CONFIG)
|
||||
find_path(LIBZIP_INCLUDE_DIRS zip.h)
|
||||
find_library(LIBZIP_LIBRARIES zip)
|
||||
|
||||
# Boost
|
||||
if(DEBUG)
|
||||
set(Boost_DEBUG ON)
|
||||
|
|
|
@ -41,9 +41,11 @@ via the `CMAKE_PREFIX_PATH` definition. For me this is:
|
|||
|
||||
There are some Monero/Feather related options/definitions that you may pass:
|
||||
|
||||
- `-DLOCALMONERO=OFF` - disable LocalMonero feature
|
||||
- `-DXMRIG=OFF` - disable XMRig feature
|
||||
- `-DTOR_BIN=/path/to/tor` - Embed a Tor executable inside Feather
|
||||
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
|
||||
- `-DCHECK_UPDATES=ON` - enable checking for updates, only for standalone binaries
|
||||
|
||||
And:
|
||||
|
||||
|
|
6
Makefile
|
@ -30,7 +30,9 @@ CMAKEFLAGS = \
|
|||
-DARCH=x86_64 \
|
||||
-DBUILD_64=On \
|
||||
-DBUILD_TESTS=Off \
|
||||
-DLOCALMONERO=On \
|
||||
-DXMRIG=On \
|
||||
-DCHECK_UPDATES=Off \
|
||||
-DTOR_BIN=Off \
|
||||
-DCMAKE_CXX_STANDARD=11 \
|
||||
-DCMAKE_VERBOSE_MAKEFILE=On \
|
||||
|
@ -42,6 +44,7 @@ CMAKEFLAGS = \
|
|||
|
||||
release-static: CMAKEFLAGS += -DBUILD_TAG="linux-x64"
|
||||
release-static: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
|
||||
release-static: CMAKEFLAGS += -DCHECK_UPDATES=$(or ${CHECK_UPDATES}, Off)
|
||||
release-static: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
|
||||
release-static: CMAKEFLAGS += -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF)
|
||||
release-static:
|
||||
|
@ -50,10 +53,11 @@ release-static:
|
|||
|
||||
depends:
|
||||
mkdir -p build/$(target)/release
|
||||
cd build/$(target)/release && cmake -D STATIC=ON -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF) -DTOR_VERSION=$(or ${TOR_VERSION}, OFF) -DTOR_BIN=$(or ${TOR_BIN},OFF) -D DEV_MODE=$(or ${DEV_MODE},OFF) -D BUILD_TAG=$(tag) -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=$(root)/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
|
||||
cd build/$(target)/release && cmake -D STATIC=ON -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF) -DTOR_VERSION=$(or ${TOR_VERSION}, OFF) -DTOR_BIN=$(or ${TOR_BIN},OFF) -DCHECK_UPDATES=$(or ${CHECK_UPDATES}, OFF) -D DEV_MODE=$(or ${DEV_MODE},OFF) -D BUILD_TAG=$(tag) -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=$(root)/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
|
||||
|
||||
mac-release: CMAKEFLAGS += -DSTATIC=Off
|
||||
mac-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
|
||||
mac-release: CMAKEFLAGS += -DCHECK_UPDATES=$(or ${CHECK_UPDATES}, Off)
|
||||
mac-release: CMAKEFLAGS += -DBUILD_TAG="mac-x64"
|
||||
mac-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
|
||||
mac-release:
|
||||
|
|
11
PKGBUILD
|
@ -2,24 +2,19 @@
|
|||
# Contributor: wowario <wowario[at]protonmail[dot]com>
|
||||
|
||||
pkgname='monero-feather-git'
|
||||
pkgver=0.1.0.925ef5683
|
||||
pkgver=0.5.0.d1bb4c143f
|
||||
pkgrel=1
|
||||
pkgdesc='a free Monero desktop wallet'
|
||||
license=('BSD')
|
||||
arch=('x86_64')
|
||||
url="https://featherwallet.org"
|
||||
depends=('boost-libs' 'libunwind' 'openssl' 'readline' 'pcsclite' 'hidapi' 'protobuf' 'miniupnpc' 'libgcrypt' 'qrencode' 'libsodium' 'libpgm' 'expat' 'qt5-base' 'qt5-websockets' 'tor')
|
||||
depends=('boost-libs' 'libunwind' 'openssl' 'readline' 'zeromq' 'pcsclite' 'hidapi' 'protobuf' 'libusb' 'libudev.so' 'miniupnpc' 'libgcrypt' 'qrencode' 'libsodium' 'libpgm' 'expat' 'qt5-base' 'qt5-websockets' 'qt5-svg' 'tor' 'libzip')
|
||||
makedepends=('git' 'cmake' 'boost')
|
||||
|
||||
source=("${pkgname}"::"git+https://git.featherwallet.org/feather/feather")
|
||||
|
||||
sha256sums=('SKIP')
|
||||
|
||||
pkgver() {
|
||||
cd "${srcdir}/${pkgname}"
|
||||
printf "%s.%s" "$(git describe --tags --abbrev=0)" "$(git rev-parse --short=9 HEAD)"
|
||||
}
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}"
|
||||
git submodule update --init --recursive
|
||||
|
@ -32,4 +27,6 @@ build() {
|
|||
package_monero-feather-git() {
|
||||
install -Dm644 "${srcdir}/${pkgname}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
||||
install -Dm755 "${srcdir}/${pkgname}/build/bin/feather" "${pkgdir}/usr/bin/feather"
|
||||
install -Dm644 "${srcdir}/${pkgname}/src/assets/feather.desktop" "${pkgdir}/usr/share/applications/feather.desktop"
|
||||
install -Dm644 "${srcdir}/${pkgname}/src/assets/images/feather.png" "${pkgdir}/usr/share/pixmaps/feather.png"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
# - try to find HIDAPI library
|
||||
# from http://www.signal11.us/oss/hidapi/
|
||||
#
|
||||
# Cache Variables: (probably not for direct use in your scripts)
|
||||
# HIDAPI_INCLUDE_DIR
|
||||
# HIDAPI_LIBRARY
|
||||
#
|
||||
# Non-cache variables you might use in your CMakeLists.txt:
|
||||
# HIDAPI_FOUND
|
||||
# HIDAPI_INCLUDE_DIRS
|
||||
# HIDAPI_LIBRARIES
|
||||
#
|
||||
# Requires these CMake modules:
|
||||
# FindPackageHandleStandardArgs (known included with CMake >=2.6.2)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
find_library(HIDAPI_LIBRARY
|
||||
NAMES hidapi hidapi-libusb)
|
||||
|
||||
find_path(HIDAPI_INCLUDE_DIR
|
||||
NAMES hidapi.h
|
||||
PATH_SUFFIXES
|
||||
hidapi)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(HIDAPI
|
||||
DEFAULT_MSG
|
||||
HIDAPI_LIBRARY
|
||||
HIDAPI_INCLUDE_DIR)
|
||||
|
||||
if(HIDAPI_FOUND)
|
||||
set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARY}")
|
||||
if((STATIC AND UNIX AND NOT APPLE) OR (DEPENDS AND CMAKE_SYSTEM_NAME STREQUAL "Linux"))
|
||||
find_library(LIBUSB-1.0_LIBRARY usb-1.0)
|
||||
find_library(LIBUDEV_LIBRARY udev)
|
||||
if(LIBUSB-1.0_LIBRARY)
|
||||
set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARIES};${LIBUSB-1.0_LIBRARY}")
|
||||
if(LIBUDEV_LIBRARY)
|
||||
set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARIES};${LIBUDEV_LIBRARY}")
|
||||
else()
|
||||
message(WARNING "libudev library not found, binaries may fail to link.")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "libusb-1.0 library not found, binaries may fail to link.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(HIDAPI_INCLUDE_DIRS "${HIDAPI_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
mark_as_advanced(HIDAPI_INCLUDE_DIR HIDAPI_LIBRARY)
|
2
monero
|
@ -1 +1 @@
|
|||
Subproject commit 41327974116dedccc2f9709d8ad3a8a1f591faed
|
||||
Subproject commit 626f1b4ba1c1a490ca97fe265230aea84eac0ed2
|
|
@ -18,6 +18,8 @@ qt5_add_resources(RESOURCES assets.qrc)
|
|||
file(GLOB SOURCE_FILES
|
||||
"*.h"
|
||||
"*.cpp"
|
||||
"api/*.h"
|
||||
"api/*.cpp"
|
||||
"utils/*.h"
|
||||
"utils/*.cpp"
|
||||
"libwalletqt/*.h"
|
||||
|
@ -116,12 +118,22 @@ target_include_directories(feather PUBLIC
|
|||
${Qt5Svg_INCLUDE_DIRS}
|
||||
${Qt5Xml_INCLUDE_DIRS}
|
||||
${Qt5WebSockets_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIRS}
|
||||
${LIBZIP_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(DONATE_BEG)
|
||||
target_compile_definitions(feather PRIVATE DONATE_BEG=1)
|
||||
endif()
|
||||
|
||||
if (CHECK_UPDATES)
|
||||
target_compile_definitions(feather PRIVATE CHECK_UPDATES=1)
|
||||
endif()
|
||||
|
||||
if(LOCALMONERO)
|
||||
target_compile_definitions(feather PRIVATE HAS_LOCALMONERO=1)
|
||||
endif()
|
||||
|
||||
if(TOR_BIN)
|
||||
target_compile_definitions(feather PRIVATE HAS_TOR_BIN=1)
|
||||
endif()
|
||||
|
@ -130,6 +142,9 @@ if(XMRIG)
|
|||
target_compile_definitions(feather PRIVATE HAS_XMRIG=1)
|
||||
endif()
|
||||
|
||||
# TODO: PLACEHOLDER
|
||||
target_compile_definitions(feather PRIVATE HAS_WEBSOCKET=1)
|
||||
|
||||
if(HAVE_SYS_PRCTL_H)
|
||||
target_compile_definitions(feather PRIVATE HAVE_SYS_PRCTL_H=1)
|
||||
endif()
|
||||
|
@ -191,6 +206,8 @@ target_link_libraries(feather
|
|||
openpgp
|
||||
Threads::Threads
|
||||
${QRENCODE_LIBRARY}
|
||||
${ZLIB_LIBRARIES}
|
||||
${LIBZIP_LIBRARIES}
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
|
|
121
src/api/LocalMoneroApi.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#include "LocalMoneroApi.h"
|
||||
|
||||
LocalMoneroApi::LocalMoneroApi(QObject *parent, UtilsNetworking *network, const QString &baseUrl)
|
||||
: QObject(parent)
|
||||
, m_network(network)
|
||||
, m_baseUrl(baseUrl)
|
||||
{
|
||||
}
|
||||
|
||||
void LocalMoneroApi::countryCodes() {
|
||||
QString url = QString("%1/countrycodes").arg(m_baseUrl);
|
||||
QNetworkReply *reply = m_network->getJson(url);
|
||||
connect(reply, &QNetworkReply::finished, std::bind(&LocalMoneroApi::onResponse, this, reply, Endpoint::COUNTRY_CODES));
|
||||
}
|
||||
|
||||
void LocalMoneroApi::currencies() {
|
||||
QString url = QString("%1/currencies").arg(m_baseUrl);
|
||||
QNetworkReply *reply = m_network->getJson(url);
|
||||
connect(reply, &QNetworkReply::finished, std::bind(&LocalMoneroApi::onResponse, this, reply, Endpoint::CURRENCIES));
|
||||
}
|
||||
|
||||
void LocalMoneroApi::paymentMethods(const QString &countryCode) {
|
||||
QString url;
|
||||
if (countryCode.isEmpty()) {
|
||||
url = QString("%1/payment_methods").arg(m_baseUrl);
|
||||
} else {
|
||||
url = QString("%1/payment_methods/%2").arg(m_baseUrl, countryCode);
|
||||
}
|
||||
QNetworkReply *reply = m_network->getJson(url);
|
||||
connect(reply, &QNetworkReply::finished, std::bind(&LocalMoneroApi::onResponse, this, reply, Endpoint::PAYMENT_METHODS));
|
||||
}
|
||||
|
||||
void LocalMoneroApi::buyMoneroOnline(const QString ¤cyCode, const QString &countryCode,
|
||||
const QString &paymentMethod, const QString &amount, int page)
|
||||
{
|
||||
QString url = this->getBuySellUrl(true, currencyCode, countryCode, paymentMethod, amount, page);
|
||||
QNetworkReply *reply = m_network->getJson(url);
|
||||
connect(reply, &QNetworkReply::finished, std::bind(&LocalMoneroApi::onResponse, this, reply, Endpoint::BUY_MONERO_ONLINE));
|
||||
}
|
||||
|
||||
void LocalMoneroApi::sellMoneroOnline(const QString ¤cyCode, const QString &countryCode,
|
||||
const QString &paymentMethod, const QString &amount, int page)
|
||||
{
|
||||
QString url = this->getBuySellUrl(false, currencyCode, countryCode, paymentMethod, amount, page);
|
||||
QNetworkReply *reply = m_network->getJson(url);
|
||||
connect(reply, &QNetworkReply::finished, std::bind(&LocalMoneroApi::onResponse, this, reply, Endpoint::SELL_MONERO_ONLINE));
|
||||
}
|
||||
|
||||
void LocalMoneroApi::accountInfo(const QString &username) {
|
||||
QString url = QString("%1/account_info/%2").arg(m_baseUrl, username);
|
||||
QNetworkReply *reply = m_network->getJson(url);
|
||||
connect(reply, &QNetworkReply::finished, std::bind(&LocalMoneroApi::onResponse, this, reply, Endpoint::ACCOUNT_INFO));
|
||||
}
|
||||
|
||||
void LocalMoneroApi::onResponse(QNetworkReply *reply, LocalMoneroApi::Endpoint endpoint) {
|
||||
const bool ok = reply->error() == QNetworkReply::NoError;
|
||||
const QString err = reply->errorString();
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
|
||||
qDebug() << "Response";
|
||||
qDebug() << data;
|
||||
for (const auto header : reply->rawHeaderList()) {
|
||||
qDebug() << header << ": " << reply->rawHeader(header);
|
||||
}
|
||||
|
||||
qDebug() << reply->rawHeaderPairs();
|
||||
|
||||
qDebug() << "Request";
|
||||
for (const auto header : reply->request().rawHeaderList()) {
|
||||
qDebug() << "header: " << header << ": " << reply->request().rawHeader(header);
|
||||
}
|
||||
qDebug() << reply->request().url();
|
||||
|
||||
reply->deleteLater();
|
||||
|
||||
QJsonObject obj;
|
||||
if (!data.isEmpty() && Utils::validateJSON(data)) {
|
||||
auto doc = QJsonDocument::fromJson(data);
|
||||
obj = doc.object();
|
||||
}
|
||||
else if (!ok) {
|
||||
emit ApiResponse(LocalMoneroResponse{false, endpoint, err, {}});
|
||||
return;
|
||||
}
|
||||
else {
|
||||
emit ApiResponse(LocalMoneroResponse{false, endpoint, "Invalid response from LocalMonero", {}});
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj.contains("error")) {
|
||||
QString errorStr = QJsonDocument(obj["error"].toObject()).toJson(QJsonDocument::Compact);
|
||||
emit ApiResponse(LocalMoneroResponse{false, endpoint, errorStr, obj});
|
||||
return;
|
||||
}
|
||||
|
||||
emit ApiResponse(LocalMoneroResponse{true, endpoint, "", obj});
|
||||
}
|
||||
|
||||
QString LocalMoneroApi::getBuySellUrl(bool buy, const QString ¤cyCode, const QString &countryCode,
|
||||
const QString &paymentMethod, const QString &amount, int page)
|
||||
{
|
||||
QString url = QString("%1/%2-monero-online/%3").arg(m_baseUrl, buy ? "buy" : "sell", currencyCode);
|
||||
if (!countryCode.isEmpty() && paymentMethod.isEmpty())
|
||||
url += QString("/%1").arg(countryCode);
|
||||
else if (countryCode.isEmpty() && !paymentMethod.isEmpty())
|
||||
url += QString("/%1").arg(paymentMethod);
|
||||
else if (!countryCode.isEmpty() && !paymentMethod.isEmpty())
|
||||
url += QString("/%1/%2").arg(countryCode, paymentMethod);
|
||||
|
||||
QUrlQuery query;
|
||||
if (!amount.isEmpty())
|
||||
query.addQueryItem("amount", amount);
|
||||
if (page > 0)
|
||||
query.addQueryItem("page", QString::number(page));
|
||||
url += "?" + query.toString();
|
||||
return url;
|
||||
}
|
53
src/api/LocalMoneroApi.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_LOCALMONEROAPI_H
|
||||
#define FEATHER_LOCALMONEROAPI_H
|
||||
|
||||
#include <QObject>
|
||||
#include "utils/networking.h"
|
||||
|
||||
class LocalMoneroApi : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Endpoint {
|
||||
COUNTRY_CODES,
|
||||
CURRENCIES,
|
||||
PAYMENT_METHODS,
|
||||
BUY_MONERO_ONLINE,
|
||||
SELL_MONERO_ONLINE,
|
||||
ACCOUNT_INFO
|
||||
};
|
||||
|
||||
struct LocalMoneroResponse {
|
||||
bool ok;
|
||||
Endpoint endpoint;
|
||||
QString message;
|
||||
QJsonObject obj;
|
||||
};
|
||||
|
||||
explicit LocalMoneroApi(QObject *parent, UtilsNetworking *network, const QString &baseUrl = "https://agoradesk.com/api/v1");
|
||||
|
||||
void countryCodes();
|
||||
void currencies();
|
||||
void paymentMethods(const QString &countryCode = "");
|
||||
void buyMoneroOnline(const QString ¤cyCode, const QString &countryCode="", const QString &paymentMethod="", const QString &amount = "", int page = 0);
|
||||
void sellMoneroOnline(const QString ¤cyCode, const QString &countryCode="", const QString &paymentMethod="", const QString &amount = "", int page = 0);
|
||||
void accountInfo(const QString &username);
|
||||
|
||||
signals:
|
||||
void ApiResponse(LocalMoneroResponse resp);
|
||||
|
||||
private slots:
|
||||
void onResponse(QNetworkReply *reply, Endpoint endpoint);
|
||||
|
||||
private:
|
||||
QString getBuySellUrl(bool buy, const QString ¤cyCode, const QString &countryCode="", const QString &paymentMethod="", const QString &amount = "", int page = 0);
|
||||
|
||||
QString m_baseUrl;
|
||||
UtilsNetworking *m_network;
|
||||
};
|
||||
|
||||
|
||||
#endif //FEATHER_LOCALMONEROAPI_H
|
|
@ -13,20 +13,16 @@
|
|||
#include "libwalletqt/Coins.h"
|
||||
#include "model/TransactionHistoryModel.h"
|
||||
#include "model/SubaddressModel.h"
|
||||
#include "utils/NetworkManager.h"
|
||||
#include "utils/WebsocketClient.h"
|
||||
#include "utils/WebsocketNotifier.h"
|
||||
|
||||
|
||||
Prices *AppContext::prices = nullptr;
|
||||
WalletKeysFilesModel *AppContext::wallets = nullptr;
|
||||
TxFiatHistory *AppContext::txFiatHistory = nullptr;
|
||||
double AppContext::balance = 0;
|
||||
QMap<QString, QString> AppContext::txCache;
|
||||
|
||||
AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||
this->network = new QNetworkAccessManager();
|
||||
this->networkClearnet = new QNetworkAccessManager();
|
||||
this->cmdargs = cmdargs;
|
||||
|
||||
this->isTorSocks = Utils::isTorsocks();
|
||||
this->isTails = TailsOS::detect();
|
||||
this->isWhonix = WhonixOS::detect();
|
||||
|
||||
|
@ -45,21 +41,20 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
|||
|
||||
// ----------------- Network Type -----------------
|
||||
|
||||
if (this->cmdargs->isSet("stagenet"))
|
||||
if (this->cmdargs->isSet("stagenet")) {
|
||||
this->networkType = NetworkType::STAGENET;
|
||||
else if (this->cmdargs->isSet("testnet"))
|
||||
config()->set(Config::networkType, NetworkType::STAGENET);
|
||||
}
|
||||
else if (this->cmdargs->isSet("testnet")) {
|
||||
this->networkType = NetworkType::TESTNET;
|
||||
else
|
||||
config()->set(Config::networkType, NetworkType::TESTNET);
|
||||
}
|
||||
else {
|
||||
this->networkType = NetworkType::MAINNET;
|
||||
config()->set(Config::networkType, NetworkType::MAINNET);
|
||||
}
|
||||
|
||||
|
||||
this->nodes = new Nodes(this, this->networkClearnet);
|
||||
connect(this, &AppContext::nodeSourceChanged, this->nodes, &Nodes::onNodeSourceChanged);
|
||||
connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes);
|
||||
|
||||
// Tor & socks proxy
|
||||
this->ws = new WSClient(this, globals::websocketUrl);
|
||||
connect(this->ws, &WSClient::WSMessage, this, &AppContext::onWSMessage);
|
||||
this->nodes = new Nodes(this, this);
|
||||
|
||||
// Store the wallet every 2 minutes
|
||||
m_storeTimer.start(2 * 60 * 1000);
|
||||
|
@ -67,31 +62,6 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
|||
this->storeWallet();
|
||||
});
|
||||
|
||||
// restore height lookup
|
||||
this->initRestoreHeights();
|
||||
|
||||
// price history lookup
|
||||
auto genesis_timestamp = this->restoreHeights[NetworkType::Type::MAINNET]->data.firstKey();
|
||||
AppContext::txFiatHistory = new TxFiatHistory(genesis_timestamp, configDir);
|
||||
connect(this->ws, &WSClient::connectionEstablished, AppContext::txFiatHistory, &TxFiatHistory::onUpdateDatabase);
|
||||
connect(AppContext::txFiatHistory, &TxFiatHistory::requestYear, [=](int year){
|
||||
QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1}})").arg(year).toUtf8();
|
||||
this->ws->sendMsg(data);
|
||||
});
|
||||
connect(AppContext::txFiatHistory, &TxFiatHistory::requestYearMonth, [=](int year, int month) {
|
||||
QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1, "month": %2}})").arg(year).arg(month).toUtf8();
|
||||
this->ws->sendMsg(data);
|
||||
});
|
||||
|
||||
// fiat/crypto lookup
|
||||
AppContext::prices = new Prices();
|
||||
|
||||
// XMRig
|
||||
#ifdef HAS_XMRIG
|
||||
this->XMRig = new XmRig(configDir, this);
|
||||
this->XMRig->prepare();
|
||||
#endif
|
||||
|
||||
this->walletManager = WalletManager::instance();
|
||||
QString logPath = QString("%1/daemon.log").arg(configDir);
|
||||
Monero::Utils::onStartup();
|
||||
|
@ -108,23 +78,32 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
|||
|
||||
// libwallet connects
|
||||
connect(this->walletManager, &WalletManager::walletOpened, this, &AppContext::onWalletOpened);
|
||||
connect(this->walletManager, &WalletManager::walletCreated, this, &AppContext::onWalletCreated);
|
||||
connect(this->walletManager, &WalletManager::deviceButtonRequest, this, &AppContext::onDeviceButtonRequest);
|
||||
connect(this->walletManager, &WalletManager::deviceError, this, &AppContext::onDeviceError);
|
||||
|
||||
// TODO: move me
|
||||
connect(websocketNotifier(), &WebsocketNotifier::NodesReceived, this->nodes, &Nodes::onWSNodesReceived);
|
||||
}
|
||||
|
||||
void AppContext::initTor() {
|
||||
this->tor = new Tor(this, this);
|
||||
this->tor->start();
|
||||
if (this->cmdargs->isSet("tor-host"))
|
||||
config()->set(Config::socks5Host, this->cmdargs->value("tor-host"));
|
||||
if (this->cmdargs->isSet("tor-port"))
|
||||
config()->set(Config::socks5Port, this->cmdargs->value("tor-port"));
|
||||
if (this->cmdargs->isSet("use-local-tor"))
|
||||
config()->set(Config::useLocalTor, true);
|
||||
|
||||
if (!(isWhonix) && !(isTorSocks)) {
|
||||
this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort);
|
||||
this->network->setProxy(*networkProxy);
|
||||
if (globals::websocketUrl.host().endsWith(".onion")) {
|
||||
this->ws->webSocket.setProxy(*networkProxy);
|
||||
}
|
||||
}
|
||||
torManager()->init();
|
||||
torManager()->start();
|
||||
|
||||
connect(torManager(), &TorManager::connectionStateChanged, &websocketNotifier()->websocketClient, &WebsocketClient::onToggleConnect);
|
||||
|
||||
this->onTorSettingsChanged();
|
||||
}
|
||||
|
||||
void AppContext::initWS() {
|
||||
this->ws->start();
|
||||
websocketNotifier()->websocketClient.start();
|
||||
}
|
||||
|
||||
void AppContext::onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address) {
|
||||
|
@ -249,6 +228,17 @@ void AppContext::onOpenWallet(const QString &path, const QString &password){
|
|||
this->walletManager->openWalletAsync(path, password, this->networkType, 1);
|
||||
}
|
||||
|
||||
void AppContext::onWalletCreated(Wallet * wallet) {
|
||||
// Currently only called when a wallet is created from device.
|
||||
auto state = wallet->status();
|
||||
if (state != Wallet::Status_Ok) {
|
||||
emit walletCreatedError(wallet->errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
this->onWalletOpened(wallet);
|
||||
}
|
||||
|
||||
void AppContext::onPreferredFiatCurrencyChanged(const QString &symbol) {
|
||||
if(this->currentWallet) {
|
||||
auto *model = this->currentWallet->transactionHistoryModel();
|
||||
|
@ -276,8 +266,7 @@ void AppContext::commitTransaction(PendingTransaction *tx) {
|
|||
}
|
||||
|
||||
void AppContext::onMultiBroadcast(PendingTransaction *tx) {
|
||||
UtilsNetworking *net = new UtilsNetworking(this->network, this);
|
||||
DaemonRpc *rpc = new DaemonRpc(this, net, "");
|
||||
DaemonRpc rpc{this, getNetworkTor(), ""};
|
||||
|
||||
int count = tx->txCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
@ -286,40 +275,81 @@ void AppContext::onMultiBroadcast(PendingTransaction *tx) {
|
|||
for (const auto& node: this->nodes->websocketNodes()) {
|
||||
if (!node.online) continue;
|
||||
|
||||
QString address = node.as_url();
|
||||
QString address = node.toURL();
|
||||
qDebug() << QString("Relaying %1 to: %2").arg(tx->txid()[i], address);
|
||||
rpc->setDaemonAddress(address);
|
||||
rpc->sendRawTransaction(txData);
|
||||
rpc.setDaemonAddress(address);
|
||||
rpc.sendRawTransaction(txData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AppContext::onDeviceButtonRequest(quint64 code) {
|
||||
emit deviceButtonRequest(code);
|
||||
}
|
||||
|
||||
void AppContext::onDeviceError(const QString &message) {
|
||||
qCritical() << "Device error: " << message;
|
||||
emit deviceError(message);
|
||||
}
|
||||
|
||||
void AppContext::onTorSettingsChanged() {
|
||||
if (WhonixOS::detect() || Utils::isTorsocks()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// use local tor -> bundled tor
|
||||
QString host = config()->get(Config::socks5Host).toString();
|
||||
quint16 port = config()->get(Config::socks5Port).toString().toUShort();
|
||||
if (!torManager()->isLocalTor()) {
|
||||
host = torManager()->featherTorHost;
|
||||
port = torManager()->featherTorPort;
|
||||
}
|
||||
|
||||
QNetworkProxy proxy{QNetworkProxy::Socks5Proxy, host, port};
|
||||
getNetworkTor()->setProxy(proxy);
|
||||
websocketNotifier()->websocketClient.webSocket.setProxy(proxy);
|
||||
|
||||
this->nodes->connectToNode();
|
||||
|
||||
auto privacyLevel = config()->get(Config::torPrivacyLevel).toInt();
|
||||
qDebug() << "Changed privacyLevel to " << privacyLevel;
|
||||
}
|
||||
|
||||
void AppContext::onInitialNetworkConfigured() {
|
||||
this->initTor();
|
||||
this->initWS();
|
||||
}
|
||||
|
||||
void AppContext::onWalletOpened(Wallet *wallet) {
|
||||
auto state = wallet->status();
|
||||
if (state != Wallet::Status_Ok) {
|
||||
auto errMsg = wallet->errorString();
|
||||
if(errMsg == QString("basic_string::_M_replace_aux") || errMsg == QString("std::bad_alloc")) {
|
||||
if (state == Wallet::Status_BadPassword) {
|
||||
this->closeWallet(false);
|
||||
// Don't show incorrect password when we try with empty password for the first time
|
||||
bool showIncorrectPassword = m_openWalletTriedOnce;
|
||||
m_openWalletTriedOnce = true;
|
||||
emit walletOpenPasswordNeeded(showIncorrectPassword, wallet->path());
|
||||
}
|
||||
else if (errMsg == QString("basic_string::_M_replace_aux") || errMsg == QString("std::bad_alloc")) {
|
||||
qCritical() << errMsg;
|
||||
this->walletManager->clearWalletCache(this->walletPath);
|
||||
errMsg = QString("%1\n\nAttempted to clean wallet cache. Please restart Feather.").arg(errMsg);
|
||||
this->closeWallet(false);
|
||||
emit walletOpenedError(errMsg);
|
||||
} else if(errMsg.contains("wallet cannot be opened as")) {
|
||||
this->closeWallet(false);
|
||||
emit walletOpenedError(errMsg);
|
||||
} else if(errMsg.contains("is opened by another wallet program")) {
|
||||
this->closeWallet(false);
|
||||
emit walletOpenedError(errMsg);
|
||||
} else {
|
||||
this->closeWallet(false);
|
||||
emit walletOpenPasswordNeeded(!this->walletPassword.isEmpty(), wallet->path());
|
||||
emit walletOpenedError(errMsg);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_openWalletTriedOnce = false;
|
||||
this->refreshed = false;
|
||||
this->currentWallet = wallet;
|
||||
this->walletPath = this->currentWallet->path() + ".keys";
|
||||
this->walletPassword = this->currentWallet->getPassword();
|
||||
config()->set(Config::walletPath, this->walletPath);
|
||||
|
||||
connect(this->currentWallet, &Wallet::moneySpent, this, &AppContext::onMoneySpent);
|
||||
|
@ -331,6 +361,7 @@ void AppContext::onWalletOpened(Wallet *wallet) {
|
|||
connect(this->currentWallet, &Wallet::transactionCommitted, this, &AppContext::onTransactionCommitted);
|
||||
connect(this->currentWallet, &Wallet::heightRefreshed, this, &AppContext::onHeightRefreshed);
|
||||
connect(this->currentWallet, &Wallet::transactionCreated, this, &AppContext::onTransactionCreated);
|
||||
connect(this->currentWallet, &Wallet::deviceError, this, &AppContext::onDeviceError);
|
||||
|
||||
emit walletOpened();
|
||||
|
||||
|
@ -346,142 +377,6 @@ void AppContext::onWalletOpened(Wallet *wallet) {
|
|||
|
||||
// force trigger preferredFiat signal for history model
|
||||
this->onPreferredFiatCurrencyChanged(config()->get(Config::preferredFiatCurrency).toString());
|
||||
this->setWindowTitle();
|
||||
}
|
||||
|
||||
void AppContext::setWindowTitle(bool mining) {
|
||||
QFileInfo fileInfo(this->walletPath);
|
||||
auto title = QString("Feather - [%1]").arg(fileInfo.fileName());
|
||||
if(this->currentWallet && this->currentWallet->viewOnly())
|
||||
title += " [view-only]";
|
||||
if(mining)
|
||||
title += " [mining]";
|
||||
|
||||
emit setTitle(title);
|
||||
}
|
||||
|
||||
void AppContext::onWSMessage(const QJsonObject &msg) {
|
||||
QString cmd = msg.value("cmd").toString();
|
||||
|
||||
if (cmd == "blockheights") {
|
||||
QJsonObject data = msg.value("data").toObject();
|
||||
int mainnet = data.value("mainnet").toInt();
|
||||
int stagenet = data.value("stagenet").toInt();
|
||||
|
||||
this->heights[NetworkType::MAINNET] = mainnet;
|
||||
this->heights[NetworkType::STAGENET] = stagenet;
|
||||
}
|
||||
|
||||
else if(cmd == "nodes") {
|
||||
this->onWSNodes(msg.value("data").toArray());
|
||||
}
|
||||
#if defined(HAS_XMRIG)
|
||||
else if(cmd == "xmrig") {
|
||||
this->XMRigDownloads(msg.value("data").toObject());
|
||||
}
|
||||
#endif
|
||||
else if(cmd == "crypto_rates") {
|
||||
QJsonArray crypto_rates = msg.value("data").toArray();
|
||||
AppContext::prices->cryptoPricesReceived(crypto_rates);
|
||||
}
|
||||
|
||||
else if(cmd == "fiat_rates") {
|
||||
QJsonObject fiat_rates = msg.value("data").toObject();
|
||||
AppContext::prices->fiatPricesReceived(fiat_rates);
|
||||
}
|
||||
else if(cmd == "reddit") {
|
||||
QJsonArray reddit_data = msg.value("data").toArray();
|
||||
this->onWSReddit(reddit_data);
|
||||
}
|
||||
|
||||
else if(cmd == "ccs") {
|
||||
auto ccs_data = msg.value("data").toArray();
|
||||
this->onWSCCS(ccs_data);
|
||||
}
|
||||
|
||||
else if(cmd == "txFiatHistory") {
|
||||
auto txFiatHistory_data = msg.value("data").toObject();
|
||||
AppContext::txFiatHistory->onWSData(txFiatHistory_data);
|
||||
}
|
||||
}
|
||||
|
||||
void AppContext::onWSNodes(const QJsonArray &nodes) {
|
||||
QList<QSharedPointer<FeatherNode>> l;
|
||||
for (auto &&entry: nodes) {
|
||||
auto obj = entry.toObject();
|
||||
auto nettype = obj.value("nettype");
|
||||
auto type = obj.value("type");
|
||||
|
||||
// filter remote node network types
|
||||
if(nettype == "mainnet" && this->networkType != NetworkType::MAINNET)
|
||||
continue;
|
||||
if(nettype == "stagenet" && this->networkType != NetworkType::STAGENET)
|
||||
continue;
|
||||
if(nettype == "testnet" && this->networkType != NetworkType::TESTNET)
|
||||
continue;
|
||||
|
||||
if(type == "clearnet" && (this->isTails || this->isWhonix || this->isTorSocks))
|
||||
continue;
|
||||
if(type == "tor" && (!(this->isTails || this->isWhonix || this->isTorSocks)))
|
||||
continue;
|
||||
|
||||
auto node = new FeatherNode(
|
||||
obj.value("address").toString(),
|
||||
obj.value("height").toInt(),
|
||||
obj.value("target_height").toInt(),
|
||||
obj.value("online").toBool());
|
||||
QSharedPointer<FeatherNode> r = QSharedPointer<FeatherNode>(node);
|
||||
l.append(r);
|
||||
}
|
||||
this->nodes->onWSNodesReceived(l);
|
||||
}
|
||||
|
||||
void AppContext::onWSReddit(const QJsonArray& reddit_data) {
|
||||
QList<QSharedPointer<RedditPost>> l;
|
||||
|
||||
for (auto &&entry: reddit_data) {
|
||||
auto obj = entry.toObject();
|
||||
auto redditPost = new RedditPost(
|
||||
obj.value("title").toString(),
|
||||
obj.value("author").toString(),
|
||||
obj.value("permalink").toString(),
|
||||
obj.value("comments").toInt());
|
||||
QSharedPointer<RedditPost> r = QSharedPointer<RedditPost>(redditPost);
|
||||
l.append(r);
|
||||
}
|
||||
|
||||
emit redditUpdated(l);
|
||||
}
|
||||
|
||||
void AppContext::onWSCCS(const QJsonArray &ccs_data) {
|
||||
QList<QSharedPointer<CCSEntry>> l;
|
||||
|
||||
|
||||
QStringList fonts = {"state", "address", "author", "date",
|
||||
"title", "target_amount", "raised_amount",
|
||||
"percentage_funded", "contributions"};
|
||||
|
||||
for (auto &&entry: ccs_data) {
|
||||
auto obj = entry.toObject();
|
||||
auto c = QSharedPointer<CCSEntry>(new CCSEntry());
|
||||
|
||||
if (obj.value("state").toString() != "FUNDING-REQUIRED")
|
||||
continue;
|
||||
|
||||
c->state = obj.value("state").toString();
|
||||
c->address = obj.value("address").toString();
|
||||
c->author = obj.value("author").toString();
|
||||
c->date = obj.value("date").toString();
|
||||
c->title = obj.value("title").toString();
|
||||
c->url = obj.value("url").toString();
|
||||
c->target_amount = obj.value("target_amount").toDouble();
|
||||
c->raised_amount = obj.value("raised_amount").toDouble();
|
||||
c->percentage_funded = obj.value("percentage_funded").toDouble();
|
||||
c->contributions = obj.value("contributions").toInt();
|
||||
l.append(c);
|
||||
}
|
||||
|
||||
emit ccsUpdated(l);
|
||||
}
|
||||
|
||||
void AppContext::createConfigDirectory(const QString &dir) {
|
||||
|
@ -528,7 +423,18 @@ void AppContext::createWallet(FeatherSeed seed, const QString &path, const QStri
|
|||
return;
|
||||
}
|
||||
|
||||
this->createWalletFinish(password);
|
||||
this->onWalletOpened(wallet);
|
||||
}
|
||||
|
||||
void AppContext::createWalletFromDevice(const QString &path, const QString &password, int restoreHeight) {
|
||||
if(Utils::fileExists(path)) {
|
||||
auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path);
|
||||
qCritical() << err;
|
||||
emit walletCreatedError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
this->walletManager->createWalletFromDeviceAsync(path, password, this->networkType, "Ledger", restoreHeight);
|
||||
}
|
||||
|
||||
void AppContext::createWalletFromKeys(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight, bool deterministic) {
|
||||
|
@ -539,7 +445,7 @@ void AppContext::createWalletFromKeys(const QString &path, const QString &passwo
|
|||
return;
|
||||
}
|
||||
|
||||
if(!this->walletManager->addressValid(address, this->networkType)) {
|
||||
if(!WalletManager::addressValid(address, this->networkType)) {
|
||||
auto err = QString("Failed to create wallet. Invalid address provided.").arg(path);
|
||||
qCritical() << err;
|
||||
emit walletCreatedError(err);
|
||||
|
@ -560,21 +466,8 @@ void AppContext::createWalletFromKeys(const QString &path, const QString &passwo
|
|||
return;
|
||||
}
|
||||
|
||||
this->currentWallet = this->walletManager->createWalletFromKeys(path, this->seedLanguage, this->networkType, address, viewkey, spendkey, restoreHeight);
|
||||
this->createWalletFinish(password);
|
||||
}
|
||||
|
||||
void AppContext::createWalletFinish(const QString &password) {
|
||||
this->currentWallet->setPassword(password);
|
||||
this->currentWallet->store();
|
||||
this->walletPassword = password;
|
||||
emit walletCreated(this->currentWallet);
|
||||
}
|
||||
|
||||
void AppContext::initRestoreHeights() {
|
||||
restoreHeights[NetworkType::TESTNET] = new RestoreHeightLookup(NetworkType::TESTNET);
|
||||
restoreHeights[NetworkType::STAGENET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_monero_stagenet.txt", NetworkType::STAGENET);
|
||||
restoreHeights[NetworkType::MAINNET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_monero_mainnet.txt", NetworkType::MAINNET);
|
||||
Wallet *wallet = this->walletManager->createWalletFromKeys(path, password, this->seedLanguage, this->networkType, address, viewkey, spendkey, restoreHeight);
|
||||
this->walletManager->walletOpened(wallet);
|
||||
}
|
||||
|
||||
void AppContext::onSetRestoreHeight(quint64 height){
|
||||
|
@ -643,7 +536,7 @@ void AppContext::donateBeg() {
|
|||
if (this->currentWallet->viewOnly()) return;
|
||||
|
||||
auto donationCounter = config()->get(Config::donateBeg).toInt();
|
||||
if(donationCounter == -1)
|
||||
if (donationCounter == -1)
|
||||
return; // previously donated
|
||||
|
||||
donationCounter += 1;
|
||||
|
@ -688,7 +581,13 @@ void AppContext::onWalletUpdate() {
|
|||
this->updateBalance();
|
||||
}
|
||||
|
||||
void AppContext::onWalletRefreshed(bool success) {
|
||||
void AppContext::onWalletRefreshed(bool success, const QString &message) {
|
||||
if (!success) {
|
||||
// Something went wrong during refresh, in some cases we need to notify the user
|
||||
qCritical() << "Exception during refresh: " << message; // Can't use ->errorString() here, other SLOT might snipe it first
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->refreshed) {
|
||||
refreshModels();
|
||||
this->refreshed = true;
|
||||
|
@ -778,11 +677,10 @@ void AppContext::updateBalance() {
|
|||
if (!this->currentWallet)
|
||||
return;
|
||||
|
||||
quint64 balance_u = this->currentWallet->balance();
|
||||
AppContext::balance = balance_u / globals::cdiv;
|
||||
double spendable = this->currentWallet->unlockedBalance();
|
||||
quint64 balance = this->currentWallet->balance();
|
||||
quint64 spendable = this->currentWallet->unlockedBalance();
|
||||
|
||||
emit balanceUpdated(balance_u, spendable);
|
||||
emit balanceUpdated(balance, spendable);
|
||||
}
|
||||
|
||||
void AppContext::syncStatusUpdated(quint64 height, quint64 target) {
|
||||
|
|
|
@ -5,16 +5,13 @@
|
|||
#define FEATHER_APPCONTEXT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QTimer>
|
||||
|
||||
#include "utils/tails.h"
|
||||
#include "utils/whonix.h"
|
||||
#include "utils/prices.h"
|
||||
#include "utils/networking.h"
|
||||
#include "utils/tor.h"
|
||||
#include "utils/xmrig.h"
|
||||
#include "utils/TorManager.h"
|
||||
#include "utils/wsclient.h"
|
||||
#include "utils/txfiathistory.h"
|
||||
#include "utils/FeatherSeed.h"
|
||||
|
@ -28,9 +25,6 @@
|
|||
#include "utils/keysfiles.h"
|
||||
#include "PendingTransaction.h"
|
||||
|
||||
#define SUBADDRESS_LOOKAHEAD_MINOR 200
|
||||
|
||||
|
||||
class AppContext : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -43,7 +37,6 @@ public:
|
|||
|
||||
bool isTails = false;
|
||||
bool isWhonix = false;
|
||||
bool isTorSocks = false;
|
||||
|
||||
bool donationSending = false;
|
||||
|
||||
|
@ -54,25 +47,13 @@ public:
|
|||
QString walletPassword = "";
|
||||
NetworkType::Type networkType;
|
||||
|
||||
QMap<NetworkType::Type, int> heights;
|
||||
QMap<NetworkType::Type, RestoreHeightLookup*> restoreHeights;
|
||||
PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low;
|
||||
QString seedLanguage = "English"; // 14 word `monero-seed` only has English
|
||||
|
||||
QNetworkAccessManager *network;
|
||||
QNetworkAccessManager *networkClearnet;
|
||||
QNetworkProxy *networkProxy{};
|
||||
Nodes *nodes; // TODO: move this to mainwindow (?)
|
||||
|
||||
Tor *tor{};
|
||||
WSClient *ws;
|
||||
XmRig *XMRig;
|
||||
Nodes *nodes;
|
||||
DaemonRpc *daemonRpc;
|
||||
static Prices *prices;
|
||||
static WalletKeysFilesModel *wallets;
|
||||
static double balance;
|
||||
static QMap<QString, QString> txCache;
|
||||
static TxFiatHistory *txFiatHistory;
|
||||
|
||||
static void createConfigDirectory(const QString &dir);
|
||||
|
||||
|
@ -81,17 +62,15 @@ public:
|
|||
WalletManager *walletManager;
|
||||
Wallet *currentWallet = nullptr;
|
||||
void createWallet(FeatherSeed seed, const QString &path, const QString &password, const QString &seedOffset = "");
|
||||
void createWalletFromDevice(const QString &path, const QString &password, int restoreHeight);
|
||||
void createWalletFromKeys(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight, bool deterministic = false);
|
||||
void createWalletFinish(const QString &password);
|
||||
void commitTransaction(PendingTransaction *tx);
|
||||
void syncStatusUpdated(quint64 height, quint64 target);
|
||||
void updateBalance();
|
||||
void initTor();
|
||||
void initRestoreHeights();
|
||||
void initWS();
|
||||
void donateBeg();
|
||||
void refreshModels();
|
||||
void setWindowTitle(bool mining = false);
|
||||
|
||||
// Closes the currently opened wallet
|
||||
void closeWallet(bool emitClosedSignal = true, bool storeWallet = false);
|
||||
|
@ -99,6 +78,7 @@ public:
|
|||
|
||||
public slots:
|
||||
void onOpenWallet(const QString& path, const QString &password);
|
||||
void onWalletCreated(Wallet * wallet);
|
||||
void onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all);
|
||||
void onCreateTransactionMultiDest(const QVector<QString> &addresses, const QVector<quint64> &amounts, const QString &description);
|
||||
void onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address);
|
||||
|
@ -109,18 +89,17 @@ public slots:
|
|||
void onPreferredFiatCurrencyChanged(const QString &symbol);
|
||||
void onAmountPrecisionChanged(int precision);
|
||||
void onMultiBroadcast(PendingTransaction *tx);
|
||||
void onDeviceButtonRequest(quint64 code);
|
||||
void onTorSettingsChanged();
|
||||
void onInitialNetworkConfigured();
|
||||
void onDeviceError(const QString &message);
|
||||
|
||||
private slots:
|
||||
void onWSNodes(const QJsonArray &nodes);
|
||||
void onWSMessage(const QJsonObject& msg);
|
||||
void onWSCCS(const QJsonArray &ccs_data);
|
||||
void onWSReddit(const QJsonArray& reddit_data);
|
||||
|
||||
void onMoneySpent(const QString &txId, quint64 amount);
|
||||
void onMoneyReceived(const QString &txId, quint64 amount);
|
||||
void onUnconfirmedMoneyReceived(const QString &txId, quint64 amount);
|
||||
void onWalletUpdate();
|
||||
void onWalletRefreshed(bool success);
|
||||
void onWalletRefreshed(bool success, const QString &message);
|
||||
void onWalletOpened(Wallet *wallet);
|
||||
void onWalletNewBlock(quint64 blockheight, quint64 targetHeight);
|
||||
void onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight);
|
||||
|
@ -148,12 +127,6 @@ signals:
|
|||
void createTransactionError(QString message);
|
||||
void createTransactionCancelled(const QVector<QString> &address, double amount);
|
||||
void createTransactionSuccess(PendingTransaction *tx, const QVector<QString> &address);
|
||||
void redditUpdated(QList<QSharedPointer<RedditPost>> &posts);
|
||||
void nodesUpdated(QList<QSharedPointer<FeatherNode>> &nodes);
|
||||
void ccsUpdated(QList<QSharedPointer<CCSEntry>> &entries);
|
||||
void nodeSourceChanged(NodeSource nodeSource);
|
||||
void XMRigDownloads(const QJsonObject &data);
|
||||
void setCustomNodes(QList<FeatherNode> nodes);
|
||||
void openAliasResolveError(const QString &msg);
|
||||
void openAliasResolved(const QString &address, const QString &openAlias);
|
||||
void setRestoreHeightError(const QString &msg);
|
||||
|
@ -162,10 +135,13 @@ signals:
|
|||
void donationNag();
|
||||
void initiateTransaction();
|
||||
void endTransaction();
|
||||
void setTitle(const QString &title); // set window title
|
||||
void deviceButtonRequest(quint64 code);
|
||||
void updatesAvailable(const QJsonObject &updates);
|
||||
void deviceError(const QString &message);
|
||||
|
||||
private:
|
||||
QTimer m_storeTimer;
|
||||
bool m_openWalletTriedOnce = false;
|
||||
};
|
||||
|
||||
#endif //FEATHER_APPCONTEXT_H
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<file>assets/contributors.txt</file>
|
||||
<file>assets/feather.desktop</file>
|
||||
<file>assets/nodes.json</file>
|
||||
<file>assets/gpg_keys/featherwallet.asc</file>
|
||||
<file>assets/images/appicons/32x32.png</file>
|
||||
<file>assets/images/appicons/48x48.png</file>
|
||||
<file>assets/images/appicons/64x64.png</file>
|
||||
|
@ -44,10 +45,18 @@
|
|||
<file>assets/images/gnome-calc.png</file>
|
||||
<file>assets/images/history.png</file>
|
||||
<file>assets/images/info.png</file>
|
||||
<file>assets/images/info2.svg</file>
|
||||
<file>assets/images/key.png</file>
|
||||
<file>assets/images/ledger.png</file>
|
||||
<file>assets/images/ledger_unpaired.png</file>
|
||||
<file>assets/images/lightning.png</file>
|
||||
<file>assets/images/localMonero_search.svg</file>
|
||||
<file>assets/images/localMonero_buy.svg</file>
|
||||
<file>assets/images/localMonero_buy_white.svg</file>
|
||||
<file>assets/images/localMonero_sell.svg</file>
|
||||
<file>assets/images/localMonero_sell_white.svg</file>
|
||||
<file>assets/images/localMonero_logo.png</file>
|
||||
<file>assets/images/localMonero_register.svg</file>
|
||||
<file>assets/images/lock.png</file>
|
||||
<file>assets/images/lock_icon.png</file>
|
||||
<file>assets/images/lock.svg</file>
|
||||
|
@ -63,6 +72,12 @@
|
|||
<file>assets/images/revealer_c.png</file>
|
||||
<file>assets/images/revealer.png</file>
|
||||
<file>assets/images/seal.png</file>
|
||||
<file>assets/images/securityLevelSafer.png</file>
|
||||
<file>assets/images/securityLevelSafest.png</file>
|
||||
<file>assets/images/securityLevelStandard.png</file>
|
||||
<file>assets/images/securityLevelSaferWhite.png</file>
|
||||
<file>assets/images/securityLevelSafestWhite.png</file>
|
||||
<file>assets/images/securityLevelStandardWhite.png</file>
|
||||
<file>assets/images/seed.png</file>
|
||||
<file>assets/images/speaker.png</file>
|
||||
<file>assets/images/status_connected_fork.png</file>
|
||||
|
|
52
src/assets/gpg_keys/featherwallet.asc
Normal file
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBF/HogkBEAChsjCJUsZhDxOx5FrnRA3X5/mJd2xdKskLSPFtnYiQUtKvpRW6
|
||||
i/RVNMkTwFovzbXB6ucKJtY+OoEMu7xDhIkDWp//UlfHuP9AWAvqbhq6V5xVrZ41
|
||||
9oQ7JNN4gwAI8+ZjcNq3IVFQQ9mZ3py9t1IUdgWtWM3P/SD7vWiPIjG0D3Bt3Ptl
|
||||
/mZjIFZZWUtFBItJLkiTpW0Ue4t98XMP6mvQiQ/LhP82OtSyCZ6agj4Wa3ve5KjA
|
||||
pdEqamBGytx2kmN+AQFgMt66yOvr+97zzuEzI6mlWYORzOc1CFMsmPd6bu/dtQ4Z
|
||||
96T8PNI6i1Lv5VqvqC7RBErvD7hO36JZb8j+PnbE1YADTKrw0HmgpI6d3RLyVop3
|
||||
n6ZQri0+nZ+TH0JG74MiihyZIz826zJO5OIwltexRcW0ZiRSpRCxZekU894lEs5Q
|
||||
SxacRLeqM8ZVawB+9brqbeU3IJxmOCZgXLkkns0dBiSWGxtt+Tji+KXjogNfghmA
|
||||
dVw9NQoBS+W5+pBtKEORD0YIGiUou9a7ukyMe2uvsl7rT+7BCOdvYtMBRbsfV5NP
|
||||
s644wfJNIGa7OOjkWhuGwy6BVKTohDhJdKeZUpiTPKLV7ZLHjT4pkjuJgGQB7c+w
|
||||
v7QYeUpwARwQNi8ZHuij2loG3Fb4l+3ejkcvivw0DLnDDhvUY57ezq53JwARAQAB
|
||||
tCVGZWF0aGVyV2FsbGV0IDxkZXZAZmVhdGhlcndhbGxldC5vcmc+iQJOBBMBCgA4
|
||||
FiEEgYXhWKMzMMf9YbwNH3bhVc77pxwFAl/HogkCGwMFCwkIBwMFFQoJCAsFFgID
|
||||
AQACHgECF4AACgkQH3bhVc77pxzAxw/9GYXGm71lUlZl2yfBPmo91euSc3w/irEC
|
||||
88X1kFBsdKwL19B8HUaksCOQJRG8fJQmKvJmFnRZg3NK/GLIHam+1WVObFZc1MTv
|
||||
y2ERzX5ILr9sb7FptB0Wr9gk0y0Nv032ZKci3wn1j2nA87o40uopDoQTaadDTKXa
|
||||
s3M2+y6zM4dCmCaV6ylJromTzIaL2Q+tWSHDD8EDF2GbnfSeeEV6TV4xj3vqfT5P
|
||||
34rK4vuVNxEy/YvRQJVRYntveNMJu9C4KJvIpo8onauUHEgBu4m+qfFpixDLwQzq
|
||||
bJiJQaCUrwJ3liKMolBKiPqjGNl5JRRDy+YR1Dgsj6CRobWg1fDNnrGXUwDLaBwx
|
||||
zVdCB0VSmcjXpt+FKTxw1mbY+6i6trUfJSjaaawXJbktOkO6sl0bVX83oQxEgod1
|
||||
aHwuo+eFCAW5zF0r+8R9Lk97Y5jkLWRKjXMFnMIyHaRhPdc24fOfojIQrXzQBMEO
|
||||
lDhbWVd5vdOALhqvSOGYvjGjxBd9TE0pGzayNfPaee6kFEbxO3wZgF/QLPABl8i9
|
||||
b6hHJewpY5W9mM9/yP4lHL2TRcEMzk6I7XxPQUGEb3fzTAEHRM+My4SLwaUBIFvM
|
||||
L8+hRhbfNnLZPd0xDAmvH6wToL3qgK/xSl9SYwuZkzaynblmyXE4+dCFp+T2XTam
|
||||
FIbphOl8Yt+5Ag0EX8eiCQEQAKv0XnHtGhWTaq/sQ4lulYWNRjBsFQRMqwSFIosO
|
||||
PfzWwATQeHxxIgRlWkc25w8W0O//t8x0UcNA5rU4R+C7kVrchVSYYYl9PY0vBhKP
|
||||
3efVtPgntl/VgGH8LAdShHEt3H8ZDMFjqT6gx4xnpgt3C5OdGOA3bIWuvSZ1P7qp
|
||||
SYiFZakrDfPeCdI/ifucipd+EnZhFv7ivnaoIGs+jgaImQH/5uEEVxpA89Bpxoju
|
||||
gXlEKSVkVAanZsUwQkc/xzhsh8dzuEF5yKomVbwTYmXDTYmpff02ycdUP7gHw0Qg
|
||||
WrWaQ2M0Xq1qcZL3ZpoaWUa/A92OfuncCSDNq1pRLqwJrExqQUP9cHGwGbqeGl8K
|
||||
n2tFds8Pnnv+57ZKiO8E1VTDyBey1J3/Y1hOzctfEz6BzrL52Vj4vPWh2WNNh5fL
|
||||
u1ZEIdykflH/Kho0zQkRfBfD93FbN/nH1xL3V7pO/wXVGqHSD3HbFLIcJ9Ax+Jgc
|
||||
Z9fm9Bvc2RkXC8lJU5+htQ+YwHPLDExvUKrBL8b8xksODCvJSWLKcTPooFQyKgbK
|
||||
EnPW5kmn3eT0SHHHOArn6EHoQttkR0pV2Lrgpfg+uhy3LSTmKbtRWo7VgDY0kfVL
|
||||
hsatIUqYAVdDTBzsuMhehaoWwtLAsJ01OqxAoc6+0velLddLBuLxtzGtsF0u2mEF
|
||||
QJmBABEBAAGJAjYEGAEKACAWIQSBheFYozMwx/1hvA0fduFVzvunHAUCX8eiCQIb
|
||||
DAAKCRAfduFVzvunHDx1D/45GVAtIP1X640PR6N8qa4Iysc/crKepgDqm8zzvpQ8
|
||||
58MdeJZ9oPFEHDMkIMM8FGK9GbK4UE5mJzWJ2y5acMDOwvX4C9M206YaWQW9jPZt
|
||||
fTfElP1KdAfTWz2/1UeOZKtOUuq9Wq+QlZGYg532JlX09TMyvINRM/w0+f4IBDlE
|
||||
XIeRzRI6UQfz3BxpFpfWtMq/ayJnmJPrDsKQBPalai01OsbC+h4BUysZf1n7eTRF
|
||||
DVaAKkSeOu+4gOVguE9PgKr11lDlKOI38tR6xBXzidBe3cPdun6vQbd1Bdfdmx3J
|
||||
yFtlQo16kwwG2ZiVicXXugASBsrOFJa2/0lrtAPOnUWJsp2+1Ea6IzpRN8d1mNqr
|
||||
6ND+CLxBsWj16UXq34GW6vt/QM7N1Br4/6SuPtv8OmDGRkRH7h2pz5yMf5GOwQFq
|
||||
kgvOHt/x/sFPwk0GMgGn8aFr3vPH2YDg90mPn306Kv12e0JGkYVl4KqdL7u51gxT
|
||||
3z5C/4+hhPVGHSPkf+g0VY/eY136kuuAZjV3P36M6UaBeCyqeD7b3fJ5IJcLwD9N
|
||||
R0ustnn8IJ9zEwn+LY8kjRG8J3V57t2qAVGkMCiXnwFu3Vb+AYozOYi2ibu/N9QX
|
||||
V4dTHarw64HUtLu/HEtcYuzuM5nGOXYvWPz3pQBtlqsyrhIfeaywQ+O55h5/KBo8
|
||||
Ig==
|
||||
=2rq8
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
5
src/assets/images/info2.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<svg version="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" enable-background="new 0 0 48 48">
|
||||
<circle fill="#2196F3" cx="24" cy="24" r="21"/>
|
||||
<rect x="22" y="22" fill="#fff" width="4" height="11"/>
|
||||
<circle fill="#fff" cx="24" cy="16.5" r="2.5"/>
|
||||
</svg>
|
After Width: | Height: | Size: 278 B |
1
src/assets/images/localMonero_buy.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="margin-right: 8px;"><path d="M10,0V4H8L12,8L16,4H14V0M1,2V4H3L6.6,11.59L5.25,14.04C5.09,14.32 5,14.65 5,15A2,2 0 0,0 7,17H19V15H7.42C7.29,15 7.17,14.89 7.17,14.75L7.2,14.63L8.1,13H15.55C16.3,13 16.96,12.59 17.3,11.97L21.16,4.96L19.42,4H19.41L18.31,6L15.55,11H8.53L8.4,10.73L6.16,6L5.21,4L4.27,2M7,18A2,2 0 0,0 5,20A2,2 0 0,0 7,22A2,2 0 0,0 9,20A2,2 0 0,0 7,18M17,18A2,2 0 0,0 15,20A2,2 0 0,0 17,22A2,2 0 0,0 19,20A2,2 0 0,0 17,18Z"></path></svg>
|
After Width: | Height: | Size: 574 B |
1
src/assets/images/localMonero_buy_white.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="margin-right: 8px;"><path d="M10,0V4H8L12,8L16,4H14V0M1,2V4H3L6.6,11.59L5.25,14.04C5.09,14.32 5,14.65 5,15A2,2 0 0,0 7,17H19V15H7.42C7.29,15 7.17,14.89 7.17,14.75L7.2,14.63L8.1,13H15.55C16.3,13 16.96,12.59 17.3,11.97L21.16,4.96L19.42,4H19.41L18.31,6L15.55,11H8.53L8.4,10.73L6.16,6L5.21,4L4.27,2M7,18A2,2 0 0,0 5,20A2,2 0 0,0 7,22A2,2 0 0,0 9,20A2,2 0 0,0 7,18M17,18A2,2 0 0,0 15,20A2,2 0 0,0 17,22A2,2 0 0,0 19,20A2,2 0 0,0 17,18Z"></path></svg>
|
After Width: | Height: | Size: 589 B |
BIN
src/assets/images/localMonero_logo.png
Normal file
After Width: | Height: | Size: 10 KiB |
1
src/assets/images/localMonero_register.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" class="MuiSvgIcon-root-684" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="margin-right: 8px;"><path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"></path></svg>
|
After Width: | Height: | Size: 318 B |
1
src/assets/images/localMonero_search.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" class="MuiSvgIcon-root-6593" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="margin-right: 8px;"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path></svg>
|
After Width: | Height: | Size: 412 B |
1
src/assets/images/localMonero_sell.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="margin-right: 8px;"><path d="M12,0L8,4H10V8H14V4H16M1,2V4H3L6.6,11.59L5.25,14.04C5.09,14.32 5,14.65 5,15A2,2 0 0,0 7,17H19V15H7.42C7.29,15 7.17,14.89 7.17,14.75L7.2,14.63L8.1,13H15.55C16.3,13 16.96,12.59 17.3,11.97L21.16,4.96L19.42,4H19.41L18.31,6L15.55,11H8.53L8.4,10.73L6.16,6L5.21,4L4.27,2M7,18A2,2 0 0,0 5,20A2,2 0 0,0 7,22A2,2 0 0,0 9,20A2,2 0 0,0 7,18M17,18A2,2 0 0,0 15,20A2,2 0 0,0 17,22A2,2 0 0,0 19,20A2,2 0 0,0 17,18Z"></path></svg>
|
After Width: | Height: | Size: 572 B |
1
src/assets/images/localMonero_sell_white.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="margin-right: 8px;"><path d="M12,0L8,4H10V8H14V4H16M1,2V4H3L6.6,11.59L5.25,14.04C5.09,14.32 5,14.65 5,15A2,2 0 0,0 7,17H19V15H7.42C7.29,15 7.17,14.89 7.17,14.75L7.2,14.63L8.1,13H15.55C16.3,13 16.96,12.59 17.3,11.97L21.16,4.96L19.42,4H19.41L18.31,6L15.55,11H8.53L8.4,10.73L6.16,6L5.21,4L4.27,2M7,18A2,2 0 0,0 5,20A2,2 0 0,0 7,22A2,2 0 0,0 9,20A2,2 0 0,0 7,18M17,18A2,2 0 0,0 15,20A2,2 0 0,0 17,22A2,2 0 0,0 19,20A2,2 0 0,0 17,18Z"></path></svg>
|
After Width: | Height: | Size: 587 B |
BIN
src/assets/images/securityLevelSafer.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
13
src/assets/images/securityLevelSafer.svg
Normal file
|
@ -0,0 +1,13 @@
|
|||
<svg width="14px" height="16px" viewBox="0 0 14 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<style>
|
||||
use:not(:target) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<defs>
|
||||
<g id="safer_icon" stroke="none" stroke-width="1">
|
||||
<path fill-rule="nonzero" d="M7.0 2.1658351C7.0 13.931584 7.0 2.1658351 7.0 13.931584C9.8656467 13.057677 12.0 10.241354 12.0 7.2727273C12.0 7.2727273 12.0 4.2437572 12.0 4.2437572C12.0 4.2437572 7.0 2.1658351 7.0 2.1658351C7.0 2.1658351 7.0 2.1658351 7.0 2.1658351M7.0 0.0C7.0 0.0 14.0 2.9090909 14.0 2.9090909C14.0 2.9090909 14.0 7.2727273 14.0 7.2727273C14.0 11.309091 11.013333 15.083636 7.0 16.0C2.9866667 15.083636 0.0 11.309091 0.0 7.2727273C0.0 7.2727273 0.0 2.9090909 0.0 2.9090909C0.0 2.9090909 7.0 0.0 7.0 0.0"/>
|
||||
</g>
|
||||
</defs>
|
||||
<use id="safer" fill="context-fill" fill-opacity="context-fill-opacity" href="#safer_icon" />
|
||||
</svg>
|
After Width: | Height: | Size: 932 B |
BIN
src/assets/images/securityLevelSaferWhite.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/assets/images/securityLevelSafest.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
13
src/assets/images/securityLevelSafest.svg
Normal file
|
@ -0,0 +1,13 @@
|
|||
<svg width="14px" height="16px" viewBox="0 0 14 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<style>
|
||||
use:not(:target) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<defs>
|
||||
<g id="safest_icon" stroke="none" stroke-width="1">
|
||||
<path d="M7.0 0.0C7.0 0.0 14.0 2.90909091 14.0 2.90909091C14.0 2.90909091 14.0 7.27272727 14.0 7.27272727C14.0 11.3090909 11.0133333 15.0836364 7.0 16.0C2.98666667 15.0836364 0.0 11.3090909 0.0 7.27272727C0.0 7.27272727 0.0 2.90909091 0.0 2.90909091C0.0 2.90909091 7.0 0.0 7.0 0.0C7.0 0.0 7.0 0.0 7.0 0.0" />
|
||||
</g>
|
||||
</defs>
|
||||
<use id="safest" fill="context-fill" fill-opacity="context-fill-opacity" href="#safest_icon" />
|
||||
</svg>
|
After Width: | Height: | Size: 720 B |
BIN
src/assets/images/securityLevelSafestWhite.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/assets/images/securityLevelStandard.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
13
src/assets/images/securityLevelStandard.svg
Normal file
|
@ -0,0 +1,13 @@
|
|||
<svg width="14px" height="16px" viewBox="0 0 14 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<style>
|
||||
use:not(:target) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<defs>
|
||||
<g id="standard_icon" stroke="none" stroke-width="1">
|
||||
<path d="M7.0 2.16583509C7.0 2.16583509 2.0 4.24375717 2.0 4.24375717C2.0 4.24375717 2.0 7.27272727 2.0 7.27272727C2.0 10.2413541 4.13435329 13.0576771 7.0 13.9315843C9.8656467 13.0576771 12.0 10.2413541 12.0 7.27272727C12.0 7.27272727 12.0 4.24375717 12.0 4.24375717C12.0 4.24375717 7.0 2.16583509 7.0 2.16583509C7.0 2.16583509 7.0 2.16583509 7.0 2.16583509M7.0 0.0C7.0 0.0 14.0 2.90909091 14.0 2.90909091C14.0 2.90909091 14.0 7.27272727 14.0 7.27272727C14.0 11.3090909 11.0133333 15.0836364 7.0 16.0C2.98666667 15.0836364 0.0 11.3090909 0.0 7.27272727C0.0 7.27272727 0.0 2.90909091 0.0 2.90909091C0.0 2.90909091 7.0 0.0 7.0 0.0C7.0 0.0 7.0 0.0 7.0 0.0" />
|
||||
</g>
|
||||
</defs>
|
||||
<use id="standard" fill="context-fill" fill-opacity="context-fill-opacity" href="#standard_icon" />
|
||||
</svg>
|
After Width: | Height: | Size: 1 KiB |
BIN
src/assets/images/securityLevelStandardWhite.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
|
@ -5,16 +5,15 @@
|
|||
|
||||
#include "calcwidget.h"
|
||||
#include "ui_calcwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "components.h"
|
||||
#include "utils/ColorScheme.h"
|
||||
#include "utils/AppData.h"
|
||||
#include "utils/config.h"
|
||||
|
||||
CalcWidget::CalcWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::CalcWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_ctx = MainWindow::getContext();
|
||||
|
||||
ui->imageExchange->setBackgroundRole(QPalette::Base);
|
||||
ui->imageExchange->setAssets(":/assets/images/exchange.png", ":/assets/images/exchange_white.png");
|
||||
|
@ -30,8 +29,8 @@ CalcWidget::CalcWidget(QWidget *parent) :
|
|||
ui->lineFrom->setValidator(dv);
|
||||
ui->lineTo->setValidator(dv);
|
||||
|
||||
connect(AppContext::prices, &Prices::fiatPricesUpdated, this, &CalcWidget::initFiat);
|
||||
connect(AppContext::prices, &Prices::cryptoPricesUpdated, this, &CalcWidget::initCrypto);
|
||||
connect(&appData()->prices, &Prices::fiatPricesUpdated, this, &CalcWidget::initFiat);
|
||||
connect(&appData()->prices, &Prices::cryptoPricesUpdated, this, &CalcWidget::initCrypto);;
|
||||
}
|
||||
|
||||
void CalcWidget::fromChanged(const QString &data) {
|
||||
|
@ -56,12 +55,12 @@ void CalcWidget::fromChanged(const QString &data) {
|
|||
}
|
||||
|
||||
double amount = amount_str.toDouble();
|
||||
double result = AppContext::prices->convert(symbolFrom, symbolTo, amount);
|
||||
double result = appData()->prices.convert(symbolFrom, symbolTo, amount);
|
||||
|
||||
this->m_changing = true;
|
||||
|
||||
int precision = 10;
|
||||
if(AppContext::prices->rates.contains(symbolTo))
|
||||
if (appData()->prices.rates.contains(symbolTo))
|
||||
precision = 2;
|
||||
|
||||
ui->lineTo->setText(QString::number(result, 'f', precision));
|
||||
|
@ -91,12 +90,12 @@ void CalcWidget::toChanged(const QString &data) {
|
|||
}
|
||||
|
||||
double amount = amount_str.toDouble();
|
||||
double result = AppContext::prices->convert(symbolTo, symbolFrom, amount);
|
||||
double result = appData()->prices.convert(symbolTo, symbolFrom, amount);
|
||||
|
||||
this->m_changing = true;
|
||||
|
||||
int precision = 10;
|
||||
if(AppContext::prices->rates.contains(symbolFrom))
|
||||
if(appData()->prices.rates.contains(symbolFrom))
|
||||
precision = 2;
|
||||
|
||||
ui->lineFrom->setText(QString::number(result, 'f', precision));
|
||||
|
@ -116,8 +115,8 @@ void CalcWidget::initFiat() {
|
|||
|
||||
void CalcWidget::initComboBox() {
|
||||
if(m_comboBoxInit) return;
|
||||
QList<QString> marketsKeys = AppContext::prices->markets.keys();
|
||||
QList<QString> ratesKeys = AppContext::prices->rates.keys();
|
||||
QList<QString> marketsKeys = appData()->prices.markets.keys();
|
||||
QList<QString> ratesKeys = appData()->prices.rates.keys();
|
||||
if(marketsKeys.count() <= 0 || ratesKeys.count() <= 0) return;
|
||||
|
||||
ui->comboCalcFrom->addItems(marketsKeys);
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
#ifndef CALC_H
|
||||
#define CALC_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include "appcontext.h"
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class CalcWidget;
|
||||
|
@ -32,7 +31,7 @@ public slots:
|
|||
|
||||
private:
|
||||
Ui::CalcWidget *ui;
|
||||
AppContext *m_ctx;
|
||||
|
||||
bool m_comboBoxInit = false;
|
||||
void initComboBox();
|
||||
bool m_changing = false;
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "calcwindow.h"
|
||||
#include "mainwindow.h"
|
||||
#include "utils/Icons.h"
|
||||
#include "utils/AppData.h"
|
||||
|
||||
#include "ui_calcwindow.h"
|
||||
|
||||
|
@ -14,10 +16,10 @@ CalcWindow::CalcWindow(QWidget *parent) :
|
|||
this->setWindowFlags(flags|Qt::WindowStaysOnTopHint); // on top
|
||||
|
||||
ui->setupUi(this);
|
||||
this->setWindowIcon(QIcon("://assets/images/gnome-calc.png"));
|
||||
this->setWindowIcon(icons()->icon("gnome-calc.png"));
|
||||
|
||||
connect(AppContext::prices, &Prices::fiatPricesUpdated, this, &CalcWindow::initFiat);
|
||||
connect(AppContext::prices, &Prices::cryptoPricesUpdated, this, &CalcWindow::initCrypto);
|
||||
connect(&appData()->prices, &Prices::fiatPricesUpdated, this, &CalcWindow::initFiat);
|
||||
connect(&appData()->prices, &Prices::cryptoPricesUpdated, this, &CalcWindow::initCrypto);
|
||||
}
|
||||
|
||||
void CalcWindow::initFiat() {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "dialog/outputinfodialog.h"
|
||||
#include "dialog/outputsweepdialog.h"
|
||||
#include "mainwindow.h"
|
||||
#include "utils/Icons.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QMessageBox>
|
||||
|
@ -26,7 +27,7 @@ CoinsWidget::CoinsWidget(QWidget *parent)
|
|||
connect(ui->coins->header(), &QHeaderView::customContextMenuRequested, this, &CoinsWidget::showHeaderMenu);
|
||||
|
||||
// copy menu
|
||||
m_copyMenu->setIcon(QIcon(":/assets/images/copy.png"));
|
||||
m_copyMenu->setIcon(icons()->icon("copy.png"));
|
||||
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);});
|
||||
|
@ -44,7 +45,7 @@ CoinsWidget::CoinsWidget(QWidget *parent)
|
|||
m_freezeAllSelectedAction = new QAction("Freeze selected", this);
|
||||
m_thawAllSelectedAction = new QAction("Thaw selected", this);
|
||||
|
||||
m_viewOutputAction = new QAction(QIcon(":/assets/images/info.png"), "Details", this);
|
||||
m_viewOutputAction = new QAction(icons()->icon("info2.svg"), "Details", this);
|
||||
m_sweepOutputAction = new QAction("Sweep output", this);
|
||||
connect(m_freezeOutputAction, &QAction::triggered, this, &CoinsWidget::freezeOutput);
|
||||
connect(m_thawOutputAction, &QAction::triggered, this, &CoinsWidget::thawOutput);
|
||||
|
@ -129,7 +130,7 @@ void CoinsWidget::setShowSpent(bool show)
|
|||
void CoinsWidget::freezeOutput() {
|
||||
QModelIndex index = ui->coins->currentIndex();
|
||||
QVector<int> indexes = {m_proxyModel->mapToSource(index).row()};
|
||||
emit freeze(indexes);
|
||||
this->freezeCoins(indexes);
|
||||
}
|
||||
|
||||
void CoinsWidget::freezeAllSelected() {
|
||||
|
@ -139,13 +140,13 @@ void CoinsWidget::freezeAllSelected() {
|
|||
for (QModelIndex index: list) {
|
||||
indexes.push_back(m_proxyModel->mapToSource(index).row()); // todo: will segfault if index get invalidated
|
||||
}
|
||||
emit freeze(indexes);
|
||||
this->freezeCoins(indexes);
|
||||
}
|
||||
|
||||
void CoinsWidget::thawOutput() {
|
||||
QModelIndex index = ui->coins->currentIndex();
|
||||
QVector<int> indexes = {m_proxyModel->mapToSource(index).row()};
|
||||
emit thaw(indexes);
|
||||
this->thawCoins(indexes);
|
||||
}
|
||||
|
||||
void CoinsWidget::thawAllSelected() {
|
||||
|
@ -155,7 +156,7 @@ void CoinsWidget::thawAllSelected() {
|
|||
for (QModelIndex index: list) {
|
||||
indexes.push_back(m_proxyModel->mapToSource(index).row());
|
||||
}
|
||||
emit thaw(indexes);
|
||||
this->thawCoins(indexes);
|
||||
}
|
||||
|
||||
void CoinsWidget::viewOutput() {
|
||||
|
@ -181,7 +182,7 @@ void CoinsWidget::onSweepOutput() {
|
|||
int ret = dialog->exec();
|
||||
if (!ret) return;
|
||||
|
||||
emit sweepOutput(keyImage, dialog->address(), dialog->churn(), dialog->outputs());
|
||||
m_ctx->onSweepOutput(keyImage, dialog->address(), dialog->churn(), dialog->outputs());
|
||||
dialog->deleteLater();
|
||||
}
|
||||
|
||||
|
@ -230,6 +231,22 @@ CoinsInfo* CoinsWidget::currentEntry() {
|
|||
}
|
||||
}
|
||||
|
||||
void CoinsWidget::freezeCoins(const QVector<int>& indexes) {
|
||||
for (int i : indexes) {
|
||||
m_ctx->currentWallet->coins()->freeze(i);
|
||||
}
|
||||
m_ctx->currentWallet->coins()->refresh(m_ctx->currentWallet->currentSubaddressAccount());
|
||||
m_ctx->updateBalance();
|
||||
}
|
||||
|
||||
void CoinsWidget::thawCoins(const QVector<int> &indexes) {
|
||||
for (int i : indexes) {
|
||||
m_ctx->currentWallet->coins()->thaw(i);
|
||||
}
|
||||
m_ctx->currentWallet->coins()->refresh(m_ctx->currentWallet->currentSubaddressAccount());
|
||||
m_ctx->updateBalance();
|
||||
}
|
||||
|
||||
CoinsWidget::~CoinsWidget() {
|
||||
delete ui;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "model/CoinsProxyModel.h"
|
||||
#include "libwalletqt/Coins.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QWidget>
|
||||
#include <QtSvg/QSvgWidget>
|
||||
|
||||
|
@ -38,12 +39,10 @@ private slots:
|
|||
void viewOutput();
|
||||
void onSweepOutput();
|
||||
|
||||
signals:
|
||||
void freeze(QVector<int> indexes);
|
||||
void thaw(QVector<int> indexes);
|
||||
void sweepOutput(const QString &keyImage, const QString &address, bool isChurn, int outputs);
|
||||
|
||||
private:
|
||||
void freezeCoins(const QVector<int>& indexes);
|
||||
void thawCoins(const QVector<int>& indexes);
|
||||
|
||||
enum copyField {
|
||||
PubKey = 0,
|
||||
KeyImage,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "model/ModelUtils.h"
|
||||
#include "mainwindow.h"
|
||||
#include "libwalletqt/AddressBook.h"
|
||||
#include "utils/Icons.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
|
@ -28,14 +29,14 @@ ContactsWidget::ContactsWidget(QWidget *parent) :
|
|||
// context menu
|
||||
ui->contacts->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_contextMenu = new QMenu(ui->contacts);
|
||||
m_contextMenu->addAction(QIcon(":/assets/images/person.svg"), "New contact", [this]{
|
||||
m_contextMenu->addAction(icons()->icon("person.svg"), "New contact", [this]{
|
||||
this->newContact();
|
||||
});
|
||||
|
||||
// row context menu
|
||||
m_rowMenu = new QMenu(ui->contacts);
|
||||
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy address", this, &ContactsWidget::copyAddress);
|
||||
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy name", this, &ContactsWidget::copyName);
|
||||
m_rowMenu->addAction(icons()->icon("copy.png"), "Copy address", this, &ContactsWidget::copyAddress);
|
||||
m_rowMenu->addAction(icons()->icon("copy.png"), "Copy name", this, &ContactsWidget::copyName);
|
||||
m_rowMenu->addAction("Pay to", this, &ContactsWidget::payTo);
|
||||
m_rowMenu->addAction("Delete", this, &ContactsWidget::deleteContact);
|
||||
|
||||
|
|
19
src/dialog/InfoDialog.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#include "InfoDialog.h"
|
||||
#include "ui_InfoDialog.h"
|
||||
|
||||
InfoDialog::InfoDialog(QWidget *parent, const QString &title, const QString &infoData)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::InfoDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
this->setWindowTitle(title);
|
||||
ui->info->setPlainText(infoData);
|
||||
}
|
||||
|
||||
InfoDialog::~InfoDialog() {
|
||||
delete ui;
|
||||
}
|
26
src/dialog/InfoDialog.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_INFODIALOG_H
|
||||
#define FEATHER_INFODIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class InfoDialog;
|
||||
}
|
||||
|
||||
class InfoDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit InfoDialog(QWidget *parent, const QString &title, const QString &infoText);
|
||||
~InfoDialog() override;
|
||||
|
||||
private:
|
||||
Ui::InfoDialog *ui;
|
||||
};
|
||||
|
||||
|
||||
#endif //FEATHER_INFODIALOG_H
|
71
src/dialog/InfoDialog.ui
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>InfoDialog</class>
|
||||
<widget class="QDialog" name="InfoDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>689</width>
|
||||
<height>439</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="info">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>InfoDialog</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>InfoDialog</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>
|
51
src/dialog/LocalMoneroInfoDialog.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#include "LocalMoneroInfoDialog.h"
|
||||
#include "ui_LocalMoneroInfoDialog.h"
|
||||
|
||||
#include "utils/config.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
LocalMoneroInfoDialog::LocalMoneroInfoDialog(QWidget *parent, LocalMoneroModel *model, int row)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::LocalMoneroInfoDialog)
|
||||
, m_model(model)
|
||||
, m_row(row)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
setLabelText(ui->label_price, LocalMoneroModel::PriceXMR);
|
||||
setLabelText(ui->label_seller, LocalMoneroModel::Seller);
|
||||
setLabelText(ui->label_paymentMethod, LocalMoneroModel::PaymentMethod);
|
||||
setLabelText(ui->label_paymentDetail, LocalMoneroModel::PaymentMethodDetail);
|
||||
setLabelText(ui->label_tradeLimits, LocalMoneroModel::Limits);
|
||||
|
||||
QJsonObject offerData = model->getOffer(row);
|
||||
QString details = offerData["data"].toObject()["msg"].toString();
|
||||
details.remove("*");
|
||||
|
||||
if (details.isEmpty()) {
|
||||
details = "No details.";
|
||||
}
|
||||
|
||||
ui->info->setPlainText(details);
|
||||
|
||||
connect(ui->btn_goToOffer, &QPushButton::clicked, this, &LocalMoneroInfoDialog::onGoToOffer);
|
||||
}
|
||||
|
||||
void LocalMoneroInfoDialog::setLabelText(QLabel *label, LocalMoneroModel::Column column) {
|
||||
QString data = m_model->data(m_model->index(m_row, column)).toString();
|
||||
label->setText(data);
|
||||
}
|
||||
|
||||
void LocalMoneroInfoDialog::onGoToOffer() {
|
||||
QJsonObject offerData = m_model->getOffer(m_row);
|
||||
QString frontend = config()->get(Config::localMoneroFrontend).toString();
|
||||
QString offerUrl = QString("%1/ad/%2").arg(frontend, offerData["data"].toObject()["ad_id"].toString());
|
||||
Utils::externalLinkWarning(this, offerUrl);
|
||||
}
|
||||
|
||||
LocalMoneroInfoDialog::~LocalMoneroInfoDialog() {
|
||||
delete ui;
|
||||
}
|
35
src/dialog/LocalMoneroInfoDialog.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_LOCALMONEROINFODIALOG_H
|
||||
#define FEATHER_LOCALMONEROINFODIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include "model/LocalMoneroModel.h"
|
||||
|
||||
namespace Ui {
|
||||
class LocalMoneroInfoDialog;
|
||||
}
|
||||
|
||||
class LocalMoneroInfoDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LocalMoneroInfoDialog(QWidget *parent, LocalMoneroModel *model, int row);
|
||||
~LocalMoneroInfoDialog() override;
|
||||
|
||||
private slots:
|
||||
void onGoToOffer();
|
||||
|
||||
private:
|
||||
void setLabelText(QLabel *label, LocalMoneroModel::Column column);
|
||||
|
||||
Ui::LocalMoneroInfoDialog *ui;
|
||||
LocalMoneroModel *m_model;
|
||||
int m_row;
|
||||
};
|
||||
|
||||
|
||||
#endif //FEATHER_INFODIALOG_H
|
195
src/dialog/LocalMoneroInfoDialog.ui
Normal file
|
@ -0,0 +1,195 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LocalMoneroInfoDialog</class>
|
||||
<widget class="QDialog" name="LocalMoneroInfoDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>758</width>
|
||||
<height>557</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Offer info</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Info</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Price:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_price">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Seller:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_seller">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Payment method:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_paymentMethod">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Trade limits:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="label_tradeLimits">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Payment detail:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_paymentDetail">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="info">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_goToOffer">
|
||||
<property name="text">
|
||||
<string>Go to offer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>LocalMoneroInfoDialog</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>LocalMoneroInfoDialog</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>
|
|
@ -211,7 +211,8 @@ TxProof TxProofDialog::getProof() {
|
|||
return m_wallet->getSpendProof(m_txid, message);
|
||||
}
|
||||
case Mode::OutProof:
|
||||
case Mode::InProof: { // Todo: split this into separate functions
|
||||
case Mode::InProof:
|
||||
default: { // Todo: split this into separate functions
|
||||
return m_wallet->getTxProof(m_txid, address, message);
|
||||
}
|
||||
}
|
||||
|
|
223
src/dialog/UpdateDialog.cpp
Normal file
|
@ -0,0 +1,223 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#include "UpdateDialog.h"
|
||||
#include "ui_UpdateDialog.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <utility>
|
||||
|
||||
#include "utils/networking.h"
|
||||
#include "utils/NetworkManager.h"
|
||||
#include "utils/AsyncTask.h"
|
||||
#include "utils/Updater.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
#include "zip.h"
|
||||
|
||||
UpdateDialog::UpdateDialog(QWidget *parent, QString version, QString downloadUrl, QString hash, QString signer)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::UpdateDialog)
|
||||
, m_version(std::move(version))
|
||||
, m_downloadUrl(std::move(downloadUrl))
|
||||
, m_hash(std::move(hash))
|
||||
, m_signer(std::move(signer))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->btn_installUpdate->hide();
|
||||
ui->btn_restart->hide();
|
||||
ui->progressBar->hide();
|
||||
|
||||
auto bigFont = Utils::relativeFont(4);
|
||||
ui->label_header->setFont(bigFont);
|
||||
ui->label_header->setText(QString("New Feather version %1 is available").arg(m_version));
|
||||
|
||||
connect(ui->btn_cancel, &QPushButton::clicked, [this]{
|
||||
if (m_reply) {
|
||||
m_reply->abort();
|
||||
}
|
||||
this->reject();
|
||||
});
|
||||
connect(ui->btn_download, &QPushButton::clicked, this, &UpdateDialog::onDownloadClicked);
|
||||
connect(ui->btn_installUpdate, &QPushButton::clicked, this, &UpdateDialog::onInstallUpdate);
|
||||
connect(ui->btn_restart, &QPushButton::clicked, this, &UpdateDialog::onRestartClicked);
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
void UpdateDialog::onDownloadClicked() {
|
||||
ui->btn_download->hide();
|
||||
ui->progressBar->show();
|
||||
|
||||
UtilsNetworking network{getNetworkTor()};
|
||||
|
||||
m_reply = network.get(m_downloadUrl);
|
||||
connect(m_reply, &QNetworkReply::downloadProgress, this, &UpdateDialog::onDownloadProgress);
|
||||
connect(m_reply, &QNetworkReply::finished, this, &UpdateDialog::onDownloadFinished);
|
||||
}
|
||||
|
||||
void UpdateDialog::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
||||
ui->progressBar->setValue(bytesReceived);
|
||||
ui->progressBar->setMaximum(bytesTotal);
|
||||
}
|
||||
|
||||
void UpdateDialog::onDownloadFinished() {
|
||||
bool error = (m_reply->error() != QNetworkReply::NoError);
|
||||
if (error) {
|
||||
this->onDownloadError(QString("Network error: %1").arg(m_reply->errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray response = m_reply->readAll();
|
||||
if (response.isEmpty()) {
|
||||
this->onDownloadError("Network error: Empty response");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string responseStr = response.toStdString();
|
||||
|
||||
try {
|
||||
const QByteArray calculatedHash = AsyncTask::runAndWaitForFuture([this, responseStr]{
|
||||
return Updater().getHash(&responseStr[0], responseStr.size());
|
||||
});
|
||||
|
||||
const QByteArray signedHash = QByteArray::fromHex(m_hash.toUtf8());
|
||||
|
||||
if (signedHash != calculatedHash) {
|
||||
this->onDownloadError("Error: Hash sum mismatch.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
this->onDownloadError(QString("Error: Unable to calculate sha256sum: %1").arg(e.what()));
|
||||
return;
|
||||
}
|
||||
|
||||
this->setStatus("Download finished and verified.", true);
|
||||
|
||||
ui->btn_installUpdate->show();
|
||||
ui->progressBar->hide();
|
||||
|
||||
m_updateZipArchive = responseStr;
|
||||
}
|
||||
|
||||
void UpdateDialog::onDownloadError(const QString &errMsg) {
|
||||
// Clean up so download can be retried
|
||||
this->setStatus(errMsg);
|
||||
ui->progressBar->hide();
|
||||
ui->progressBar->setMaximum(100);
|
||||
ui->progressBar->setValue(0);
|
||||
ui->btn_download->show();
|
||||
ui->btn_download->setText("Retry download");
|
||||
}
|
||||
|
||||
void UpdateDialog::onInstallUpdate() {
|
||||
ui->btn_installUpdate->hide();
|
||||
this->setStatus("Unzipping archive...");
|
||||
|
||||
zip_error_t err;
|
||||
zip_error_init(&err);
|
||||
|
||||
zip_source_t *zip_source = zip_source_buffer_create(&m_updateZipArchive[0], m_updateZipArchive.size(), 0, &err);
|
||||
if (!zip_source) {
|
||||
this->onInstallError(QString("Error in libzip: Unable to create zip source from buffer: %1").arg(QString::fromStdString(err.str)));
|
||||
return;
|
||||
}
|
||||
|
||||
zip_t *zip_archive = zip_open_from_source(zip_source, 0, &err);
|
||||
if (!zip_archive) {
|
||||
this->onInstallError(QString("Error in libzip: Unable to open archive from source: %1").arg(QString::fromStdString(err.str)));
|
||||
return;
|
||||
}
|
||||
|
||||
auto num_entries = zip_get_num_entries(zip_archive, 0);
|
||||
if (num_entries <= 0) {
|
||||
this->onInstallError("Error in libzip: Archive has no entries");
|
||||
return;
|
||||
}
|
||||
|
||||
// We only expect the archive to contain 1 file
|
||||
std::string fname = zip_get_name(zip_archive, 0, 0);
|
||||
if (fname.empty()) {
|
||||
this->onInstallError("Error in libzip: Invalid filename in archive");
|
||||
return;
|
||||
}
|
||||
|
||||
struct zip_stat sb;
|
||||
if (zip_stat_index(zip_archive, 0, 0, &sb) != 0) {
|
||||
this->onInstallError("Error in libzip: Entry index not found");
|
||||
return;
|
||||
}
|
||||
|
||||
QString name = QString::fromStdString(sb.name);
|
||||
qDebug() << "File found in archive: " << name << ", with size: " << QString::number(sb.size);
|
||||
|
||||
struct zip_file *zf;
|
||||
zf = zip_fopen_index(zip_archive, 0, 0);
|
||||
if (!zf) {
|
||||
this->onInstallError("Error in libzip: Unable to open entry");
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<char[]> contents{new char[sb.size]};
|
||||
|
||||
auto bytes_read = zip_fread(zf, contents.get(), sb.size);
|
||||
if (bytes_read != sb.size){
|
||||
this->onInstallError("Error in libzip: File size inconsistent");
|
||||
return;
|
||||
}
|
||||
|
||||
zip_fclose(zf);
|
||||
zip_close(zip_archive);
|
||||
|
||||
QString applicationPath = qgetenv("APPIMAGE");
|
||||
if (applicationPath.isEmpty()) {
|
||||
applicationPath = QCoreApplication::applicationDirPath();
|
||||
}
|
||||
|
||||
QDir applicationDir(applicationPath);
|
||||
QString filePath = applicationDir.filePath(name);
|
||||
m_updatePath = filePath;
|
||||
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
this->onInstallError("Error: Could not write to application directory");
|
||||
return;
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(file.write(&contents[0], sb.size)) != sb.size) {
|
||||
this->onInstallError("Error: Unable to write file");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.setPermissions(QFile::ExeUser | QFile::ExeOwner | QFile::ReadUser | QFile::ReadOwner | QFile::WriteUser | QFile::WriteOwner)) {
|
||||
this->onInstallError("Error: Unable to set executable flags");
|
||||
return;
|
||||
}
|
||||
|
||||
this->setStatus("Installation successful. Do you want to restart Feather now?");
|
||||
ui->btn_restart->show();
|
||||
}
|
||||
|
||||
void UpdateDialog::onInstallError(const QString &errMsg) {
|
||||
this->setStatus(errMsg);
|
||||
}
|
||||
|
||||
void UpdateDialog::onRestartClicked() {
|
||||
emit restartWallet(m_updatePath);
|
||||
}
|
||||
|
||||
void UpdateDialog::setStatus(const QString &msg, bool success) {
|
||||
ui->label_body->setText(msg);
|
||||
if (success)
|
||||
ui->label_body->setStyleSheet("QLabel { color : #2EB358; }");
|
||||
else
|
||||
ui->label_body->setStyleSheet("");
|
||||
}
|
||||
|
||||
UpdateDialog::~UpdateDialog() {
|
||||
delete ui;
|
||||
}
|
51
src/dialog/UpdateDialog.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_UPDATEDIALOG_H
|
||||
#define FEATHER_UPDATEDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QNetworkReply>
|
||||
|
||||
namespace Ui {
|
||||
class UpdateDialog;
|
||||
}
|
||||
|
||||
class UpdateDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit UpdateDialog(QWidget *parent, QString version, QString downloadUrl, QString hash, QString signer);
|
||||
~UpdateDialog() override;
|
||||
|
||||
private slots:
|
||||
void onDownloadClicked();
|
||||
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||
void onDownloadFinished();
|
||||
void onDownloadError(const QString &errMsg);
|
||||
void onInstallUpdate();
|
||||
void onInstallError(const QString &errMsg);
|
||||
void onRestartClicked();
|
||||
|
||||
signals:
|
||||
void restartWallet(const QString &binaryFilename);
|
||||
|
||||
private:
|
||||
void setStatus(const QString &msg, bool success = false);
|
||||
|
||||
QString m_version;
|
||||
QString m_downloadUrl;
|
||||
QString m_hash;
|
||||
QString m_signer;
|
||||
|
||||
QString m_updatePath;
|
||||
|
||||
std::string m_updateZipArchive;
|
||||
|
||||
QNetworkReply *m_reply = nullptr;
|
||||
|
||||
Ui::UpdateDialog *ui;
|
||||
};
|
||||
|
||||
#endif //FEATHER_UPDATEDIALOG_H
|
87
src/dialog/UpdateDialog.ui
Normal file
|
@ -0,0 +1,87 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>UpdateDialog</class>
|
||||
<widget class="QDialog" name="UpdateDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>569</width>
|
||||
<height>148</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Update Available</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_header">
|
||||
<property name="text">
|
||||
<string>New Feather version is available.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_body">
|
||||
<property name="text">
|
||||
<string>Do you want to download and verify the new version?</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_cancel">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_download">
|
||||
<property name="text">
|
||||
<string>Download</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_installUpdate">
|
||||
<property name="text">
|
||||
<string>Install Update</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_restart">
|
||||
<property name="text">
|
||||
<string>Restart Feather</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "broadcasttxdialog.h"
|
||||
#include "ui_broadcasttxdialog.h"
|
||||
#include "utils/NetworkManager.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
|
@ -13,10 +14,8 @@ BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, AppContext *ctx, const QSt
|
|||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
m_network = new UtilsNetworking(m_ctx->network, this);
|
||||
|
||||
auto node = ctx->nodes->connection();
|
||||
m_rpc = new DaemonRpc(this, m_network, node.full);
|
||||
m_rpc = new DaemonRpc(this, getNetworkTor(), node.toAddress());
|
||||
|
||||
connect(ui->btn_Broadcast, &QPushButton::clicked, this, &BroadcastTxDialog::broadcastTx);
|
||||
connect(ui->btn_Close, &QPushButton::clicked, this, &BroadcastTxDialog::reject);
|
||||
|
@ -38,7 +37,7 @@ void BroadcastTxDialog::broadcastTx() {
|
|||
if (ui->radio_useCustom->isChecked())
|
||||
node = ui->customNode->text();
|
||||
else if (ui->radio_useDefault->isChecked())
|
||||
node = m_ctx->nodes->connection().full;
|
||||
node = m_ctx->nodes->connection().toAddress();
|
||||
|
||||
if (!node.startsWith("http://"))
|
||||
node = QString("http://%1").arg(node);
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#include "debuginfodialog.h"
|
||||
#include "ui_debuginfodialog.h"
|
||||
#include "config-feather.h"
|
||||
#include "utils/WebsocketClient.h"
|
||||
#include "utils/TorManager.h"
|
||||
#include "utils/WebsocketNotifier.h"
|
||||
|
||||
DebugInfoDialog::DebugInfoDialog(AppContext *ctx, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
|
@ -26,16 +29,16 @@ void DebugInfoDialog::updateInfo() {
|
|||
|
||||
// Special case for Tails because we know the status of the daemon by polling tails-tor-has-bootstrapped.target
|
||||
if(m_ctx->isTails) {
|
||||
if(m_ctx->tor->torConnected)
|
||||
if(torManager()->torConnected)
|
||||
torStatus = "Connected";
|
||||
else
|
||||
torStatus = "Disconnected";
|
||||
}
|
||||
else if(m_ctx->isTorSocks)
|
||||
else if(Utils::isTorsocks())
|
||||
torStatus = "Torsocks";
|
||||
else if(m_ctx->tor->localTor)
|
||||
else if(torManager()->isLocalTor())
|
||||
torStatus = "Local (assumed to be running)";
|
||||
else if(m_ctx->tor->torConnected)
|
||||
else if(torManager()->torConnected)
|
||||
torStatus = "Running";
|
||||
else
|
||||
torStatus = "Unknown";
|
||||
|
@ -50,13 +53,37 @@ void DebugInfoDialog::updateInfo() {
|
|||
ui->label_synchronized->setText(m_ctx->currentWallet->isSynchronized() ? "True" : "False");
|
||||
|
||||
auto node = m_ctx->nodes->connection();
|
||||
ui->label_remoteNode->setText(node.full);
|
||||
ui->label_remoteNode->setText(node.toAddress());
|
||||
ui->label_walletStatus->setText(this->statusToString(m_ctx->currentWallet->connectionStatus()));
|
||||
ui->label_torStatus->setText(torStatus);
|
||||
ui->label_websocketStatus->setText(Utils::QtEnumToString(m_ctx->ws->webSocket.state()).remove("State"));
|
||||
ui->label_websocketStatus->setText(Utils::QtEnumToString(websocketNotifier()->websocketClient.webSocket.state()).remove("State"));
|
||||
|
||||
QString seedType = [this](){
|
||||
if (m_ctx->currentWallet->isHwBacked())
|
||||
return "Hardware";
|
||||
if (m_ctx->currentWallet->getCacheAttribute("feather.seed").isEmpty())
|
||||
return "25 word";
|
||||
else
|
||||
return "14 word";
|
||||
}();
|
||||
|
||||
QString deviceType = [this](){
|
||||
if (m_ctx->currentWallet->isHwBacked()) {
|
||||
if (m_ctx->currentWallet->isLedger())
|
||||
return "Ledger";
|
||||
else if (m_ctx->currentWallet->isTrezor())
|
||||
return "Trezor";
|
||||
else
|
||||
return "Unknown";
|
||||
}
|
||||
else {
|
||||
return "Software";
|
||||
}
|
||||
}();
|
||||
|
||||
ui->label_netType->setText(Utils::QtEnumToString(m_ctx->currentWallet->nettype()));
|
||||
ui->label_seedType->setText(m_ctx->currentWallet->getCacheAttribute("feather.seed").isEmpty() ? "25 word" : "14 word");
|
||||
ui->label_seedType->setText(seedType);
|
||||
ui->label_deviceType->setText(deviceType);
|
||||
ui->label_viewOnly->setText(m_ctx->currentWallet->viewOnly() ? "True" : "False");
|
||||
ui->label_primaryOnly->setText(m_ctx->currentWallet->balance(0) == m_ctx->currentWallet->balanceAll() ? "True" : "False");
|
||||
|
||||
|
@ -107,6 +134,7 @@ void DebugInfoDialog::copyToClipboad() {
|
|||
|
||||
text += QString("Network type: %1 \n").arg(ui->label_netType->text());
|
||||
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());
|
||||
|
||||
|
|
|
@ -91,6 +91,23 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Target height:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="label_targetHeight">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
|
@ -242,13 +259,27 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="16" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Device type:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="1">
|
||||
<widget class="QLabel" name="label_deviceType">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>View only:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="1">
|
||||
<item row="17" column="1">
|
||||
<widget class="QLabel" name="label_viewOnly">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
|
@ -258,65 +289,14 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="20" column="0">
|
||||
<widget class="QLabel" name="label_24">
|
||||
<item row="18" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Timestamp:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="20" column="1">
|
||||
<widget class="QLabel" name="label_timestamp">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="19" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Operating system:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="19" column="1">
|
||||
<widget class="QLabel" name="label_OS">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
<string>Primary only:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="1">
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Target height:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="label_targetHeight">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="1">
|
||||
<widget class="QLabel" name="label_primaryOnly">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
|
@ -326,10 +306,44 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<item row="19" column="1">
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="20" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Primary only:</string>
|
||||
<string>Operating system:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="20" column="1">
|
||||
<widget class="QLabel" name="label_OS">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="21" column="0">
|
||||
<widget class="QLabel" name="label_24">
|
||||
<property name="text">
|
||||
<string>Timestamp:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="21" column="1">
|
||||
<widget class="QLabel" name="label_timestamp">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -10,12 +10,15 @@ KeysDialog::KeysDialog(AppContext *ctx, QWidget *parent)
|
|||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->label_restoreHeight->setText(QString::number(ctx->currentWallet->getWalletCreationHeight()));
|
||||
ui->label_primaryAddress->setText(ctx->currentWallet->address(0, 0));
|
||||
ui->label_secretSpendKey->setText(ctx->currentWallet->getSecretSpendKey());
|
||||
ui->label_secretViewKey->setText(ctx->currentWallet->getSecretViewKey());
|
||||
ui->label_publicSpendKey->setText(ctx->currentWallet->getPublicSpendKey());
|
||||
ui->label_publicViewKey->setText(ctx->currentWallet->getPublicViewKey());
|
||||
auto w = ctx->currentWallet;
|
||||
QString unavailable = "Unavailable: Key is stored on hardware device";
|
||||
|
||||
ui->label_restoreHeight->setText(QString::number(w->getWalletCreationHeight()));
|
||||
ui->label_primaryAddress->setText(w->address(0, 0));
|
||||
ui->label_secretSpendKey->setText(w->isHwBacked() ? unavailable : w->getSecretSpendKey());
|
||||
ui->label_secretViewKey->setText(w->getSecretViewKey());
|
||||
ui->label_publicSpendKey->setText(w->getPublicSpendKey());
|
||||
ui->label_publicViewKey->setText(w->getPublicViewKey());
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ RestoreDialog::RestoreDialog(AppContext *ctx, QWidget *parent)
|
|||
ui->restoreHeightWidget->hideSlider();
|
||||
} else {
|
||||
// load restoreHeight lookup db
|
||||
ui->restoreHeightWidget->initRestoreHeights(m_ctx->restoreHeights[m_ctx->networkType]);
|
||||
ui->restoreHeightWidget->initRestoreHeights(appData()->restoreHeights[m_ctx->networkType]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
24
src/dialog/splashdialog.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#include "splashdialog.h"
|
||||
#include "ui_splashdialog.h"
|
||||
|
||||
SplashDialog::SplashDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::SplashDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
QPixmap pixmap = QPixmap(":/assets/images/key.png");
|
||||
ui->icon->setPixmap(pixmap.scaledToWidth(32, Qt::SmoothTransformation));
|
||||
|
||||
this->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
SplashDialog::~SplashDialog() {
|
||||
delete ui;
|
||||
}
|
||||
|
25
src/dialog/splashdialog.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_SPLASHDIALOG_H
|
||||
#define FEATHER_SPLASHDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class SplashDialog;
|
||||
}
|
||||
|
||||
class SplashDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SplashDialog(QWidget *parent = nullptr);
|
||||
~SplashDialog() override;
|
||||
|
||||
private:
|
||||
Ui::SplashDialog *ui;
|
||||
};
|
||||
|
||||
#endif //FEATHER_SPLASHDIALOG_H
|
60
src/dialog/splashdialog.ui
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SplashDialog</class>
|
||||
<widget class="QDialog" name="SplashDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>751</width>
|
||||
<height>101</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Device Action Required</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>icon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_message">
|
||||
<property name="text">
|
||||
<string>Action required on device: Export the view key to open the wallet.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -4,32 +4,146 @@
|
|||
#include "torinfodialog.h"
|
||||
#include "ui_torinfodialog.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QDesktopServices>
|
||||
#include <QMessageBox>
|
||||
|
||||
TorInfoDialog::TorInfoDialog(AppContext *ctx, QWidget *parent)
|
||||
#include "utils/TorManager.h"
|
||||
|
||||
TorInfoDialog::TorInfoDialog(QWidget *parent, AppContext *ctx)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::TorInfoDialog)
|
||||
, m_ctx(ctx)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
if (!m_ctx->tor->torConnected && !m_ctx->tor->errorMsg.isEmpty()) {
|
||||
ui->message->setText(m_ctx->tor->errorMsg);
|
||||
if (!torManager()->torConnected && !torManager()->errorMsg.isEmpty()) {
|
||||
ui->message->setText(torManager()->errorMsg);
|
||||
} else {
|
||||
ui->message->setText(QString("Currently using Tor instance: %1:%2").arg(Tor::torHost).arg(Tor::torPort));
|
||||
ui->message->hide();
|
||||
}
|
||||
|
||||
if (m_ctx->tor->localTor) {
|
||||
ui->logs->setHidden(true);
|
||||
if (torManager()->isLocalTor()) {
|
||||
ui->frame_logs->setHidden(true);
|
||||
} else {
|
||||
ui->logs->setPlainText(m_ctx->tor->torLogs);
|
||||
ui->logs->setPlainText(torManager()->torLogs);
|
||||
}
|
||||
|
||||
initConnectionSettings();
|
||||
initPrivacyLevel();
|
||||
onConnectionStatusChanged(torManager()->torConnected);
|
||||
|
||||
connect(torManager(), &TorManager::connectionStateChanged, this, &TorInfoDialog::onConnectionStatusChanged);
|
||||
connect(torManager(), &TorManager::logsUpdated, this, &TorInfoDialog::onLogsUpdated);
|
||||
|
||||
connect(ui->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &TorInfoDialog::onApplySettings);
|
||||
|
||||
connect(ui->line_host, &QLineEdit::textEdited, this, &TorInfoDialog::onSettingsChanged);
|
||||
connect(ui->line_port, &QLineEdit::textEdited, this, &TorInfoDialog::onSettingsChanged);
|
||||
connect(ui->check_useLocalTor, &QCheckBox::stateChanged, this, &TorInfoDialog::onSettingsChanged);
|
||||
connect(ui->btnGroup_privacyLevel, &QButtonGroup::idToggled, this, &TorInfoDialog::onSettingsChanged);
|
||||
|
||||
ui->label_changes->hide();
|
||||
|
||||
#ifndef HAS_TOR_BIN
|
||||
ui->check_useLocalTor->setChecked(true);
|
||||
ui->check_useLocalTor->setEnabled(false);
|
||||
ui->check_useLocalTor->setToolTip("Feather was bundled without Tor");
|
||||
#endif
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
void TorInfoDialog::onLogsUpdated() {
|
||||
ui->logs->setPlainText(m_ctx->tor->torLogs);
|
||||
ui->logs->setPlainText(torManager()->torLogs);
|
||||
}
|
||||
|
||||
void TorInfoDialog::onConnectionStatusChanged(bool connected) {
|
||||
if (connected) {
|
||||
ui->icon_connectionStatus->setPixmap(QPixmap(":/assets/images/status_connected.png").scaledToWidth(16, Qt::SmoothTransformation));
|
||||
ui->label_testConnectionStatus->setText("Connected");
|
||||
} else {
|
||||
ui->icon_connectionStatus->setPixmap(QPixmap(":/assets/images/status_disconnected.png").scaledToWidth(16, Qt::SmoothTransformation));
|
||||
ui->label_testConnectionStatus->setText("Disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
void TorInfoDialog::onApplySettings() {
|
||||
config()->set(Config::socks5Host, ui->line_host->text());
|
||||
config()->set(Config::socks5Port, ui->line_port->text());
|
||||
|
||||
int id = ui->btnGroup_privacyLevel->checkedId();
|
||||
config()->set(Config::torPrivacyLevel, id);
|
||||
|
||||
ui->label_changes->hide();
|
||||
|
||||
bool useLocalTor = ui->check_useLocalTor->isChecked();
|
||||
if (config()->get(Config::useLocalTor).toBool() && useLocalTor && torManager()->isStarted()) {
|
||||
QMessageBox::warning(this, "Warning", "Feather is running the bundled Tor daemon, "
|
||||
"but the option to never start a bundled Tor daemon was selected. "
|
||||
"A restart is required to apply the setting.");
|
||||
}
|
||||
config()->set(Config::useLocalTor, useLocalTor);
|
||||
|
||||
ui->icon_connectionStatus->setPixmap(QPixmap(":/assets/images/status_lagging.png").scaledToWidth(16, Qt::SmoothTransformation));
|
||||
ui->label_testConnectionStatus->setText("Connecting");
|
||||
|
||||
emit torSettingsChanged();
|
||||
}
|
||||
|
||||
void TorInfoDialog::onSettingsChanged() {
|
||||
ui->label_changes->show();
|
||||
}
|
||||
|
||||
void TorInfoDialog::initConnectionSettings() {
|
||||
bool localTor = torManager()->isLocalTor();
|
||||
ui->label_connectionSettingsMessage->setVisible(!localTor);
|
||||
ui->frame_connectionSettings->setVisible(localTor);
|
||||
|
||||
ui->line_host->setText(config()->get(Config::socks5Host).toString());
|
||||
ui->line_port->setText(config()->get(Config::socks5Port).toString());
|
||||
|
||||
ui->check_useLocalTor->setChecked(config()->get(Config::useLocalTor).toBool());
|
||||
}
|
||||
|
||||
void TorInfoDialog::initPrivacyLevel() {
|
||||
ui->btnGroup_privacyLevel->setId(ui->radio_allTorExceptNode, Config::allTorExceptNode);
|
||||
ui->btnGroup_privacyLevel->setId(ui->radio_allTorExceptInitSync, Config::allTorExceptInitSync);
|
||||
ui->btnGroup_privacyLevel->setId(ui->radio_allTor, Config::allTor);
|
||||
|
||||
int privacyLevel = config()->get(Config::torPrivacyLevel).toInt();
|
||||
auto button = ui->btnGroup_privacyLevel->button(privacyLevel);
|
||||
if (button) {
|
||||
button->setChecked(true);
|
||||
}
|
||||
|
||||
if (m_ctx->nodes->connection().isLocal()) {
|
||||
ui->label_notice->setText("You are connected to a local node. Traffic is not routed over Tor.");
|
||||
}
|
||||
else if (Utils::isTorsocks() || WhonixOS::detect() || TailsOS::detect()) {
|
||||
ui->radio_allTorExceptNode->setEnabled(false);
|
||||
ui->radio_allTorExceptInitSync->setEnabled(false);
|
||||
|
||||
if (Utils::isTorsocks())
|
||||
ui->label_notice->setText("Feather was started with torsocks, all traffic is routed over Tor");
|
||||
else if (WhonixOS::detect())
|
||||
ui->label_notice->setText("Feather is running on Whonix, all traffic is routed over Tor");
|
||||
else if (TailsOS::detect())
|
||||
ui->label_notice->setText("Feather is running on Tails, all traffic is routed over Tor");
|
||||
} else {
|
||||
ui->frame_notice->hide();
|
||||
}
|
||||
|
||||
QPixmap iconNoTor(":/assets/images/securityLevelStandardWhite.png");
|
||||
QPixmap iconNoSync(":/assets/images/securityLevelSaferWhite.png");
|
||||
QPixmap iconAllTor(":/assets/images/securityLevelSafestWhite.png");
|
||||
ui->icon_noTor->setPixmap(iconNoTor.scaledToHeight(16, Qt::SmoothTransformation));
|
||||
ui->icon_noSync->setPixmap(iconNoSync.scaledToHeight(16, Qt::SmoothTransformation));
|
||||
ui->icon_allTor->setPixmap(iconAllTor.scaledToHeight(16, Qt::SmoothTransformation));
|
||||
}
|
||||
|
||||
void TorInfoDialog::onStopTor() {
|
||||
torManager()->stop();
|
||||
}
|
||||
|
||||
TorInfoDialog::~TorInfoDialog() {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#define FEATHER_TORINFODIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QAbstractButton>
|
||||
|
||||
#include "appcontext.h"
|
||||
|
||||
|
@ -17,13 +18,25 @@ class TorInfoDialog : public QDialog
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TorInfoDialog(AppContext *ctx, QWidget *parent = nullptr);
|
||||
explicit TorInfoDialog(QWidget *parent, AppContext *ctx);
|
||||
~TorInfoDialog() override;
|
||||
|
||||
public slots:
|
||||
void onLogsUpdated();
|
||||
|
||||
private slots:
|
||||
void onConnectionStatusChanged(bool connected);
|
||||
void onApplySettings();
|
||||
void onSettingsChanged();
|
||||
void onStopTor();
|
||||
|
||||
signals:
|
||||
void torSettingsChanged();
|
||||
|
||||
private:
|
||||
void initConnectionSettings();
|
||||
void initPrivacyLevel();
|
||||
|
||||
Ui::TorInfoDialog *ui;
|
||||
AppContext *m_ctx;
|
||||
};
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>618</width>
|
||||
<height>386</height>
|
||||
<width>703</width>
|
||||
<height>804</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -15,24 +15,335 @@
|
|||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="message">
|
||||
<property name="text">
|
||||
<string>Message</string>
|
||||
<widget class="QGroupBox" name="group_connectionSettings">
|
||||
<property name="title">
|
||||
<string>Connection settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_connectionSettings">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Host</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="line_host">
|
||||
<property name="text">
|
||||
<string>127.0.0.1</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>127.0.0.1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Port</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="line_port">
|
||||
<property name="text">
|
||||
<string>9050</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>9050</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_connectionSettingsMessage">
|
||||
<property name="text">
|
||||
<string>Tor daemon is managed by Feather.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="check_useLocalTor">
|
||||
<property name="text">
|
||||
<string>Never start bundled Tor (requires local Tor daemon)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="logs">
|
||||
<property name="minimumSize">
|
||||
<widget class="QGroupBox" name="group_privacyLevel">
|
||||
<property name="title">
|
||||
<string>Privacy Level</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon_noTor">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>icon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radio_allTorExceptNode">
|
||||
<property name="text">
|
||||
<string>Route all traffic over Tor, except traffic to node</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">btnGroup_privacyLevel</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon_noSync">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>icon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radio_allTorExceptInitSync">
|
||||
<property name="text">
|
||||
<string>Route all traffic over Tor, except initial wallet synchronization</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">btnGroup_privacyLevel</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon_allTor">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>icon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radio_allTor">
|
||||
<property name="text">
|
||||
<string>Route all traffic over Tor</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">btnGroup_privacyLevel</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_notice">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_notice">
|
||||
<property name="text">
|
||||
<string>notice</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Status</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon_connectionStatus">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>icon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_testConnectionStatus">
|
||||
<property name="text">
|
||||
<string>status</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_changes">
|
||||
<property name="text">
|
||||
<string>(changes not applied)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="message">
|
||||
<property name="text">
|
||||
<string>Message</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_logs">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Logs</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="logs">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
|
@ -40,7 +351,7 @@
|
|||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Ok</set>
|
||||
<set>QDialogButtonBox::Apply|QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -81,4 +392,7 @@
|
|||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<buttongroups>
|
||||
<buttongroup name="btnGroup_privacyLevel"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "libwalletqt/Transfer.h"
|
||||
#include "libwalletqt/Input.h"
|
||||
#include "model/ModelUtils.h"
|
||||
#include "utils/ColorScheme.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "model/ModelUtils.h"
|
||||
#include "txconfadvdialog.h"
|
||||
#include "globals.h"
|
||||
#include "utils/AppData.h"
|
||||
#include "utils/ColorScheme.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
@ -26,7 +27,7 @@ TxConfDialog::TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QStrin
|
|||
QString preferredCur = config()->get(Config::preferredFiatCurrency).toString();
|
||||
|
||||
auto convert = [preferredCur](double amount){
|
||||
return QString::number(AppContext::prices->convert("XMR", preferredCur, amount), 'f', 2);
|
||||
return QString::number(appData()->prices.convert("XMR", preferredCur, amount), 'f', 2);
|
||||
};
|
||||
|
||||
QString amount = WalletManager::displayAmount(tx->amount());
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "tximportdialog.h"
|
||||
#include "ui_tximportdialog.h"
|
||||
#include "utils/NetworkManager.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
|
@ -16,10 +17,8 @@ TxImportDialog::TxImportDialog(QWidget *parent, AppContext *ctx)
|
|||
ui->resp->hide();
|
||||
ui->label_loading->hide();
|
||||
|
||||
m_network = new UtilsNetworking(m_ctx->network, this);
|
||||
|
||||
auto node = ctx->nodes->connection();
|
||||
m_rpc = new DaemonRpc(this, m_network, node.full);
|
||||
m_rpc = new DaemonRpc(this, getNetworkTor(), node.toAddress());
|
||||
|
||||
connect(ui->btn_load, &QPushButton::clicked, this, &TxImportDialog::loadTx);
|
||||
connect(ui->btn_import, &QPushButton::clicked, this, &TxImportDialog::onImport);
|
||||
|
@ -35,7 +34,7 @@ TxImportDialog::TxImportDialog(QWidget *parent, AppContext *ctx)
|
|||
|
||||
void TxImportDialog::loadTx() {
|
||||
QString txid = ui->line_txid->text();
|
||||
QString node = m_ctx->nodes->connection().full;
|
||||
QString node = m_ctx->nodes->connection().toAddress();
|
||||
|
||||
if (!node.startsWith("http://"))
|
||||
node = QString("http://%1").arg(node);
|
||||
|
|
|
@ -22,6 +22,9 @@ namespace globals
|
|||
|
||||
// websocket constants
|
||||
const QUrl websocketUrl = QUrl(QStringLiteral("ws://7e6egbawekbkxzkv4244pqeqgoo4axko2imgjbedwnn6s5yb6b7oliqd.onion/ws"));
|
||||
|
||||
// website constants
|
||||
const QString websiteUrl = "https://featherwallet.org";
|
||||
}
|
||||
|
||||
#endif //FEATHER_GLOBALS_H
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#include "ui_historywidget.h"
|
||||
#include "dialog/transactioninfodialog.h"
|
||||
#include "dialog/TxProofDialog.h"
|
||||
#include "utils/Icons.h"
|
||||
#include "utils/config.h"
|
||||
#include "appcontext.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
HistoryWidget::HistoryWidget(QWidget *parent)
|
||||
|
@ -15,11 +19,11 @@ HistoryWidget::HistoryWidget(QWidget *parent)
|
|||
{
|
||||
ui->setupUi(this);
|
||||
m_contextMenu->addMenu(m_copyMenu);
|
||||
m_contextMenu->addAction(QIcon(":/assets/images/info.png"), "Show details", this, &HistoryWidget::showTxDetails);
|
||||
m_contextMenu->addAction(QIcon(":/assets/images/network.png"), "View on block explorer", this, &HistoryWidget::onViewOnBlockExplorer);
|
||||
m_contextMenu->addAction(icons()->icon("info2.svg"), "Show details", this, &HistoryWidget::showTxDetails);
|
||||
m_contextMenu->addAction("View on block explorer", this, &HistoryWidget::onViewOnBlockExplorer);
|
||||
|
||||
// copy menu
|
||||
m_copyMenu->setIcon(QIcon(":/assets/images/copy.png"));
|
||||
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);});
|
||||
|
@ -56,12 +60,12 @@ void HistoryWidget::showContextMenu(const QPoint &point) {
|
|||
|
||||
bool unconfirmed = tx->isFailed() || tx->isPending();
|
||||
if (AppContext::txCache.contains(tx->hash()) && unconfirmed && tx->direction() != TransactionInfo::Direction_In) {
|
||||
menu.addAction(QIcon(":/assets/images/info.png"), "Resend transaction", this, &HistoryWidget::onResendTransaction);
|
||||
menu.addAction(icons()->icon("info2.svg"), "Resend transaction", this, &HistoryWidget::onResendTransaction);
|
||||
}
|
||||
|
||||
menu.addMenu(m_copyMenu);
|
||||
menu.addAction(QIcon(":/assets/images/info.png"), "Show details", this, &HistoryWidget::showTxDetails);
|
||||
menu.addAction(QIcon(":/assets/images/network.png"), "View on block explorer", this, &HistoryWidget::onViewOnBlockExplorer);
|
||||
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.exec(ui->history->viewport()->mapToGlobal(point));
|
||||
|
|
|
@ -10,6 +10,11 @@ Subaddress::Subaddress(Monero::Subaddress *subaddressImpl, QObject *parent)
|
|||
getAll();
|
||||
}
|
||||
|
||||
QString Subaddress::errorString() const
|
||||
{
|
||||
return QString::fromStdString(m_subaddressImpl->errorString());
|
||||
}
|
||||
|
||||
void Subaddress::getAll() const
|
||||
{
|
||||
emit refreshStarted();
|
||||
|
@ -46,17 +51,24 @@ bool Subaddress::getRow(int index, std::function<void (Monero::SubaddressRow &ro
|
|||
return true;
|
||||
}
|
||||
|
||||
void Subaddress::addRow(quint32 accountIndex, const QString &label) const
|
||||
bool Subaddress::addRow(quint32 accountIndex, const QString &label) const
|
||||
{
|
||||
m_subaddressImpl->addRow(accountIndex, label.toStdString());
|
||||
getAll();
|
||||
bool r = m_subaddressImpl->addRow(accountIndex, label.toStdString());
|
||||
|
||||
if (r)
|
||||
getAll();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void Subaddress::setLabel(quint32 accountIndex, quint32 addressIndex, const QString &label) const
|
||||
bool Subaddress::setLabel(quint32 accountIndex, quint32 addressIndex, const QString &label) const
|
||||
{
|
||||
m_subaddressImpl->setLabel(accountIndex, addressIndex, label.toStdString());
|
||||
getAll();
|
||||
emit labelChanged();
|
||||
bool r = m_subaddressImpl->setLabel(accountIndex, addressIndex, label.toStdString());
|
||||
if (r) {
|
||||
getAll();
|
||||
emit labelChanged();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void Subaddress::refresh(quint32 accountIndex) const
|
||||
|
|
|
@ -16,13 +16,14 @@ class Subaddress : public QObject
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Q_INVOKABLE void getAll() const;
|
||||
Q_INVOKABLE bool getRow(int index, std::function<void (Monero::SubaddressRow &row)> callback) const;
|
||||
Q_INVOKABLE void addRow(quint32 accountIndex, const QString &label) const;
|
||||
Q_INVOKABLE void setLabel(quint32 accountIndex, quint32 addressIndex, const QString &label) const;
|
||||
Q_INVOKABLE void refresh(quint32 accountIndex) const;
|
||||
Q_INVOKABLE quint64 unusedLookahead() const;
|
||||
void getAll() const;
|
||||
bool getRow(int index, std::function<void (Monero::SubaddressRow &row)> callback) const;
|
||||
bool addRow(quint32 accountIndex, const QString &label) const;
|
||||
bool setLabel(quint32 accountIndex, quint32 addressIndex, const QString &label) const;
|
||||
void refresh(quint32 accountIndex) const;
|
||||
quint64 unusedLookahead() const;
|
||||
quint64 count() const;
|
||||
QString errorString() const;
|
||||
Monero::SubaddressRow* row(int index) const;
|
||||
|
||||
signals:
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "TransactionHistory.h"
|
||||
#include "TransactionInfo.h"
|
||||
#include "utils/utils.h"
|
||||
#include "appcontext.h"
|
||||
|
||||
#include "utils/AppData.h"
|
||||
#include "utils/config.h"
|
||||
|
||||
bool TransactionHistory::transaction(int index, std::function<void (TransactionInfo &)> callback)
|
||||
{
|
||||
|
@ -164,11 +164,11 @@ bool TransactionHistory::writeCSV(const QString &path) {
|
|||
// calc historical fiat price
|
||||
QString fiatAmount;
|
||||
QString preferredFiatSymbol = config()->get(Config::preferredFiatCurrency).toString();
|
||||
const double usd_price = AppContext::txFiatHistory->get(timeStamp.toString("yyyyMMdd"));
|
||||
const double usd_price = appData()->txFiatHistory->get(timeStamp.toString("yyyyMMdd"));
|
||||
double fiat_price = usd_price * amount;
|
||||
|
||||
if(preferredFiatSymbol != "USD")
|
||||
fiat_price = AppContext::prices->convert("USD", preferredFiatSymbol, fiat_price);
|
||||
fiat_price = appData()->prices.convert("USD", preferredFiatSymbol, fiat_price);
|
||||
double fiat_rounded = ceil(Utils::roundSignificant(fiat_price, 3) * 100.0) / 100.0;
|
||||
if(fiat_price != 0)
|
||||
fiatAmount = QString("%1 %2").arg(QString::number(fiat_rounded)).arg(preferredFiatSymbol);
|
||||
|
|
|
@ -283,6 +283,11 @@ bool Wallet::isTrezor() const
|
|||
return m_walletImpl->getDeviceType() == Monero::Wallet::Device_Trezor;
|
||||
}
|
||||
|
||||
bool Wallet::reconnectDevice()
|
||||
{
|
||||
return m_walletImpl->reconnectDevice();
|
||||
}
|
||||
|
||||
//! create a view only wallet
|
||||
bool Wallet::createViewOnly(const QString &path, const QString &password) const
|
||||
{
|
||||
|
@ -1224,13 +1229,23 @@ void Wallet::onWalletPassphraseNeeded(bool on_device)
|
|||
}
|
||||
|
||||
quint64 Wallet::getBytesReceived() const {
|
||||
return m_walletImpl->getBytesReceived();
|
||||
// TODO: this can segfault. Unclear why.
|
||||
try {
|
||||
return m_walletImpl->getBytesReceived();
|
||||
}
|
||||
catch (...) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
quint64 Wallet::getBytesSent() const {
|
||||
return m_walletImpl->getBytesSent();
|
||||
}
|
||||
|
||||
bool Wallet::isDeviceConnected() const {
|
||||
return m_walletImpl->isDeviceConnected();
|
||||
}
|
||||
|
||||
void Wallet::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
|
||||
{
|
||||
if (m_walletListener != nullptr)
|
||||
|
@ -1303,14 +1318,14 @@ Wallet::~Wallet()
|
|||
delete m_coins;
|
||||
m_coins = NULL;
|
||||
//Monero::WalletManagerFactory::getWalletManager()->closeWallet(m_walletImpl);
|
||||
if(status() == Status_Critical)
|
||||
if(status() == Status_Critical || status() == Status_BadPassword)
|
||||
qDebug("Not storing wallet cache");
|
||||
else if( m_walletImpl->store(""))
|
||||
qDebug("Wallet cache stored successfully");
|
||||
else
|
||||
qDebug("Error storing wallet cache");
|
||||
delete m_walletImpl;
|
||||
m_walletImpl = NULL;
|
||||
m_walletImpl = nullptr;
|
||||
delete m_walletListener;
|
||||
m_walletListener = NULL;
|
||||
qDebug("m_walletImpl deleted");
|
||||
|
@ -1325,7 +1340,7 @@ void Wallet::startRefreshThread()
|
|||
auto last = std::chrono::steady_clock::now();
|
||||
while (!m_scheduler.stopping())
|
||||
{
|
||||
if (m_refreshEnabled)
|
||||
if (m_refreshEnabled && (!isHwBacked() || isDeviceConnected()))
|
||||
{
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto elapsed = now - last;
|
||||
|
|
|
@ -78,9 +78,10 @@ Q_OBJECT
|
|||
public:
|
||||
|
||||
enum Status {
|
||||
Status_Ok = Monero::Wallet::Status_Ok,
|
||||
Status_Error = Monero::Wallet::Status_Error,
|
||||
Status_Critical = Monero::Wallet::Status_Critical
|
||||
Status_Ok = Monero::Wallet::Status_Ok,
|
||||
Status_Error = Monero::Wallet::Status_Error,
|
||||
Status_Critical = Monero::Wallet::Status_Critical,
|
||||
Status_BadPassword = Monero::Wallet::Status_BadPassword
|
||||
};
|
||||
|
||||
Q_ENUM(Status)
|
||||
|
@ -198,6 +199,9 @@ public:
|
|||
bool isLedger() const;
|
||||
bool isTrezor() const;
|
||||
|
||||
//! attempt to reconnect to hw-device
|
||||
bool reconnectDevice();
|
||||
|
||||
//! returns if view only wallet
|
||||
bool viewOnly() const;
|
||||
|
||||
|
@ -424,6 +428,8 @@ public:
|
|||
quint64 getBytesReceived() const;
|
||||
quint64 getBytesSent() const;
|
||||
|
||||
bool isDeviceConnected() const;
|
||||
|
||||
// TODO: setListenter() when it implemented in API
|
||||
signals:
|
||||
// emitted on every event happened with wallet
|
||||
|
@ -432,7 +438,7 @@ signals:
|
|||
|
||||
// emitted when refresh process finished (could take a long time)
|
||||
// signalling only after we
|
||||
void refreshed(bool success);
|
||||
void refreshed(bool success, const QString &message);
|
||||
|
||||
void moneySpent(const QString &txId, quint64 amount);
|
||||
void moneyReceived(const QString &txId, quint64 amount);
|
||||
|
@ -443,6 +449,7 @@ signals:
|
|||
void walletCreationHeightChanged();
|
||||
void deviceButtonRequest(quint64 buttonCode);
|
||||
void deviceButtonPressed();
|
||||
void deviceError(const QString &message);
|
||||
void walletPassphraseNeeded(bool onDevice);
|
||||
void transactionCommitted(bool status, PendingTransaction *t, const QStringList& txid);
|
||||
void heightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) const;
|
||||
|
|
|
@ -45,8 +45,9 @@ void WalletListenerImpl::updated()
|
|||
void WalletListenerImpl::refreshed(bool success)
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
QString message = m_wallet->errorString();
|
||||
m_wallet->onRefreshed(success);
|
||||
emit m_wallet->refreshed(success);
|
||||
emit m_wallet->refreshed(success, message);
|
||||
}
|
||||
|
||||
void WalletListenerImpl::onDeviceButtonRequest(uint64_t code)
|
||||
|
@ -61,6 +62,12 @@ void WalletListenerImpl::onDeviceButtonPressed()
|
|||
emit m_wallet->deviceButtonPressed();
|
||||
}
|
||||
|
||||
void WalletListenerImpl::onDeviceError(const std::string &message)
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_wallet->deviceError(QString::fromStdString(message));
|
||||
}
|
||||
|
||||
void WalletListenerImpl::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
|
|
|
@ -31,6 +31,8 @@ public:
|
|||
|
||||
virtual void onDeviceButtonPressed() override;
|
||||
|
||||
virtual void onDeviceError(const std::string &message) override;
|
||||
|
||||
virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override;
|
||||
|
||||
virtual Monero::optional<std::string> onDevicePassphraseRequest(bool & on_device) override;
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "libwalletqt/WalletManager.h"
|
||||
#include "Wallet.h"
|
||||
|
||||
//#include "qt/updater.h"
|
||||
#include "utils/ScopeGuard.h"
|
||||
|
||||
class WalletPassphraseListenerImpl : public Monero::WalletListener, public PassphraseReceiver
|
||||
|
@ -31,11 +30,11 @@ public:
|
|||
// return m_phelper.onDevicePassphraseRequest(on_device);
|
||||
// }
|
||||
//
|
||||
// virtual void onDeviceButtonRequest(uint64_t code) override
|
||||
// {
|
||||
// qDebug() << __FUNCTION__;
|
||||
// emit m_mgr->deviceButtonRequest(code);
|
||||
// }
|
||||
virtual void onDeviceButtonRequest(uint64_t code) override
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_mgr->deviceButtonRequest(code);
|
||||
}
|
||||
//
|
||||
// virtual void onDeviceButtonPressed() override
|
||||
// {
|
||||
|
@ -43,6 +42,12 @@ public:
|
|||
// emit m_mgr->deviceButtonPressed();
|
||||
// }
|
||||
|
||||
virtual void onDeviceError(const std::string &message) override
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_mgr->deviceError(QString::fromStdString(message));
|
||||
}
|
||||
|
||||
private:
|
||||
WalletManager * m_mgr;
|
||||
PassphraseHelper m_phelper;
|
||||
|
@ -125,17 +130,17 @@ Wallet *WalletManager::recoveryWallet(const QString &path, const QString &passwo
|
|||
return m_currentWallet;
|
||||
}
|
||||
|
||||
Wallet *WalletManager::createWalletFromKeys(const QString &path, const QString &language, NetworkType::Type nettype,
|
||||
const QString &address, const QString &viewkey, const QString &spendkey,
|
||||
quint64 restoreHeight, quint64 kdfRounds)
|
||||
Wallet *WalletManager::createWalletFromKeys(const QString &path, const QString &password, const QString &language,
|
||||
NetworkType::Type nettype, const QString &address, const QString &viewkey,
|
||||
const QString &spendkey, quint64 restoreHeight, quint64 kdfRounds)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
if (m_currentWallet) {
|
||||
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
||||
delete m_currentWallet;
|
||||
m_currentWallet = NULL;
|
||||
m_currentWallet = nullptr;
|
||||
}
|
||||
Monero::Wallet * w = m_pimpl->createWalletFromKeys(path.toStdString(), "", language.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight,
|
||||
Monero::Wallet * w = m_pimpl->createWalletFromKeys(path.toStdString(), password.toStdString(), language.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight,
|
||||
address.toStdString(), viewkey.toStdString(), spendkey.toStdString(), kdfRounds);
|
||||
m_currentWallet = new Wallet(w);
|
||||
return m_currentWallet;
|
||||
|
@ -148,9 +153,9 @@ Wallet *WalletManager::createDeterministicWalletFromSpendKey(const QString &path
|
|||
if (m_currentWallet) {
|
||||
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
||||
delete m_currentWallet;
|
||||
m_currentWallet = NULL;
|
||||
m_currentWallet = nullptr;
|
||||
}
|
||||
Monero::Wallet * w = m_pimpl->createDeterministicWalletFromSpendKey(path.toStdString(), "", language.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight,
|
||||
Monero::Wallet * w = m_pimpl->createDeterministicWalletFromSpendKey(path.toStdString(), password.toStdString(), language.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight,
|
||||
spendkey.toStdString(), kdfRounds, offset_passphrase.toStdString());
|
||||
m_currentWallet = new Wallet(w);
|
||||
return m_currentWallet;
|
||||
|
@ -172,7 +177,7 @@ Wallet *WalletManager::createWalletFromDevice(const QString &path, const QString
|
|||
if (m_currentWallet) {
|
||||
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
||||
delete m_currentWallet;
|
||||
m_currentWallet = NULL;
|
||||
m_currentWallet = nullptr;
|
||||
}
|
||||
Monero::Wallet * w = m_pimpl->createWalletFromDevice(path.toStdString(), password.toStdString(), static_cast<Monero::NetworkType>(nettype),
|
||||
deviceName.toStdString(), restoreHeight, subaddressLookahead.toStdString(), 1, &tmpListener);
|
||||
|
@ -201,10 +206,12 @@ void WalletManager::createWalletFromDeviceAsync(const QString &path, const QStri
|
|||
QString WalletManager::closeWallet()
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
qDebug() << Q_FUNC_INFO ;
|
||||
QString result;
|
||||
if (m_currentWallet) {
|
||||
result = m_currentWallet->address(0, 0);
|
||||
delete m_currentWallet;
|
||||
m_currentWallet = nullptr;
|
||||
} else {
|
||||
qCritical() << "Trying to close non existing wallet " << m_currentWallet;
|
||||
result = "0";
|
||||
|
@ -428,15 +435,6 @@ QUrl WalletManager::localPathToUrl(const QString &path) const
|
|||
return QUrl::fromLocalFile(path);
|
||||
}
|
||||
|
||||
QString WalletManager::checkUpdates(const QString &software, const QString &subdir) const
|
||||
{
|
||||
qDebug() << "Checking for updates";
|
||||
const std::tuple<bool, std::string, std::string, std::string, std::string> result = Monero::WalletManager::checkUpdates(software.toStdString(), subdir.toStdString());
|
||||
if (!std::get<0>(result))
|
||||
return QString("");
|
||||
return QString::fromStdString(std::get<1>(result) + "|" + std::get<2>(result) + "|" + std::get<3>(result) + "|" + std::get<4>(result));
|
||||
}
|
||||
|
||||
bool WalletManager::clearWalletCache(const QString &wallet_path) const
|
||||
{
|
||||
|
||||
|
@ -459,6 +457,7 @@ WalletManager::WalletManager(QObject *parent)
|
|||
: QObject(parent)
|
||||
, m_passphraseReceiver(nullptr)
|
||||
, m_scheduler(this)
|
||||
, m_currentWallet(nullptr)
|
||||
{
|
||||
m_pimpl = Monero::WalletManagerFactory::getWalletManager();
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ public:
|
|||
NetworkType::Type nettype = NetworkType::MAINNET, quint64 restoreHeight = 0, quint64 kdfRounds = 1);
|
||||
|
||||
Q_INVOKABLE Wallet * createWalletFromKeys(const QString &path,
|
||||
const QString &password,
|
||||
const QString &language,
|
||||
NetworkType::Type nettype,
|
||||
const QString &address,
|
||||
|
@ -159,12 +160,6 @@ public:
|
|||
Q_INVOKABLE bool parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector<QString> &unknown_parameters, QString &error) const;
|
||||
Q_INVOKABLE QVariantMap parse_uri_to_object(const QString &uri) const;
|
||||
// Q_INVOKABLE bool saveQrCode(const QString &, const QString &) const;
|
||||
// Q_INVOKABLE void checkUpdatesAsync(
|
||||
// const QString &software,
|
||||
// const QString &subdir,
|
||||
// const QString &buildTag,
|
||||
// const QString &version);
|
||||
Q_INVOKABLE QString checkUpdates(const QString &software, const QString &subdir) const;
|
||||
|
||||
// clear/rename wallet cache
|
||||
Q_INVOKABLE bool clearWalletCache(const QString &fileName) const;
|
||||
|
@ -182,12 +177,7 @@ signals:
|
|||
void walletPassphraseNeeded(bool onDevice);
|
||||
void deviceButtonRequest(quint64 buttonCode);
|
||||
void deviceButtonPressed();
|
||||
void checkUpdatesComplete(
|
||||
const QString &version,
|
||||
const QString &downloadUrl,
|
||||
const QString &hash,
|
||||
const QString &firstSigner,
|
||||
const QString &secondSigner) const;
|
||||
void deviceError(const QString &message);
|
||||
void miningStatus(bool isMining) const;
|
||||
void proxyAddressChanged() const;
|
||||
|
||||
|
|
1202
src/mainwindow.cpp
113
src/mainwindow.h
|
@ -4,32 +4,16 @@
|
|||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include "src/kdmactouchbar.h"
|
||||
#endif
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QScreen>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QMenu>
|
||||
#include <utility>
|
||||
#include <model/SubaddressModel.h>
|
||||
#include <model/SubaddressProxyModel.h>
|
||||
#include <model/TransactionHistoryModel.h>
|
||||
#include <model/CoinsModel.h>
|
||||
#include <model/CoinsProxyModel.h>
|
||||
|
||||
#include "appcontext.h"
|
||||
#include "components.h"
|
||||
#include "calcwindow.h"
|
||||
#include "widgets/ccswidget.h"
|
||||
#include "widgets/redditwidget.h"
|
||||
#include "widgets/tickerwidget.h"
|
||||
#include "widgets/xmrigwidget.h"
|
||||
#include "utils/networking.h"
|
||||
#include "appcontext.h"
|
||||
#include "utils/config.h"
|
||||
#include "wizard/WalletWizard.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include "dialog/aboutdialog.h"
|
||||
#include "dialog/signverifydialog.h"
|
||||
#include "dialog/verifyproofdialog.h"
|
||||
|
@ -38,7 +22,31 @@
|
|||
#include "dialog/keysdialog.h"
|
||||
#include "dialog/aboutdialog.h"
|
||||
#include "dialog/restoredialog.h"
|
||||
#include "dialog/splashdialog.h"
|
||||
#include "libwalletqt/Wallet.h"
|
||||
#include "model/SubaddressModel.h"
|
||||
#include "model/SubaddressProxyModel.h"
|
||||
#include "model/TransactionHistoryModel.h"
|
||||
#include "model/CoinsModel.h"
|
||||
#include "model/CoinsProxyModel.h"
|
||||
#include "utils/networking.h"
|
||||
#include "utils/config.h"
|
||||
#include "widgets/ccswidget.h"
|
||||
#include "widgets/redditwidget.h"
|
||||
#include "widgets/tickerwidget.h"
|
||||
#include "wizard/WalletWizard.h"
|
||||
|
||||
#ifdef HAS_LOCALMONERO
|
||||
#include "widgets/LocalMoneroWidget.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAS_XMRIG
|
||||
#include "widgets/xmrigwidget.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include "src/kdmactouchbar.h"
|
||||
#endif
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
|
@ -64,13 +72,6 @@ public:
|
|||
static AppContext *getContext();
|
||||
~MainWindow() override;
|
||||
|
||||
qreal screenDpiRef;
|
||||
QRect screenGeo;
|
||||
QRect screenRect;
|
||||
qreal screenDpi;
|
||||
qreal screenDpiPhysical;
|
||||
qreal screenRatio;
|
||||
|
||||
enum Tabs {
|
||||
HOME = 0,
|
||||
HISTORY,
|
||||
|
@ -88,8 +89,6 @@ public:
|
|||
};
|
||||
|
||||
public slots:
|
||||
void initWidgets();
|
||||
void initMenu();
|
||||
void showWizard(WalletWizard::Page startPage);
|
||||
void menuNewRestoreClicked();
|
||||
void menuQuitClicked();
|
||||
|
@ -116,14 +115,17 @@ public slots:
|
|||
void onRefreshSync(int height, int target);
|
||||
void onWalletOpenedError(const QString &err);
|
||||
void onWalletCreatedError(const QString &err);
|
||||
void onWalletCreated(Wallet *wallet);
|
||||
void menuWalletCloseClicked();
|
||||
void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
|
||||
void onDeviceButtonRequest(quint64 code);
|
||||
void onViewOnBlockExplorer(const QString &txid);
|
||||
void onResendTransaction(const QString &txid);
|
||||
void importContacts();
|
||||
void showRestoreHeightDialog();
|
||||
void importTransaction();
|
||||
void onDeviceError(const QString &error);
|
||||
void menuHwDeviceClicked();
|
||||
void onUpdatesAvailable(const QJsonObject &updates);
|
||||
|
||||
// offline tx signing
|
||||
void exportKeyImages();
|
||||
|
@ -148,15 +150,42 @@ public slots:
|
|||
signals:
|
||||
void closed();
|
||||
|
||||
private slots:
|
||||
void onInitialNetworkConfigured();
|
||||
void onCheckUpdatesComplete(const QString &version, const QString &binaryFilename, const QString &hash, const QString &signer);
|
||||
void onShowUpdateCheck(const QString &version, const QString &binaryFilename, const QString &hash, const QString &signer);
|
||||
void onRestartApplication(const QString &binaryFilename);
|
||||
void onSignedHashesReceived(QNetworkReply *reply, const QString &platformTag, const QString &version);
|
||||
void onShowDonationNag();
|
||||
void onInitiateTransaction();
|
||||
void onEndTransaction();
|
||||
void onCustomRestoreHeightSet(int height);
|
||||
void onWalletAboutToClose();
|
||||
|
||||
// Menu
|
||||
void onExportHistoryCSV(bool checked);
|
||||
void onExportContactsCSV(bool checked);
|
||||
void onCreateDesktopEntry(bool checked);
|
||||
void onReportBug(bool checked);
|
||||
|
||||
private:
|
||||
void initSkins();
|
||||
void initStatusBar();
|
||||
void initWidgets();
|
||||
void initMenu();
|
||||
void initTray();
|
||||
void initHome();
|
||||
void initTouchBar();
|
||||
void initWalletContext();
|
||||
void initWizard();
|
||||
void startupWarning();
|
||||
bool autoOpenWallet();
|
||||
|
||||
AppContext *m_ctx;
|
||||
|
||||
static MainWindow * pMainWindow;
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
void cleanupBeforeClose();
|
||||
void create_status_bar();
|
||||
void initMain();
|
||||
void loadSkins();
|
||||
QString loadStylesheet(const QString &resource);
|
||||
void saveGeo();
|
||||
void restoreGeo();
|
||||
|
@ -173,7 +202,10 @@ private:
|
|||
void showBalanceDialog();
|
||||
QString statusDots();
|
||||
void bringToFront();
|
||||
void centerWidget(QWidget &w);
|
||||
QString getPlatformTag();
|
||||
void displayWalletErrorMsg(const QString &err);
|
||||
QString getHardwareDevice();
|
||||
void setTitle(bool mining);
|
||||
|
||||
WalletWizard *createWizard(WalletWizard::Page startPage);
|
||||
|
||||
|
@ -181,8 +213,12 @@ private:
|
|||
Settings *m_windowSettings = nullptr;
|
||||
CalcWindow *m_windowCalc = nullptr;
|
||||
RestoreDialog *m_restoreDialog = nullptr;
|
||||
AboutDialog *m_aboutDialog = nullptr;
|
||||
XMRigWidget *m_xmrig = nullptr;
|
||||
SplashDialog *m_splashDialog = nullptr;
|
||||
|
||||
#ifdef HAS_LOCALMONERO
|
||||
LocalMoneroWidget *m_localMoneroWidget = nullptr;
|
||||
#endif
|
||||
|
||||
QSystemTrayIcon *m_trayIcon;
|
||||
QMenu m_trayMenu;
|
||||
|
@ -195,6 +231,7 @@ private:
|
|||
TickerWidget *m_balanceWidget;
|
||||
|
||||
// lower status bar
|
||||
QPushButton *m_statusUpdateAvailable;
|
||||
ClickableLabel *m_statusLabelBalance;
|
||||
QLabel *m_statusLabelStatus;
|
||||
QLabel *m_statusLabelNetStats;
|
||||
|
@ -203,6 +240,7 @@ private:
|
|||
StatusBarButton *m_statusBtnPreferences;
|
||||
StatusBarButton *m_statusBtnSeed;
|
||||
StatusBarButton *m_statusBtnTor;
|
||||
StatusBarButton *m_statusBtnHwDevice;
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QAction *m_touchbarActionWelcome;
|
||||
|
@ -210,6 +248,7 @@ private:
|
|||
QList<QAction *> m_touchbarWalletItems;
|
||||
QList<QAction *> m_touchbarWizardItems;
|
||||
#endif
|
||||
|
||||
QSignalMapper *m_tabShowHideSignalMapper;
|
||||
QMap<QString, ToggleTab*> m_tabShowHideMapper;
|
||||
WalletWizard *m_wizard = nullptr;
|
||||
|
@ -222,13 +261,9 @@ private:
|
|||
int m_statusDots;
|
||||
bool m_constructingTransaction = false;
|
||||
bool m_statusOverrideActive = false;
|
||||
bool m_showDeviceError = false;
|
||||
QTimer m_txTimer;
|
||||
|
||||
QIcon m_statusDisconnected;
|
||||
QIcon m_statusConnecting;
|
||||
QIcon m_statusSynchronizing;
|
||||
QIcon m_statusSynchronized;
|
||||
|
||||
private slots:
|
||||
void menuToggleTabVisible(const QString &key);
|
||||
};
|
||||
|
|
|
@ -293,6 +293,39 @@
|
|||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidgetExchanges">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tabLocalMonero">
|
||||
<attribute name="icon">
|
||||
<iconset resource="assets.qrc">
|
||||
<normaloff>:/assets/images/localMonero_logo.png</normaloff>:/assets/images/localMonero_logo.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>LocalMonero</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="localMoneroLayout"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabXmrRig">
|
||||
|
@ -320,7 +353,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1156</width>
|
||||
<height>30</height>
|
||||
<height>27</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "AddressBook.h"
|
||||
#include "ModelUtils.h"
|
||||
#include "utils/utils.h"
|
||||
#include "utils/Icons.h"
|
||||
|
||||
AddressBookModel::AddressBookModel(QObject *parent, AddressBook *addressBook)
|
||||
: QAbstractTableModel(parent),
|
||||
|
@ -13,7 +14,7 @@ AddressBookModel::AddressBookModel(QObject *parent, AddressBook *addressBook)
|
|||
{
|
||||
connect(m_addressBook, &AddressBook::refreshStarted, this, &AddressBookModel::startReset);
|
||||
connect(m_addressBook, &AddressBook::refreshFinished, this, &AddressBookModel::endReset);
|
||||
m_contactIcon = QIcon(":/assets/images/person.svg");
|
||||
m_contactIcon = icons()->icon("person.svg");
|
||||
}
|
||||
|
||||
void AddressBookModel::startReset(){
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
#include "ModelUtils.h"
|
||||
#include "globals.h"
|
||||
#include "utils/ColorScheme.h"
|
||||
#include "utils/Icons.h"
|
||||
|
||||
#include <QBrush>
|
||||
#include <QFont>
|
||||
|
||||
CoinsModel::CoinsModel(QObject *parent, Coins *coins)
|
||||
: QAbstractTableModel(parent),
|
||||
|
@ -17,9 +17,6 @@ CoinsModel::CoinsModel(QObject *parent, Coins *coins)
|
|||
{
|
||||
connect(m_coins, &Coins::refreshStarted, this, &CoinsModel::startReset);
|
||||
connect(m_coins, &Coins::refreshFinished, this, &CoinsModel::endReset);
|
||||
|
||||
m_eye = QIcon(":/assets/images/eye1.png");
|
||||
m_eyeBlind = QIcon(":/assets/images/eye_blind.png");
|
||||
}
|
||||
|
||||
void CoinsModel::startReset(){
|
||||
|
@ -88,10 +85,10 @@ QVariant CoinsModel::data(const QModelIndex &index, int role) const
|
|||
case KeyImageKnown:
|
||||
{
|
||||
if (cInfo.keyImageKnown()) {
|
||||
result = QVariant(m_eye);
|
||||
result = QVariant(icons()->icon("eye1.png"));
|
||||
}
|
||||
else {
|
||||
result = QVariant(m_eyeBlind);
|
||||
result = QVariant(icons()->icon("eye_blind.png"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,8 +53,6 @@ private:
|
|||
QVariant parseTransactionInfo(const CoinsInfo &cInfo, int column, int role) const;
|
||||
|
||||
Coins *m_coins;
|
||||
QIcon m_eye;
|
||||
QIcon m_eyeBlind;
|
||||
};
|
||||
|
||||
#endif //FEATHER_COINSMODEL_H
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "TransactionHistoryProxyModel.h"
|
||||
#include "libwalletqt/TransactionInfo.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <QActionGroup>
|
||||
|
||||
#include "TransactionHistoryModel.h"
|
||||
#include "TransactionHistoryProxyModel.h"
|
||||
|
||||
class HistoryView : public QTreeView
|
||||
{
|
||||
|
|
172
src/model/LocalMoneroModel.cpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#include "LocalMoneroModel.h"
|
||||
#include <QColor>
|
||||
#include "utils/utils.h"
|
||||
|
||||
LocalMoneroModel::LocalMoneroModel(QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
int LocalMoneroModel::rowCount(const QModelIndex &parent) const {
|
||||
return m_data.count();
|
||||
}
|
||||
|
||||
int LocalMoneroModel::columnCount(const QModelIndex &parent) const {
|
||||
return Column::COUNT;
|
||||
}
|
||||
|
||||
QVariant LocalMoneroModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
|
||||
switch (section) {
|
||||
case Seller:
|
||||
return QString("Seller");
|
||||
case Country:
|
||||
return QString("Country");
|
||||
case PaymentMethod:
|
||||
return QString("Payment Method");
|
||||
case PaymentMethodDetail:
|
||||
return QString("Detail");
|
||||
case PriceXMR:
|
||||
return QString("Price/XMR");
|
||||
case Limits:
|
||||
return QString("Limits");
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant LocalMoneroModel::data(const QModelIndex &index, int role) const {
|
||||
const int col = index.column();
|
||||
const auto row = m_data.at(index.row()).toObject()["data"].toObject();
|
||||
|
||||
if (row.isEmpty()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
switch (col) {
|
||||
case Column::Seller: {
|
||||
auto seller = row["profile"].toObject();
|
||||
return seller["name"].toString();
|
||||
// TODO: online indicator
|
||||
}
|
||||
case Column::Country: {
|
||||
return row["countrycode"].toString();
|
||||
}
|
||||
case Column::PaymentMethod: {
|
||||
auto paymentMethodCode = row["online_provider"].toString();
|
||||
if (paymentMethodCode == "NATIONAL_BANK") {
|
||||
return QString("National bank transfer");
|
||||
}
|
||||
return m_paymentMethodNames.value(paymentMethodCode, paymentMethodCode);
|
||||
}
|
||||
case Column::PaymentMethodDetail: {
|
||||
auto paymentMethodDetailText = row["payment_method_detail"].toString();
|
||||
QString filteredString; // We can't display emojis in QTreeView
|
||||
for (const auto &Char : paymentMethodDetailText) {
|
||||
if (Char.unicode() < 256)
|
||||
filteredString.append(Char);
|
||||
else
|
||||
filteredString.append(" ");
|
||||
}
|
||||
|
||||
return filteredString.trimmed();
|
||||
}
|
||||
case Column::PriceXMR: {
|
||||
return QString("%1 %2").arg(row["temp_price"].toString(), row["currency"].toString());
|
||||
}
|
||||
case Column::Limits: {
|
||||
auto minAmount = row["min_amount"].toString();
|
||||
auto maxAmount = row["max_amount"].toString();
|
||||
if (maxAmount.isEmpty()) {
|
||||
maxAmount = row["max_amount_available"].toString();
|
||||
}
|
||||
auto currency = row["currency"].toString();
|
||||
|
||||
if (minAmount.isEmpty() && maxAmount.isEmpty()) {
|
||||
return QString("Up to any amount %1").arg(currency);
|
||||
}
|
||||
|
||||
if (!minAmount.isEmpty() && maxAmount.isEmpty()) {
|
||||
return QString("%1 - any amount %2").arg(minAmount, currency);
|
||||
}
|
||||
|
||||
if (!minAmount.isEmpty() && !maxAmount.isEmpty()) {
|
||||
return QString("%1 - %2 %3").arg(minAmount, maxAmount, currency);
|
||||
}
|
||||
|
||||
if (minAmount.isEmpty() && !maxAmount.isEmpty()) {
|
||||
return QString("Up to %1 %2").arg(maxAmount, currency);
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (role == Qt::ForegroundRole) {
|
||||
switch (col) {
|
||||
case Column::PriceXMR: {
|
||||
return QVariant(QColor("#388538"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (role == Qt::FontRole) {
|
||||
switch (col) {
|
||||
case Column::PriceXMR: {
|
||||
auto bigFont = Utils::relativeFont(2);
|
||||
bigFont.setBold(true);
|
||||
return bigFont;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void LocalMoneroModel::setData(const QJsonArray &data) {
|
||||
beginResetModel();
|
||||
m_data = data;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void LocalMoneroModel::addData(const QJsonArray &data) {
|
||||
beginResetModel();
|
||||
|
||||
for (const auto &row : data) {
|
||||
m_data.append(row);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void LocalMoneroModel::clearData() {
|
||||
beginResetModel();
|
||||
m_data = {};
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void LocalMoneroModel::setPaymentMethods(const QJsonObject &data) {
|
||||
beginResetModel();
|
||||
|
||||
m_paymentMethods = data;
|
||||
m_paymentMethodNames.clear();
|
||||
for (const auto &payment_method : data) {
|
||||
auto code = payment_method["code"].toString();
|
||||
auto name = payment_method["name"].toString();
|
||||
|
||||
if (!code.isEmpty() && !name.isEmpty()) {
|
||||
m_paymentMethodNames[code] = name;
|
||||
}
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QJsonObject LocalMoneroModel::getOffer(int index) const {
|
||||
return m_data.at(index).toObject();
|
||||
}
|
47
src/model/LocalMoneroModel.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_LOCALMONEROMODEL_H
|
||||
#define FEATHER_LOCALMONEROMODEL_H
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
class LocalMoneroModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Column
|
||||
{
|
||||
Seller = 0,
|
||||
Country,
|
||||
PaymentMethod,
|
||||
PaymentMethodDetail,
|
||||
PriceXMR,
|
||||
Limits,
|
||||
COUNT
|
||||
};
|
||||
|
||||
LocalMoneroModel(QObject *parent = nullptr);
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
void setData(const QJsonArray &data);
|
||||
void setPaymentMethods(const QJsonObject &data);
|
||||
void addData(const QJsonArray &data);
|
||||
void clearData();
|
||||
|
||||
QJsonObject getOffer(int index) const;
|
||||
|
||||
private:
|
||||
QJsonArray m_data;
|
||||
QJsonObject m_paymentMethods;
|
||||
QHash<QString, QString> m_paymentMethodNames;
|
||||
};
|
||||
|
||||
#endif //FEATHER_LOCALMONEROMODEL_H
|
|
@ -4,12 +4,13 @@
|
|||
#include "NodeModel.h"
|
||||
#include "utils/nodes.h"
|
||||
#include "utils/ColorScheme.h"
|
||||
#include "utils/Icons.h"
|
||||
|
||||
NodeModel::NodeModel(int nodeSource, QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
, m_nodeSource(nodeSource)
|
||||
, m_offline(QIcon(":/assets/images/expired_icon.png"))
|
||||
, m_online(QIcon(":/assets/images/confirmed_icon.png"))
|
||||
, m_offline(icons()->icon("expired_icon.png"))
|
||||
, m_online(icons()->icon("confirmed_icon.png"))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -47,7 +48,7 @@ QVariant NodeModel::data(const QModelIndex &index, int role) const {
|
|||
if(role == Qt::DisplayRole) {
|
||||
switch(index.column()) {
|
||||
case NodeModel::URL:
|
||||
return node.full;
|
||||
return node.toFullAddress();
|
||||
case NodeModel::Height:
|
||||
if(node.online)
|
||||
return node.height == 0 ? QString("-") : QString::number(node.height);
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <QPoint>
|
||||
#include <QColor>
|
||||
#include <QBrush>
|
||||
#include <QFont>
|
||||
|
||||
SubaddressModel::SubaddressModel(QObject *parent, Subaddress *subaddress)
|
||||
: QAbstractTableModel(parent),
|
||||
|
|
|
@ -5,21 +5,16 @@
|
|||
#include "TransactionHistory.h"
|
||||
#include "TransactionInfo.h"
|
||||
#include "globals.h"
|
||||
#include "utils/config.h"
|
||||
#include "utils/ColorScheme.h"
|
||||
#include "utils/Icons.h"
|
||||
#include "utils/AppData.h"
|
||||
#include "ModelUtils.h"
|
||||
|
||||
TransactionHistoryModel::TransactionHistoryModel(QObject *parent)
|
||||
: QAbstractTableModel(parent),
|
||||
m_transactionHistory(nullptr)
|
||||
{
|
||||
m_unconfirmedTx = QIcon(":/assets/images/unconfirmed.png");
|
||||
m_warning = QIcon(":/assets/images/warning.png");
|
||||
m_clock1 = QIcon(":/assets/images/clock1.png");
|
||||
m_clock2 = QIcon(":/assets/images/clock2.png");
|
||||
m_clock3 = QIcon(":/assets/images/clock3.png");
|
||||
m_clock4 = QIcon(":/assets/images/clock4.png");
|
||||
m_clock5 = QIcon(":/assets/images/clock5.png");
|
||||
m_confirmedTx = QIcon(":/assets/images/confirmed.png");
|
||||
}
|
||||
|
||||
void TransactionHistoryModel::setTransactionHistory(TransactionHistory *th) {
|
||||
|
@ -86,21 +81,21 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
|
|||
case Column::Date:
|
||||
{
|
||||
if (tInfo.isFailed())
|
||||
result = QVariant(m_warning);
|
||||
result = QVariant(icons()->icon("warning.png"));
|
||||
else if (tInfo.isPending())
|
||||
result = QVariant(m_unconfirmedTx);
|
||||
result = QVariant(icons()->icon("unconfirmed.png"));
|
||||
else if (tInfo.confirmations() <= (1.0/5.0 * tInfo.confirmationsRequired()))
|
||||
result = QVariant(m_clock1);
|
||||
result = QVariant(icons()->icon("clock1.png"));
|
||||
else if (tInfo.confirmations() <= (2.0/5.0 * tInfo.confirmationsRequired()))
|
||||
result = QVariant(m_clock2);
|
||||
result = QVariant(icons()->icon("clock2.png"));
|
||||
else if (tInfo.confirmations() <= (3.0/5.0 * tInfo.confirmationsRequired()))
|
||||
result = QVariant(m_clock3);
|
||||
result = QVariant(icons()->icon("clock3.png"));
|
||||
else if (tInfo.confirmations() <= (4.0/5.0 * tInfo.confirmationsRequired()))
|
||||
result = QVariant(m_clock4);
|
||||
result = QVariant(icons()->icon("clock4.png"));
|
||||
else if (tInfo.confirmations() < tInfo.confirmationsRequired())
|
||||
result = QVariant(m_clock5);
|
||||
result = QVariant(icons()->icon("clock5.png"));
|
||||
else if (tInfo.confirmations())
|
||||
result = QVariant(m_confirmedTx);
|
||||
result = QVariant(icons()->icon("confirmed.png"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,13 +156,13 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tI
|
|||
}
|
||||
case Column::FiatAmount:
|
||||
{
|
||||
double usd_price = AppContext::txFiatHistory->get(tInfo.timestamp().toString("yyyyMMdd"));
|
||||
double usd_price = appData()->txFiatHistory->get(tInfo.timestamp().toString("yyyyMMdd"));
|
||||
if (usd_price == 0.0)
|
||||
return QVariant("?");
|
||||
|
||||
double usd_amount = usd_price * (tInfo.balanceDelta() / globals::cdiv);
|
||||
if(this->preferredFiatSymbol != "USD")
|
||||
usd_amount = AppContext::prices->convert("USD", this->preferredFiatSymbol, usd_amount);
|
||||
usd_amount = appData()->prices.convert("USD", this->preferredFiatSymbol, usd_amount);
|
||||
if (role == Qt::UserRole) {
|
||||
return usd_amount;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include <QAbstractListModel>
|
||||
#include <QIcon>
|
||||
#include "appcontext.h"
|
||||
|
||||
class TransactionHistory;
|
||||
class TransactionInfo;
|
||||
|
@ -54,14 +53,6 @@ private:
|
|||
QVariant parseTransactionInfo(const TransactionInfo &tInfo, int column, int role) const;
|
||||
|
||||
TransactionHistory * m_transactionHistory;
|
||||
QIcon m_unconfirmedTx;
|
||||
QIcon m_warning;
|
||||
QIcon m_clock1;
|
||||
QIcon m_clock2;
|
||||
QIcon m_clock3;
|
||||
QIcon m_clock4;
|
||||
QIcon m_clock5;
|
||||
QIcon m_confirmedTx;
|
||||
};
|
||||
|
||||
#endif // TRANSACTIONHISTORYMODEL_H
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
#include "receivewidget.h"
|
||||
#include "model/ModelUtils.h"
|
||||
#include "dialog/qrcodedialog.h"
|
||||
#include "utils/Icons.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
||||
ReceiveWidget::ReceiveWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
|
@ -29,9 +31,7 @@ ReceiveWidget::ReceiveWidget(QWidget *parent) :
|
|||
connect(m_showTransactionsAction, &QAction::triggered, this, &ReceiveWidget::onShowTransactions);
|
||||
connect(ui->addresses, &QTreeView::customContextMenuRequested, this, &ReceiveWidget::showContextMenu);
|
||||
|
||||
connect(ui->btn_generateSubaddress, &QPushButton::clicked, [=]() {
|
||||
emit generateSubaddress();
|
||||
});
|
||||
connect(ui->btn_generateSubaddress, &QPushButton::clicked, this, &ReceiveWidget::generateSubaddress);
|
||||
|
||||
connect(ui->qrCode, &ClickableLabel::clicked, this, &ReceiveWidget::showQrCodeDialog);
|
||||
connect(ui->label_addressSearch, &QLineEdit::textChanged, this, &ReceiveWidget::setSearchFilter);
|
||||
|
@ -88,9 +88,9 @@ void ReceiveWidget::showContextMenu(const QPoint &point) {
|
|||
|
||||
auto *menu = new QMenu(ui->addresses);
|
||||
|
||||
menu->addAction(QIcon(":/assets/images/copy.png"), "Copy address", this, &ReceiveWidget::copyAddress);
|
||||
menu->addAction(QIcon(":/assets/images/copy.png"), "Copy label", this, &ReceiveWidget::copyLabel);
|
||||
menu->addAction(QIcon(":/assets/images/edit.png"), "Edit label", this, &ReceiveWidget::editLabel);
|
||||
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);
|
||||
|
||||
if (isUsed) {
|
||||
menu->addAction(m_showTransactionsAction);
|
||||
|
@ -103,6 +103,10 @@ void ReceiveWidget::showContextMenu(const QPoint &point) {
|
|||
menu->addAction("Hide address", this, &ReceiveWidget::hideAddress);
|
||||
}
|
||||
|
||||
if (m_wallet->isHwBacked()) {
|
||||
menu->addAction("Show on device", this, &ReceiveWidget::showOnDevice);
|
||||
}
|
||||
|
||||
menu->popup(ui->addresses->viewport()->mapToGlobal(point));
|
||||
}
|
||||
|
||||
|
@ -164,6 +168,21 @@ void ReceiveWidget::showAddress()
|
|||
m_proxyModel->setHiddenAddresses(this->getHiddenAddresses());
|
||||
}
|
||||
|
||||
void ReceiveWidget::showOnDevice() {
|
||||
Monero::SubaddressRow* row = this->currentEntry();
|
||||
if (!row) return;
|
||||
m_wallet->deviceShowAddressAsync(m_wallet->currentSubaddressAccount(), row->getRowId(), "");
|
||||
}
|
||||
|
||||
void ReceiveWidget::generateSubaddress() {
|
||||
if (!m_wallet) return;
|
||||
|
||||
bool r = m_wallet->subaddress()->addRow(m_wallet->currentSubaddressAccount(), "");
|
||||
if (!r) {
|
||||
QMessageBox::warning(this, "Warning", QString("Failed to generate subaddress:\n\n%1").arg(m_wallet->subaddress()->errorString()));
|
||||
}
|
||||
}
|
||||
|
||||
void ReceiveWidget::updateQrCode(){
|
||||
QModelIndex index = ui->addresses->currentIndex();
|
||||
if (!index.isValid()) {
|
||||
|
|
|
@ -40,13 +40,14 @@ public slots:
|
|||
void resetModel();
|
||||
|
||||
signals:
|
||||
void generateSubaddress();
|
||||
void showTransactions(const QString& address);
|
||||
|
||||
private slots:
|
||||
void showHeaderMenu(const QPoint& position);
|
||||
void hideAddress();
|
||||
void showAddress();
|
||||
void showOnDevice();
|
||||
void generateSubaddress();
|
||||
|
||||
private:
|
||||
Ui::ReceiveWidget *ui;
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
#include "mainwindow.h"
|
||||
#include "ui_sendwidget.h"
|
||||
#include "globals.h"
|
||||
#include "utils/AppData.h"
|
||||
|
||||
SendWidget::SendWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::SendWidget)
|
||||
SendWidget::SendWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, ui(new Ui::SendWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_ctx = MainWindow::getContext();
|
||||
|
@ -20,6 +21,12 @@ SendWidget::SendWidget(QWidget *parent) :
|
|||
QValidator *validator = new QRegExpValidator(rx, this);
|
||||
ui->lineAmount->setValidator(validator);
|
||||
|
||||
connect(m_ctx, &AppContext::initiateTransaction, this, &SendWidget::onInitiateTransaction);
|
||||
connect(m_ctx, &AppContext::endTransaction, this, &SendWidget::onEndTransaction);
|
||||
connect(m_ctx, &AppContext::openAliasResolved, this, &SendWidget::onOpenAliasResolved);
|
||||
connect(m_ctx, &AppContext::openAliasResolveError, this, &SendWidget::onOpenAliasResolveError);
|
||||
connect(m_ctx, &AppContext::walletClosed, this, &SendWidget::onWalletClosed);
|
||||
|
||||
connect(ui->btnSend, &QPushButton::clicked, this, &SendWidget::sendClicked);
|
||||
connect(ui->btnClear, &QPushButton::clicked, this, &SendWidget::clearClicked);
|
||||
connect(ui->btnMax, &QPushButton::clicked, this, &SendWidget::btnMaxClicked);
|
||||
|
@ -140,7 +147,7 @@ void SendWidget::sendClicked() {
|
|||
amounts.push_back(output.amount);
|
||||
}
|
||||
|
||||
emit createTransactionMultiDest(addresses, amounts, description);
|
||||
m_ctx->onCreateTransactionMultiDest(addresses, amounts, description);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -152,20 +159,20 @@ void SendWidget::sendClicked() {
|
|||
QMessageBox::warning(this, "Amount error", "Invalid amount specified.");
|
||||
return;
|
||||
}
|
||||
emit createTransaction(recipient, amount, description, sendAll);
|
||||
m_ctx->onCreateTransaction(recipient, amount, description, sendAll);
|
||||
} else {
|
||||
amount = WalletManager::amountFromDouble(this->conversionAmount());
|
||||
if (amount == 0) {
|
||||
QMessageBox::warning(this, "Fiat conversion error", "Could not create transaction.");
|
||||
return;
|
||||
}
|
||||
emit createTransaction(recipient, amount, description, false);
|
||||
m_ctx->onCreateTransaction(recipient, amount, description, false);
|
||||
}
|
||||
}
|
||||
|
||||
void SendWidget::aliasClicked() {
|
||||
auto address = ui->lineAddress->text();
|
||||
emit resolveOpenAlias(address);
|
||||
m_ctx->onOpenAliasResolve(address);
|
||||
}
|
||||
|
||||
void SendWidget::clearClicked() {
|
||||
|
@ -195,7 +202,7 @@ void SendWidget::updateConversionLabel() {
|
|||
|
||||
} else {
|
||||
auto preferredFiatCurrency = config()->get(Config::preferredFiatCurrency).toString();
|
||||
double conversionAmount = AppContext::prices->convert("XMR", preferredFiatCurrency, this->amountDouble());
|
||||
double conversionAmount = appData()->prices.convert("XMR", preferredFiatCurrency, this->amountDouble());
|
||||
return QString("~%1 %2").arg(QString::number(conversionAmount, 'f', 2), preferredFiatCurrency);
|
||||
}
|
||||
}();
|
||||
|
@ -206,7 +213,7 @@ void SendWidget::updateConversionLabel() {
|
|||
|
||||
double SendWidget::conversionAmount() {
|
||||
QString currency = ui->comboCurrencySelection->currentText();
|
||||
return AppContext::prices->convert(currency, "XMR", this->amountDouble());
|
||||
return appData()->prices.convert(currency, "XMR", this->amountDouble());
|
||||
}
|
||||
|
||||
quint64 SendWidget::amount() {
|
||||
|
|
|
@ -43,11 +43,6 @@ public slots:
|
|||
void onInitiateTransaction();
|
||||
void onEndTransaction();
|
||||
|
||||
signals:
|
||||
void resolveOpenAlias(const QString &address);
|
||||
void createTransaction(const QString &address, quint64 amount, const QString &description, bool all);
|
||||
void createTransactionMultiDest(const QVector<QString> &addresses, const QVector<quint64> &amounts, const QString &description);
|
||||
|
||||
private:
|
||||
void setupComboBox();
|
||||
double amountDouble();
|
||||
|
|
|
@ -17,7 +17,7 @@ Settings::Settings(QWidget *parent) :
|
|||
this->setWindowIcon(QIcon("://assets/images/appicons/64x64.png"));
|
||||
|
||||
ui->tabWidget->setTabVisible(2, false);
|
||||
ui->tabWidget->setTabVisible(4, false);
|
||||
ui->tabWidget->setTabVisible(5, false);
|
||||
|
||||
connect(ui->btnCopyToClipboard, &QPushButton::clicked, this, &Settings::copyToClipboard);
|
||||
connect(ui->checkBox_multiBroadcast, &QCheckBox::toggled, [](bool toggled){
|
||||
|
@ -70,8 +70,6 @@ Settings::Settings(QWidget *parent) :
|
|||
ui->comboBox_timeFormat->setCurrentIndex(m_timeFormats.indexOf(timeFormatSetting));
|
||||
|
||||
connect(ui->comboBox_skin, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_skinChanged);
|
||||
connect(ui->comboBox_blockExplorer, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_blockExplorerChanged);
|
||||
connect(ui->comboBox_redditFrontend, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_redditFrontendChanged);
|
||||
connect(ui->comboBox_amountPrecision, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_amountPrecisionChanged);
|
||||
connect(ui->comboBox_dateFormat, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_dateFormatChanged);
|
||||
connect(ui->comboBox_timeFormat, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_timeFormatChanged);
|
||||
|
@ -98,6 +96,16 @@ Settings::Settings(QWidget *parent) :
|
|||
ui->lineEdit_defaultWalletDir->setText(m_ctx->defaultWalletDir);
|
||||
});
|
||||
|
||||
// Links tab
|
||||
connect(ui->combo_blockExplorer, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_blockExplorerChanged);
|
||||
connect(ui->combo_redditFrontend, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_redditFrontendChanged);
|
||||
connect(ui->combo_localMoneroFrontend, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_localMoneroFrontendChanged);
|
||||
|
||||
ui->combo_blockExplorer->setCurrentIndex(ui->combo_blockExplorer->findText(config()->get(Config::blockExplorer).toString()));
|
||||
ui->combo_redditFrontend->setCurrentIndex(ui->combo_redditFrontend->findText(config()->get(Config::redditFrontend).toString()));
|
||||
|
||||
this->setupLocalMoneroFrontendCombobox();
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
|
@ -118,16 +126,21 @@ void Settings::comboBox_skinChanged(int pos) {
|
|||
}
|
||||
|
||||
void Settings::comboBox_blockExplorerChanged(int pos) {
|
||||
QString blockExplorer = ui->comboBox_blockExplorer->currentText();
|
||||
QString blockExplorer = ui->combo_blockExplorer->currentText();
|
||||
config()->set(Config::blockExplorer, blockExplorer);
|
||||
emit blockExplorerChanged(blockExplorer);
|
||||
}
|
||||
|
||||
void Settings::comboBox_redditFrontendChanged(int pos) {
|
||||
QString redditFrontend = ui->comboBox_redditFrontend->currentText();
|
||||
QString redditFrontend = ui->combo_redditFrontend->currentText();
|
||||
config()->set(Config::redditFrontend, redditFrontend);
|
||||
}
|
||||
|
||||
void Settings::comboBox_localMoneroFrontendChanged(int pos) {
|
||||
QString localMoneroFrontend = ui->combo_localMoneroFrontend->currentData().toString();
|
||||
config()->set(Config::localMoneroFrontend, localMoneroFrontend);
|
||||
}
|
||||
|
||||
void Settings::comboBox_amountPrecisionChanged(int pos) {
|
||||
config()->set(Config::amountPrecision, pos);
|
||||
emit amountPrecisionChanged(pos);
|
||||
|
@ -161,6 +174,15 @@ void Settings::setupSkinCombobox() {
|
|||
ui->comboBox_skin->insertItems(0, m_skins);
|
||||
}
|
||||
|
||||
void Settings::setupLocalMoneroFrontendCombobox() {
|
||||
ui->combo_localMoneroFrontend->addItem("localmonero.co", "https://localmonero.co");
|
||||
ui->combo_localMoneroFrontend->addItem("localmonero.co/nojs", "https://localmonero.co/nojs");
|
||||
ui->combo_localMoneroFrontend->addItem("nehdddktmhvqklsnkjqcbpmb63htee2iznpcbs5tgzctipxykpj6yrid.onion",
|
||||
"http://nehdddktmhvqklsnkjqcbpmb63htee2iznpcbs5tgzctipxykpj6yrid.onion");
|
||||
|
||||
ui->combo_localMoneroFrontend->setCurrentIndex(ui->combo_localMoneroFrontend->findData(config()->get(Config::localMoneroFrontend).toString()));
|
||||
}
|
||||
|
||||
Settings::~Settings() {
|
||||
delete ui;
|
||||
}
|
||||
|
|
|
@ -37,14 +37,17 @@ public slots:
|
|||
void checkboxExternalLinkWarn();
|
||||
void fiatCurrencySelected(int index);
|
||||
void comboBox_skinChanged(int pos);
|
||||
void comboBox_blockExplorerChanged(int pos);
|
||||
void comboBox_redditFrontendChanged(int pos);
|
||||
void comboBox_amountPrecisionChanged(int pos);
|
||||
void comboBox_dateFormatChanged(int pos);
|
||||
void comboBox_timeFormatChanged(int pos);
|
||||
|
||||
void comboBox_blockExplorerChanged(int pos);
|
||||
void comboBox_redditFrontendChanged(int pos);
|
||||
void comboBox_localMoneroFrontendChanged(int pos);
|
||||
|
||||
private:
|
||||
void setupSkinCombobox();
|
||||
void setupLocalMoneroFrontendCombobox();
|
||||
|
||||
AppContext *m_ctx;
|
||||
Ui::Settings *ui;
|
||||
|
|
145
src/settings.ui
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1123</width>
|
||||
<height>555</height>
|
||||
<width>915</width>
|
||||
<height>519</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -133,90 +133,33 @@
|
|||
<widget class="QComboBox" name="comboBox_skin"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Block explorer:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="comboBox_blockExplorer">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>exploremonero.com</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>xmrchain.net</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>moneroblocks.info</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>blockchair.com</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Reddit frontend:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="comboBox_redditFrontend">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>old.reddit.com</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>reddit.com</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>teddit.net</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Amount precision:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="comboBox_amountPrecision"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Date format:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="comboBox_dateFormat"/>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Time format:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="comboBox_timeFormat"/>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -420,6 +363,80 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_links">
|
||||
<attribute name="title">
|
||||
<string>Links</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>Block explorer:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="combo_blockExplorer">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>exploremonero.com</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>xmrchain.net</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>moneroblocks.info</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>blockchair.com</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="text">
|
||||
<string>Reddit frontend:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="combo_redditFrontend">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>old.reddit.com</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>reddit.com</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>teddit.net</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="text">
|
||||
<string>LocalMonero frontend:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="combo_localMoneroFrontend"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_console">
|
||||
<attribute name="icon">
|
||||
<iconset theme=":/assets/images/terminal.png">
|
||||
|
|
52
src/utils/AppData.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#include "AppData.h"
|
||||
#include "config.h"
|
||||
#include "WebsocketNotifier.h"
|
||||
|
||||
AppData::AppData(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
this->initRestoreHeights();
|
||||
|
||||
auto genesis_timestamp = this->restoreHeights[NetworkType::Type::MAINNET]->data.firstKey();
|
||||
this->txFiatHistory = new TxFiatHistory(genesis_timestamp, Config::defaultConfigDir().path());
|
||||
|
||||
connect(&websocketNotifier()->websocketClient, &WebsocketClient::connectionEstablished, this->txFiatHistory, &TxFiatHistory::onUpdateDatabase);
|
||||
connect(this->txFiatHistory, &TxFiatHistory::requestYear, [](int year){
|
||||
QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1}})").arg(year).toUtf8();
|
||||
websocketNotifier()->websocketClient.sendMsg(data);
|
||||
});
|
||||
connect(this->txFiatHistory, &TxFiatHistory::requestYearMonth, [](int year, int month){
|
||||
QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1, "month": %2}})").arg(year).arg(month).toUtf8();
|
||||
websocketNotifier()->websocketClient.sendMsg(data);
|
||||
});
|
||||
|
||||
connect(websocketNotifier(), &WebsocketNotifier::CryptoRatesReceived, &this->prices, &Prices::cryptoPricesReceived);
|
||||
connect(websocketNotifier(), &WebsocketNotifier::FiatRatesReceived, &this->prices, &Prices::fiatPricesReceived);
|
||||
connect(websocketNotifier(), &WebsocketNotifier::TxFiatHistoryReceived, this->txFiatHistory, &TxFiatHistory::onWSData);
|
||||
connect(websocketNotifier(), &WebsocketNotifier::BlockHeightsReceived, this, &AppData::onBlockHeightsReceived);
|
||||
}
|
||||
|
||||
QPointer<AppData> AppData::m_instance(nullptr);
|
||||
|
||||
void AppData::onBlockHeightsReceived(int mainnet, int stagenet) {
|
||||
this->heights[NetworkType::MAINNET] = mainnet;
|
||||
this->heights[NetworkType::STAGENET] = stagenet;
|
||||
}
|
||||
|
||||
void AppData::initRestoreHeights() {
|
||||
restoreHeights[NetworkType::TESTNET] = new RestoreHeightLookup(NetworkType::TESTNET);
|
||||
restoreHeights[NetworkType::STAGENET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_monero_stagenet.txt", NetworkType::STAGENET);
|
||||
restoreHeights[NetworkType::MAINNET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_monero_mainnet.txt", NetworkType::MAINNET);
|
||||
}
|
||||
|
||||
AppData* AppData::instance()
|
||||
{
|
||||
if (!m_instance) {
|
||||
m_instance = new AppData(QCoreApplication::instance());
|
||||
}
|
||||
|
||||
return m_instance;
|
||||
}
|
41
src/utils/AppData.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_APPDATA_H
|
||||
#define FEATHER_APPDATA_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "prices.h"
|
||||
#include "txfiathistory.h"
|
||||
#include "RestoreHeightLookup.h"
|
||||
|
||||
class AppData : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AppData(QObject *parent);
|
||||
static AppData* instance();
|
||||
|
||||
Prices prices;
|
||||
TxFiatHistory *txFiatHistory;
|
||||
QMap<NetworkType::Type, int> heights;
|
||||
QMap<NetworkType::Type, RestoreHeightLookup*> restoreHeights;
|
||||
|
||||
private slots:
|
||||
void onBlockHeightsReceived(int mainnet, int stagenet);
|
||||
|
||||
private:
|
||||
void initRestoreHeights();
|
||||
|
||||
static QPointer<AppData> m_instance;
|
||||
};
|
||||
|
||||
inline AppData* appData()
|
||||
{
|
||||
return AppData::instance();
|
||||
}
|
||||
|
||||
#endif //FEATHER_APPDATA_H
|
85
src/utils/AsyncTask.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_ASYNCTASK_HPP
|
||||
#define KEEPASSXC_ASYNCTASK_HPP
|
||||
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
/**
|
||||
* Asynchronously run computations outside the GUI thread.
|
||||
*/
|
||||
namespace AsyncTask
|
||||
{
|
||||
|
||||
/**
|
||||
* Wait for the given future without blocking the event loop.
|
||||
*
|
||||
* @param future future to wait for
|
||||
* @return async task result
|
||||
*/
|
||||
template <typename FunctionObject>
|
||||
typename std::result_of<FunctionObject()>::type
|
||||
waitForFuture(QFuture<typename std::result_of<FunctionObject()>::type> future)
|
||||
{
|
||||
QEventLoop loop;
|
||||
QFutureWatcher<typename std::result_of<FunctionObject()>::type> watcher;
|
||||
QObject::connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
watcher.setFuture(future);
|
||||
loop.exec();
|
||||
return future.result();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a given task and wait for it to finish without blocking the event loop.
|
||||
*
|
||||
* @param task std::function object to run
|
||||
* @return async task result
|
||||
*/
|
||||
template <typename FunctionObject>
|
||||
typename std::result_of<FunctionObject()>::type runAndWaitForFuture(FunctionObject task)
|
||||
{
|
||||
return waitForFuture<FunctionObject>(QtConcurrent::run(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a given task then call the defined callback. Prevents event loop blocking and
|
||||
* ensures the validity of the follow-on task through the context. If the context is
|
||||
* deleted, the callback will not be processed preventing use after free errors.
|
||||
*
|
||||
* @param task std::function object to run
|
||||
* @param context QObject responsible for calling this function
|
||||
* @param callback std::function object to run after the task completess
|
||||
*/
|
||||
template <typename FunctionObject, typename FunctionObject2>
|
||||
void runThenCallback(FunctionObject task, QObject* context, FunctionObject2 callback)
|
||||
{
|
||||
typedef QFutureWatcher<typename std::result_of<FunctionObject()>::type> FutureWatcher;
|
||||
auto future = QtConcurrent::run(task);
|
||||
auto watcher = new FutureWatcher(context);
|
||||
QObject::connect(watcher, &QFutureWatcherBase::finished, context, [=]() {
|
||||
watcher->deleteLater();
|
||||
callback(future.result());
|
||||
});
|
||||
watcher->setFuture(future);
|
||||
}
|
||||
|
||||
}; // namespace AsyncTask
|
||||
|
||||
#endif // KEEPASSXC_ASYNCTASK_HPP
|
|
@ -3,6 +3,7 @@
|
|||
// Copyright (c) 2012 thomasv@gitorious
|
||||
|
||||
#include "ColorScheme.h"
|
||||
#include <QDebug>
|
||||
|
||||
bool ColorScheme::darkScheme = false;
|
||||
ColorSchemeItem ColorScheme::GREEN = ColorSchemeItem("#117c11", "#8af296");
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "libwalletqt/WalletManager.h"
|
||||
#include "libwalletqt/Wallet.h"
|
||||
#include "utils/AppData.h"
|
||||
|
||||
#include <sstream>
|
||||
#include "RestoreHeightLookup.h"
|
||||
|
@ -13,6 +14,8 @@ enum SeedType {
|
|||
};
|
||||
|
||||
struct FeatherSeed {
|
||||
// TODO: this is spaghetti, needs refactor
|
||||
|
||||
QString coin;
|
||||
QString language;
|
||||
SeedType seedType;
|
||||
|
@ -21,17 +24,18 @@ struct FeatherSeed {
|
|||
QString spendKey;
|
||||
QString correction;
|
||||
|
||||
NetworkType::Type netType;
|
||||
|
||||
time_t time;
|
||||
int restoreHeight = 0;
|
||||
RestoreHeightLookup *lookup = nullptr;
|
||||
|
||||
QString errorString;
|
||||
|
||||
explicit FeatherSeed(RestoreHeightLookup *lookup,
|
||||
explicit FeatherSeed(NetworkType::Type networkType = NetworkType::MAINNET,
|
||||
const QString &coin = "monero",
|
||||
const QString &language = "English",
|
||||
const QStringList &mnemonic = {})
|
||||
: lookup(lookup), coin(coin), language(language), mnemonic(mnemonic)
|
||||
: netType(networkType), coin(coin), language(language), mnemonic(mnemonic)
|
||||
{
|
||||
// Generate a new mnemonic if none was given
|
||||
if (mnemonic.length() == 0) {
|
||||
|
@ -85,21 +89,17 @@ struct FeatherSeed {
|
|||
}
|
||||
}
|
||||
|
||||
int setRestoreHeight() {
|
||||
if (this->lookup == nullptr)
|
||||
return 1;
|
||||
|
||||
void setRestoreHeight() {
|
||||
if (this->time == 0)
|
||||
return 1;
|
||||
this->restoreHeight = 1;
|
||||
|
||||
this->restoreHeight = this->lookup->dateToRestoreHeight(this->time);
|
||||
return this->restoreHeight;
|
||||
this->restoreHeight = appData()->restoreHeights[netType]->dateToRestoreHeight(this->time);
|
||||
}
|
||||
|
||||
int setRestoreHeight(int height) {
|
||||
auto now = std::time(nullptr);
|
||||
auto nowClearance = 3600 * 24;
|
||||
auto currentBlockHeight = this->lookup->dateToRestoreHeight(now - nowClearance);
|
||||
auto currentBlockHeight = appData()->restoreHeights[netType]->dateToRestoreHeight(now - nowClearance);
|
||||
if (height >= currentBlockHeight + nowClearance) {
|
||||
qCritical() << "unrealistic restore height detected, setting to current blockheight instead: " << currentBlockHeight;
|
||||
this->restoreHeight = currentBlockHeight;
|
||||
|
|
32
src/utils/Icons.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#include "Icons.h"
|
||||
|
||||
Icons* Icons::m_instance(nullptr);
|
||||
|
||||
Icons::Icons()
|
||||
= default;
|
||||
|
||||
QIcon Icons::icon(const QString& name)
|
||||
{
|
||||
QIcon icon = m_iconCache.value(name);
|
||||
|
||||
if (!icon.isNull()) {
|
||||
return icon;
|
||||
}
|
||||
|
||||
icon = QIcon{":/assets/images/" + name};
|
||||
|
||||
m_iconCache.insert(name, icon);
|
||||
return icon;
|
||||
}
|
||||
|
||||
Icons* Icons::instance()
|
||||
{
|
||||
if (!m_instance) {
|
||||
m_instance = new Icons();
|
||||
}
|
||||
|
||||
return m_instance;
|
||||
}
|