Merge pull request 'Beta-6' (#357) from tobtoht/feather:hw_ledger into master
Reviewed-on: https://git.featherwallet.org/feather/feather/pulls/357
1
.gitignore
vendored
|
@ -14,3 +14,4 @@ src/tor/*
|
||||||
!src/tor/.gitkeep
|
!src/tor/.gitkeep
|
||||||
src/config-feather.h
|
src/config-feather.h
|
||||||
src/assets/exec/*
|
src/assets/exec/*
|
||||||
|
feather.AppDir/*
|
||||||
|
|
|
@ -22,7 +22,7 @@ git clone --branch master --recursive https://git.featherwallet.org/feather/feat
|
||||||
cd feather
|
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
|
#### 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
|
##### Standalone binary
|
||||||
|
|
||||||
```bash
|
```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.
|
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
|
#### 3. Build
|
||||||
|
|
||||||
```bash
|
```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.
|
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_MAJOR "0")
|
||||||
set(VERSION_MINOR "1")
|
set(VERSION_MINOR "1")
|
||||||
set(VERSION_REVISION "0")
|
set(VERSION_REVISION "0")
|
||||||
set(VERSION "beta-5")
|
set(VERSION "beta-6")
|
||||||
|
|
||||||
option(FETCH_DEPS "Download dependencies if they are not found" ON)
|
option(FETCH_DEPS "Download dependencies if they are not found" ON)
|
||||||
|
option(LOCALMONERO "Include LocalMonero module" ON)
|
||||||
option(XMRIG "Include XMRig module" ON)
|
option(XMRIG "Include XMRig module" ON)
|
||||||
option(TOR_BIN "Path to Tor binary to embed inside Feather" OFF)
|
option(TOR_BIN "Path to Tor binary to embed inside Feather" OFF)
|
||||||
|
option(CHECK_UPDATES "Enable checking for application updates" OFF)
|
||||||
|
|
||||||
option(STATIC "Link libraries statically, requires static Qt")
|
option(STATIC "Link libraries statically, requires static Qt")
|
||||||
option(USE_DEVICE_TREZOR "Trezor support compilation" OFF)
|
option(USE_DEVICE_TREZOR "Trezor support compilation" OFF)
|
||||||
|
@ -29,7 +31,7 @@ if(DEBUG)
|
||||||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(MONERO_HEAD "41327974116dedccc2f9709d8ad3a8a1f591faed")
|
set(MONERO_HEAD "36fb05da3394505f8033ceb8806b28909617696f")
|
||||||
set(BUILD_GUI_DEPS ON)
|
set(BUILD_GUI_DEPS ON)
|
||||||
set(ARCH "x86-64")
|
set(ARCH "x86-64")
|
||||||
set(BUILD_64 ON)
|
set(BUILD_64 ON)
|
||||||
|
@ -139,6 +141,11 @@ if(NOT monero-seed_FOUND)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# libzip
|
||||||
|
find_package(zlib CONFIG)
|
||||||
|
find_path(LIBZIP_INCLUDE_DIRS zip.h)
|
||||||
|
find_library(LIBZIP_LIBRARIES zip)
|
||||||
|
|
||||||
# Boost
|
# Boost
|
||||||
if(DEBUG)
|
if(DEBUG)
|
||||||
set(Boost_DEBUG ON)
|
set(Boost_DEBUG ON)
|
||||||
|
|
19
Dockerfile
|
@ -323,17 +323,17 @@ RUN rm /usr/lib/x86_64-linux-gnu/libX11.a && \
|
||||||
cd ../../../.. && \
|
cd ../../../.. && \
|
||||||
rm -rf $(pwd)
|
rm -rf $(pwd)
|
||||||
|
|
||||||
RUN git clone -b v1.0.23 --depth 1 https://github.com/libusb/libusb && \
|
RUN git clone -b v1.0.24 --depth 1 https://github.com/libusb/libusb && \
|
||||||
cd libusb && \
|
cd libusb && \
|
||||||
git reset --hard e782eeb2514266f6738e242cdcb18e3ae1ed06fa && \
|
git reset --hard c6a35c56016ea2ab2f19115d2ea1e85e0edae155 && \
|
||||||
./autogen.sh --disable-shared --enable-static && \
|
./autogen.sh --disable-shared --enable-static && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install && \
|
||||||
rm -rf $(pwd)
|
rm -rf $(pwd)
|
||||||
|
|
||||||
RUN git clone -b hidapi-0.9.0 --depth 1 https://github.com/libusb/hidapi && \
|
RUN git clone -b hidapi-0.10.1 --depth 1 https://github.com/libusb/hidapi && \
|
||||||
cd hidapi && \
|
cd hidapi && \
|
||||||
git reset --hard 7da5cc91fc0d2dbe4df4f08cd31f6ca1a262418f && \
|
git reset --hard f6d0073fcddbdda24549199445e844971d3c9cef && \
|
||||||
./bootstrap && \
|
./bootstrap && \
|
||||||
./configure --disable-shared --enable-static && \
|
./configure --disable-shared --enable-static && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
|
@ -384,9 +384,9 @@ RUN git clone -b v3.18.4 --depth 1 https://github.com/Kitware/CMake && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install && \
|
||||||
rm -rf $(pwd)
|
rm -rf $(pwd)
|
||||||
|
|
||||||
RUN git clone -b v4.0.2 --depth 1 https://github.com/fukuchi/libqrencode.git && \
|
RUN git clone -b v4.1.1 --depth 1 https://github.com/fukuchi/libqrencode.git && \
|
||||||
cd libqrencode && \
|
cd libqrencode && \
|
||||||
git reset --hard 59ee597f913fcfda7a010a6e106fbee2595f68e4 && \
|
git reset --hard 715e29fd4cd71b6e452ae0f4e36d917b43122ce8 && \
|
||||||
cmake -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=/usr . && \
|
cmake -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=/usr . && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install && \
|
||||||
|
@ -414,3 +414,10 @@ RUN mkdir linuxdeployqt && \
|
||||||
chmod +x linuxdeployqt-7-x86_64.AppImage && \
|
chmod +x linuxdeployqt-7-x86_64.AppImage && \
|
||||||
./linuxdeployqt-7-x86_64.AppImage --appimage-extract && \
|
./linuxdeployqt-7-x86_64.AppImage --appimage-extract && \
|
||||||
rm linuxdeployqt-7-x86_64.AppImage
|
rm linuxdeployqt-7-x86_64.AppImage
|
||||||
|
|
||||||
|
RUN git clone -b v1.7.3 --depth 1 https://github.com/nih-at/libzip.git && \
|
||||||
|
cd libzip && \
|
||||||
|
git reset --hard 66e496489bdae81bfda8b0088172871d8fda0032 && \
|
||||||
|
cmake -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=/usr . && \
|
||||||
|
make -j$THREADS && \
|
||||||
|
make -j$THREADS install
|
||||||
|
|
|
@ -15,9 +15,9 @@ RUN apt update && \
|
||||||
RUN update-alternatives --set x86_64-w64-mingw32-g++ $(which x86_64-w64-mingw32-g++-posix) && \
|
RUN update-alternatives --set x86_64-w64-mingw32-g++ $(which x86_64-w64-mingw32-g++-posix) && \
|
||||||
update-alternatives --set x86_64-w64-mingw32-gcc $(which x86_64-w64-mingw32-gcc-posix)
|
update-alternatives --set x86_64-w64-mingw32-gcc $(which x86_64-w64-mingw32-gcc-posix)
|
||||||
|
|
||||||
RUN git clone -b v0.17.1.9 --depth 1 https://github.com/monero-project/monero && \
|
RUN git clone -b v0.17.2.0 --depth 1 https://github.com/monero-project/monero && \
|
||||||
cd monero && \
|
cd monero && \
|
||||||
git reset --hard 8fef32e45c80aec41f25be9d1d8fb75adc883c64 && \
|
git reset --hard f6e63ef260e795aacd408c28008398785b84103a && \
|
||||||
cp -a contrib/depends / && \
|
cp -a contrib/depends / && \
|
||||||
cd .. && \
|
cd .. && \
|
||||||
rm -rf monero
|
rm -rf monero
|
||||||
|
@ -34,12 +34,12 @@ RUN git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \
|
||||||
git clone git://code.qt.io/qt/qttranslations.git -b ${QT_VERSION} --depth 1 && \
|
git clone git://code.qt.io/qt/qttranslations.git -b ${QT_VERSION} --depth 1 && \
|
||||||
git clone git://code.qt.io/qt/qtxmlpatterns.git -b ${QT_VERSION} --depth 1 && \
|
git clone git://code.qt.io/qt/qtxmlpatterns.git -b ${QT_VERSION} --depth 1 && \
|
||||||
git clone git://code.qt.io/qt/qtwebsockets.git -b ${QT_VERSION} --depth 1 && \
|
git clone git://code.qt.io/qt/qtwebsockets.git -b ${QT_VERSION} --depth 1 && \
|
||||||
OPENSSL_LIBS="-lssl -lcrypto -lpthread -ldl" \
|
OPENSSL_LIBS="-lssl -lcrypto -lws2_32" \
|
||||||
./configure --prefix=/depends/x86_64-w64-mingw32 -xplatform win32-g++ \
|
./configure --prefix=/depends/x86_64-w64-mingw32 -xplatform win32-g++ \
|
||||||
-device-option CROSS_COMPILE=/usr/bin/x86_64-w64-mingw32- \
|
-device-option CROSS_COMPILE=/usr/bin/x86_64-w64-mingw32- \
|
||||||
-I $(pwd)/qtbase/src/3rdparty/angle/include \
|
-I $(pwd)/qtbase/src/3rdparty/angle/include \
|
||||||
-opensource -confirm-license -release -static -static-runtime -no-opengl \
|
-opensource -confirm-license -release -static -static-runtime -no-opengl \
|
||||||
-no-avx -openssl -I /depends/x86_64-w64-mingw32/include -L /depends/x86_64-w64-mingw32/lib \
|
-no-avx -openssl-linked -I /depends/x86_64-w64-mingw32/include -L /depends/x86_64-w64-mingw32/lib \
|
||||||
-qt-freetype -qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \
|
-qt-freetype -qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \
|
||||||
-skip gamepad -skip location -skip qt3d -skip qtactiveqt -skip qtandroidextras \
|
-skip gamepad -skip location -skip qt3d -skip qtactiveqt -skip qtandroidextras \
|
||||||
-skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdoc \
|
-skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdoc \
|
||||||
|
@ -122,11 +122,11 @@ RUN wget https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.16.tar.gz && \
|
||||||
rm -rf $(pwd)
|
rm -rf $(pwd)
|
||||||
|
|
||||||
# OpenSSL -> Tor
|
# OpenSSL -> Tor
|
||||||
RUN wget https://www.openssl.org/source/openssl-1.1.1.k.tar.gz && \
|
RUN wget https://www.openssl.org/source/openssl-1.1.1k.tar.gz && \
|
||||||
echo "892a0875b9872acd04a9fde79b1f943075d5ea162415de3047c327df33fbaee5 openssl-1.1.1.k.tar.gz" | sha256sum -c && \
|
echo "892a0875b9872acd04a9fde79b1f943075d5ea162415de3047c327df33fbaee5 openssl-1.1.1k.tar.gz" | sha256sum -c && \
|
||||||
tar -xzf openssl-1.1.1.k.tar.gz && \
|
tar -xzf openssl-1.1.1k.tar.gz && \
|
||||||
rm openssl-1.1.1.k.tar.gz && \
|
rm openssl-1.1.1k.tar.gz && \
|
||||||
cd openssl-1.1.1.k && \
|
cd openssl-1.1.1k && \
|
||||||
./Configure mingw64 no-shared no-dso --cross-compile-prefix=x86_64-w64-mingw32- --prefix=/usr/local/openssl && \
|
./Configure mingw64 no-shared no-dso --cross-compile-prefix=x86_64-w64-mingw32- --prefix=/usr/local/openssl && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install_sw && \
|
make -j$THREADS install_sw && \
|
||||||
|
@ -180,3 +180,13 @@ RUN git clone https://git.featherwallet.org/feather/monero-seed.git && \
|
||||||
make -Cbuild -j$THREADS && \
|
make -Cbuild -j$THREADS && \
|
||||||
make -Cbuild install && \
|
make -Cbuild install && \
|
||||||
rm -rf $(pwd)
|
rm -rf $(pwd)
|
||||||
|
|
||||||
|
RUN git clone https://github.com/nih-at/libzip.git && \
|
||||||
|
cd libzip && \
|
||||||
|
git reset --hard 66e496489bdae81bfda8b0088172871d8fda0032 && \
|
||||||
|
cmake -DCMAKE_INSTALL_PREFIX=/depends/x86_64-w64-mingw32 \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=/depends/x86_64-w64-mingw32/share/toolchain.cmake \
|
||||||
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
|
-DCMAKE_PREFIX_PATH=/usr/x86_64-w64-mingw32 && \
|
||||||
|
make -j$THREADS && \
|
||||||
|
make -j$THREADS install
|
|
@ -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:
|
There are some Monero/Feather related options/definitions that you may pass:
|
||||||
|
|
||||||
|
- `-DLOCALMONERO=OFF` - disable LocalMonero feature
|
||||||
- `-DXMRIG=OFF` - disable XMRig feature
|
- `-DXMRIG=OFF` - disable XMRig feature
|
||||||
- `-DTOR_BIN=/path/to/tor` - Embed a Tor executable inside Feather
|
- `-DTOR_BIN=/path/to/tor` - Embed a Tor executable inside Feather
|
||||||
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
|
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
|
||||||
|
- `-DCHECK_UPDATES=ON` - enable checking for updates, only for standalone binaries
|
||||||
|
|
||||||
And:
|
And:
|
||||||
|
|
||||||
|
|
6
Makefile
|
@ -30,7 +30,9 @@ CMAKEFLAGS = \
|
||||||
-DARCH=x86_64 \
|
-DARCH=x86_64 \
|
||||||
-DBUILD_64=On \
|
-DBUILD_64=On \
|
||||||
-DBUILD_TESTS=Off \
|
-DBUILD_TESTS=Off \
|
||||||
|
-DLOCALMONERO=On \
|
||||||
-DXMRIG=On \
|
-DXMRIG=On \
|
||||||
|
-DCHECK_UPDATES=Off \
|
||||||
-DTOR_BIN=Off \
|
-DTOR_BIN=Off \
|
||||||
-DCMAKE_CXX_STANDARD=11 \
|
-DCMAKE_CXX_STANDARD=11 \
|
||||||
-DCMAKE_VERBOSE_MAKEFILE=On \
|
-DCMAKE_VERBOSE_MAKEFILE=On \
|
||||||
|
@ -42,6 +44,7 @@ CMAKEFLAGS = \
|
||||||
|
|
||||||
release-static: CMAKEFLAGS += -DBUILD_TAG="linux-x64"
|
release-static: CMAKEFLAGS += -DBUILD_TAG="linux-x64"
|
||||||
release-static: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
|
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 += -DCMAKE_BUILD_TYPE=Release
|
||||||
release-static: CMAKEFLAGS += -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF)
|
release-static: CMAKEFLAGS += -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF)
|
||||||
release-static:
|
release-static:
|
||||||
|
@ -50,10 +53,11 @@ release-static:
|
||||||
|
|
||||||
depends:
|
depends:
|
||||||
mkdir -p build/$(target)/release
|
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 += -DSTATIC=Off
|
||||||
mac-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},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 += -DBUILD_TAG="mac-x64"
|
||||||
mac-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
|
mac-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
|
||||||
mac-release:
|
mac-release:
|
||||||
|
|
11
PKGBUILD
|
@ -2,24 +2,19 @@
|
||||||
# Contributor: wowario <wowario[at]protonmail[dot]com>
|
# Contributor: wowario <wowario[at]protonmail[dot]com>
|
||||||
|
|
||||||
pkgname='monero-feather-git'
|
pkgname='monero-feather-git'
|
||||||
pkgver=0.1.0.925ef5683
|
pkgver=0.5.0.d1bb4c143f
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc='a free Monero desktop wallet'
|
pkgdesc='a free Monero desktop wallet'
|
||||||
license=('BSD')
|
license=('BSD')
|
||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
url="https://featherwallet.org"
|
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')
|
makedepends=('git' 'cmake' 'boost')
|
||||||
|
|
||||||
source=("${pkgname}"::"git+https://git.featherwallet.org/feather/feather")
|
source=("${pkgname}"::"git+https://git.featherwallet.org/feather/feather")
|
||||||
|
|
||||||
sha256sums=('SKIP')
|
sha256sums=('SKIP')
|
||||||
|
|
||||||
pkgver() {
|
|
||||||
cd "${srcdir}/${pkgname}"
|
|
||||||
printf "%s.%s" "$(git describe --tags --abbrev=0)" "$(git rev-parse --short=9 HEAD)"
|
|
||||||
}
|
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "${srcdir}/${pkgname}"
|
cd "${srcdir}/${pkgname}"
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
|
@ -32,4 +27,6 @@ build() {
|
||||||
package_monero-feather-git() {
|
package_monero-feather-git() {
|
||||||
install -Dm644 "${srcdir}/${pkgname}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
install -Dm644 "${srcdir}/${pkgname}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
||||||
install -Dm755 "${srcdir}/${pkgname}/build/bin/feather" "${pkgdir}/usr/bin/feather"
|
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 36fb05da3394505f8033ceb8806b28909617696f
|
|
@ -18,6 +18,8 @@ qt5_add_resources(RESOURCES assets.qrc)
|
||||||
file(GLOB SOURCE_FILES
|
file(GLOB SOURCE_FILES
|
||||||
"*.h"
|
"*.h"
|
||||||
"*.cpp"
|
"*.cpp"
|
||||||
|
"api/*.h"
|
||||||
|
"api/*.cpp"
|
||||||
"utils/*.h"
|
"utils/*.h"
|
||||||
"utils/*.cpp"
|
"utils/*.cpp"
|
||||||
"libwalletqt/*.h"
|
"libwalletqt/*.h"
|
||||||
|
@ -116,12 +118,22 @@ target_include_directories(feather PUBLIC
|
||||||
${Qt5Svg_INCLUDE_DIRS}
|
${Qt5Svg_INCLUDE_DIRS}
|
||||||
${Qt5Xml_INCLUDE_DIRS}
|
${Qt5Xml_INCLUDE_DIRS}
|
||||||
${Qt5WebSockets_INCLUDE_DIRS}
|
${Qt5WebSockets_INCLUDE_DIRS}
|
||||||
|
${ZLIB_INCLUDE_DIRS}
|
||||||
|
${LIBZIP_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(DONATE_BEG)
|
if(DONATE_BEG)
|
||||||
target_compile_definitions(feather PRIVATE DONATE_BEG=1)
|
target_compile_definitions(feather PRIVATE DONATE_BEG=1)
|
||||||
endif()
|
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)
|
if(TOR_BIN)
|
||||||
target_compile_definitions(feather PRIVATE HAS_TOR_BIN=1)
|
target_compile_definitions(feather PRIVATE HAS_TOR_BIN=1)
|
||||||
endif()
|
endif()
|
||||||
|
@ -130,6 +142,9 @@ if(XMRIG)
|
||||||
target_compile_definitions(feather PRIVATE HAS_XMRIG=1)
|
target_compile_definitions(feather PRIVATE HAS_XMRIG=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# TODO: PLACEHOLDER
|
||||||
|
target_compile_definitions(feather PRIVATE HAS_WEBSOCKET=1)
|
||||||
|
|
||||||
if(HAVE_SYS_PRCTL_H)
|
if(HAVE_SYS_PRCTL_H)
|
||||||
target_compile_definitions(feather PRIVATE HAVE_SYS_PRCTL_H=1)
|
target_compile_definitions(feather PRIVATE HAVE_SYS_PRCTL_H=1)
|
||||||
endif()
|
endif()
|
||||||
|
@ -191,6 +206,8 @@ target_link_libraries(feather
|
||||||
openpgp
|
openpgp
|
||||||
Threads::Threads
|
Threads::Threads
|
||||||
${QRENCODE_LIBRARY}
|
${QRENCODE_LIBRARY}
|
||||||
|
${ZLIB_LIBRARIES}
|
||||||
|
${LIBZIP_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(APPLE)
|
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() && amount != "0")
|
||||||
|
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 "libwalletqt/Coins.h"
|
||||||
#include "model/TransactionHistoryModel.h"
|
#include "model/TransactionHistoryModel.h"
|
||||||
#include "model/SubaddressModel.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;
|
WalletKeysFilesModel *AppContext::wallets = nullptr;
|
||||||
TxFiatHistory *AppContext::txFiatHistory = nullptr;
|
|
||||||
double AppContext::balance = 0;
|
|
||||||
QMap<QString, QString> AppContext::txCache;
|
QMap<QString, QString> AppContext::txCache;
|
||||||
|
|
||||||
AppContext::AppContext(QCommandLineParser *cmdargs) {
|
AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||||
this->network = new QNetworkAccessManager();
|
|
||||||
this->networkClearnet = new QNetworkAccessManager();
|
|
||||||
this->cmdargs = cmdargs;
|
this->cmdargs = cmdargs;
|
||||||
|
|
||||||
this->isTorSocks = Utils::isTorsocks();
|
|
||||||
this->isTails = TailsOS::detect();
|
this->isTails = TailsOS::detect();
|
||||||
this->isWhonix = WhonixOS::detect();
|
this->isWhonix = WhonixOS::detect();
|
||||||
|
|
||||||
|
@ -45,21 +41,20 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||||
|
|
||||||
// ----------------- Network Type -----------------
|
// ----------------- Network Type -----------------
|
||||||
|
|
||||||
if (this->cmdargs->isSet("stagenet"))
|
if (this->cmdargs->isSet("stagenet")) {
|
||||||
this->networkType = NetworkType::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;
|
this->networkType = NetworkType::TESTNET;
|
||||||
else
|
config()->set(Config::networkType, NetworkType::TESTNET);
|
||||||
|
}
|
||||||
|
else {
|
||||||
this->networkType = NetworkType::MAINNET;
|
this->networkType = NetworkType::MAINNET;
|
||||||
|
config()->set(Config::networkType, NetworkType::MAINNET);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->nodes = new Nodes(this, this);
|
||||||
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);
|
|
||||||
|
|
||||||
// Store the wallet every 2 minutes
|
// Store the wallet every 2 minutes
|
||||||
m_storeTimer.start(2 * 60 * 1000);
|
m_storeTimer.start(2 * 60 * 1000);
|
||||||
|
@ -67,31 +62,6 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||||
this->storeWallet();
|
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();
|
this->walletManager = WalletManager::instance();
|
||||||
QString logPath = QString("%1/daemon.log").arg(configDir);
|
QString logPath = QString("%1/daemon.log").arg(configDir);
|
||||||
Monero::Utils::onStartup();
|
Monero::Utils::onStartup();
|
||||||
|
@ -108,23 +78,34 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||||
|
|
||||||
// libwallet connects
|
// libwallet connects
|
||||||
connect(this->walletManager, &WalletManager::walletOpened, this, &AppContext::onWalletOpened);
|
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);
|
||||||
|
|
||||||
|
m_rpc = new DaemonRpc{this, getNetworkTor(), ""};
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::initTor() {
|
void AppContext::initTor() {
|
||||||
this->tor = new Tor(this, this);
|
if (this->cmdargs->isSet("tor-host"))
|
||||||
this->tor->start();
|
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)) {
|
torManager()->init();
|
||||||
this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort);
|
torManager()->start();
|
||||||
this->network->setProxy(*networkProxy);
|
|
||||||
if (globals::websocketUrl.host().endsWith(".onion")) {
|
connect(torManager(), &TorManager::connectionStateChanged, &websocketNotifier()->websocketClient, &WebsocketClient::onToggleConnect);
|
||||||
this->ws->webSocket.setProxy(*networkProxy);
|
|
||||||
}
|
this->onTorSettingsChanged();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::initWS() {
|
void AppContext::initWS() {
|
||||||
this->ws->start();
|
websocketNotifier()->websocketClient.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address) {
|
void AppContext::onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address) {
|
||||||
|
@ -134,7 +115,7 @@ void AppContext::onCancelTransaction(PendingTransaction *tx, const QVector<QStri
|
||||||
this->currentWallet->disposeTransaction(tx);
|
this->currentWallet->disposeTransaction(tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) const {
|
void AppContext::onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) {
|
||||||
if(this->currentWallet == nullptr){
|
if(this->currentWallet == nullptr){
|
||||||
qCritical() << "Cannot create transaction; no wallet loaded";
|
qCritical() << "Cannot create transaction; no wallet loaded";
|
||||||
return;
|
return;
|
||||||
|
@ -146,6 +127,8 @@ void AppContext::onSweepOutput(const QString &keyImage, QString address, bool ch
|
||||||
|
|
||||||
qCritical() << "Creating transaction";
|
qCritical() << "Creating transaction";
|
||||||
this->currentWallet->createTransactionSingleAsync(keyImage, address, outputs, this->tx_priority);
|
this->currentWallet->createTransactionSingleAsync(keyImage, address, outputs, this->tx_priority);
|
||||||
|
|
||||||
|
emit initiateTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all) {
|
void AppContext::onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all) {
|
||||||
|
@ -249,6 +232,17 @@ void AppContext::onOpenWallet(const QString &path, const QString &password){
|
||||||
this->walletManager->openWalletAsync(path, password, this->networkType, 1);
|
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) {
|
void AppContext::onPreferredFiatCurrencyChanged(const QString &symbol) {
|
||||||
if(this->currentWallet) {
|
if(this->currentWallet) {
|
||||||
auto *model = this->currentWallet->transactionHistoryModel();
|
auto *model = this->currentWallet->transactionHistoryModel();
|
||||||
|
@ -276,9 +270,6 @@ void AppContext::commitTransaction(PendingTransaction *tx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::onMultiBroadcast(PendingTransaction *tx) {
|
void AppContext::onMultiBroadcast(PendingTransaction *tx) {
|
||||||
UtilsNetworking *net = new UtilsNetworking(this->network, this);
|
|
||||||
DaemonRpc *rpc = new DaemonRpc(this, net, "");
|
|
||||||
|
|
||||||
int count = tx->txCount();
|
int count = tx->txCount();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
QString txData = tx->signedTxToHex(i);
|
QString txData = tx->signedTxToHex(i);
|
||||||
|
@ -286,40 +277,81 @@ void AppContext::onMultiBroadcast(PendingTransaction *tx) {
|
||||||
for (const auto& node: this->nodes->websocketNodes()) {
|
for (const auto& node: this->nodes->websocketNodes()) {
|
||||||
if (!node.online) continue;
|
if (!node.online) continue;
|
||||||
|
|
||||||
QString address = node.as_url();
|
QString address = node.toURL();
|
||||||
qDebug() << QString("Relaying %1 to: %2").arg(tx->txid()[i], address);
|
qDebug() << QString("Relaying %1 to: %2").arg(tx->txid()[i], address);
|
||||||
rpc->setDaemonAddress(address);
|
m_rpc->setDaemonAddress(address);
|
||||||
rpc->sendRawTransaction(txData);
|
m_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) {
|
void AppContext::onWalletOpened(Wallet *wallet) {
|
||||||
auto state = wallet->status();
|
auto state = wallet->status();
|
||||||
if (state != Wallet::Status_Ok) {
|
if (state != Wallet::Status_Ok) {
|
||||||
auto errMsg = wallet->errorString();
|
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;
|
qCritical() << errMsg;
|
||||||
this->walletManager->clearWalletCache(this->walletPath);
|
this->walletManager->clearWalletCache(this->walletPath);
|
||||||
errMsg = QString("%1\n\nAttempted to clean wallet cache. Please restart Feather.").arg(errMsg);
|
errMsg = QString("%1\n\nAttempted to clean wallet cache. Please restart Feather.").arg(errMsg);
|
||||||
this->closeWallet(false);
|
this->closeWallet(false);
|
||||||
emit walletOpenedError(errMsg);
|
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 {
|
} else {
|
||||||
this->closeWallet(false);
|
this->closeWallet(false);
|
||||||
emit walletOpenPasswordNeeded(!this->walletPassword.isEmpty(), wallet->path());
|
emit walletOpenedError(errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_openWalletTriedOnce = false;
|
||||||
this->refreshed = false;
|
this->refreshed = false;
|
||||||
this->currentWallet = wallet;
|
this->currentWallet = wallet;
|
||||||
this->walletPath = this->currentWallet->path() + ".keys";
|
this->walletPath = this->currentWallet->path() + ".keys";
|
||||||
|
this->walletPassword = this->currentWallet->getPassword();
|
||||||
config()->set(Config::walletPath, this->walletPath);
|
config()->set(Config::walletPath, this->walletPath);
|
||||||
|
|
||||||
connect(this->currentWallet, &Wallet::moneySpent, this, &AppContext::onMoneySpent);
|
connect(this->currentWallet, &Wallet::moneySpent, this, &AppContext::onMoneySpent);
|
||||||
|
@ -331,6 +363,8 @@ void AppContext::onWalletOpened(Wallet *wallet) {
|
||||||
connect(this->currentWallet, &Wallet::transactionCommitted, this, &AppContext::onTransactionCommitted);
|
connect(this->currentWallet, &Wallet::transactionCommitted, this, &AppContext::onTransactionCommitted);
|
||||||
connect(this->currentWallet, &Wallet::heightRefreshed, this, &AppContext::onHeightRefreshed);
|
connect(this->currentWallet, &Wallet::heightRefreshed, this, &AppContext::onHeightRefreshed);
|
||||||
connect(this->currentWallet, &Wallet::transactionCreated, this, &AppContext::onTransactionCreated);
|
connect(this->currentWallet, &Wallet::transactionCreated, this, &AppContext::onTransactionCreated);
|
||||||
|
connect(this->currentWallet, &Wallet::deviceError, this, &AppContext::onDeviceError);
|
||||||
|
connect(this->currentWallet, &Wallet::deviceButtonRequest, this, &AppContext::onDeviceButtonRequest);
|
||||||
|
|
||||||
emit walletOpened();
|
emit walletOpened();
|
||||||
|
|
||||||
|
@ -346,142 +380,6 @@ void AppContext::onWalletOpened(Wallet *wallet) {
|
||||||
|
|
||||||
// force trigger preferredFiat signal for history model
|
// force trigger preferredFiat signal for history model
|
||||||
this->onPreferredFiatCurrencyChanged(config()->get(Config::preferredFiatCurrency).toString());
|
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) {
|
void AppContext::createConfigDirectory(const QString &dir) {
|
||||||
|
@ -528,7 +426,18 @@ void AppContext::createWallet(FeatherSeed seed, const QString &path, const QStri
|
||||||
return;
|
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) {
|
void AppContext::createWalletFromKeys(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight, bool deterministic) {
|
||||||
|
@ -539,7 +448,7 @@ void AppContext::createWalletFromKeys(const QString &path, const QString &passwo
|
||||||
return;
|
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);
|
auto err = QString("Failed to create wallet. Invalid address provided.").arg(path);
|
||||||
qCritical() << err;
|
qCritical() << err;
|
||||||
emit walletCreatedError(err);
|
emit walletCreatedError(err);
|
||||||
|
@ -560,21 +469,8 @@ void AppContext::createWalletFromKeys(const QString &path, const QString &passwo
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->currentWallet = this->walletManager->createWalletFromKeys(path, this->seedLanguage, this->networkType, address, viewkey, spendkey, restoreHeight);
|
Wallet *wallet = this->walletManager->createWalletFromKeys(path, password, this->seedLanguage, this->networkType, address, viewkey, spendkey, restoreHeight);
|
||||||
this->createWalletFinish(password);
|
this->walletManager->walletOpened(wallet);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::onSetRestoreHeight(quint64 height){
|
void AppContext::onSetRestoreHeight(quint64 height){
|
||||||
|
@ -643,7 +539,7 @@ void AppContext::donateBeg() {
|
||||||
if (this->currentWallet->viewOnly()) return;
|
if (this->currentWallet->viewOnly()) return;
|
||||||
|
|
||||||
auto donationCounter = config()->get(Config::donateBeg).toInt();
|
auto donationCounter = config()->get(Config::donateBeg).toInt();
|
||||||
if(donationCounter == -1)
|
if (donationCounter == -1)
|
||||||
return; // previously donated
|
return; // previously donated
|
||||||
|
|
||||||
donationCounter += 1;
|
donationCounter += 1;
|
||||||
|
@ -688,7 +584,13 @@ void AppContext::onWalletUpdate() {
|
||||||
this->updateBalance();
|
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) {
|
if (!this->refreshed) {
|
||||||
refreshModels();
|
refreshModels();
|
||||||
this->refreshed = true;
|
this->refreshed = true;
|
||||||
|
@ -727,6 +629,8 @@ void AppContext::onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, q
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::onTransactionCreated(PendingTransaction *tx, const QVector<QString> &address) {
|
void AppContext::onTransactionCreated(PendingTransaction *tx, const QVector<QString> &address) {
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
|
||||||
for (auto &addr : address) {
|
for (auto &addr : address) {
|
||||||
if (addr == globals::donationAddress) {
|
if (addr == globals::donationAddress) {
|
||||||
this->donationSending = true;
|
this->donationSending = true;
|
||||||
|
@ -778,11 +682,10 @@ void AppContext::updateBalance() {
|
||||||
if (!this->currentWallet)
|
if (!this->currentWallet)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
quint64 balance_u = this->currentWallet->balance();
|
quint64 balance = this->currentWallet->balance();
|
||||||
AppContext::balance = balance_u / globals::cdiv;
|
quint64 spendable = this->currentWallet->unlockedBalance();
|
||||||
double spendable = this->currentWallet->unlockedBalance();
|
|
||||||
|
|
||||||
emit balanceUpdated(balance_u, spendable);
|
emit balanceUpdated(balance, spendable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::syncStatusUpdated(quint64 height, quint64 target) {
|
void AppContext::syncStatusUpdated(quint64 height, quint64 target) {
|
||||||
|
|
|
@ -5,16 +5,13 @@
|
||||||
#define FEATHER_APPCONTEXT_H
|
#define FEATHER_APPCONTEXT_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QProcess>
|
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "utils/tails.h"
|
#include "utils/tails.h"
|
||||||
#include "utils/whonix.h"
|
#include "utils/whonix.h"
|
||||||
#include "utils/prices.h"
|
#include "utils/prices.h"
|
||||||
#include "utils/networking.h"
|
#include "utils/networking.h"
|
||||||
#include "utils/tor.h"
|
#include "utils/TorManager.h"
|
||||||
#include "utils/xmrig.h"
|
|
||||||
#include "utils/wsclient.h"
|
#include "utils/wsclient.h"
|
||||||
#include "utils/txfiathistory.h"
|
#include "utils/txfiathistory.h"
|
||||||
#include "utils/FeatherSeed.h"
|
#include "utils/FeatherSeed.h"
|
||||||
|
@ -28,9 +25,6 @@
|
||||||
#include "utils/keysfiles.h"
|
#include "utils/keysfiles.h"
|
||||||
#include "PendingTransaction.h"
|
#include "PendingTransaction.h"
|
||||||
|
|
||||||
#define SUBADDRESS_LOOKAHEAD_MINOR 200
|
|
||||||
|
|
||||||
|
|
||||||
class AppContext : public QObject
|
class AppContext : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -43,7 +37,6 @@ public:
|
||||||
|
|
||||||
bool isTails = false;
|
bool isTails = false;
|
||||||
bool isWhonix = false;
|
bool isWhonix = false;
|
||||||
bool isTorSocks = false;
|
|
||||||
|
|
||||||
bool donationSending = false;
|
bool donationSending = false;
|
||||||
|
|
||||||
|
@ -54,25 +47,13 @@ public:
|
||||||
QString walletPassword = "";
|
QString walletPassword = "";
|
||||||
NetworkType::Type networkType;
|
NetworkType::Type networkType;
|
||||||
|
|
||||||
QMap<NetworkType::Type, int> heights;
|
|
||||||
QMap<NetworkType::Type, RestoreHeightLookup*> restoreHeights;
|
|
||||||
PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low;
|
PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low;
|
||||||
QString seedLanguage = "English"; // 14 word `monero-seed` only has English
|
QString seedLanguage = "English"; // 14 word `monero-seed` only has English
|
||||||
|
|
||||||
QNetworkAccessManager *network;
|
Nodes *nodes; // TODO: move this to mainwindow (?)
|
||||||
QNetworkAccessManager *networkClearnet;
|
|
||||||
QNetworkProxy *networkProxy{};
|
|
||||||
|
|
||||||
Tor *tor{};
|
|
||||||
WSClient *ws;
|
|
||||||
XmRig *XMRig;
|
|
||||||
Nodes *nodes;
|
|
||||||
DaemonRpc *daemonRpc;
|
|
||||||
static Prices *prices;
|
|
||||||
static WalletKeysFilesModel *wallets;
|
static WalletKeysFilesModel *wallets;
|
||||||
static double balance;
|
|
||||||
static QMap<QString, QString> txCache;
|
static QMap<QString, QString> txCache;
|
||||||
static TxFiatHistory *txFiatHistory;
|
|
||||||
|
|
||||||
static void createConfigDirectory(const QString &dir);
|
static void createConfigDirectory(const QString &dir);
|
||||||
|
|
||||||
|
@ -81,17 +62,15 @@ public:
|
||||||
WalletManager *walletManager;
|
WalletManager *walletManager;
|
||||||
Wallet *currentWallet = nullptr;
|
Wallet *currentWallet = nullptr;
|
||||||
void createWallet(FeatherSeed seed, const QString &path, const QString &password, const QString &seedOffset = "");
|
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 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 commitTransaction(PendingTransaction *tx);
|
||||||
void syncStatusUpdated(quint64 height, quint64 target);
|
void syncStatusUpdated(quint64 height, quint64 target);
|
||||||
void updateBalance();
|
void updateBalance();
|
||||||
void initTor();
|
void initTor();
|
||||||
void initRestoreHeights();
|
|
||||||
void initWS();
|
void initWS();
|
||||||
void donateBeg();
|
void donateBeg();
|
||||||
void refreshModels();
|
void refreshModels();
|
||||||
void setWindowTitle(bool mining = false);
|
|
||||||
|
|
||||||
// Closes the currently opened wallet
|
// Closes the currently opened wallet
|
||||||
void closeWallet(bool emitClosedSignal = true, bool storeWallet = false);
|
void closeWallet(bool emitClosedSignal = true, bool storeWallet = false);
|
||||||
|
@ -99,28 +78,28 @@ public:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void onOpenWallet(const QString& path, const QString &password);
|
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 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 onCreateTransactionMultiDest(const QVector<QString> &addresses, const QVector<quint64> &amounts, const QString &description);
|
||||||
void onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address);
|
void onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address);
|
||||||
void onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) const;
|
void onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs);
|
||||||
void onCreateTransactionError(const QString &msg);
|
void onCreateTransactionError(const QString &msg);
|
||||||
void onOpenAliasResolve(const QString &openAlias);
|
void onOpenAliasResolve(const QString &openAlias);
|
||||||
void onSetRestoreHeight(quint64 height);
|
void onSetRestoreHeight(quint64 height);
|
||||||
void onPreferredFiatCurrencyChanged(const QString &symbol);
|
void onPreferredFiatCurrencyChanged(const QString &symbol);
|
||||||
void onAmountPrecisionChanged(int precision);
|
void onAmountPrecisionChanged(int precision);
|
||||||
void onMultiBroadcast(PendingTransaction *tx);
|
void onMultiBroadcast(PendingTransaction *tx);
|
||||||
|
void onDeviceButtonRequest(quint64 code);
|
||||||
|
void onTorSettingsChanged();
|
||||||
|
void onInitialNetworkConfigured();
|
||||||
|
void onDeviceError(const QString &message);
|
||||||
|
|
||||||
private slots:
|
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 onMoneySpent(const QString &txId, quint64 amount);
|
||||||
void onMoneyReceived(const QString &txId, quint64 amount);
|
void onMoneyReceived(const QString &txId, quint64 amount);
|
||||||
void onUnconfirmedMoneyReceived(const QString &txId, quint64 amount);
|
void onUnconfirmedMoneyReceived(const QString &txId, quint64 amount);
|
||||||
void onWalletUpdate();
|
void onWalletUpdate();
|
||||||
void onWalletRefreshed(bool success);
|
void onWalletRefreshed(bool success, const QString &message);
|
||||||
void onWalletOpened(Wallet *wallet);
|
void onWalletOpened(Wallet *wallet);
|
||||||
void onWalletNewBlock(quint64 blockheight, quint64 targetHeight);
|
void onWalletNewBlock(quint64 blockheight, quint64 targetHeight);
|
||||||
void onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight);
|
void onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight);
|
||||||
|
@ -148,12 +127,6 @@ signals:
|
||||||
void createTransactionError(QString message);
|
void createTransactionError(QString message);
|
||||||
void createTransactionCancelled(const QVector<QString> &address, double amount);
|
void createTransactionCancelled(const QVector<QString> &address, double amount);
|
||||||
void createTransactionSuccess(PendingTransaction *tx, const QVector<QString> &address);
|
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 openAliasResolveError(const QString &msg);
|
||||||
void openAliasResolved(const QString &address, const QString &openAlias);
|
void openAliasResolved(const QString &address, const QString &openAlias);
|
||||||
void setRestoreHeightError(const QString &msg);
|
void setRestoreHeightError(const QString &msg);
|
||||||
|
@ -162,10 +135,14 @@ signals:
|
||||||
void donationNag();
|
void donationNag();
|
||||||
void initiateTransaction();
|
void initiateTransaction();
|
||||||
void endTransaction();
|
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:
|
private:
|
||||||
|
DaemonRpc *m_rpc;
|
||||||
QTimer m_storeTimer;
|
QTimer m_storeTimer;
|
||||||
|
bool m_openWalletTriedOnce = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FEATHER_APPCONTEXT_H
|
#endif //FEATHER_APPCONTEXT_H
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<file>assets/contributors.txt</file>
|
<file>assets/contributors.txt</file>
|
||||||
<file>assets/feather.desktop</file>
|
<file>assets/feather.desktop</file>
|
||||||
<file>assets/nodes.json</file>
|
<file>assets/nodes.json</file>
|
||||||
|
<file>assets/gpg_keys/featherwallet.asc</file>
|
||||||
<file>assets/images/appicons/32x32.png</file>
|
<file>assets/images/appicons/32x32.png</file>
|
||||||
<file>assets/images/appicons/48x48.png</file>
|
<file>assets/images/appicons/48x48.png</file>
|
||||||
<file>assets/images/appicons/64x64.png</file>
|
<file>assets/images/appicons/64x64.png</file>
|
||||||
|
@ -44,10 +45,18 @@
|
||||||
<file>assets/images/gnome-calc.png</file>
|
<file>assets/images/gnome-calc.png</file>
|
||||||
<file>assets/images/history.png</file>
|
<file>assets/images/history.png</file>
|
||||||
<file>assets/images/info.png</file>
|
<file>assets/images/info.png</file>
|
||||||
|
<file>assets/images/info2.svg</file>
|
||||||
<file>assets/images/key.png</file>
|
<file>assets/images/key.png</file>
|
||||||
<file>assets/images/ledger.png</file>
|
<file>assets/images/ledger.png</file>
|
||||||
<file>assets/images/ledger_unpaired.png</file>
|
<file>assets/images/ledger_unpaired.png</file>
|
||||||
<file>assets/images/lightning.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.png</file>
|
||||||
<file>assets/images/lock_icon.png</file>
|
<file>assets/images/lock_icon.png</file>
|
||||||
<file>assets/images/lock.svg</file>
|
<file>assets/images/lock.svg</file>
|
||||||
|
@ -63,6 +72,12 @@
|
||||||
<file>assets/images/revealer_c.png</file>
|
<file>assets/images/revealer_c.png</file>
|
||||||
<file>assets/images/revealer.png</file>
|
<file>assets/images/revealer.png</file>
|
||||||
<file>assets/images/seal.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/seed.png</file>
|
||||||
<file>assets/images/speaker.png</file>
|
<file>assets/images/speaker.png</file>
|
||||||
<file>assets/images/status_connected_fork.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 "calcwidget.h"
|
||||||
#include "ui_calcwidget.h"
|
#include "ui_calcwidget.h"
|
||||||
#include "mainwindow.h"
|
|
||||||
#include "components.h"
|
|
||||||
#include "utils/ColorScheme.h"
|
#include "utils/ColorScheme.h"
|
||||||
|
#include "utils/AppData.h"
|
||||||
|
#include "utils/config.h"
|
||||||
|
|
||||||
CalcWidget::CalcWidget(QWidget *parent) :
|
CalcWidget::CalcWidget(QWidget *parent) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
ui(new Ui::CalcWidget)
|
ui(new Ui::CalcWidget)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
m_ctx = MainWindow::getContext();
|
|
||||||
|
|
||||||
ui->imageExchange->setBackgroundRole(QPalette::Base);
|
ui->imageExchange->setBackgroundRole(QPalette::Base);
|
||||||
ui->imageExchange->setAssets(":/assets/images/exchange.png", ":/assets/images/exchange_white.png");
|
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->lineFrom->setValidator(dv);
|
||||||
ui->lineTo->setValidator(dv);
|
ui->lineTo->setValidator(dv);
|
||||||
|
|
||||||
connect(AppContext::prices, &Prices::fiatPricesUpdated, this, &CalcWidget::initFiat);
|
connect(&appData()->prices, &Prices::fiatPricesUpdated, this, &CalcWidget::initFiat);
|
||||||
connect(AppContext::prices, &Prices::cryptoPricesUpdated, this, &CalcWidget::initCrypto);
|
connect(&appData()->prices, &Prices::cryptoPricesUpdated, this, &CalcWidget::initCrypto);;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalcWidget::fromChanged(const QString &data) {
|
void CalcWidget::fromChanged(const QString &data) {
|
||||||
|
@ -56,12 +55,12 @@ void CalcWidget::fromChanged(const QString &data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
double amount = amount_str.toDouble();
|
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;
|
this->m_changing = true;
|
||||||
|
|
||||||
int precision = 10;
|
int precision = 10;
|
||||||
if(AppContext::prices->rates.contains(symbolTo))
|
if (appData()->prices.rates.contains(symbolTo))
|
||||||
precision = 2;
|
precision = 2;
|
||||||
|
|
||||||
ui->lineTo->setText(QString::number(result, 'f', precision));
|
ui->lineTo->setText(QString::number(result, 'f', precision));
|
||||||
|
@ -91,12 +90,12 @@ void CalcWidget::toChanged(const QString &data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
double amount = amount_str.toDouble();
|
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;
|
this->m_changing = true;
|
||||||
|
|
||||||
int precision = 10;
|
int precision = 10;
|
||||||
if(AppContext::prices->rates.contains(symbolFrom))
|
if(appData()->prices.rates.contains(symbolFrom))
|
||||||
precision = 2;
|
precision = 2;
|
||||||
|
|
||||||
ui->lineFrom->setText(QString::number(result, 'f', precision));
|
ui->lineFrom->setText(QString::number(result, 'f', precision));
|
||||||
|
@ -116,8 +115,8 @@ void CalcWidget::initFiat() {
|
||||||
|
|
||||||
void CalcWidget::initComboBox() {
|
void CalcWidget::initComboBox() {
|
||||||
if(m_comboBoxInit) return;
|
if(m_comboBoxInit) return;
|
||||||
QList<QString> marketsKeys = AppContext::prices->markets.keys();
|
QList<QString> marketsKeys = appData()->prices.markets.keys();
|
||||||
QList<QString> ratesKeys = AppContext::prices->rates.keys();
|
QList<QString> ratesKeys = appData()->prices.rates.keys();
|
||||||
if(marketsKeys.count() <= 0 || ratesKeys.count() <= 0) return;
|
if(marketsKeys.count() <= 0 || ratesKeys.count() <= 0) return;
|
||||||
|
|
||||||
ui->comboCalcFrom->addItems(marketsKeys);
|
ui->comboCalcFrom->addItems(marketsKeys);
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
#ifndef CALC_H
|
#ifndef CALC_H
|
||||||
#define CALC_H
|
#define CALC_H
|
||||||
|
|
||||||
#include <QMainWindow>
|
#include <QWidget>
|
||||||
#include "appcontext.h"
|
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class CalcWidget;
|
class CalcWidget;
|
||||||
|
@ -32,7 +31,7 @@ public slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::CalcWidget *ui;
|
Ui::CalcWidget *ui;
|
||||||
AppContext *m_ctx;
|
|
||||||
bool m_comboBoxInit = false;
|
bool m_comboBoxInit = false;
|
||||||
void initComboBox();
|
void initComboBox();
|
||||||
bool m_changing = false;
|
bool m_changing = false;
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include "calcwindow.h"
|
#include "calcwindow.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
#include "utils/AppData.h"
|
||||||
|
|
||||||
#include "ui_calcwindow.h"
|
#include "ui_calcwindow.h"
|
||||||
|
|
||||||
|
@ -14,10 +16,10 @@ CalcWindow::CalcWindow(QWidget *parent) :
|
||||||
this->setWindowFlags(flags|Qt::WindowStaysOnTopHint); // on top
|
this->setWindowFlags(flags|Qt::WindowStaysOnTopHint); // on top
|
||||||
|
|
||||||
ui->setupUi(this);
|
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(&appData()->prices, &Prices::fiatPricesUpdated, this, &CalcWindow::initFiat);
|
||||||
connect(AppContext::prices, &Prices::cryptoPricesUpdated, this, &CalcWindow::initCrypto);
|
connect(&appData()->prices, &Prices::cryptoPricesUpdated, this, &CalcWindow::initCrypto);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalcWindow::initFiat() {
|
void CalcWindow::initFiat() {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "dialog/outputinfodialog.h"
|
#include "dialog/outputinfodialog.h"
|
||||||
#include "dialog/outputsweepdialog.h"
|
#include "dialog/outputsweepdialog.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
@ -26,7 +27,7 @@ CoinsWidget::CoinsWidget(QWidget *parent)
|
||||||
connect(ui->coins->header(), &QHeaderView::customContextMenuRequested, this, &CoinsWidget::showHeaderMenu);
|
connect(ui->coins->header(), &QHeaderView::customContextMenuRequested, this, &CoinsWidget::showHeaderMenu);
|
||||||
|
|
||||||
// copy menu
|
// 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("Public key", this, [this]{copy(copyField::PubKey);});
|
||||||
m_copyMenu->addAction("Key Image", this, [this]{copy(copyField::KeyImage);});
|
m_copyMenu->addAction("Key Image", this, [this]{copy(copyField::KeyImage);});
|
||||||
m_copyMenu->addAction("Transaction ID", this, [this]{copy(copyField::TxID);});
|
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_freezeAllSelectedAction = new QAction("Freeze selected", this);
|
||||||
m_thawAllSelectedAction = new QAction("Thaw 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);
|
m_sweepOutputAction = new QAction("Sweep output", this);
|
||||||
connect(m_freezeOutputAction, &QAction::triggered, this, &CoinsWidget::freezeOutput);
|
connect(m_freezeOutputAction, &QAction::triggered, this, &CoinsWidget::freezeOutput);
|
||||||
connect(m_thawOutputAction, &QAction::triggered, this, &CoinsWidget::thawOutput);
|
connect(m_thawOutputAction, &QAction::triggered, this, &CoinsWidget::thawOutput);
|
||||||
|
@ -129,7 +130,7 @@ void CoinsWidget::setShowSpent(bool show)
|
||||||
void CoinsWidget::freezeOutput() {
|
void CoinsWidget::freezeOutput() {
|
||||||
QModelIndex index = ui->coins->currentIndex();
|
QModelIndex index = ui->coins->currentIndex();
|
||||||
QVector<int> indexes = {m_proxyModel->mapToSource(index).row()};
|
QVector<int> indexes = {m_proxyModel->mapToSource(index).row()};
|
||||||
emit freeze(indexes);
|
this->freezeCoins(indexes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoinsWidget::freezeAllSelected() {
|
void CoinsWidget::freezeAllSelected() {
|
||||||
|
@ -139,13 +140,13 @@ void CoinsWidget::freezeAllSelected() {
|
||||||
for (QModelIndex index: list) {
|
for (QModelIndex index: list) {
|
||||||
indexes.push_back(m_proxyModel->mapToSource(index).row()); // todo: will segfault if index get invalidated
|
indexes.push_back(m_proxyModel->mapToSource(index).row()); // todo: will segfault if index get invalidated
|
||||||
}
|
}
|
||||||
emit freeze(indexes);
|
this->freezeCoins(indexes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoinsWidget::thawOutput() {
|
void CoinsWidget::thawOutput() {
|
||||||
QModelIndex index = ui->coins->currentIndex();
|
QModelIndex index = ui->coins->currentIndex();
|
||||||
QVector<int> indexes = {m_proxyModel->mapToSource(index).row()};
|
QVector<int> indexes = {m_proxyModel->mapToSource(index).row()};
|
||||||
emit thaw(indexes);
|
this->thawCoins(indexes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoinsWidget::thawAllSelected() {
|
void CoinsWidget::thawAllSelected() {
|
||||||
|
@ -155,7 +156,7 @@ void CoinsWidget::thawAllSelected() {
|
||||||
for (QModelIndex index: list) {
|
for (QModelIndex index: list) {
|
||||||
indexes.push_back(m_proxyModel->mapToSource(index).row());
|
indexes.push_back(m_proxyModel->mapToSource(index).row());
|
||||||
}
|
}
|
||||||
emit thaw(indexes);
|
this->thawCoins(indexes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoinsWidget::viewOutput() {
|
void CoinsWidget::viewOutput() {
|
||||||
|
@ -181,7 +182,7 @@ void CoinsWidget::onSweepOutput() {
|
||||||
int ret = dialog->exec();
|
int ret = dialog->exec();
|
||||||
if (!ret) return;
|
if (!ret) return;
|
||||||
|
|
||||||
emit sweepOutput(keyImage, dialog->address(), dialog->churn(), dialog->outputs());
|
m_ctx->onSweepOutput(keyImage, dialog->address(), dialog->churn(), dialog->outputs());
|
||||||
dialog->deleteLater();
|
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() {
|
CoinsWidget::~CoinsWidget() {
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "model/CoinsProxyModel.h"
|
#include "model/CoinsProxyModel.h"
|
||||||
#include "libwalletqt/Coins.h"
|
#include "libwalletqt/Coins.h"
|
||||||
|
|
||||||
|
#include <QMenu>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QtSvg/QSvgWidget>
|
#include <QtSvg/QSvgWidget>
|
||||||
|
|
||||||
|
@ -38,12 +39,10 @@ private slots:
|
||||||
void viewOutput();
|
void viewOutput();
|
||||||
void onSweepOutput();
|
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:
|
private:
|
||||||
|
void freezeCoins(const QVector<int>& indexes);
|
||||||
|
void thawCoins(const QVector<int>& indexes);
|
||||||
|
|
||||||
enum copyField {
|
enum copyField {
|
||||||
PubKey = 0,
|
PubKey = 0,
|
||||||
KeyImage,
|
KeyImage,
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "model/ModelUtils.h"
|
#include "model/ModelUtils.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "libwalletqt/AddressBook.h"
|
#include "libwalletqt/AddressBook.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
@ -28,14 +29,14 @@ ContactsWidget::ContactsWidget(QWidget *parent) :
|
||||||
// context menu
|
// context menu
|
||||||
ui->contacts->setContextMenuPolicy(Qt::CustomContextMenu);
|
ui->contacts->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
m_contextMenu = new QMenu(ui->contacts);
|
m_contextMenu = new QMenu(ui->contacts);
|
||||||
m_contextMenu->addAction(QIcon(":/assets/images/person.svg"), "New contact", [this]{
|
m_contextMenu->addAction(icons()->icon("person.svg"), "New contact", [this]{
|
||||||
this->newContact();
|
this->newContact();
|
||||||
});
|
});
|
||||||
|
|
||||||
// row context menu
|
// row context menu
|
||||||
m_rowMenu = new QMenu(ui->contacts);
|
m_rowMenu = new QMenu(ui->contacts);
|
||||||
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy address", this, &ContactsWidget::copyAddress);
|
m_rowMenu->addAction(icons()->icon("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 name", this, &ContactsWidget::copyName);
|
||||||
m_rowMenu->addAction("Pay to", this, &ContactsWidget::payTo);
|
m_rowMenu->addAction("Pay to", this, &ContactsWidget::payTo);
|
||||||
m_rowMenu->addAction("Delete", this, &ContactsWidget::deleteContact);
|
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);
|
return m_wallet->getSpendProof(m_txid, message);
|
||||||
}
|
}
|
||||||
case Mode::OutProof:
|
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);
|
return m_wallet->getTxProof(m_txid, address, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
228
src/dialog/UpdateDialog.cpp
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
// 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->label_body->setText("Downloading update..");
|
||||||
|
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 = QFileInfo(applicationPath).absoluteDir().path();
|
||||||
|
} else {
|
||||||
|
applicationPath = QCoreApplication::applicationDirPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir applicationDir(applicationPath);
|
||||||
|
QString filePath = applicationDir.filePath(name);
|
||||||
|
m_updatePath = filePath;
|
||||||
|
|
||||||
|
QFile file(filePath);
|
||||||
|
if (!file.open(QIODevice::WriteOnly))
|
||||||
|
{
|
||||||
|
this->onInstallError(QString("Error: Could not write to application path: %1").arg(filePath));
|
||||||
|
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::ExeGroup | QFile::ExeOther
|
||||||
|
| 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 "broadcasttxdialog.h"
|
||||||
#include "ui_broadcasttxdialog.h"
|
#include "ui_broadcasttxdialog.h"
|
||||||
|
#include "utils/NetworkManager.h"
|
||||||
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
@ -13,10 +14,8 @@ BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, AppContext *ctx, const QSt
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
m_network = new UtilsNetworking(m_ctx->network, this);
|
|
||||||
|
|
||||||
auto node = ctx->nodes->connection();
|
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_Broadcast, &QPushButton::clicked, this, &BroadcastTxDialog::broadcastTx);
|
||||||
connect(ui->btn_Close, &QPushButton::clicked, this, &BroadcastTxDialog::reject);
|
connect(ui->btn_Close, &QPushButton::clicked, this, &BroadcastTxDialog::reject);
|
||||||
|
@ -38,7 +37,7 @@ void BroadcastTxDialog::broadcastTx() {
|
||||||
if (ui->radio_useCustom->isChecked())
|
if (ui->radio_useCustom->isChecked())
|
||||||
node = ui->customNode->text();
|
node = ui->customNode->text();
|
||||||
else if (ui->radio_useDefault->isChecked())
|
else if (ui->radio_useDefault->isChecked())
|
||||||
node = m_ctx->nodes->connection().full;
|
node = m_ctx->nodes->connection().toAddress();
|
||||||
|
|
||||||
if (!node.startsWith("http://"))
|
if (!node.startsWith("http://"))
|
||||||
node = QString("http://%1").arg(node);
|
node = QString("http://%1").arg(node);
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
#include "debuginfodialog.h"
|
#include "debuginfodialog.h"
|
||||||
#include "ui_debuginfodialog.h"
|
#include "ui_debuginfodialog.h"
|
||||||
#include "config-feather.h"
|
#include "config-feather.h"
|
||||||
|
#include "utils/WebsocketClient.h"
|
||||||
|
#include "utils/TorManager.h"
|
||||||
|
#include "utils/WebsocketNotifier.h"
|
||||||
|
|
||||||
DebugInfoDialog::DebugInfoDialog(AppContext *ctx, QWidget *parent)
|
DebugInfoDialog::DebugInfoDialog(AppContext *ctx, QWidget *parent)
|
||||||
: QDialog(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
|
// 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->isTails) {
|
||||||
if(m_ctx->tor->torConnected)
|
if(torManager()->torConnected)
|
||||||
torStatus = "Connected";
|
torStatus = "Connected";
|
||||||
else
|
else
|
||||||
torStatus = "Disconnected";
|
torStatus = "Disconnected";
|
||||||
}
|
}
|
||||||
else if(m_ctx->isTorSocks)
|
else if(Utils::isTorsocks())
|
||||||
torStatus = "Torsocks";
|
torStatus = "Torsocks";
|
||||||
else if(m_ctx->tor->localTor)
|
else if(torManager()->isLocalTor())
|
||||||
torStatus = "Local (assumed to be running)";
|
torStatus = "Local (assumed to be running)";
|
||||||
else if(m_ctx->tor->torConnected)
|
else if(torManager()->torConnected)
|
||||||
torStatus = "Running";
|
torStatus = "Running";
|
||||||
else
|
else
|
||||||
torStatus = "Unknown";
|
torStatus = "Unknown";
|
||||||
|
@ -50,13 +53,37 @@ void DebugInfoDialog::updateInfo() {
|
||||||
ui->label_synchronized->setText(m_ctx->currentWallet->isSynchronized() ? "True" : "False");
|
ui->label_synchronized->setText(m_ctx->currentWallet->isSynchronized() ? "True" : "False");
|
||||||
|
|
||||||
auto node = m_ctx->nodes->connection();
|
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_walletStatus->setText(this->statusToString(m_ctx->currentWallet->connectionStatus()));
|
||||||
ui->label_torStatus->setText(torStatus);
|
ui->label_torStatus->setText(torStatus);
|
||||||
ui->label_websocketStatus->setText(Utils::QtEnumToString(m_ctx->ws->webSocket.state()).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_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_viewOnly->setText(m_ctx->currentWallet->viewOnly() ? "True" : "False");
|
||||||
ui->label_primaryOnly->setText(m_ctx->currentWallet->balance(0) == m_ctx->currentWallet->balanceAll() ? "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("Network type: %1 \n").arg(ui->label_netType->text());
|
||||||
text += QString("Seed type: %1 \n").arg(ui->label_seedType->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("View only: %1 \n").arg(ui->label_viewOnly->text());
|
||||||
text += QString("Primary only: %1 \n").arg(ui->label_primaryOnly->text());
|
text += QString("Primary only: %1 \n").arg(ui->label_primaryOnly->text());
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>693</width>
|
<width>693</width>
|
||||||
<height>612</height>
|
<height>613</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -91,6 +91,23 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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">
|
<item row="6" column="0">
|
||||||
<widget class="QLabel" name="label_10">
|
<widget class="QLabel" name="label_10">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -242,82 +259,14 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="16" column="0">
|
<item row="16" column="0">
|
||||||
<widget class="QLabel" name="label_7">
|
<widget class="QLabel" name="label_8">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>View only:</string>
|
<string>Device type:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="16" column="1">
|
<item row="16" column="1">
|
||||||
<widget class="QLabel" name="label_viewOnly">
|
<widget class="QLabel" name="label_deviceType">
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="20" column="0">
|
|
||||||
<widget class="QLabel" name="label_24">
|
|
||||||
<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>
|
|
||||||
</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">
|
<property name="text">
|
||||||
<string>TextLabel</string>
|
<string>TextLabel</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -327,12 +276,80 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="17" column="0">
|
<item row="17" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>View only:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="17" column="1">
|
||||||
|
<widget class="QLabel" name="label_viewOnly">
|
||||||
|
<property name="text">
|
||||||
|
<string>TextLabel</string>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="18" column="0">
|
||||||
<widget class="QLabel" name="label_11">
|
<widget class="QLabel" name="label_11">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Primary only:</string>
|
<string>Primary only:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="18" column="1">
|
||||||
|
<widget class="QLabel" name="label_primaryOnly">
|
||||||
|
<property name="text">
|
||||||
|
<string>TextLabel</string>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<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>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>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -10,12 +10,15 @@ KeysDialog::KeysDialog(AppContext *ctx, QWidget *parent)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
ui->label_restoreHeight->setText(QString::number(ctx->currentWallet->getWalletCreationHeight()));
|
auto w = ctx->currentWallet;
|
||||||
ui->label_primaryAddress->setText(ctx->currentWallet->address(0, 0));
|
QString unavailable = "Unavailable: Key is stored on hardware device";
|
||||||
ui->label_secretSpendKey->setText(ctx->currentWallet->getSecretSpendKey());
|
|
||||||
ui->label_secretViewKey->setText(ctx->currentWallet->getSecretViewKey());
|
ui->label_restoreHeight->setText(QString::number(w->getWalletCreationHeight()));
|
||||||
ui->label_publicSpendKey->setText(ctx->currentWallet->getPublicSpendKey());
|
ui->label_primaryAddress->setText(w->address(0, 0));
|
||||||
ui->label_publicViewKey->setText(ctx->currentWallet->getPublicViewKey());
|
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();
|
this->adjustSize();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ RestoreDialog::RestoreDialog(AppContext *ctx, QWidget *parent)
|
||||||
ui->restoreHeightWidget->hideSlider();
|
ui->restoreHeightWidget->hideSlider();
|
||||||
} else {
|
} else {
|
||||||
// load restoreHeight lookup db
|
// load restoreHeight lookup db
|
||||||
ui->restoreHeightWidget->initRestoreHeights(m_ctx->restoreHeights[m_ctx->networkType]);
|
ui->restoreHeightWidget->initRestoreHeights(appData()->restoreHeights[m_ctx->networkType]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
31
src/dialog/splashdialog.cpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SplashDialog::setMessage(const QString &message) {
|
||||||
|
ui->label_message->setText(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SplashDialog::setIcon(const QPixmap &icon) {
|
||||||
|
ui->icon->setPixmap(icon.scaledToWidth(32, Qt::SmoothTransformation));
|
||||||
|
}
|
||||||
|
|
||||||
|
SplashDialog::~SplashDialog() {
|
||||||
|
delete ui;
|
||||||
|
}
|
28
src/dialog/splashdialog.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
void setMessage(const QString &message);
|
||||||
|
void setIcon(const QPixmap &icon);
|
||||||
|
|
||||||
|
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,145 @@
|
||||||
#include "torinfodialog.h"
|
#include "torinfodialog.h"
|
||||||
#include "ui_torinfodialog.h"
|
#include "ui_torinfodialog.h"
|
||||||
|
|
||||||
|
#include <QPushButton>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
TorInfoDialog::TorInfoDialog(AppContext *ctx, QWidget *parent)
|
#include "utils/TorManager.h"
|
||||||
|
|
||||||
|
TorInfoDialog::TorInfoDialog(QWidget *parent, AppContext *ctx)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, ui(new Ui::TorInfoDialog)
|
, ui(new Ui::TorInfoDialog)
|
||||||
, m_ctx(ctx)
|
, m_ctx(ctx)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
if (!m_ctx->tor->torConnected && !m_ctx->tor->errorMsg.isEmpty()) {
|
if (!torManager()->torConnected && !torManager()->errorMsg.isEmpty()) {
|
||||||
ui->message->setText(m_ctx->tor->errorMsg);
|
ui->message->setText(torManager()->errorMsg);
|
||||||
} else {
|
} 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) {
|
if (torManager()->isLocalTor()) {
|
||||||
ui->logs->setHidden(true);
|
ui->frame_logs->setHidden(true);
|
||||||
} else {
|
} 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();
|
this->adjustSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorInfoDialog::onLogsUpdated() {
|
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()) {
|
||||||
|
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() {
|
TorInfoDialog::~TorInfoDialog() {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#define FEATHER_TORINFODIALOG_H
|
#define FEATHER_TORINFODIALOG_H
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
#include <QAbstractButton>
|
||||||
|
|
||||||
#include "appcontext.h"
|
#include "appcontext.h"
|
||||||
|
|
||||||
|
@ -17,13 +18,25 @@ class TorInfoDialog : public QDialog
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TorInfoDialog(AppContext *ctx, QWidget *parent = nullptr);
|
explicit TorInfoDialog(QWidget *parent, AppContext *ctx);
|
||||||
~TorInfoDialog() override;
|
~TorInfoDialog() override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void onLogsUpdated();
|
void onLogsUpdated();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onConnectionStatusChanged(bool connected);
|
||||||
|
void onApplySettings();
|
||||||
|
void onSettingsChanged();
|
||||||
|
void onStopTor();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void torSettingsChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void initConnectionSettings();
|
||||||
|
void initPrivacyLevel();
|
||||||
|
|
||||||
Ui::TorInfoDialog *ui;
|
Ui::TorInfoDialog *ui;
|
||||||
AppContext *m_ctx;
|
AppContext *m_ctx;
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>618</width>
|
<width>703</width>
|
||||||
<height>386</height>
|
<height>804</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -15,24 +15,335 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="message">
|
<widget class="QGroupBox" name="group_connectionSettings">
|
||||||
<property name="text">
|
<property name="title">
|
||||||
<string>Message</string>
|
<string>Connection settings</string>
|
||||||
</property>
|
</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>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPlainTextEdit" name="logs">
|
<widget class="QGroupBox" name="group_privacyLevel">
|
||||||
<property name="minimumSize">
|
<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>
|
<size>
|
||||||
<width>600</width>
|
<width>20</width>
|
||||||
<height>0</height>
|
<height>0</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="readOnly">
|
</spacer>
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
@ -40,7 +351,7 @@
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="standardButtons">
|
<property name="standardButtons">
|
||||||
<set>QDialogButtonBox::Ok</set>
|
<set>QDialogButtonBox::Apply|QDialogButtonBox::Close</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -81,4 +392,7 @@
|
||||||
</hints>
|
</hints>
|
||||||
</connection>
|
</connection>
|
||||||
</connections>
|
</connections>
|
||||||
|
<buttongroups>
|
||||||
|
<buttongroup name="btnGroup_privacyLevel"/>
|
||||||
|
</buttongroups>
|
||||||
</ui>
|
</ui>
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include "libwalletqt/Transfer.h"
|
#include "libwalletqt/Transfer.h"
|
||||||
#include "libwalletqt/Input.h"
|
#include "libwalletqt/Input.h"
|
||||||
#include "model/ModelUtils.h"
|
#include "model/ModelUtils.h"
|
||||||
#include "utils/ColorScheme.h"
|
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "model/ModelUtils.h"
|
#include "model/ModelUtils.h"
|
||||||
#include "txconfadvdialog.h"
|
#include "txconfadvdialog.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
#include "utils/AppData.h"
|
||||||
#include "utils/ColorScheme.h"
|
#include "utils/ColorScheme.h"
|
||||||
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
@ -26,7 +27,7 @@ TxConfDialog::TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QStrin
|
||||||
QString preferredCur = config()->get(Config::preferredFiatCurrency).toString();
|
QString preferredCur = config()->get(Config::preferredFiatCurrency).toString();
|
||||||
|
|
||||||
auto convert = [preferredCur](double amount){
|
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());
|
QString amount = WalletManager::displayAmount(tx->amount());
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "tximportdialog.h"
|
#include "tximportdialog.h"
|
||||||
#include "ui_tximportdialog.h"
|
#include "ui_tximportdialog.h"
|
||||||
|
#include "utils/NetworkManager.h"
|
||||||
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
@ -16,10 +17,8 @@ TxImportDialog::TxImportDialog(QWidget *parent, AppContext *ctx)
|
||||||
ui->resp->hide();
|
ui->resp->hide();
|
||||||
ui->label_loading->hide();
|
ui->label_loading->hide();
|
||||||
|
|
||||||
m_network = new UtilsNetworking(m_ctx->network, this);
|
|
||||||
|
|
||||||
auto node = ctx->nodes->connection();
|
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_load, &QPushButton::clicked, this, &TxImportDialog::loadTx);
|
||||||
connect(ui->btn_import, &QPushButton::clicked, this, &TxImportDialog::onImport);
|
connect(ui->btn_import, &QPushButton::clicked, this, &TxImportDialog::onImport);
|
||||||
|
@ -35,7 +34,7 @@ TxImportDialog::TxImportDialog(QWidget *parent, AppContext *ctx)
|
||||||
|
|
||||||
void TxImportDialog::loadTx() {
|
void TxImportDialog::loadTx() {
|
||||||
QString txid = ui->line_txid->text();
|
QString txid = ui->line_txid->text();
|
||||||
QString node = m_ctx->nodes->connection().full;
|
QString node = m_ctx->nodes->connection().toAddress();
|
||||||
|
|
||||||
if (!node.startsWith("http://"))
|
if (!node.startsWith("http://"))
|
||||||
node = QString("http://%1").arg(node);
|
node = QString("http://%1").arg(node);
|
||||||
|
|
|
@ -22,6 +22,9 @@ namespace globals
|
||||||
|
|
||||||
// websocket constants
|
// websocket constants
|
||||||
const QUrl websocketUrl = QUrl(QStringLiteral("ws://7e6egbawekbkxzkv4244pqeqgoo4axko2imgjbedwnn6s5yb6b7oliqd.onion/ws"));
|
const QUrl websocketUrl = QUrl(QStringLiteral("ws://7e6egbawekbkxzkv4244pqeqgoo4axko2imgjbedwnn6s5yb6b7oliqd.onion/ws"));
|
||||||
|
|
||||||
|
// website constants
|
||||||
|
const QString websiteUrl = "https://featherwallet.org";
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //FEATHER_GLOBALS_H
|
#endif //FEATHER_GLOBALS_H
|
||||||
|
|
|
@ -5,6 +5,10 @@
|
||||||
#include "ui_historywidget.h"
|
#include "ui_historywidget.h"
|
||||||
#include "dialog/transactioninfodialog.h"
|
#include "dialog/transactioninfodialog.h"
|
||||||
#include "dialog/TxProofDialog.h"
|
#include "dialog/TxProofDialog.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
#include "utils/config.h"
|
||||||
|
#include "appcontext.h"
|
||||||
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
HistoryWidget::HistoryWidget(QWidget *parent)
|
HistoryWidget::HistoryWidget(QWidget *parent)
|
||||||
|
@ -15,11 +19,11 @@ HistoryWidget::HistoryWidget(QWidget *parent)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
m_contextMenu->addMenu(m_copyMenu);
|
m_contextMenu->addMenu(m_copyMenu);
|
||||||
m_contextMenu->addAction(QIcon(":/assets/images/info.png"), "Show details", this, &HistoryWidget::showTxDetails);
|
m_contextMenu->addAction(icons()->icon("info2.svg"), "Show details", this, &HistoryWidget::showTxDetails);
|
||||||
m_contextMenu->addAction(QIcon(":/assets/images/network.png"), "View on block explorer", this, &HistoryWidget::onViewOnBlockExplorer);
|
m_contextMenu->addAction("View on block explorer", this, &HistoryWidget::onViewOnBlockExplorer);
|
||||||
|
|
||||||
// copy menu
|
// 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("Transaction ID", this, [this]{copy(copyField::TxID);});
|
||||||
m_copyMenu->addAction("Description", this, [this]{copy(copyField::Description);});
|
m_copyMenu->addAction("Description", this, [this]{copy(copyField::Description);});
|
||||||
m_copyMenu->addAction("Date", this, [this]{copy(copyField::Date);});
|
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();
|
bool unconfirmed = tx->isFailed() || tx->isPending();
|
||||||
if (AppContext::txCache.contains(tx->hash()) && unconfirmed && tx->direction() != TransactionInfo::Direction_In) {
|
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.addMenu(m_copyMenu);
|
||||||
menu.addAction(QIcon(":/assets/images/info.png"), "Show details", this, &HistoryWidget::showTxDetails);
|
menu.addAction(icons()->icon("info2.svg"), "Show details", this, &HistoryWidget::showTxDetails);
|
||||||
menu.addAction(QIcon(":/assets/images/network.png"), "View on block explorer", this, &HistoryWidget::onViewOnBlockExplorer);
|
menu.addAction(icons()->icon("network.png"), "View on block explorer", this, &HistoryWidget::onViewOnBlockExplorer);
|
||||||
menu.addAction("Create tx proof", this, &HistoryWidget::createTxProof);
|
menu.addAction("Create tx proof", this, &HistoryWidget::createTxProof);
|
||||||
|
|
||||||
menu.exec(ui->history->viewport()->mapToGlobal(point));
|
menu.exec(ui->history->viewport()->mapToGlobal(point));
|
||||||
|
|
|
@ -10,6 +10,11 @@ Subaddress::Subaddress(Monero::Subaddress *subaddressImpl, QObject *parent)
|
||||||
getAll();
|
getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Subaddress::errorString() const
|
||||||
|
{
|
||||||
|
return QString::fromStdString(m_subaddressImpl->errorString());
|
||||||
|
}
|
||||||
|
|
||||||
void Subaddress::getAll() const
|
void Subaddress::getAll() const
|
||||||
{
|
{
|
||||||
emit refreshStarted();
|
emit refreshStarted();
|
||||||
|
@ -46,17 +51,24 @@ bool Subaddress::getRow(int index, std::function<void (Monero::SubaddressRow &ro
|
||||||
return true;
|
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());
|
bool r = m_subaddressImpl->addRow(accountIndex, label.toStdString());
|
||||||
getAll();
|
|
||||||
|
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());
|
bool r = m_subaddressImpl->setLabel(accountIndex, addressIndex, label.toStdString());
|
||||||
getAll();
|
if (r) {
|
||||||
emit labelChanged();
|
getAll();
|
||||||
|
emit labelChanged();
|
||||||
|
}
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Subaddress::refresh(quint32 accountIndex) const
|
void Subaddress::refresh(quint32 accountIndex) const
|
||||||
|
|
|
@ -16,13 +16,14 @@ class Subaddress : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE void getAll() const;
|
void getAll() const;
|
||||||
Q_INVOKABLE bool getRow(int index, std::function<void (Monero::SubaddressRow &row)> callback) const;
|
bool getRow(int index, std::function<void (Monero::SubaddressRow &row)> callback) const;
|
||||||
Q_INVOKABLE void addRow(quint32 accountIndex, const QString &label) const;
|
bool addRow(quint32 accountIndex, const QString &label) const;
|
||||||
Q_INVOKABLE void setLabel(quint32 accountIndex, quint32 addressIndex, const QString &label) const;
|
bool setLabel(quint32 accountIndex, quint32 addressIndex, const QString &label) const;
|
||||||
Q_INVOKABLE void refresh(quint32 accountIndex) const;
|
void refresh(quint32 accountIndex) const;
|
||||||
Q_INVOKABLE quint64 unusedLookahead() const;
|
quint64 unusedLookahead() const;
|
||||||
quint64 count() const;
|
quint64 count() const;
|
||||||
|
QString errorString() const;
|
||||||
Monero::SubaddressRow* row(int index) const;
|
Monero::SubaddressRow* row(int index) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
#include "TransactionHistory.h"
|
#include "TransactionHistory.h"
|
||||||
#include "TransactionInfo.h"
|
#include "TransactionInfo.h"
|
||||||
#include "utils/utils.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)
|
bool TransactionHistory::transaction(int index, std::function<void (TransactionInfo &)> callback)
|
||||||
{
|
{
|
||||||
|
@ -164,11 +164,11 @@ bool TransactionHistory::writeCSV(const QString &path) {
|
||||||
// calc historical fiat price
|
// calc historical fiat price
|
||||||
QString fiatAmount;
|
QString fiatAmount;
|
||||||
QString preferredFiatSymbol = config()->get(Config::preferredFiatCurrency).toString();
|
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;
|
double fiat_price = usd_price * amount;
|
||||||
|
|
||||||
if(preferredFiatSymbol != "USD")
|
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;
|
double fiat_rounded = ceil(Utils::roundSignificant(fiat_price, 3) * 100.0) / 100.0;
|
||||||
if(fiat_price != 0)
|
if(fiat_price != 0)
|
||||||
fiatAmount = QString("%1 %2").arg(QString::number(fiat_rounded)).arg(preferredFiatSymbol);
|
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;
|
return m_walletImpl->getDeviceType() == Monero::Wallet::Device_Trezor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Wallet::reconnectDevice()
|
||||||
|
{
|
||||||
|
return m_walletImpl->reconnectDevice();
|
||||||
|
}
|
||||||
|
|
||||||
//! create a view only wallet
|
//! create a view only wallet
|
||||||
bool Wallet::createViewOnly(const QString &path, const QString &password) const
|
bool Wallet::createViewOnly(const QString &path, const QString &password) const
|
||||||
{
|
{
|
||||||
|
@ -600,15 +605,15 @@ PendingTransaction *Wallet::createTransaction(const QString &dst_addr, const QSt
|
||||||
quint64 amount, quint32 mixin_count,
|
quint64 amount, quint32 mixin_count,
|
||||||
PendingTransaction::Priority priority)
|
PendingTransaction::Priority priority)
|
||||||
{
|
{
|
||||||
pauseRefresh();
|
// pauseRefresh();
|
||||||
|
|
||||||
std::set<uint32_t> subaddr_indices;
|
std::set<uint32_t> subaddr_indices;
|
||||||
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction(
|
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction(
|
||||||
dst_addr.toStdString(), payment_id.toStdString(), amount, mixin_count,
|
dst_addr.toStdString(), payment_id.toStdString(), amount, mixin_count,
|
||||||
static_cast<Monero::PendingTransaction::Priority>(priority), currentSubaddressAccount(), subaddr_indices);
|
static_cast<Monero::PendingTransaction::Priority>(priority), currentSubaddressAccount(), subaddr_indices);
|
||||||
PendingTransaction * result = new PendingTransaction(ptImpl,0);
|
PendingTransaction * result = new PendingTransaction(ptImpl, nullptr);
|
||||||
|
|
||||||
startRefresh();
|
// startRefresh();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,7 +631,7 @@ void Wallet::createTransactionAsync(const QString &dst_addr, const QString &paym
|
||||||
PendingTransaction* Wallet::createTransactionMultiDest(const QVector<QString> &dst_addr, const QVector<quint64> &amount,
|
PendingTransaction* Wallet::createTransactionMultiDest(const QVector<QString> &dst_addr, const QVector<quint64> &amount,
|
||||||
PendingTransaction::Priority priority)
|
PendingTransaction::Priority priority)
|
||||||
{
|
{
|
||||||
pauseRefresh();
|
// pauseRefresh();
|
||||||
|
|
||||||
std::vector<std::string> dests;
|
std::vector<std::string> dests;
|
||||||
for (auto &addr : dst_addr) {
|
for (auto &addr : dst_addr) {
|
||||||
|
@ -642,7 +647,7 @@ PendingTransaction* Wallet::createTransactionMultiDest(const QVector<QString> &d
|
||||||
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransactionMultDest(dests, "", amounts, 11, static_cast<Monero::PendingTransaction::Priority>(priority));
|
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransactionMultDest(dests, "", amounts, 11, static_cast<Monero::PendingTransaction::Priority>(priority));
|
||||||
PendingTransaction * result = new PendingTransaction(ptImpl);
|
PendingTransaction * result = new PendingTransaction(ptImpl);
|
||||||
|
|
||||||
startRefresh();
|
// startRefresh();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -662,7 +667,7 @@ void Wallet::createTransactionMultiDestAsync(const QVector<QString> &dst_addr, c
|
||||||
PendingTransaction *Wallet::createTransactionAll(const QString &dst_addr, const QString &payment_id,
|
PendingTransaction *Wallet::createTransactionAll(const QString &dst_addr, const QString &payment_id,
|
||||||
quint32 mixin_count, PendingTransaction::Priority priority)
|
quint32 mixin_count, PendingTransaction::Priority priority)
|
||||||
{
|
{
|
||||||
pauseRefresh();
|
// pauseRefresh();
|
||||||
|
|
||||||
std::set<uint32_t> subaddr_indices;
|
std::set<uint32_t> subaddr_indices;
|
||||||
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction(
|
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction(
|
||||||
|
@ -670,7 +675,7 @@ PendingTransaction *Wallet::createTransactionAll(const QString &dst_addr, const
|
||||||
static_cast<Monero::PendingTransaction::Priority>(priority), currentSubaddressAccount(), subaddr_indices);
|
static_cast<Monero::PendingTransaction::Priority>(priority), currentSubaddressAccount(), subaddr_indices);
|
||||||
PendingTransaction * result = new PendingTransaction(ptImpl, this);
|
PendingTransaction * result = new PendingTransaction(ptImpl, this);
|
||||||
|
|
||||||
startRefresh();
|
// startRefresh();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -688,13 +693,13 @@ void Wallet::createTransactionAllAsync(const QString &dst_addr, const QString &p
|
||||||
PendingTransaction *Wallet::createTransactionSingle(const QString &key_image, const QString &dst_addr, const size_t outputs,
|
PendingTransaction *Wallet::createTransactionSingle(const QString &key_image, const QString &dst_addr, const size_t outputs,
|
||||||
PendingTransaction::Priority priority)
|
PendingTransaction::Priority priority)
|
||||||
{
|
{
|
||||||
pauseRefresh();
|
// pauseRefresh();
|
||||||
|
|
||||||
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransactionSingle(key_image.toStdString(), dst_addr.toStdString(),
|
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransactionSingle(key_image.toStdString(), dst_addr.toStdString(),
|
||||||
outputs, static_cast<Monero::PendingTransaction::Priority>(priority));
|
outputs, static_cast<Monero::PendingTransaction::Priority>(priority));
|
||||||
PendingTransaction * result = new PendingTransaction(ptImpl, this);
|
PendingTransaction * result = new PendingTransaction(ptImpl, this);
|
||||||
|
|
||||||
startRefresh();
|
// startRefresh();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -710,12 +715,12 @@ void Wallet::createTransactionSingleAsync(const QString &key_image, const QStrin
|
||||||
|
|
||||||
PendingTransaction *Wallet::createSweepUnmixableTransaction()
|
PendingTransaction *Wallet::createSweepUnmixableTransaction()
|
||||||
{
|
{
|
||||||
pauseRefresh();
|
// pauseRefresh();
|
||||||
|
|
||||||
Monero::PendingTransaction * ptImpl = m_walletImpl->createSweepUnmixableTransaction();
|
Monero::PendingTransaction * ptImpl = m_walletImpl->createSweepUnmixableTransaction();
|
||||||
PendingTransaction * result = new PendingTransaction(ptImpl, this);
|
PendingTransaction * result = new PendingTransaction(ptImpl, this);
|
||||||
|
|
||||||
startRefresh();
|
// startRefresh();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1224,13 +1229,23 @@ void Wallet::onWalletPassphraseNeeded(bool on_device)
|
||||||
}
|
}
|
||||||
|
|
||||||
quint64 Wallet::getBytesReceived() const {
|
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 {
|
quint64 Wallet::getBytesSent() const {
|
||||||
return m_walletImpl->getBytesSent();
|
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)
|
void Wallet::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
|
||||||
{
|
{
|
||||||
if (m_walletListener != nullptr)
|
if (m_walletListener != nullptr)
|
||||||
|
@ -1303,14 +1318,14 @@ Wallet::~Wallet()
|
||||||
delete m_coins;
|
delete m_coins;
|
||||||
m_coins = NULL;
|
m_coins = NULL;
|
||||||
//Monero::WalletManagerFactory::getWalletManager()->closeWallet(m_walletImpl);
|
//Monero::WalletManagerFactory::getWalletManager()->closeWallet(m_walletImpl);
|
||||||
if(status() == Status_Critical)
|
if(status() == Status_Critical || status() == Status_BadPassword)
|
||||||
qDebug("Not storing wallet cache");
|
qDebug("Not storing wallet cache");
|
||||||
else if( m_walletImpl->store(""))
|
else if( m_walletImpl->store(""))
|
||||||
qDebug("Wallet cache stored successfully");
|
qDebug("Wallet cache stored successfully");
|
||||||
else
|
else
|
||||||
qDebug("Error storing wallet cache");
|
qDebug("Error storing wallet cache");
|
||||||
delete m_walletImpl;
|
delete m_walletImpl;
|
||||||
m_walletImpl = NULL;
|
m_walletImpl = nullptr;
|
||||||
delete m_walletListener;
|
delete m_walletListener;
|
||||||
m_walletListener = NULL;
|
m_walletListener = NULL;
|
||||||
qDebug("m_walletImpl deleted");
|
qDebug("m_walletImpl deleted");
|
||||||
|
@ -1325,7 +1340,7 @@ void Wallet::startRefreshThread()
|
||||||
auto last = std::chrono::steady_clock::now();
|
auto last = std::chrono::steady_clock::now();
|
||||||
while (!m_scheduler.stopping())
|
while (!m_scheduler.stopping())
|
||||||
{
|
{
|
||||||
if (m_refreshEnabled)
|
if (m_refreshEnabled && (!isHwBacked() || isDeviceConnected()))
|
||||||
{
|
{
|
||||||
const auto now = std::chrono::steady_clock::now();
|
const auto now = std::chrono::steady_clock::now();
|
||||||
const auto elapsed = now - last;
|
const auto elapsed = now - last;
|
||||||
|
|
|
@ -78,9 +78,10 @@ Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum Status {
|
enum Status {
|
||||||
Status_Ok = Monero::Wallet::Status_Ok,
|
Status_Ok = Monero::Wallet::Status_Ok,
|
||||||
Status_Error = Monero::Wallet::Status_Error,
|
Status_Error = Monero::Wallet::Status_Error,
|
||||||
Status_Critical = Monero::Wallet::Status_Critical
|
Status_Critical = Monero::Wallet::Status_Critical,
|
||||||
|
Status_BadPassword = Monero::Wallet::Status_BadPassword
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_ENUM(Status)
|
Q_ENUM(Status)
|
||||||
|
@ -198,6 +199,9 @@ public:
|
||||||
bool isLedger() const;
|
bool isLedger() const;
|
||||||
bool isTrezor() const;
|
bool isTrezor() const;
|
||||||
|
|
||||||
|
//! attempt to reconnect to hw-device
|
||||||
|
bool reconnectDevice();
|
||||||
|
|
||||||
//! returns if view only wallet
|
//! returns if view only wallet
|
||||||
bool viewOnly() const;
|
bool viewOnly() const;
|
||||||
|
|
||||||
|
@ -424,6 +428,8 @@ public:
|
||||||
quint64 getBytesReceived() const;
|
quint64 getBytesReceived() const;
|
||||||
quint64 getBytesSent() const;
|
quint64 getBytesSent() const;
|
||||||
|
|
||||||
|
bool isDeviceConnected() const;
|
||||||
|
|
||||||
// TODO: setListenter() when it implemented in API
|
// TODO: setListenter() when it implemented in API
|
||||||
signals:
|
signals:
|
||||||
// emitted on every event happened with wallet
|
// emitted on every event happened with wallet
|
||||||
|
@ -432,7 +438,7 @@ signals:
|
||||||
|
|
||||||
// emitted when refresh process finished (could take a long time)
|
// emitted when refresh process finished (could take a long time)
|
||||||
// signalling only after we
|
// signalling only after we
|
||||||
void refreshed(bool success);
|
void refreshed(bool success, const QString &message);
|
||||||
|
|
||||||
void moneySpent(const QString &txId, quint64 amount);
|
void moneySpent(const QString &txId, quint64 amount);
|
||||||
void moneyReceived(const QString &txId, quint64 amount);
|
void moneyReceived(const QString &txId, quint64 amount);
|
||||||
|
@ -443,6 +449,7 @@ signals:
|
||||||
void walletCreationHeightChanged();
|
void walletCreationHeightChanged();
|
||||||
void deviceButtonRequest(quint64 buttonCode);
|
void deviceButtonRequest(quint64 buttonCode);
|
||||||
void deviceButtonPressed();
|
void deviceButtonPressed();
|
||||||
|
void deviceError(const QString &message);
|
||||||
void walletPassphraseNeeded(bool onDevice);
|
void walletPassphraseNeeded(bool onDevice);
|
||||||
void transactionCommitted(bool status, PendingTransaction *t, const QStringList& txid);
|
void transactionCommitted(bool status, PendingTransaction *t, const QStringList& txid);
|
||||||
void heightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) const;
|
void heightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) const;
|
||||||
|
|
|
@ -45,8 +45,9 @@ void WalletListenerImpl::updated()
|
||||||
void WalletListenerImpl::refreshed(bool success)
|
void WalletListenerImpl::refreshed(bool success)
|
||||||
{
|
{
|
||||||
qDebug() << __FUNCTION__;
|
qDebug() << __FUNCTION__;
|
||||||
|
QString message = m_wallet->errorString();
|
||||||
m_wallet->onRefreshed(success);
|
m_wallet->onRefreshed(success);
|
||||||
emit m_wallet->refreshed(success);
|
emit m_wallet->refreshed(success, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WalletListenerImpl::onDeviceButtonRequest(uint64_t code)
|
void WalletListenerImpl::onDeviceButtonRequest(uint64_t code)
|
||||||
|
@ -61,6 +62,12 @@ void WalletListenerImpl::onDeviceButtonPressed()
|
||||||
emit m_wallet->deviceButtonPressed();
|
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)
|
void WalletListenerImpl::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
|
||||||
{
|
{
|
||||||
qDebug() << __FUNCTION__;
|
qDebug() << __FUNCTION__;
|
||||||
|
|
|
@ -31,6 +31,8 @@ public:
|
||||||
|
|
||||||
virtual void onDeviceButtonPressed() override;
|
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 void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override;
|
||||||
|
|
||||||
virtual Monero::optional<std::string> onDevicePassphraseRequest(bool & on_device) override;
|
virtual Monero::optional<std::string> onDevicePassphraseRequest(bool & on_device) override;
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include "libwalletqt/WalletManager.h"
|
#include "libwalletqt/WalletManager.h"
|
||||||
#include "Wallet.h"
|
#include "Wallet.h"
|
||||||
|
|
||||||
//#include "qt/updater.h"
|
|
||||||
#include "utils/ScopeGuard.h"
|
#include "utils/ScopeGuard.h"
|
||||||
|
|
||||||
class WalletPassphraseListenerImpl : public Monero::WalletListener, public PassphraseReceiver
|
class WalletPassphraseListenerImpl : public Monero::WalletListener, public PassphraseReceiver
|
||||||
|
@ -31,11 +30,11 @@ public:
|
||||||
// return m_phelper.onDevicePassphraseRequest(on_device);
|
// return m_phelper.onDevicePassphraseRequest(on_device);
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// virtual void onDeviceButtonRequest(uint64_t code) override
|
virtual void onDeviceButtonRequest(uint64_t code) override
|
||||||
// {
|
{
|
||||||
// qDebug() << __FUNCTION__;
|
qDebug() << __FUNCTION__;
|
||||||
// emit m_mgr->deviceButtonRequest(code);
|
emit m_mgr->deviceButtonRequest(code);
|
||||||
// }
|
}
|
||||||
//
|
//
|
||||||
// virtual void onDeviceButtonPressed() override
|
// virtual void onDeviceButtonPressed() override
|
||||||
// {
|
// {
|
||||||
|
@ -43,6 +42,12 @@ public:
|
||||||
// emit m_mgr->deviceButtonPressed();
|
// emit m_mgr->deviceButtonPressed();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
virtual void onDeviceError(const std::string &message) override
|
||||||
|
{
|
||||||
|
qDebug() << __FUNCTION__;
|
||||||
|
emit m_mgr->deviceError(QString::fromStdString(message));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WalletManager * m_mgr;
|
WalletManager * m_mgr;
|
||||||
PassphraseHelper m_phelper;
|
PassphraseHelper m_phelper;
|
||||||
|
@ -125,17 +130,17 @@ Wallet *WalletManager::recoveryWallet(const QString &path, const QString &passwo
|
||||||
return m_currentWallet;
|
return m_currentWallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
Wallet *WalletManager::createWalletFromKeys(const QString &path, const QString &language, NetworkType::Type nettype,
|
Wallet *WalletManager::createWalletFromKeys(const QString &path, const QString &password, const QString &language,
|
||||||
const QString &address, const QString &viewkey, const QString &spendkey,
|
NetworkType::Type nettype, const QString &address, const QString &viewkey,
|
||||||
quint64 restoreHeight, quint64 kdfRounds)
|
const QString &spendkey, quint64 restoreHeight, quint64 kdfRounds)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
if (m_currentWallet) {
|
if (m_currentWallet) {
|
||||||
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
||||||
delete 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);
|
address.toStdString(), viewkey.toStdString(), spendkey.toStdString(), kdfRounds);
|
||||||
m_currentWallet = new Wallet(w);
|
m_currentWallet = new Wallet(w);
|
||||||
return m_currentWallet;
|
return m_currentWallet;
|
||||||
|
@ -148,9 +153,9 @@ Wallet *WalletManager::createDeterministicWalletFromSpendKey(const QString &path
|
||||||
if (m_currentWallet) {
|
if (m_currentWallet) {
|
||||||
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
||||||
delete 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());
|
spendkey.toStdString(), kdfRounds, offset_passphrase.toStdString());
|
||||||
m_currentWallet = new Wallet(w);
|
m_currentWallet = new Wallet(w);
|
||||||
return m_currentWallet;
|
return m_currentWallet;
|
||||||
|
@ -172,7 +177,7 @@ Wallet *WalletManager::createWalletFromDevice(const QString &path, const QString
|
||||||
if (m_currentWallet) {
|
if (m_currentWallet) {
|
||||||
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
||||||
delete 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),
|
Monero::Wallet * w = m_pimpl->createWalletFromDevice(path.toStdString(), password.toStdString(), static_cast<Monero::NetworkType>(nettype),
|
||||||
deviceName.toStdString(), restoreHeight, subaddressLookahead.toStdString(), 1, &tmpListener);
|
deviceName.toStdString(), restoreHeight, subaddressLookahead.toStdString(), 1, &tmpListener);
|
||||||
|
@ -201,10 +206,12 @@ void WalletManager::createWalletFromDeviceAsync(const QString &path, const QStri
|
||||||
QString WalletManager::closeWallet()
|
QString WalletManager::closeWallet()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
|
qDebug() << Q_FUNC_INFO ;
|
||||||
QString result;
|
QString result;
|
||||||
if (m_currentWallet) {
|
if (m_currentWallet) {
|
||||||
result = m_currentWallet->address(0, 0);
|
result = m_currentWallet->address(0, 0);
|
||||||
delete m_currentWallet;
|
delete m_currentWallet;
|
||||||
|
m_currentWallet = nullptr;
|
||||||
} else {
|
} else {
|
||||||
qCritical() << "Trying to close non existing wallet " << m_currentWallet;
|
qCritical() << "Trying to close non existing wallet " << m_currentWallet;
|
||||||
result = "0";
|
result = "0";
|
||||||
|
@ -428,15 +435,6 @@ QUrl WalletManager::localPathToUrl(const QString &path) const
|
||||||
return QUrl::fromLocalFile(path);
|
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
|
bool WalletManager::clearWalletCache(const QString &wallet_path) const
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -459,6 +457,7 @@ WalletManager::WalletManager(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_passphraseReceiver(nullptr)
|
, m_passphraseReceiver(nullptr)
|
||||||
, m_scheduler(this)
|
, m_scheduler(this)
|
||||||
|
, m_currentWallet(nullptr)
|
||||||
{
|
{
|
||||||
m_pimpl = Monero::WalletManagerFactory::getWalletManager();
|
m_pimpl = Monero::WalletManagerFactory::getWalletManager();
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@ public:
|
||||||
NetworkType::Type nettype = NetworkType::MAINNET, quint64 restoreHeight = 0, quint64 kdfRounds = 1);
|
NetworkType::Type nettype = NetworkType::MAINNET, quint64 restoreHeight = 0, quint64 kdfRounds = 1);
|
||||||
|
|
||||||
Q_INVOKABLE Wallet * createWalletFromKeys(const QString &path,
|
Q_INVOKABLE Wallet * createWalletFromKeys(const QString &path,
|
||||||
|
const QString &password,
|
||||||
const QString &language,
|
const QString &language,
|
||||||
NetworkType::Type nettype,
|
NetworkType::Type nettype,
|
||||||
const QString &address,
|
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 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 QVariantMap parse_uri_to_object(const QString &uri) const;
|
||||||
// Q_INVOKABLE bool saveQrCode(const QString &, const QString &) 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
|
// clear/rename wallet cache
|
||||||
Q_INVOKABLE bool clearWalletCache(const QString &fileName) const;
|
Q_INVOKABLE bool clearWalletCache(const QString &fileName) const;
|
||||||
|
@ -182,12 +177,7 @@ signals:
|
||||||
void walletPassphraseNeeded(bool onDevice);
|
void walletPassphraseNeeded(bool onDevice);
|
||||||
void deviceButtonRequest(quint64 buttonCode);
|
void deviceButtonRequest(quint64 buttonCode);
|
||||||
void deviceButtonPressed();
|
void deviceButtonPressed();
|
||||||
void checkUpdatesComplete(
|
void deviceError(const QString &message);
|
||||||
const QString &version,
|
|
||||||
const QString &downloadUrl,
|
|
||||||
const QString &hash,
|
|
||||||
const QString &firstSigner,
|
|
||||||
const QString &secondSigner) const;
|
|
||||||
void miningStatus(bool isMining) const;
|
void miningStatus(bool isMining) const;
|
||||||
void proxyAddressChanged() const;
|
void proxyAddressChanged() const;
|
||||||
|
|
||||||
|
|
1260
src/mainwindow.cpp
113
src/mainwindow.h
|
@ -4,32 +4,16 @@
|
||||||
#ifndef MAINWINDOW_H
|
#ifndef MAINWINDOW_H
|
||||||
#define MAINWINDOW_H
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
#include "src/kdmactouchbar.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QSystemTrayIcon>
|
#include <QSystemTrayIcon>
|
||||||
#include <QScreen>
|
#include <QMenu>
|
||||||
#include <QtWidgets/QMenu>
|
|
||||||
#include <utility>
|
#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 "components.h"
|
||||||
#include "calcwindow.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 "settings.h"
|
||||||
|
|
||||||
#include "dialog/aboutdialog.h"
|
#include "dialog/aboutdialog.h"
|
||||||
#include "dialog/signverifydialog.h"
|
#include "dialog/signverifydialog.h"
|
||||||
#include "dialog/verifyproofdialog.h"
|
#include "dialog/verifyproofdialog.h"
|
||||||
|
@ -38,7 +22,31 @@
|
||||||
#include "dialog/keysdialog.h"
|
#include "dialog/keysdialog.h"
|
||||||
#include "dialog/aboutdialog.h"
|
#include "dialog/aboutdialog.h"
|
||||||
#include "dialog/restoredialog.h"
|
#include "dialog/restoredialog.h"
|
||||||
|
#include "dialog/splashdialog.h"
|
||||||
#include "libwalletqt/Wallet.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 {
|
namespace Ui {
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
@ -64,13 +72,6 @@ public:
|
||||||
static AppContext *getContext();
|
static AppContext *getContext();
|
||||||
~MainWindow() override;
|
~MainWindow() override;
|
||||||
|
|
||||||
qreal screenDpiRef;
|
|
||||||
QRect screenGeo;
|
|
||||||
QRect screenRect;
|
|
||||||
qreal screenDpi;
|
|
||||||
qreal screenDpiPhysical;
|
|
||||||
qreal screenRatio;
|
|
||||||
|
|
||||||
enum Tabs {
|
enum Tabs {
|
||||||
HOME = 0,
|
HOME = 0,
|
||||||
HISTORY,
|
HISTORY,
|
||||||
|
@ -88,8 +89,6 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void initWidgets();
|
|
||||||
void initMenu();
|
|
||||||
void showWizard(WalletWizard::Page startPage);
|
void showWizard(WalletWizard::Page startPage);
|
||||||
void menuNewRestoreClicked();
|
void menuNewRestoreClicked();
|
||||||
void menuQuitClicked();
|
void menuQuitClicked();
|
||||||
|
@ -116,14 +115,17 @@ public slots:
|
||||||
void onRefreshSync(int height, int target);
|
void onRefreshSync(int height, int target);
|
||||||
void onWalletOpenedError(const QString &err);
|
void onWalletOpenedError(const QString &err);
|
||||||
void onWalletCreatedError(const QString &err);
|
void onWalletCreatedError(const QString &err);
|
||||||
void onWalletCreated(Wallet *wallet);
|
|
||||||
void menuWalletCloseClicked();
|
void menuWalletCloseClicked();
|
||||||
void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
|
void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
|
||||||
|
void onDeviceButtonRequest(quint64 code);
|
||||||
void onViewOnBlockExplorer(const QString &txid);
|
void onViewOnBlockExplorer(const QString &txid);
|
||||||
void onResendTransaction(const QString &txid);
|
void onResendTransaction(const QString &txid);
|
||||||
void importContacts();
|
void importContacts();
|
||||||
void showRestoreHeightDialog();
|
void showRestoreHeightDialog();
|
||||||
void importTransaction();
|
void importTransaction();
|
||||||
|
void onDeviceError(const QString &error);
|
||||||
|
void menuHwDeviceClicked();
|
||||||
|
void onUpdatesAvailable(const QJsonObject &updates);
|
||||||
|
|
||||||
// offline tx signing
|
// offline tx signing
|
||||||
void exportKeyImages();
|
void exportKeyImages();
|
||||||
|
@ -148,15 +150,42 @@ public slots:
|
||||||
signals:
|
signals:
|
||||||
void closed();
|
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:
|
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;
|
AppContext *m_ctx;
|
||||||
|
|
||||||
static MainWindow * pMainWindow;
|
static MainWindow * pMainWindow;
|
||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent *event) override;
|
||||||
void cleanupBeforeClose();
|
void cleanupBeforeClose();
|
||||||
void create_status_bar();
|
|
||||||
void initMain();
|
|
||||||
void loadSkins();
|
|
||||||
QString loadStylesheet(const QString &resource);
|
QString loadStylesheet(const QString &resource);
|
||||||
void saveGeo();
|
void saveGeo();
|
||||||
void restoreGeo();
|
void restoreGeo();
|
||||||
|
@ -173,7 +202,10 @@ private:
|
||||||
void showBalanceDialog();
|
void showBalanceDialog();
|
||||||
QString statusDots();
|
QString statusDots();
|
||||||
void bringToFront();
|
void bringToFront();
|
||||||
void centerWidget(QWidget &w);
|
QString getPlatformTag();
|
||||||
|
void displayWalletErrorMsg(const QString &err);
|
||||||
|
QString getHardwareDevice();
|
||||||
|
void setTitle(bool mining);
|
||||||
|
|
||||||
WalletWizard *createWizard(WalletWizard::Page startPage);
|
WalletWizard *createWizard(WalletWizard::Page startPage);
|
||||||
|
|
||||||
|
@ -181,8 +213,12 @@ private:
|
||||||
Settings *m_windowSettings = nullptr;
|
Settings *m_windowSettings = nullptr;
|
||||||
CalcWindow *m_windowCalc = nullptr;
|
CalcWindow *m_windowCalc = nullptr;
|
||||||
RestoreDialog *m_restoreDialog = nullptr;
|
RestoreDialog *m_restoreDialog = nullptr;
|
||||||
AboutDialog *m_aboutDialog = nullptr;
|
|
||||||
XMRigWidget *m_xmrig = nullptr;
|
XMRigWidget *m_xmrig = nullptr;
|
||||||
|
SplashDialog *m_splashDialog = nullptr;
|
||||||
|
|
||||||
|
#ifdef HAS_LOCALMONERO
|
||||||
|
LocalMoneroWidget *m_localMoneroWidget = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
QSystemTrayIcon *m_trayIcon;
|
QSystemTrayIcon *m_trayIcon;
|
||||||
QMenu m_trayMenu;
|
QMenu m_trayMenu;
|
||||||
|
@ -195,6 +231,7 @@ private:
|
||||||
TickerWidget *m_balanceWidget;
|
TickerWidget *m_balanceWidget;
|
||||||
|
|
||||||
// lower status bar
|
// lower status bar
|
||||||
|
QPushButton *m_statusUpdateAvailable;
|
||||||
ClickableLabel *m_statusLabelBalance;
|
ClickableLabel *m_statusLabelBalance;
|
||||||
QLabel *m_statusLabelStatus;
|
QLabel *m_statusLabelStatus;
|
||||||
QLabel *m_statusLabelNetStats;
|
QLabel *m_statusLabelNetStats;
|
||||||
|
@ -203,6 +240,7 @@ private:
|
||||||
StatusBarButton *m_statusBtnPreferences;
|
StatusBarButton *m_statusBtnPreferences;
|
||||||
StatusBarButton *m_statusBtnSeed;
|
StatusBarButton *m_statusBtnSeed;
|
||||||
StatusBarButton *m_statusBtnTor;
|
StatusBarButton *m_statusBtnTor;
|
||||||
|
StatusBarButton *m_statusBtnHwDevice;
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
QAction *m_touchbarActionWelcome;
|
QAction *m_touchbarActionWelcome;
|
||||||
|
@ -210,6 +248,7 @@ private:
|
||||||
QList<QAction *> m_touchbarWalletItems;
|
QList<QAction *> m_touchbarWalletItems;
|
||||||
QList<QAction *> m_touchbarWizardItems;
|
QList<QAction *> m_touchbarWizardItems;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QSignalMapper *m_tabShowHideSignalMapper;
|
QSignalMapper *m_tabShowHideSignalMapper;
|
||||||
QMap<QString, ToggleTab*> m_tabShowHideMapper;
|
QMap<QString, ToggleTab*> m_tabShowHideMapper;
|
||||||
WalletWizard *m_wizard = nullptr;
|
WalletWizard *m_wizard = nullptr;
|
||||||
|
@ -222,13 +261,9 @@ private:
|
||||||
int m_statusDots;
|
int m_statusDots;
|
||||||
bool m_constructingTransaction = false;
|
bool m_constructingTransaction = false;
|
||||||
bool m_statusOverrideActive = false;
|
bool m_statusOverrideActive = false;
|
||||||
|
bool m_showDeviceError = false;
|
||||||
QTimer m_txTimer;
|
QTimer m_txTimer;
|
||||||
|
|
||||||
QIcon m_statusDisconnected;
|
|
||||||
QIcon m_statusConnecting;
|
|
||||||
QIcon m_statusSynchronizing;
|
|
||||||
QIcon m_statusSynchronized;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void menuToggleTabVisible(const QString &key);
|
void menuToggleTabVisible(const QString &key);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1156</width>
|
<width>977</width>
|
||||||
<height>496</height>
|
<height>499</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -293,6 +293,39 @@
|
||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</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>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tabXmrRig">
|
<widget class="QWidget" name="tabXmrRig">
|
||||||
|
@ -319,8 +352,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1156</width>
|
<width>977</width>
|
||||||
<height>30</height>
|
<height>28</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuFile">
|
<widget class="QMenu" name="menuFile">
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "AddressBook.h"
|
#include "AddressBook.h"
|
||||||
#include "ModelUtils.h"
|
#include "ModelUtils.h"
|
||||||
#include "utils/utils.h"
|
#include "utils/utils.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
|
||||||
AddressBookModel::AddressBookModel(QObject *parent, AddressBook *addressBook)
|
AddressBookModel::AddressBookModel(QObject *parent, AddressBook *addressBook)
|
||||||
: QAbstractTableModel(parent),
|
: QAbstractTableModel(parent),
|
||||||
|
@ -13,7 +14,7 @@ AddressBookModel::AddressBookModel(QObject *parent, AddressBook *addressBook)
|
||||||
{
|
{
|
||||||
connect(m_addressBook, &AddressBook::refreshStarted, this, &AddressBookModel::startReset);
|
connect(m_addressBook, &AddressBook::refreshStarted, this, &AddressBookModel::startReset);
|
||||||
connect(m_addressBook, &AddressBook::refreshFinished, this, &AddressBookModel::endReset);
|
connect(m_addressBook, &AddressBook::refreshFinished, this, &AddressBookModel::endReset);
|
||||||
m_contactIcon = QIcon(":/assets/images/person.svg");
|
m_contactIcon = icons()->icon("person.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddressBookModel::startReset(){
|
void AddressBookModel::startReset(){
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
#include "ModelUtils.h"
|
#include "ModelUtils.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "utils/ColorScheme.h"
|
#include "utils/ColorScheme.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
|
||||||
#include <QBrush>
|
#include <QBrush>
|
||||||
#include <QFont>
|
|
||||||
|
|
||||||
CoinsModel::CoinsModel(QObject *parent, Coins *coins)
|
CoinsModel::CoinsModel(QObject *parent, Coins *coins)
|
||||||
: QAbstractTableModel(parent),
|
: QAbstractTableModel(parent),
|
||||||
|
@ -17,9 +17,6 @@ CoinsModel::CoinsModel(QObject *parent, Coins *coins)
|
||||||
{
|
{
|
||||||
connect(m_coins, &Coins::refreshStarted, this, &CoinsModel::startReset);
|
connect(m_coins, &Coins::refreshStarted, this, &CoinsModel::startReset);
|
||||||
connect(m_coins, &Coins::refreshFinished, this, &CoinsModel::endReset);
|
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(){
|
void CoinsModel::startReset(){
|
||||||
|
@ -88,10 +85,10 @@ QVariant CoinsModel::data(const QModelIndex &index, int role) const
|
||||||
case KeyImageKnown:
|
case KeyImageKnown:
|
||||||
{
|
{
|
||||||
if (cInfo.keyImageKnown()) {
|
if (cInfo.keyImageKnown()) {
|
||||||
result = QVariant(m_eye);
|
result = QVariant(icons()->icon("eye1.png"));
|
||||||
}
|
}
|
||||||
else {
|
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;
|
QVariant parseTransactionInfo(const CoinsInfo &cInfo, int column, int role) const;
|
||||||
|
|
||||||
Coins *m_coins;
|
Coins *m_coins;
|
||||||
QIcon m_eye;
|
|
||||||
QIcon m_eyeBlind;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FEATHER_COINSMODEL_H
|
#endif //FEATHER_COINSMODEL_H
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "TransactionHistoryProxyModel.h"
|
#include "TransactionHistoryProxyModel.h"
|
||||||
#include "libwalletqt/TransactionInfo.h"
|
#include "libwalletqt/TransactionInfo.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <QActionGroup>
|
#include <QActionGroup>
|
||||||
|
|
||||||
#include "TransactionHistoryModel.h"
|
#include "TransactionHistoryModel.h"
|
||||||
|
#include "TransactionHistoryProxyModel.h"
|
||||||
|
|
||||||
class HistoryView : public QTreeView
|
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 "NodeModel.h"
|
||||||
#include "utils/nodes.h"
|
#include "utils/nodes.h"
|
||||||
#include "utils/ColorScheme.h"
|
#include "utils/ColorScheme.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
|
||||||
NodeModel::NodeModel(int nodeSource, QObject *parent)
|
NodeModel::NodeModel(int nodeSource, QObject *parent)
|
||||||
: QAbstractTableModel(parent)
|
: QAbstractTableModel(parent)
|
||||||
, m_nodeSource(nodeSource)
|
, m_nodeSource(nodeSource)
|
||||||
, m_offline(QIcon(":/assets/images/expired_icon.png"))
|
, m_offline(icons()->icon("expired_icon.png"))
|
||||||
, m_online(QIcon(":/assets/images/confirmed_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) {
|
if(role == Qt::DisplayRole) {
|
||||||
switch(index.column()) {
|
switch(index.column()) {
|
||||||
case NodeModel::URL:
|
case NodeModel::URL:
|
||||||
return node.full;
|
return node.toFullAddress();
|
||||||
case NodeModel::Height:
|
case NodeModel::Height:
|
||||||
if(node.online)
|
if(node.online)
|
||||||
return node.height == 0 ? QString("-") : QString::number(node.height);
|
return node.height == 0 ? QString("-") : QString::number(node.height);
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include <QPoint>
|
#include <QPoint>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QBrush>
|
#include <QBrush>
|
||||||
#include <QFont>
|
|
||||||
|
|
||||||
SubaddressModel::SubaddressModel(QObject *parent, Subaddress *subaddress)
|
SubaddressModel::SubaddressModel(QObject *parent, Subaddress *subaddress)
|
||||||
: QAbstractTableModel(parent),
|
: QAbstractTableModel(parent),
|
||||||
|
|
|
@ -5,21 +5,16 @@
|
||||||
#include "TransactionHistory.h"
|
#include "TransactionHistory.h"
|
||||||
#include "TransactionInfo.h"
|
#include "TransactionInfo.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
#include "utils/config.h"
|
||||||
#include "utils/ColorScheme.h"
|
#include "utils/ColorScheme.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
#include "utils/AppData.h"
|
||||||
#include "ModelUtils.h"
|
#include "ModelUtils.h"
|
||||||
|
|
||||||
TransactionHistoryModel::TransactionHistoryModel(QObject *parent)
|
TransactionHistoryModel::TransactionHistoryModel(QObject *parent)
|
||||||
: QAbstractTableModel(parent),
|
: QAbstractTableModel(parent),
|
||||||
m_transactionHistory(nullptr)
|
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) {
|
void TransactionHistoryModel::setTransactionHistory(TransactionHistory *th) {
|
||||||
|
@ -86,21 +81,21 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
|
||||||
case Column::Date:
|
case Column::Date:
|
||||||
{
|
{
|
||||||
if (tInfo.isFailed())
|
if (tInfo.isFailed())
|
||||||
result = QVariant(m_warning);
|
result = QVariant(icons()->icon("warning.png"));
|
||||||
else if (tInfo.isPending())
|
else if (tInfo.isPending())
|
||||||
result = QVariant(m_unconfirmedTx);
|
result = QVariant(icons()->icon("unconfirmed.png"));
|
||||||
else if (tInfo.confirmations() <= (1.0/5.0 * tInfo.confirmationsRequired()))
|
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()))
|
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()))
|
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()))
|
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())
|
else if (tInfo.confirmations() < tInfo.confirmationsRequired())
|
||||||
result = QVariant(m_clock5);
|
result = QVariant(icons()->icon("clock5.png"));
|
||||||
else if (tInfo.confirmations())
|
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:
|
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)
|
if (usd_price == 0.0)
|
||||||
return QVariant("?");
|
return QVariant("?");
|
||||||
|
|
||||||
double usd_amount = usd_price * (tInfo.balanceDelta() / globals::cdiv);
|
double usd_amount = usd_price * (tInfo.balanceDelta() / globals::cdiv);
|
||||||
if(this->preferredFiatSymbol != "USD")
|
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) {
|
if (role == Qt::UserRole) {
|
||||||
return usd_amount;
|
return usd_amount;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include "appcontext.h"
|
|
||||||
|
|
||||||
class TransactionHistory;
|
class TransactionHistory;
|
||||||
class TransactionInfo;
|
class TransactionInfo;
|
||||||
|
@ -54,14 +53,6 @@ private:
|
||||||
QVariant parseTransactionInfo(const TransactionInfo &tInfo, int column, int role) const;
|
QVariant parseTransactionInfo(const TransactionInfo &tInfo, int column, int role) const;
|
||||||
|
|
||||||
TransactionHistory * m_transactionHistory;
|
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
|
#endif // TRANSACTIONHISTORYMODEL_H
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
#include "receivewidget.h"
|
#include "receivewidget.h"
|
||||||
#include "model/ModelUtils.h"
|
#include "model/ModelUtils.h"
|
||||||
#include "dialog/qrcodedialog.h"
|
#include "dialog/qrcodedialog.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
ReceiveWidget::ReceiveWidget(QWidget *parent) :
|
ReceiveWidget::ReceiveWidget(QWidget *parent) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
|
@ -29,9 +31,7 @@ ReceiveWidget::ReceiveWidget(QWidget *parent) :
|
||||||
connect(m_showTransactionsAction, &QAction::triggered, this, &ReceiveWidget::onShowTransactions);
|
connect(m_showTransactionsAction, &QAction::triggered, this, &ReceiveWidget::onShowTransactions);
|
||||||
connect(ui->addresses, &QTreeView::customContextMenuRequested, this, &ReceiveWidget::showContextMenu);
|
connect(ui->addresses, &QTreeView::customContextMenuRequested, this, &ReceiveWidget::showContextMenu);
|
||||||
|
|
||||||
connect(ui->btn_generateSubaddress, &QPushButton::clicked, [=]() {
|
connect(ui->btn_generateSubaddress, &QPushButton::clicked, this, &ReceiveWidget::generateSubaddress);
|
||||||
emit generateSubaddress();
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(ui->qrCode, &ClickableLabel::clicked, this, &ReceiveWidget::showQrCodeDialog);
|
connect(ui->qrCode, &ClickableLabel::clicked, this, &ReceiveWidget::showQrCodeDialog);
|
||||||
connect(ui->label_addressSearch, &QLineEdit::textChanged, this, &ReceiveWidget::setSearchFilter);
|
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);
|
auto *menu = new QMenu(ui->addresses);
|
||||||
|
|
||||||
menu->addAction(QIcon(":/assets/images/copy.png"), "Copy address", this, &ReceiveWidget::copyAddress);
|
menu->addAction(icons()->icon("copy.png"), "Copy address", this, &ReceiveWidget::copyAddress);
|
||||||
menu->addAction(QIcon(":/assets/images/copy.png"), "Copy label", this, &ReceiveWidget::copyLabel);
|
menu->addAction(icons()->icon("copy.png"), "Copy label", this, &ReceiveWidget::copyLabel);
|
||||||
menu->addAction(QIcon(":/assets/images/edit.png"), "Edit label", this, &ReceiveWidget::editLabel);
|
menu->addAction(icons()->icon("edit.png"), "Edit label", this, &ReceiveWidget::editLabel);
|
||||||
|
|
||||||
if (isUsed) {
|
if (isUsed) {
|
||||||
menu->addAction(m_showTransactionsAction);
|
menu->addAction(m_showTransactionsAction);
|
||||||
|
@ -103,6 +103,10 @@ void ReceiveWidget::showContextMenu(const QPoint &point) {
|
||||||
menu->addAction("Hide address", this, &ReceiveWidget::hideAddress);
|
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));
|
menu->popup(ui->addresses->viewport()->mapToGlobal(point));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +168,21 @@ void ReceiveWidget::showAddress()
|
||||||
m_proxyModel->setHiddenAddresses(this->getHiddenAddresses());
|
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(){
|
void ReceiveWidget::updateQrCode(){
|
||||||
QModelIndex index = ui->addresses->currentIndex();
|
QModelIndex index = ui->addresses->currentIndex();
|
||||||
if (!index.isValid()) {
|
if (!index.isValid()) {
|
||||||
|
|
|
@ -40,13 +40,14 @@ public slots:
|
||||||
void resetModel();
|
void resetModel();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void generateSubaddress();
|
|
||||||
void showTransactions(const QString& address);
|
void showTransactions(const QString& address);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void showHeaderMenu(const QPoint& position);
|
void showHeaderMenu(const QPoint& position);
|
||||||
void hideAddress();
|
void hideAddress();
|
||||||
void showAddress();
|
void showAddress();
|
||||||
|
void showOnDevice();
|
||||||
|
void generateSubaddress();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::ReceiveWidget *ui;
|
Ui::ReceiveWidget *ui;
|
||||||
|
|
|
@ -6,10 +6,11 @@
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "ui_sendwidget.h"
|
#include "ui_sendwidget.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
#include "utils/AppData.h"
|
||||||
|
|
||||||
SendWidget::SendWidget(QWidget *parent) :
|
SendWidget::SendWidget(QWidget *parent)
|
||||||
QWidget(parent),
|
: QWidget(parent)
|
||||||
ui(new Ui::SendWidget)
|
, ui(new Ui::SendWidget)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
m_ctx = MainWindow::getContext();
|
m_ctx = MainWindow::getContext();
|
||||||
|
@ -20,6 +21,12 @@ SendWidget::SendWidget(QWidget *parent) :
|
||||||
QValidator *validator = new QRegExpValidator(rx, this);
|
QValidator *validator = new QRegExpValidator(rx, this);
|
||||||
ui->lineAmount->setValidator(validator);
|
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->btnSend, &QPushButton::clicked, this, &SendWidget::sendClicked);
|
||||||
connect(ui->btnClear, &QPushButton::clicked, this, &SendWidget::clearClicked);
|
connect(ui->btnClear, &QPushButton::clicked, this, &SendWidget::clearClicked);
|
||||||
connect(ui->btnMax, &QPushButton::clicked, this, &SendWidget::btnMaxClicked);
|
connect(ui->btnMax, &QPushButton::clicked, this, &SendWidget::btnMaxClicked);
|
||||||
|
@ -140,7 +147,7 @@ void SendWidget::sendClicked() {
|
||||||
amounts.push_back(output.amount);
|
amounts.push_back(output.amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit createTransactionMultiDest(addresses, amounts, description);
|
m_ctx->onCreateTransactionMultiDest(addresses, amounts, description);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,20 +159,20 @@ void SendWidget::sendClicked() {
|
||||||
QMessageBox::warning(this, "Amount error", "Invalid amount specified.");
|
QMessageBox::warning(this, "Amount error", "Invalid amount specified.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit createTransaction(recipient, amount, description, sendAll);
|
m_ctx->onCreateTransaction(recipient, amount, description, sendAll);
|
||||||
} else {
|
} else {
|
||||||
amount = WalletManager::amountFromDouble(this->conversionAmount());
|
amount = WalletManager::amountFromDouble(this->conversionAmount());
|
||||||
if (amount == 0) {
|
if (amount == 0) {
|
||||||
QMessageBox::warning(this, "Fiat conversion error", "Could not create transaction.");
|
QMessageBox::warning(this, "Fiat conversion error", "Could not create transaction.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit createTransaction(recipient, amount, description, false);
|
m_ctx->onCreateTransaction(recipient, amount, description, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendWidget::aliasClicked() {
|
void SendWidget::aliasClicked() {
|
||||||
auto address = ui->lineAddress->text();
|
auto address = ui->lineAddress->text();
|
||||||
emit resolveOpenAlias(address);
|
m_ctx->onOpenAliasResolve(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendWidget::clearClicked() {
|
void SendWidget::clearClicked() {
|
||||||
|
@ -195,7 +202,7 @@ void SendWidget::updateConversionLabel() {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
auto preferredFiatCurrency = config()->get(Config::preferredFiatCurrency).toString();
|
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);
|
return QString("~%1 %2").arg(QString::number(conversionAmount, 'f', 2), preferredFiatCurrency);
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
@ -206,7 +213,7 @@ void SendWidget::updateConversionLabel() {
|
||||||
|
|
||||||
double SendWidget::conversionAmount() {
|
double SendWidget::conversionAmount() {
|
||||||
QString currency = ui->comboCurrencySelection->currentText();
|
QString currency = ui->comboCurrencySelection->currentText();
|
||||||
return AppContext::prices->convert(currency, "XMR", this->amountDouble());
|
return appData()->prices.convert(currency, "XMR", this->amountDouble());
|
||||||
}
|
}
|
||||||
|
|
||||||
quint64 SendWidget::amount() {
|
quint64 SendWidget::amount() {
|
||||||
|
|
|
@ -43,11 +43,6 @@ public slots:
|
||||||
void onInitiateTransaction();
|
void onInitiateTransaction();
|
||||||
void onEndTransaction();
|
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:
|
private:
|
||||||
void setupComboBox();
|
void setupComboBox();
|
||||||
double amountDouble();
|
double amountDouble();
|
||||||
|
|
|
@ -17,7 +17,7 @@ Settings::Settings(QWidget *parent) :
|
||||||
this->setWindowIcon(QIcon("://assets/images/appicons/64x64.png"));
|
this->setWindowIcon(QIcon("://assets/images/appicons/64x64.png"));
|
||||||
|
|
||||||
ui->tabWidget->setTabVisible(2, false);
|
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->btnCopyToClipboard, &QPushButton::clicked, this, &Settings::copyToClipboard);
|
||||||
connect(ui->checkBox_multiBroadcast, &QCheckBox::toggled, [](bool toggled){
|
connect(ui->checkBox_multiBroadcast, &QCheckBox::toggled, [](bool toggled){
|
||||||
|
@ -70,8 +70,6 @@ Settings::Settings(QWidget *parent) :
|
||||||
ui->comboBox_timeFormat->setCurrentIndex(m_timeFormats.indexOf(timeFormatSetting));
|
ui->comboBox_timeFormat->setCurrentIndex(m_timeFormats.indexOf(timeFormatSetting));
|
||||||
|
|
||||||
connect(ui->comboBox_skin, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_skinChanged);
|
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_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_dateFormat, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_dateFormatChanged);
|
||||||
connect(ui->comboBox_timeFormat, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_timeFormatChanged);
|
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);
|
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();
|
this->adjustSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,16 +126,21 @@ void Settings::comboBox_skinChanged(int pos) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::comboBox_blockExplorerChanged(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);
|
config()->set(Config::blockExplorer, blockExplorer);
|
||||||
emit blockExplorerChanged(blockExplorer);
|
emit blockExplorerChanged(blockExplorer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::comboBox_redditFrontendChanged(int pos) {
|
void Settings::comboBox_redditFrontendChanged(int pos) {
|
||||||
QString redditFrontend = ui->comboBox_redditFrontend->currentText();
|
QString redditFrontend = ui->combo_redditFrontend->currentText();
|
||||||
config()->set(Config::redditFrontend, redditFrontend);
|
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) {
|
void Settings::comboBox_amountPrecisionChanged(int pos) {
|
||||||
config()->set(Config::amountPrecision, pos);
|
config()->set(Config::amountPrecision, pos);
|
||||||
emit amountPrecisionChanged(pos);
|
emit amountPrecisionChanged(pos);
|
||||||
|
@ -161,6 +174,15 @@ void Settings::setupSkinCombobox() {
|
||||||
ui->comboBox_skin->insertItems(0, m_skins);
|
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() {
|
Settings::~Settings() {
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,14 +37,17 @@ public slots:
|
||||||
void checkboxExternalLinkWarn();
|
void checkboxExternalLinkWarn();
|
||||||
void fiatCurrencySelected(int index);
|
void fiatCurrencySelected(int index);
|
||||||
void comboBox_skinChanged(int pos);
|
void comboBox_skinChanged(int pos);
|
||||||
void comboBox_blockExplorerChanged(int pos);
|
|
||||||
void comboBox_redditFrontendChanged(int pos);
|
|
||||||
void comboBox_amountPrecisionChanged(int pos);
|
void comboBox_amountPrecisionChanged(int pos);
|
||||||
void comboBox_dateFormatChanged(int pos);
|
void comboBox_dateFormatChanged(int pos);
|
||||||
void comboBox_timeFormatChanged(int pos);
|
void comboBox_timeFormatChanged(int pos);
|
||||||
|
|
||||||
|
void comboBox_blockExplorerChanged(int pos);
|
||||||
|
void comboBox_redditFrontendChanged(int pos);
|
||||||
|
void comboBox_localMoneroFrontendChanged(int pos);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupSkinCombobox();
|
void setupSkinCombobox();
|
||||||
|
void setupLocalMoneroFrontendCombobox();
|
||||||
|
|
||||||
AppContext *m_ctx;
|
AppContext *m_ctx;
|
||||||
Ui::Settings *ui;
|
Ui::Settings *ui;
|
||||||
|
|
145
src/settings.ui
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1123</width>
|
<width>915</width>
|
||||||
<height>555</height>
|
<height>519</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -133,90 +133,33 @@
|
||||||
<widget class="QComboBox" name="comboBox_skin"/>
|
<widget class="QComboBox" name="comboBox_skin"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<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">
|
<widget class="QLabel" name="label_9">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Amount precision:</string>
|
<string>Amount precision:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QComboBox" name="comboBox_amountPrecision"/>
|
<widget class="QComboBox" name="comboBox_amountPrecision"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="label_10">
|
<widget class="QLabel" name="label_10">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Date format:</string>
|
<string>Date format:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QComboBox" name="comboBox_dateFormat"/>
|
<widget class="QComboBox" name="comboBox_dateFormat"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0">
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="label_11">
|
<widget class="QLabel" name="label_11">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Time format:</string>
|
<string>Time format:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QComboBox" name="comboBox_timeFormat"/>
|
<widget class="QComboBox" name="comboBox_timeFormat"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
@ -420,6 +363,80 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</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">
|
<widget class="QWidget" name="tab_console">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset theme=":/assets/images/terminal.png">
|
<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
|
// Copyright (c) 2012 thomasv@gitorious
|
||||||
|
|
||||||
#include "ColorScheme.h"
|
#include "ColorScheme.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
bool ColorScheme::darkScheme = false;
|
bool ColorScheme::darkScheme = false;
|
||||||
ColorSchemeItem ColorScheme::GREEN = ColorSchemeItem("#117c11", "#8af296");
|
ColorSchemeItem ColorScheme::GREEN = ColorSchemeItem("#117c11", "#8af296");
|
||||||
|
|