Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d41d526da0 |
@@ -14,10 +14,9 @@ steps:
|
|||||||
commands:
|
commands:
|
||||||
- git config --global url."http://gitea:3000/tor/".insteadOf https://git.torproject.org/
|
- git config --global url."http://gitea:3000/tor/".insteadOf https://git.torproject.org/
|
||||||
- git config --global url."http://gitea:3000/".insteadOf https://github.com/
|
- git config --global url."http://gitea:3000/".insteadOf https://github.com/
|
||||||
- git config --global url."http://gitea:3000/".insteadOf https://git.wownero.com/
|
- git submodule update --init --depth 120 monero
|
||||||
- git submodule update --init monero
|
- git submodule update --init --depth 120 --recursive monero
|
||||||
- git submodule update --init --depth 1 --recursive monero
|
- TOR="/usr/local/tor/bin/tor" XMRIG="/xmrig/xmrig" make -j6 release-static
|
||||||
- TOR_BIN="/usr/local/tor/bin/tor" make -j8 release-static
|
|
||||||
environment:
|
environment:
|
||||||
OPENSSL_ROOT_DIR: /usr/local/openssl/
|
OPENSSL_ROOT_DIR: /usr/local/openssl/
|
||||||
CMAKEFLAGS_EXTRA: -DFETCH_DEPS=Off
|
CMAKEFLAGS_EXTRA: -DFETCH_DEPS=Off
|
||||||
@@ -46,19 +45,17 @@ volumes:
|
|||||||
path: /build/feather_files/files/linux-release/
|
path: /build/feather_files/files/linux-release/
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: linux-release-appimage
|
name: linux-release-appimage
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: build
|
||||||
image: feather:appimage
|
image: feather:appimage
|
||||||
volumes:
|
|
||||||
- name: files_linux_release
|
|
||||||
path: /files
|
|
||||||
commands:
|
commands:
|
||||||
- export FN="feather-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
|
- export FN="feather-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
|
||||||
- export BRANCH="$DRONE_SOURCE_BRANCH"
|
- export BRANCH="$DRONE_SOURCE_BRANCH"
|
||||||
- cp /files/$BRANCH/$FN feather.zip
|
|
||||||
- bash ./contrib/build-appimage.sh
|
- bash ./contrib/build-appimage.sh
|
||||||
- name: deploy
|
- name: deploy
|
||||||
image: feather:appimage
|
image: feather:appimage
|
||||||
@@ -72,18 +69,18 @@ steps:
|
|||||||
- echo "writing to $TARGET_DIR/$FN"
|
- echo "writing to $TARGET_DIR/$FN"
|
||||||
- mv "Feather-1.0-x86_64.AppImage" "$TARGET_DIR/$FN"
|
- mv "Feather-1.0-x86_64.AppImage" "$TARGET_DIR/$FN"
|
||||||
- echo "[*] written to https://build.featherwallet.org/files/linux-release-appimage/$DRONE_SOURCE_BRANCH/$FN"
|
- echo "[*] written to https://build.featherwallet.org/files/linux-release-appimage/$DRONE_SOURCE_BRANCH/$FN"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- name: files_linux_appimage
|
- name: files_linux_appimage
|
||||||
host:
|
host:
|
||||||
path: /build/feather_files/files/linux-release-appimage/
|
path: /build/feather_files/files/linux-release-appimage/
|
||||||
- name: files_linux_release
|
|
||||||
host:
|
|
||||||
path: /build/feather_files/files/linux-release/
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: windows-mxe-release
|
name: windows-mxe-release
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: build
|
||||||
image: feather:win
|
image: feather:win
|
||||||
@@ -95,10 +92,9 @@ steps:
|
|||||||
commands:
|
commands:
|
||||||
- git config --global url."http://gitea:3000/tor/".insteadOf https://git.torproject.org/
|
- git config --global url."http://gitea:3000/tor/".insteadOf https://git.torproject.org/
|
||||||
- git config --global url."http://gitea:3000/".insteadOf https://github.com/
|
- git config --global url."http://gitea:3000/".insteadOf https://github.com/
|
||||||
- git config --global url."http://gitea:3000/".insteadOf https://git.wownero.com/
|
- git submodule update --init --depth 120 monero
|
||||||
- git submodule update --init monero
|
- git submodule update --init --depth 120 --recursive monero
|
||||||
- git submodule update --init --depth 1 --recursive monero
|
- PATH="/mxe/usr/bin/:$PATH" TOR="/mxe/usr/x86_64-w64-mingw32.static/bin/tor.exe" XMRIG="/xmrig/xmrig.exe" make -j6 windows-mxe-release
|
||||||
- PATH="/mxe/usr/bin/:$PATH" TOR_BIN="/mxe/usr/x86_64-w64-mingw32.static/bin/tor.exe" make -j8 windows-mxe-release
|
|
||||||
environment:
|
environment:
|
||||||
CMAKEFLAGS_EXTRA: -DFETCH_DEPS=Off
|
CMAKEFLAGS_EXTRA: -DFETCH_DEPS=Off
|
||||||
- name: deploy
|
- name: deploy
|
||||||
@@ -115,6 +111,7 @@ steps:
|
|||||||
- echo "writing to $TARGET_DIR/$FN"
|
- echo "writing to $TARGET_DIR/$FN"
|
||||||
- zip -j "$TARGET_DIR/$FN" build/feather.log build/bin/feather.exe
|
- zip -j "$TARGET_DIR/$FN" build/feather.log build/bin/feather.exe
|
||||||
- echo "[*] written to https://build.featherwallet.org/files/windows-mxe-release/$DRONE_SOURCE_BRANCH/$FN"
|
- echo "[*] written to https://build.featherwallet.org/files/windows-mxe-release/$DRONE_SOURCE_BRANCH/$FN"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- name: ccache_win_release
|
- name: ccache_win_release
|
||||||
host:
|
host:
|
||||||
@@ -124,6 +121,7 @@ volumes:
|
|||||||
path: /build/feather_files/files/windows-mxe-release/
|
path: /build/feather_files/files/windows-mxe-release/
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: mac-release
|
name: mac-release
|
||||||
@@ -136,6 +134,7 @@ steps:
|
|||||||
path: /files
|
path: /files
|
||||||
commands:
|
commands:
|
||||||
- mkdir -p build
|
- mkdir -p build
|
||||||
|
- scp -P22 utils/build_macos.sh administrator@steve.jobs.xmr.pm:build_macos.sh
|
||||||
- ssh administrator@steve.jobs.xmr.pm "chmod +x build_macos.sh && PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin ~/build_macos.sh $DRONE_COMMIT_SHA"
|
- ssh administrator@steve.jobs.xmr.pm "chmod +x build_macos.sh && PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin ~/build_macos.sh $DRONE_COMMIT_SHA"
|
||||||
- scp -P22 administrator@steve.jobs.xmr.pm:feather.zip build/feather.zip
|
- scp -P22 administrator@steve.jobs.xmr.pm:feather.zip build/feather.zip
|
||||||
- name: deploy
|
- name: deploy
|
||||||
@@ -157,6 +156,6 @@ volumes:
|
|||||||
path: /build/feather_files/files/mac-release/
|
path: /build/feather_files/files/mac-release/
|
||||||
---
|
---
|
||||||
kind: signature
|
kind: signature
|
||||||
hmac: f16a0379280e2e89987930d635ec6fb938d67732fdaf4ddc488f2a9db64bda2c
|
hmac: 527d334190a8a824b3b781a05ae4c7d87f4fa2bc37ebc53a96db91f925fa4a52
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
[submodule "monero"]
|
[submodule "monero"]
|
||||||
path = monero
|
path = monero
|
||||||
url = https://git.wownero.com/feather/monero.git
|
url = https://git.wownero.com/feather/monero.git
|
||||||
|
[submodule "contrib/torsocks"]
|
||||||
|
path = contrib/torsocks
|
||||||
|
url = https://git.torproject.org/torsocks.git
|
||||||
[submodule "contrib/tor"]
|
[submodule "contrib/tor"]
|
||||||
path = contrib/tor
|
path = contrib/tor
|
||||||
url = https://git.torproject.org/tor.git
|
url = https://git.torproject.org/tor.git
|
||||||
|
|||||||
@@ -10,44 +10,6 @@ Static builds via Docker are done in 3 steps:
|
|||||||
2. Creating a base Docker image
|
2. Creating a base Docker image
|
||||||
3. Using the base image to compile a build
|
3. Using the base image to compile a build
|
||||||
|
|
||||||
### Linux (reproducible)
|
|
||||||
|
|
||||||
The docker image for reproducible Linux static builds uses Ubuntu 16.04 and compiles the required libraries statically
|
|
||||||
so that the resulting Feather binary is static. For more information, check the Dockerfile: `Dockerfile`.
|
|
||||||
|
|
||||||
#### 1. Clone
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone --branch master --recursive https://git.wownero.com/feather/feather.git
|
|
||||||
cd feather
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace `master` with the desired version tag (e.g. `beta-1`) to build the release binary.
|
|
||||||
|
|
||||||
#### 2. Base image
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker build --tag feather:linux --build-arg THREADS=4 .
|
|
||||||
```
|
|
||||||
|
|
||||||
Building the base image takes a while. You only need to build the base image once.
|
|
||||||
|
|
||||||
#### 3. Build
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run --rm -it -v $PWD:/feather --env OPENSSL_ROOT_DIR=/usr/local/openssl/ -w /feather feather:linux sh -c 'TOR_BIN="/usr/local/tor/bin/tor" make release-static -j4'
|
|
||||||
```
|
|
||||||
|
|
||||||
If you're re-running a build make sure to `rm -rf build/` first.
|
|
||||||
|
|
||||||
The resulting binary can be found in `build/bin/feather`.
|
|
||||||
|
|
||||||
Hashes for tagged commits should match:
|
|
||||||
|
|
||||||
```
|
|
||||||
beta-1: d1a52e3bac1abbae4adda1fc88cb2a7a06fbd61085868421897c6a4f3f4eb091 feather
|
|
||||||
```
|
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
The docker image for Windows static compiles uses Ubuntu 18.04 and installs [mxe](https://mxe.cc) from [our git](https://git.wownero.com/feather/mxe/src/branch/feather-patch),
|
The docker image for Windows static compiles uses Ubuntu 18.04 and installs [mxe](https://mxe.cc) from [our git](https://git.wownero.com/feather/mxe/src/branch/feather-patch),
|
||||||
@@ -73,12 +35,45 @@ Note: You only need to build the base image once.
|
|||||||
#### 3. Build
|
#### 3. Build
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --rm -it -v /tmp/ccache:/root/.ccache -v PATH_TO_FEATHER:/feather -w /feather feather:win /bin/bash -c 'PATH="/mxe/usr/bin/:$PATH" TOR_BIN="/mxe/usr/x86_64-w64-mingw32.static/bin/tor.exe" make windows-mxe-release -j8'
|
docker run --rm -it -v /tmp/ccache:/root/.ccache -v /root/feather:/feather -w /feather feather:win /bin/bash -c 'PATH="/mxe/usr/bin/:$PATH" TOR="/mxe/usr/x86_64-w64-mingw32.static/bin/tor.exe" XMRIG="/xmrig/xmrig.exe" make windows-mxe-release -j8'
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `PATH_TO_FEATHER` with the absolute path to Feather locally.
|
||||||
|
|
||||||
|
The resulting binary can be found in `build/bin/feather.exe`.
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
The docker image for Linux static compiles uses Ubuntu 18.04 and compiles the required libraries statically so that
|
||||||
|
the resulting Feather binary is static. It comes with OpenSSL 1.1.1g, Qt 5.15.0 (OpenGL disabled). For more information,
|
||||||
|
check the Dockerfile: `Dockerfile`.
|
||||||
|
|
||||||
|
#### 1. Clone
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone --recursive https://git.wownero.com/feather/feather.git
|
||||||
|
cd feather
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Base image
|
||||||
|
|
||||||
|
Warning: Building the base image takes a while, go prepare some dinner.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build --tag feather:linux --build-arg THREADS=8 .
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: You only need to build the base image once.
|
||||||
|
|
||||||
|
#### 3. Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --env OPENSSL_ROOT_DIR=/usr/local/openssl/ --rm -it -v /tmp/ccache:/root/.ccache -v PATH_TO_FEATHER:/feather -w /feather feather:linux sh -c 'TOR="/usr/local/tor/bin/tor" XMRIG="/xmrig/xmrig" make release-static -j8'
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace `PATH_TO_FEATHER` with the absolute path to Feather locally.
|
Replace `PATH_TO_FEATHER` with the absolute path to Feather locally.
|
||||||
|
|
||||||
The resulting binary can be found in `build/bin/feather.exe`.
|
The resulting binary can be found in `build/bin/feather`.
|
||||||
|
|
||||||
## macOS
|
## macOS
|
||||||
|
|
||||||
@@ -103,4 +98,4 @@ Build Feather.
|
|||||||
CMAKE_PREFIX_PATH=~/Qt5.15.1/5.15.1/clang_64 make mac-release
|
CMAKE_PREFIX_PATH=~/Qt5.15.1/5.15.1/clang_64 make mac-release
|
||||||
```
|
```
|
||||||
|
|
||||||
The resulting Mac OS application can be found `build/bin/feather.app` and will **not** have Tor embedded.
|
The resulting Mac OS application can be found `build/bin/feather.app` and will **not** have Tor embedded.
|
||||||
@@ -7,13 +7,14 @@ 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-3")
|
set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
|
||||||
|
|
||||||
option(FETCH_DEPS "Download dependencies if they are not found" ON)
|
option(FETCH_DEPS "Download dependencies if they are not found" ON)
|
||||||
option(XMRTO "Include Xmr.To module" ON)
|
option(XMRTO "Include Xmr.To module" ON)
|
||||||
option(XMRIG "Include XMRig module" ON)
|
option(MORPHTOKEN "Include MorphToken module" ON)
|
||||||
option(TOR_BIN "Path to Tor binary to embed inside Feather" OFF)
|
option(XMRIG "Path to XMRig binary to embed inside Feather" OFF)
|
||||||
|
option(TOR "Path to Tor binary to embed inside Feather" OFF)
|
||||||
|
option(TOR_VERSION "Optional git hash or tag of embedded Tor version" "tor-0.4.3.5")
|
||||||
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)
|
||||||
option(DONATE_BEG "Prompt donation window every once in a while" ON)
|
option(DONATE_BEG "Prompt donation window every once in a while" ON)
|
||||||
@@ -30,7 +31,7 @@ if(DEBUG)
|
|||||||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(MONERO_HEAD "85b0b4f73aa6114e3ff91207aa94ad2a15c939a2")
|
set(MONERO_HEAD "2c41634c13994a8a112a1e3a61e5e0b456791a97")
|
||||||
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)
|
||||||
@@ -180,8 +181,8 @@ if("$ENV{DRONE}" STREQUAL "true")
|
|||||||
message(STATUS "We are inside a static compile with Drone CI")
|
message(STATUS "We are inside a static compile with Drone CI")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# To build Feather with embedded (and static) Tor, pass CMake -DTOR_BIN=/path/to/tor
|
# To build Feather with embedded (and static) Tor, pass CMake -DTOR=/path/to/tor
|
||||||
if(TOR_BIN)
|
if(TOR)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
execute_process(COMMAND bash -c "touch ${CMAKE_CURRENT_SOURCE_DIR}/src/tor/libevent-2.1.7.dylib")
|
execute_process(COMMAND bash -c "touch ${CMAKE_CURRENT_SOURCE_DIR}/src/tor/libevent-2.1.7.dylib")
|
||||||
endif()
|
endif()
|
||||||
@@ -190,16 +191,34 @@ if(TOR_BIN)
|
|||||||
# - linux: See `Dockerfile`
|
# - linux: See `Dockerfile`
|
||||||
# - windows: https://github.com/mxe/mxe/blob/1024dc7d2db5eb7d5d3c64a2c12b5f592572f1ce/plugins/apps/tor.mk
|
# - windows: https://github.com/mxe/mxe/blob/1024dc7d2db5eb7d5d3c64a2c12b5f592572f1ce/plugins/apps/tor.mk
|
||||||
# - macos: taken from Tor Browser official release
|
# - macos: taken from Tor Browser official release
|
||||||
set(TOR_COPY_CMD "cp -u ${TOR_BIN} ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/exec/tor")
|
set(TOR_COPY_CMD "cp ${TOR} ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/exec/tor")
|
||||||
message(STATUS "${TOR_COPY_CMD}")
|
message(STATUS "${TOR_COPY_CMD}")
|
||||||
execute_process(COMMAND bash -c "${TOR_COPY_CMD}" RESULT_VARIABLE ret)
|
execute_process(COMMAND bash -c "${TOR_COPY_CMD}" RESULT_VARIABLE ret)
|
||||||
if(ret EQUAL "1")
|
if(ret EQUAL "1")
|
||||||
message(FATAL_ERROR "Tor copy failure: ${TOR_COPY_CMD}")
|
message(FATAL_ERROR "Tor copy failure: ${TOR_COPY_CMD}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS "Embedding Tor binary at ${TOR_BIN}")
|
message(STATUS "Embedding Tor binary at ${TOR}")
|
||||||
else()
|
else()
|
||||||
message(STATUS "Skipping Tor inclusion because -DTOR_BIN=Off")
|
message(STATUS "Skipping Tor inclusion because -DTOR=Off")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# To build Feather with embedded (and static) XMRig, pass CMake -DXMRIG=/path/to/xmrig
|
||||||
|
if(XMRIG)
|
||||||
|
# on the buildbot XMRig is baked into the image
|
||||||
|
# - linux: See `Dockerfile`
|
||||||
|
# - windows: See `Dockerfile_windows`
|
||||||
|
# - macos: manually downloaded an official release
|
||||||
|
set(XMRIG_COPY_CMD "cp ${XMRIG} ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/exec/xmrig")
|
||||||
|
message(STATUS "${XMRIG_COPY_CMD}")
|
||||||
|
execute_process(COMMAND bash -c "${XMRIG_COPY_CMD}" RESULT_VARIABLE ret)
|
||||||
|
if(ret EQUAL "1")
|
||||||
|
message(FATAL_ERROR "XMRig copy failure: ${XMRIG_COPY_CMD}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "Embedding XMRig binary at ${XMRIG}")
|
||||||
|
else()
|
||||||
|
message(STATUS "Skipping XMRig inclusion because -DXMRIG=Off")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MINGW)
|
if(MINGW)
|
||||||
|
|||||||
@@ -1,214 +1,188 @@
|
|||||||
FROM ubuntu:16.04
|
FROM ubuntu:18.04
|
||||||
|
|
||||||
ARG THREADS=1
|
ARG THREADS=1
|
||||||
ARG QT_VERSION=5.15.2
|
|
||||||
|
|
||||||
ENV CFLAGS="-fPIC"
|
RUN apt clean && apt update
|
||||||
ENV CPPFLAGS="-fPIC"
|
RUN apt install -y gnupg
|
||||||
ENV CXXFLAGS="-fPIC"
|
|
||||||
ENV SOURCE_DATE_EPOCH=1397818193
|
|
||||||
|
|
||||||
RUN apt-get update
|
COPY utils/pubkeys/kitware.asc /kitware.asc
|
||||||
RUN apt-get install -y nano vim ccache software-properties-common
|
RUN cat /kitware.asc | gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null
|
||||||
|
RUN apt install -y automake git pkg-config python wget python3.6-distutils software-properties-common
|
||||||
|
RUN apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main' && apt update
|
||||||
|
|
||||||
RUN add-apt-repository ppa:git-core/ppa
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
RUN apt-get update
|
ENV TZ=Europe/Amsterdam
|
||||||
|
RUN apt install -y build-essential nano vim aptitude ccache libusb-1.0-0-dev tzdata
|
||||||
|
|
||||||
RUN apt install -y automake git pkg-config python xutils-dev && \
|
RUN apt install -y xutils-dev && \
|
||||||
git clone -b xorgproto-2020.1 --depth 1 https://gitlab.freedesktop.org/xorg/proto/xorgproto && \
|
git clone -b xorgproto-2020.1 --depth 1 https://gitlab.freedesktop.org/xorg/proto/xorgproto && \
|
||||||
cd xorgproto && \
|
cd xorgproto && \
|
||||||
git reset --hard c62e8203402cafafa5ba0357b6d1c019156c9f36 && \
|
git reset --hard c62e8203402cafafa5ba0357b6d1c019156c9f36 && \
|
||||||
./autogen.sh && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b 1.12 --depth 1 https://gitlab.freedesktop.org/xorg/proto/xcbproto && \
|
RUN git clone -b xcb-proto-1.13 --depth 1 https://gitlab.freedesktop.org/xorg/proto/xcbproto && \
|
||||||
cd xcbproto && \
|
cd xcbproto && \
|
||||||
git reset --hard 6398e42131eedddde0d98759067dde933191f049 && \
|
git reset --hard 94228cde97d9aecfda04a8e699d462ba2b89e3a0 && \
|
||||||
./autogen.sh && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN apt install -y libtool-bin && \
|
RUN apt install -y libtool-bin && \
|
||||||
git clone -b libXau-1.0.9 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxau && \
|
git clone -b libXau-1.0.9 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxau && \
|
||||||
cd libxau && \
|
cd libxau && \
|
||||||
git reset --hard d9443b2c57b512cfb250b35707378654d86c7dea && \
|
git reset --hard d9443b2c57b512cfb250b35707378654d86c7dea && \
|
||||||
./autogen.sh --enable-shared --disable-static && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN apt install -y libpthread-stubs0-dev && \
|
RUN apt install -y libpthread-stubs0-dev && \
|
||||||
git clone -b 1.12 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb && \
|
git clone -b 1.13.1 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb && \
|
||||||
cd libxcb && \
|
cd libxcb && \
|
||||||
git reset --hard d34785a34f28fa6a00f8ce00d87e3132ff0f6467 && \
|
git reset --hard 8287ebd7b752c33b0cabc4982606fe4831106f7e && \
|
||||||
./autogen.sh --enable-shared --disable-static && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
make -j$THREADS clean && \
|
|
||||||
rm /usr/local/lib/libxcb-xinerama.so && \
|
|
||||||
./autogen.sh --disable-shared --enable-static && \
|
|
||||||
make -j$THREADS && \
|
|
||||||
cp src/.libs/libxcb-xinerama.a /usr/local/lib/ && \
|
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b 0.4.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-util && \
|
RUN git clone -b 0.4.0 --depth 1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-util.git && \
|
||||||
cd libxcb-util && \
|
cd libxcb-util && \
|
||||||
git reset --hard acf790d7752f36e450d476ad79807d4012ec863b && \
|
git reset --hard acf790d7752f36e450d476ad79807d4012ec863b && \
|
||||||
git submodule init && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
|
|
||||||
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
|
|
||||||
./autogen.sh --enable-shared --disable-static && \
|
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b 0.4.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-image && \
|
RUN git clone -b xtrans-1.3.5 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxtrans.git && \
|
||||||
|
cd libxtrans && \
|
||||||
|
git reset --hard 7cbad9fe2e61cd9d5caeaf361826a6f4bd320f03 && \
|
||||||
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
|
make -j$THREADS && \
|
||||||
|
make -j$THREADS install
|
||||||
|
|
||||||
|
RUN git clone -b 0.4.0 --depth 1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-image.git && \
|
||||||
cd libxcb-image && \
|
cd libxcb-image && \
|
||||||
git reset --hard d882052fb2ce439c6483fce944ba8f16f7294639 && \
|
git reset --hard d882052fb2ce439c6483fce944ba8f16f7294639 && \
|
||||||
git submodule init && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
|
|
||||||
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
|
|
||||||
./autogen.sh --enable-shared --disable-static && \
|
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b 0.4.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-keysyms && \
|
RUN git clone -b 0.4.0 --depth 1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-keysyms.git && \
|
||||||
cd libxcb-keysyms && \
|
cd libxcb-keysyms && \
|
||||||
git reset --hard 0e51ee5570a6a80bdf98770b975dfe8a57f4eeb1 && \
|
git reset --hard 0e51ee5570a6a80bdf98770b975dfe8a57f4eeb1 && \
|
||||||
git submodule init && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
|
|
||||||
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
|
|
||||||
./autogen.sh --enable-shared --disable-static && \
|
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b 0.3.9 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-render-util && \
|
RUN git clone -b 0.3.9 --depth 1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-render-util.git && \
|
||||||
cd libxcb-render-util && \
|
cd libxcb-render-util && \
|
||||||
git reset --hard 0317caf63de532fd7a0493ed6afa871a67253747 && \
|
git reset --hard 0317caf63de532fd7a0493ed6afa871a67253747 && \
|
||||||
git submodule init && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
|
|
||||||
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
|
|
||||||
./autogen.sh --enable-shared --disable-static && \
|
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b 0.4.1 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-wm && \
|
RUN git clone -b 0.4.1 --depth 1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-wm.git && \
|
||||||
cd libxcb-wm && \
|
cd libxcb-wm && \
|
||||||
git reset --hard 24eb17df2e1245885e72c9d4bbb0a0f69f0700f2 && \
|
git reset --hard 24eb17df2e1245885e72c9d4bbb0a0f69f0700f2 && \
|
||||||
git submodule init && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
|
|
||||||
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
|
|
||||||
./autogen.sh --enable-shared --disable-static && \
|
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN apt install -y bison && \
|
RUN git clone -b libX11-1.6.9 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libx11 && \
|
||||||
git clone -b xkbcommon-0.5.0 --depth 1 https://github.com/xkbcommon/libxkbcommon && \
|
cd libx11 && \
|
||||||
cd libxkbcommon && \
|
git reset --hard db7cca17ad7807e92a928da9d4c68a00f4836da2 && \
|
||||||
git reset --hard c43c3c866eb9d52cd8f61e75cbef1c30d07f3a28 && \
|
ACLOCAL='aclocal -I /usr/local/share/aclocal/' CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
./autogen.sh --prefix=/usr --enable-shared --disable-static --enable-x11 --disable-docs && \
|
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
RUN git clone -b libXext-1.3.4 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxext && \
|
||||||
|
cd libxext && \
|
||||||
|
git reset --hard ebb167f34a3514783966775fb12573c4ed209625 && \
|
||||||
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
|
make -j$THREADS && \
|
||||||
|
make -j$THREADS install
|
||||||
|
|
||||||
|
RUN apt install -y libpthread-stubs0-dev && \
|
||||||
|
git clone -b libXinerama-1.1.4 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxinerama.git && \
|
||||||
|
cd libxinerama && \
|
||||||
|
git reset --hard c3ab2361f13154921df2992f9eacc1ea1b3f946b && \
|
||||||
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
|
make -j$THREADS && \
|
||||||
|
make -j$THREADS install
|
||||||
|
|
||||||
RUN git clone -b v1.2.11 --depth 1 https://github.com/madler/zlib && \
|
RUN git clone -b v1.2.11 --depth 1 https://github.com/madler/zlib && \
|
||||||
cd zlib && \
|
cd zlib && \
|
||||||
git reset --hard cacf7f1d4e3d44d871b605da3b647f07d718623f && \
|
git reset --hard cacf7f1d4e3d44d871b605da3b647f07d718623f && \
|
||||||
./configure --static && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --static && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install && \
|
||||||
./configure --static --prefix=/usr/local/zlib && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --static --prefix=/usr/local/zlib && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b VER-2-10-2 --depth 1 https://git.savannah.gnu.org/git/freetype/freetype2.git && \
|
RUN git clone -b VER-2-10-2 https://git.sv.nongnu.org/r/freetype/freetype2.git && \
|
||||||
cd freetype2 && \
|
cd freetype2 && \
|
||||||
git reset --hard 132f19b779828b194b3fede187cee719785db4d8 && \
|
git reset --hard 132f19b779828b194b3fede187cee719785db4d8 && \
|
||||||
./autogen.sh && \
|
./autogen.sh && \
|
||||||
./configure --disable-shared --enable-static --with-zlib=no && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --with-zlib=no && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b R_2_2_9 --depth 1 https://github.com/libexpat/libexpat && \
|
RUN git clone -b R_2_2_9 --depth 1 https://github.com/libexpat/libexpat && \
|
||||||
cd libexpat/expat && \
|
cd libexpat/expat && \
|
||||||
git reset --hard a7bc26b69768f7fb24f0c7976fae24b157b85b13 && \
|
git reset --hard a7bc26b69768f7fb24f0c7976fae24b157b85b13 && \
|
||||||
./buildconf.sh && \
|
./buildconf.sh && \
|
||||||
./configure --disable-shared --enable-static && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN apt install -y autopoint gettext gperf libpng12-dev && \
|
RUN apt install -y autopoint gettext gperf libpng-dev && \
|
||||||
git clone -b 2.13.92 --depth 1 https://gitlab.freedesktop.org/fontconfig/fontconfig && \
|
git clone -b 2.13.92 --depth 1 https://gitlab.freedesktop.org/fontconfig/fontconfig && \
|
||||||
cd fontconfig && \
|
cd fontconfig && \
|
||||||
git reset --hard b1df1101a643ae16cdfa1d83b939de2497b1bf27 && \
|
git reset --hard b1df1101a643ae16cdfa1d83b939de2497b1bf27 && \
|
||||||
./autogen.sh --disable-shared --enable-static --sysconfdir=/etc --localstatedir=/var && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static --sysconfdir=/etc --localstatedir=/var && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b release-64-2 --depth 1 https://github.com/unicode-org/icu && \
|
RUN git clone -b release-64-2 --depth 1 https://github.com/unicode-org/icu && \
|
||||||
cd icu/icu4c/source && \
|
cd icu/icu4c/source && \
|
||||||
git reset --hard e2d85306162d3a0691b070b4f0a73e4012433444 && \
|
git reset --hard e2d85306162d3a0691b070b4f0a73e4012433444 && \
|
||||||
./configure --disable-shared --enable-static --disable-tests --disable-samples && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --disable-tests --disable-samples && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN apt install -y wget && \
|
RUN apt install -y wget && \
|
||||||
wget https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.gz && \
|
wget https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.gz && \
|
||||||
echo "9995e192e68528793755692917f9eb6422f3052a53c5e13ba278a228af6c7acf boost_1_73_0.tar.gz" | sha256sum -c && \
|
echo "9995e192e68528793755692917f9eb6422f3052a53c5e13ba278a228af6c7acf boost_1_73_0.tar.gz" > hashsum.txt && \
|
||||||
tar -xzf boost_1_73_0.tar.gz && \
|
sha256sum -c hashsum.txt && \
|
||||||
rm boost_1_73_0.tar.gz && \
|
tar -xvzf boost_1_73_0.tar.gz && \
|
||||||
cd boost_1_73_0 && \
|
cd boost_1_73_0 && \
|
||||||
./bootstrap.sh && \
|
./bootstrap.sh && \
|
||||||
./b2 --with-atomic --with-system --with-filesystem --with-thread --with-date_time --with-chrono --with-regex --with-serialization --with-program_options --with-locale variant=release link=static runtime-link=static cflags="${CFLAGS}" cxxflags="${CXXFLAGS}" install -a --prefix=/usr && \
|
./b2 --with-atomic --with-system --with-filesystem --with-thread --with-date_time --with-chrono --with-regex --with-serialization --with-program_options --with-locale variant=release link=static runtime-link=static cflags='-fPIC' cxxflags='-fPIC' install -a --prefix=/usr
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN wget https://www.openssl.org/source/openssl-1.1.1i.tar.gz && \
|
RUN wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz && \
|
||||||
echo "e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242 openssl-1.1.1i.tar.gz" | sha256sum -c && \
|
echo "ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46 openssl-1.1.1g.tar.gz" > hashsum.txt && \
|
||||||
tar -xzf openssl-1.1.1i.tar.gz && \
|
sha256sum -c hashsum.txt && \
|
||||||
rm openssl-1.1.1i.tar.gz && \
|
tar -xzf openssl-1.1.1g.tar.gz && \
|
||||||
cd openssl-1.1.1i && \
|
cd openssl-1.1.1g && \
|
||||||
./config no-shared no-zlib-dynamic --prefix=/usr/local/openssl && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./config no-shared no-zlib-dynamic --prefix=/usr/local/openssl && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
./config no-shared no-zlib-dynamic --openssldir=/usr && \
|
|
||||||
make -j$THREADS && \
|
|
||||||
make -j$THREADS install && \
|
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN apt install -y libgl1-mesa-dev libglib2.0-dev mesa-common-dev && \
|
RUN wget https://download.qt.io/archive/qt/5.15/5.15.0/single/qt-everywhere-src-5.15.0.tar.xz && \
|
||||||
rm /usr/lib/x86_64-linux-gnu/libX11.a && \
|
echo "22b63d7a7a45183865cc4141124f12b673e7a17b1fe2b91e433f6547c5d548c3 qt-everywhere-src-5.15.0.tar.xz" > hashsum.txt && \
|
||||||
rm /usr/lib/x86_64-linux-gnu/libXext.a && \
|
sha256sum -c hashsum.txt && \
|
||||||
rm /usr/lib/x86_64-linux-gnu/libX11-xcb.a && \
|
tar -xf qt-everywhere-src-5.15.0.tar.xz
|
||||||
git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \
|
|
||||||
cd qt5 && \
|
COPY contrib/Qt5.15_LinuxPatch.json /qt-everywhere-src-5.15.0/qtbase/src/gui/configure.json
|
||||||
git clone git://code.qt.io/qt/qtbase.git -b ${QT_VERSION} --depth 1 && \
|
RUN apt install -y libgl1-mesa-dev libglib2.0-dev libxkbcommon-dev libxkbcommon-x11-dev
|
||||||
git clone git://code.qt.io/qt/qtimageformats.git -b ${QT_VERSION} --depth 1 && \
|
|
||||||
git clone git://code.qt.io/qt/qtmultimedia.git -b ${QT_VERSION} --depth 1 && \
|
RUN cd /qt-everywhere-src-5.15.0 && \
|
||||||
git clone git://code.qt.io/qt/qtsvg.git -b ${QT_VERSION} --depth 1 && \
|
|
||||||
git clone git://code.qt.io/qt/qttools.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/qtx11extras.git -b ${QT_VERSION} --depth 1 && \
|
|
||||||
git clone git://code.qt.io/qt/qtwebsockets.git -b ${QT_VERSION} --depth 1 && \
|
|
||||||
sed -ri s/\(Libs:.*\)/\\1\ -lexpat/ /usr/local/lib/pkgconfig/fontconfig.pc && \
|
sed -ri s/\(Libs:.*\)/\\1\ -lexpat/ /usr/local/lib/pkgconfig/fontconfig.pc && \
|
||||||
sed -ri s/\(Libs:.*\)/\\1\ -lz/ /usr/local/lib/pkgconfig/freetype2.pc && \
|
sed -ri s/\(Libs:.*\)/\\1\ -lz/ /usr/local/lib/pkgconfig/freetype2.pc && \
|
||||||
sed -ri s/\(Libs:.*\)/\\1\ -lXau/ /usr/local/lib/pkgconfig/xcb.pc && \
|
sed -ri s/\(Libs:.*\)/\\1\ -lXau/ /usr/local/lib/pkgconfig/xcb.pc && \
|
||||||
sed -i s/\\/usr\\/X11R6\\/lib64/\\/usr\\/local\\/lib/ qtbase/mkspecs/linux-g++-64/qmake.conf && \
|
|
||||||
OPENSSL_LIBS="-lssl -lcrypto -lpthread -ldl" \
|
OPENSSL_LIBS="-lssl -lcrypto -lpthread -ldl" \
|
||||||
./configure --prefix=/usr -platform linux-g++-64 -opensource -confirm-license -release -static -no-avx \
|
./configure --prefix=/usr -platform linux-g++-64 -opensource -confirm-license -release -static -no-avx \
|
||||||
-no-opengl -qpa xcb --xcb -xcb-xlib -feature-xlib -openssl-linked -I /usr/local/openssl/include \
|
-no-opengl -qpa xcb -openssl-linked -I /usr/local/openssl/include -L /usr/local/openssl/lib -system-freetype -fontconfig -glib \
|
||||||
-L /usr/local/openssl/lib -system-freetype -fontconfig -glib \
|
|
||||||
-no-dbus -no-sql-sqlite -no-use-gold-linker -no-kms \
|
-no-dbus -no-sql-sqlite -no-use-gold-linker -no-kms \
|
||||||
-qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \
|
-qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \
|
||||||
-skip qt3d -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d \
|
-skip qt3d -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d \
|
||||||
@@ -219,101 +193,82 @@ RUN apt install -y libgl1-mesa-dev libglib2.0-dev mesa-common-dev && \
|
|||||||
-skip qtwinextras -skip qtx11extras -skip gamepad -skip serialbus -skip location -skip webengine \
|
-skip qtwinextras -skip qtx11extras -skip gamepad -skip serialbus -skip location -skip webengine \
|
||||||
-skip qtdeclarative \
|
-skip qtdeclarative \
|
||||||
-no-feature-cups -no-feature-ftp -no-feature-pdf -no-feature-animation \
|
-no-feature-cups -no-feature-ftp -no-feature-pdf -no-feature-animation \
|
||||||
-nomake examples -nomake tests -nomake tools && \
|
-nomake examples -nomake tests -nomake tools
|
||||||
|
|
||||||
|
RUN cd /qt-everywhere-src-5.15.0 && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
cd qttools/src/linguist/lrelease && \
|
|
||||||
../../../../qtbase/bin/qmake && \
|
RUN cd qt-everywhere-src-5.15.0/qttools/src/linguist/lrelease && \
|
||||||
|
qmake && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
cd ../../../.. && \
|
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN apt install -y libudev-dev && \
|
RUN apt install -y libudev-dev && \
|
||||||
git clone -b v1.0.23 --depth 1 https://github.com/libusb/libusb && \
|
git clone -b v1.0.23 --depth 1 https://github.com/libusb/libusb && \
|
||||||
cd libusb && \
|
cd libusb && \
|
||||||
git reset --hard e782eeb2514266f6738e242cdcb18e3ae1ed06fa && \
|
git reset --hard e782eeb2514266f6738e242cdcb18e3ae1ed06fa && \
|
||||||
./autogen.sh --disable-shared --enable-static && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b hidapi-0.9.0 --depth 1 https://github.com/libusb/hidapi && \
|
RUN git clone -b hidapi-0.9.0 --depth 1 https://github.com/libusb/hidapi && \
|
||||||
cd hidapi && \
|
cd hidapi && \
|
||||||
git reset --hard 7da5cc91fc0d2dbe4df4f08cd31f6ca1a262418f && \
|
git reset --hard 7da5cc91fc0d2dbe4df4f08cd31f6ca1a262418f && \
|
||||||
./bootstrap && \
|
./bootstrap && \
|
||||||
./configure --disable-shared --enable-static && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN apt install -y libsodium-dev && \
|
RUN apt install -y libsodium-dev && \
|
||||||
git clone -b v4.3.2 --depth 1 https://github.com/zeromq/libzmq && \
|
git clone -b v4.3.2 --depth 1 https://github.com/zeromq/libzmq && \
|
||||||
cd libzmq && \
|
cd libzmq && \
|
||||||
git reset --hard a84ffa12b2eb3569ced199660bac5ad128bff1f0 && \
|
git reset --hard a84ffa12b2eb3569ced199660bac5ad128bff1f0 && \
|
||||||
./autogen.sh && \
|
./autogen.sh && \
|
||||||
./configure --disable-shared --enable-static --disable-libunwind --with-libsodium && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --disable-libunwind --with-libsodium && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b libgpg-error-1.38 --depth 1 git://git.gnupg.org/libgpg-error.git && \
|
RUN git clone -b libgpg-error-1.38 --depth 1 git://git.gnupg.org/libgpg-error.git && \
|
||||||
cd libgpg-error && \
|
cd libgpg-error && \
|
||||||
git reset --hard 71d278824c5fe61865f7927a2ed1aa3115f9e439 && \
|
git reset --hard 71d278824c5fe61865f7927a2ed1aa3115f9e439 && \
|
||||||
./autogen.sh && \
|
./autogen.sh && \
|
||||||
./configure --disable-shared --enable-static --disable-doc --disable-tests && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --disable-doc --disable-tests && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b libgcrypt-1.8.5 --depth 1 git://git.gnupg.org/libgcrypt.git && \
|
RUN git clone -b libgcrypt-1.8.5 --depth 1 git://git.gnupg.org/libgcrypt.git && \
|
||||||
cd libgcrypt && \
|
cd libgcrypt && \
|
||||||
git reset --hard 56606331bc2a80536db9fc11ad53695126007298 && \
|
git reset --hard 56606331bc2a80536db9fc11ad53695126007298 && \
|
||||||
./autogen.sh && \
|
./autogen.sh && \
|
||||||
./configure --disable-shared --enable-static --disable-doc && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --disable-doc && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b v3.10.0 --depth 1 https://github.com/protocolbuffers/protobuf && \
|
RUN git clone -b v3.10.0 --depth 1 https://github.com/protocolbuffers/protobuf && \
|
||||||
cd protobuf && \
|
cd protobuf && \
|
||||||
git reset --hard 6d4e7fd7966c989e38024a8ea693db83758944f1 && \
|
git reset --hard 6d4e7fd7966c989e38024a8ea693db83758944f1 && \
|
||||||
./autogen.sh && \
|
./autogen.sh && \
|
||||||
./configure --enable-static --disable-shared && \
|
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --enable-static --disable-shared && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b v3.18.4 --depth 1 https://github.com/Kitware/CMake && \
|
RUN apt install -y zip cmake && git clone -b v4.0.2 --depth 1 https://github.com/fukuchi/libqrencode.git && \
|
||||||
cd CMake && \
|
|
||||||
git reset --hard 3cc3d42aba879fff5e85b363ae8f21386a3f9f9b && \
|
|
||||||
./bootstrap && \
|
|
||||||
make -j$THREADS && \
|
|
||||||
make -j$THREADS install && \
|
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN apt install -y libusb-1.0-0-dev
|
|
||||||
|
|
||||||
RUN apt install -y zip && git clone -b v4.0.2 --depth 1 https://github.com/fukuchi/libqrencode.git && \
|
|
||||||
cd libqrencode && \
|
cd libqrencode && \
|
||||||
git reset --hard 59ee597f913fcfda7a010a6e106fbee2595f68e4 && \
|
git reset --hard 59ee597f913fcfda7a010a6e106fbee2595f68e4 && \
|
||||||
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
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN apt install -y libmbedtls-dev && git clone -b release-2.1.12-stable --depth 1 https://github.com/libevent/libevent.git && \
|
RUN apt install -y libmbedtls-dev && git clone -b release-2.1.12-stable --depth 1 https://github.com/libevent/libevent.git && \
|
||||||
cd libevent && \
|
cd libevent && \
|
||||||
git reset --hard 5df3037d10556bfcb675bc73e516978b75fc7bc7 && \
|
|
||||||
mkdir build && cd build && \
|
mkdir build && cd build && \
|
||||||
cmake -DEVENT_LIBRARY_STATIC=ON -DOPENSSL_ROOT_DIR=/usr/local/openssl -DCMAKE_INSTALL_PREFIX=/usr/local/libevent .. && \
|
cmake -DEVENT_LIBRARY_STATIC=ON -DOPENSSL_ROOT_DIR=/usr/local/openssl -DCMAKE_INSTALL_PREFIX=/usr/local/libevent .. && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
|
||||||
RUN git clone -b tor-0.4.4.6 --depth 1 https://git.torproject.org/tor.git && \
|
RUN git clone -b tor-0.4.3.5 --depth 1 https://git.torproject.org/tor.git && \
|
||||||
cd tor && \
|
cd tor && \
|
||||||
git reset --hard 2a8b789ea6f308d081f369d78fa7cfdc9d00bf90 && \
|
|
||||||
bash autogen.sh && \
|
bash autogen.sh && \
|
||||||
LDFLAGS="-L/usr/local/openssl/lib/" LIBS="-lssl -lcrypto -lpthread -ldl" CPPFLAGS="-I/usr/local/openssl/include/" ./configure \
|
LDFLAGS="-L/usr/local/openssl/lib/" LIBS="-lssl -lcrypto -lpthread -ldl" CPPFLAGS="-I/usr/local/openssl/include/" ./configure \
|
||||||
--enable-static-zlib \
|
--enable-static-zlib \
|
||||||
@@ -336,13 +291,23 @@ RUN git clone -b tor-0.4.4.6 --depth 1 https://git.torproject.org/tor.git && \
|
|||||||
--disable-system-torrc \
|
--disable-system-torrc \
|
||||||
--prefix=/usr/local/tor && \
|
--prefix=/usr/local/tor && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
RUN git clone https://git.torproject.org/torsocks.git && \
|
||||||
|
cd torsocks && \
|
||||||
|
bash autogen.sh && \
|
||||||
|
./configure --prefix=/usr/local/torsocks && \
|
||||||
|
make -j$THREADS && \
|
||||||
|
make -j$THREADS install
|
||||||
|
|
||||||
RUN git clone https://git.wownero.com/feather/monero-seed.git && \
|
RUN git clone https://git.wownero.com/feather/monero-seed.git && \
|
||||||
cd monero-seed && \
|
cd monero-seed && \
|
||||||
git reset --hard 4674ef09b6faa6fe602ab5ae0b9ca8e1fd7d5e1b && \
|
|
||||||
cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \
|
cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \
|
||||||
make -Cbuild -j$THREADS && \
|
make -Cbuild -j$THREADS && \
|
||||||
make -Cbuild install && \
|
make -Cbuild install
|
||||||
rm -rf $(pwd)
|
|
||||||
|
RUN apt install -y curl && \
|
||||||
|
curl -LO "https://github.com/xmrig/xmrig/releases/download/v6.3.5/xmrig-6.3.5-linux-static-x64.tar.gz" && \
|
||||||
|
echo "24d4f07cf5850f00ab513b228f95769a5a5ed68d35808d98f9959b58d97985a0 xmrig-6.3.5-linux-static-x64.tar.gz" > hashsum.txt && \
|
||||||
|
sha256sum -c hashsum.txt && \
|
||||||
|
tar xvf xmrig-6.3.5-linux-static-x64.tar.gz --one-top-level=/xmrig --strip 1
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ FROM ubuntu:20.04
|
|||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN apt clean && apt update
|
RUN apt clean && apt update
|
||||||
RUN apt install -y golang git build-essential wget curl ngrep unzip file squashfs-tools desktop-file-utils patchelf libxkbcommon-x11-dev libx11-xcb-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render0 libxcb-render-util0 libxcb-shape0 libxcb-sync1 libxcb-xfixes0 libxcb-shm0 libxcb-keysyms1 libxcb-xkb1
|
RUN apt install -y golang git build-essential wget curl ngrep unzip file squashfs-tools desktop-file-utils patchelf libxkbcommon-x11-dev
|
||||||
|
|
||||||
RUN go get github.com/probonopd/go-appimage/src/appimagetool
|
RUN go get github.com/probonopd/go-appimage/src/appimagetool
|
||||||
RUN go build -trimpath -ldflags="-s -w" github.com/probonopd/go-appimage/src/appimagetool
|
RUN go build -trimpath -ldflags="-s -w" github.com/probonopd/go-appimage/src/appimagetool
|
||||||
@@ -15,4 +15,4 @@ RUN cd /usr/bin && \
|
|||||||
|
|
||||||
RUN cd / && \
|
RUN cd / && \
|
||||||
wget -c https://github.com/AppImage/AppImageKit/releases/download/continuous/runtime-x86_64 && \
|
wget -c https://github.com/AppImage/AppImageKit/releases/download/continuous/runtime-x86_64 && \
|
||||||
chmod +x runtime-x86_64
|
chmod +x runtime-x86_64
|
||||||
@@ -72,3 +72,10 @@ RUN git clone https://git.wownero.com/feather/monero-seed.git && \
|
|||||||
cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \
|
cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \
|
||||||
make -Cbuild -j$THREADS && \
|
make -Cbuild -j$THREADS && \
|
||||||
make -Cbuild install
|
make -Cbuild install
|
||||||
|
|
||||||
|
RUN apt install -y curl && \
|
||||||
|
curl -LO "https://github.com/xmrig/xmrig/releases/download/v6.3.5/xmrig-6.3.5-gcc-win64.zip" && \
|
||||||
|
echo "e45915ada7e6e30f6ab40abf33831056449d5914307d7706bb0ad439b6d64c12 xmrig-6.3.5-gcc-win64.zip" > hashsum.txt && \
|
||||||
|
sha256sum -c hashsum.txt && \
|
||||||
|
unzip -q xmrig-6.3.5-gcc-win64.zip -d /xmrig && \
|
||||||
|
mv /xmrig/xmrig-6.3.5/* /xmrig/
|
||||||
|
|||||||
@@ -42,8 +42,9 @@ via the `CMAKE_PREFIX_PATH` definition. For me this is:
|
|||||||
There are some Monero/Feather related options/definitions that you may pass:
|
There are some Monero/Feather related options/definitions that you may pass:
|
||||||
|
|
||||||
- `-DXMRTO=OFF` - disable Xmr.To feature
|
- `-DXMRTO=OFF` - disable Xmr.To feature
|
||||||
- `-DXMRIG=OFF` - disable XMRig feature
|
- `-DMORPHTOKEN=OFF` - diable MorphToken feature
|
||||||
- `-DTOR_BIN=/path/to/tor` - Embed a Tor executable inside Feather
|
- `-DTOR=/path/to/tor` - Embed a Tor executable inside Feather
|
||||||
|
- `-DXMRIG=/path/to/xmrig` - Embed a XMRig executable inside Feather
|
||||||
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
|
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
|
||||||
|
|
||||||
And:
|
And:
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ CMAKEFLAGS = \
|
|||||||
-DARCH=x86_64 \
|
-DARCH=x86_64 \
|
||||||
-DBUILD_64=On \
|
-DBUILD_64=On \
|
||||||
-DBUILD_TESTS=Off \
|
-DBUILD_TESTS=Off \
|
||||||
-DXMRTO=On \
|
-DXMRTO=ON \
|
||||||
-DXMRIG=On \
|
-DMORPHTOKEN=ON \
|
||||||
-DTOR_BIN=Off \
|
-DXMRIG=Off \
|
||||||
|
-DTOR=Off \
|
||||||
-DCMAKE_CXX_STANDARD=11 \
|
-DCMAKE_CXX_STANDARD=11 \
|
||||||
-DCMAKE_VERBOSE_MAKEFILE=On \
|
-DCMAKE_VERBOSE_MAKEFILE=On \
|
||||||
-DINSTALL_VENDORED_LIBUNBOUND=Off \
|
-DINSTALL_VENDORED_LIBUNBOUND=Off \
|
||||||
@@ -42,28 +43,32 @@ CMAKEFLAGS = \
|
|||||||
$(CMAKEFLAGS_EXTRA)
|
$(CMAKEFLAGS_EXTRA)
|
||||||
|
|
||||||
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=$(or ${TOR},OFF)
|
||||||
|
release-static: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
|
||||||
release-static: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
|
release-static: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
|
||||||
release-static:
|
release-static:
|
||||||
cmake -Bbuild $(CMAKEFLAGS)
|
cmake -Bbuild $(CMAKEFLAGS)
|
||||||
$(MAKE) -Cbuild
|
$(MAKE) -Cbuild
|
||||||
|
|
||||||
windows-mxe-release: CMAKEFLAGS += -DBUILD_TAG="win-x64"
|
windows-mxe-release: CMAKEFLAGS += -DBUILD_TAG="win-x64"
|
||||||
windows-mxe-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
|
windows-mxe-release: CMAKEFLAGS += -DTOR=$(or ${TOR},OFF)
|
||||||
|
windows-mxe-release: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
|
||||||
windows-mxe-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
|
windows-mxe-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
|
||||||
windows-mxe-release:
|
windows-mxe-release:
|
||||||
cmake -Bbuild $(CMAKEFLAGS)
|
cmake -Bbuild $(CMAKEFLAGS)
|
||||||
$(MAKE) -Cbuild
|
$(MAKE) -Cbuild
|
||||||
|
|
||||||
windows-mxe-debug: CMAKEFLAGS += -DBUILD_TAG="win-x64"
|
windows-mxe-debug: CMAKEFLAGS += -DBUILD_TAG="win-x64"
|
||||||
windows-mxe-debug: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
|
windows-mxe-debug: CMAKEFLAGS += -DTOR=$(or ${TOR},OFF)
|
||||||
|
windows-mxe-debug: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
|
||||||
windows-mxe-debug: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Debug
|
windows-mxe-debug: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Debug
|
||||||
windows-mxe-debug:
|
windows-mxe-debug:
|
||||||
cmake -Bbuild $(CMAKEFLAGS)
|
cmake -Bbuild $(CMAKEFLAGS)
|
||||||
$(MAKE) -Cbuild
|
$(MAKE) -Cbuild
|
||||||
|
|
||||||
mac-release: CMAKEFLAGS += -DSTATIC=Off
|
mac-release: CMAKEFLAGS += -DSTATIC=Off
|
||||||
mac-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
|
mac-release: CMAKEFLAGS += -DTOR=$(or ${TOR},OFF)
|
||||||
|
mac-release: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},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:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[](https://build.featherwallet.org/feather/feather)
|
[](https://build.featherwallet.org/feather/feather)
|
||||||
|
|
||||||
Feather is a free, open-source Monero client Linux with ports for Mac OS and Windows written in C++ with the Qt framework. It is created and maintained by [dsc](dsc@xmr.pm) and [tobtoht](thotbot@protonmail.com).
|
Feather is a free, open-source Monero client Linux with ports for Mac OS and Windows written in C++ with the Qt framework. It is created and maintained by [dsc](dsc@xmr.pm) and [tobtoht](thotbot@protonmail.com).
|
||||||
|
|
||||||
## Development resources
|
## Development resources
|
||||||
* Web: [featherwallet.org](https://featherwallet.org)
|
* Web: [featherwallet.org](https://featherwallet.org)
|
||||||
|
|||||||
@@ -2,11 +2,33 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
function verify_hash() {
|
||||||
|
local file=$1 expected_hash=$2
|
||||||
|
actual_hash=$(sha256sum $file | awk '{print $1}')
|
||||||
|
if [ "$actual_hash" == "$expected_hash" ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "$file $actual_hash (unexpected hash)" >&2
|
||||||
|
rm "$file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function download_if_not_exist() {
|
||||||
|
local file_name=$1 url=$2
|
||||||
|
if [ ! -e $file_name ] ; then
|
||||||
|
wget -q -O $file_name "$url"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
APPDIR="$PWD/feather.AppDir"
|
APPDIR="$PWD/feather.AppDir"
|
||||||
mkdir -p "$APPDIR"
|
mkdir -p "$APPDIR"
|
||||||
mkdir -p "$APPDIR/usr/share/applications/"
|
mkdir -p "$APPDIR/usr/share/applications/"
|
||||||
mkdir -p "$APPDIR/usr/bin"
|
mkdir -p "$APPDIR/usr/bin"
|
||||||
|
|
||||||
|
echo "Downloading dependencies"
|
||||||
|
|
||||||
|
download_if_not_exist "feather.zip" "https://build.featherwallet.org/files/linux-release/$BRANCH/$FN"
|
||||||
unzip -q feather.zip
|
unzip -q feather.zip
|
||||||
|
|
||||||
cp "$PWD/src/assets/feather.desktop" "$APPDIR/usr/share/applications/feather.desktop"
|
cp "$PWD/src/assets/feather.desktop" "$APPDIR/usr/share/applications/feather.desktop"
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# this file is used by feather's CMake
|
||||||
|
# arguments: ./build.tor $TAG $ROOT_FEATHER_DIR
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
ERR_WIN="This script does not work on Windows"
|
||||||
|
if [[ "$OSTYPE" == "msys" ]]; then
|
||||||
|
echo "$ERR_WIN"
|
||||||
|
exit 1
|
||||||
|
elif [[ "$OSTYPE" == "win32" ]]; then
|
||||||
|
echo "$ERR_WIN"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOR_TAG="$1"
|
||||||
|
ROOT_DIR="$2"
|
||||||
|
STATIC="$3"
|
||||||
|
TOR_DIR="$ROOT_DIR/contrib/tor"
|
||||||
|
TORSOCKS_DIR="$ROOT_DIR/contrib/torsocks"
|
||||||
|
TARGET_DIR="$ROOT_DIR/src/tor"
|
||||||
|
|
||||||
|
CPU_CORE_COUNT="$(nproc)"
|
||||||
|
|
||||||
|
#
|
||||||
|
### tor
|
||||||
|
#
|
||||||
|
|
||||||
|
pushd "$TOR_DIR"
|
||||||
|
|
||||||
|
rm -rf "$TOR_DIR/build"
|
||||||
|
mkdir -p "$TOR_DIR/build"
|
||||||
|
|
||||||
|
# configure
|
||||||
|
git -C "$TOR_DIR" fetch
|
||||||
|
git -C "$TOR_DIR" checkout tor-0.4.3.5
|
||||||
|
bash "$TOR_DIR/autogen.sh"
|
||||||
|
|
||||||
|
if [[ "$STATIC" = "ON" ]]; then
|
||||||
|
# static assumes that openssl has been compiled with:
|
||||||
|
# CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./config no-asm no-shared no-zlib-dynamic --prefix=/usr/local/openssl --openssldir=/usr/local/openssl
|
||||||
|
# and libevent with:
|
||||||
|
# cmake -DEVENT_LIBRARY_STATIC=ON -DOPENSSL_ROOT_DIR=/usr/local/openssl -DCMAKE_INSTALL_PREFIX=/usr/local/libevent
|
||||||
|
# and zlib with:
|
||||||
|
# CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --static --prefix=/usr/local/zlib
|
||||||
|
|
||||||
|
LDFLAGS="-L/usr/local/openssl/lib/" LIBS="-lssl -lcrypto -lpthread -ldl" CPPFLAGS="-I/usr/local/openssl/include/" ./configure \
|
||||||
|
--enable-static-zlib \
|
||||||
|
--enable-static-openssl \
|
||||||
|
--enable-static-libevent \
|
||||||
|
--disable-system-torrc \
|
||||||
|
--with-libevent-dir=/usr/local/libevent \
|
||||||
|
--with-openssl-dir=/usr/local/openssl/ \
|
||||||
|
--with-zlib-dir=/usr/local/zlib \
|
||||||
|
--disable-system-torrc \
|
||||||
|
--disable-tool-name-check \
|
||||||
|
--disable-systemd \
|
||||||
|
--disable-lzma \
|
||||||
|
--disable-unittests \
|
||||||
|
--disable-zstd \
|
||||||
|
--disable-seccomp \
|
||||||
|
--disable-asciidoc \
|
||||||
|
--disable-manpage \
|
||||||
|
--disable-html-manual \
|
||||||
|
--disable-system-torrc \
|
||||||
|
--prefix="$TOR_DIR/build"
|
||||||
|
else
|
||||||
|
bash "$TOR_DIR/configure" \
|
||||||
|
--disable-tool-name-check \
|
||||||
|
--disable-systemd \
|
||||||
|
--disable-lzma \
|
||||||
|
--disable-unittests \
|
||||||
|
--disable-zstd \
|
||||||
|
--disable-asciidoc \
|
||||||
|
--disable-manpage \
|
||||||
|
--disable-html-manual \
|
||||||
|
--prefix="$TOR_DIR/build"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# build
|
||||||
|
make -j "$CPU_CORE_COUNT"
|
||||||
|
make install -j "$CPU_CORE_COUNT"
|
||||||
|
|
||||||
|
# copy to lib/tor
|
||||||
|
cp "$TOR_DIR/build/bin/tor" "$TARGET_DIR"
|
||||||
|
cp "$TOR_DIR/build/etc/tor/torrc.sample"* "$TARGET_DIR"
|
||||||
|
|
||||||
|
#
|
||||||
|
### torsocks
|
||||||
|
#
|
||||||
|
|
||||||
|
pushd "$TORSOCKS_DIR"
|
||||||
|
mkdir -p "$TORSOCKS_DIR/build"
|
||||||
|
|
||||||
|
# configure
|
||||||
|
bash "$TORSOCKS_DIR/autogen.sh"
|
||||||
|
bash "$TORSOCKS_DIR/configure" --prefix="$TORSOCKS_DIR/build"
|
||||||
|
|
||||||
|
# build
|
||||||
|
make -j "$CPU_CORE_COUNT"
|
||||||
|
make install -j "$CPU_CORE_COUNT"
|
||||||
|
|
||||||
|
# copy to lib/torsocks
|
||||||
|
cp "$TORSOCKS_DIR/build/lib/torsocks/"* "$TARGET_DIR"
|
||||||
|
cp "$TORSOCKS_DIR/build/bin/"* "$TARGET_DIR"
|
||||||
|
cp "$TORSOCKS_DIR/build/etc/tor/"* "$TARGET_DIR"
|
||||||
|
|
||||||
|
#
|
||||||
|
### verify installation
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||||
|
for fn in "$TARGET_DIR/libtorsocks.so" "$TARGET_DIR/tor"; do
|
||||||
|
if [[ ! -f "$fn" ]]; then
|
||||||
|
echo "[*] Failed to install tor or torsocks: no such file $fn"
|
||||||
|
exit 1
|
||||||
|
fi; done
|
||||||
|
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
for fn in "$TARGET_DIR/libtorsocks.dylib" "$TARGET_DIR/tor"; do
|
||||||
|
if [[ ! -f "$fn" ]]; then
|
||||||
|
echo "[*] Failed to install tor or torsocks: no such file $fn"
|
||||||
|
exit 1
|
||||||
|
fi; done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[*] Compiled tor/torsocks into $TARGET_DIR"
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
From fc5eafeb2886605d4de1546846f06a12a18c87ef Mon Sep 17 00:00:00 2001
|
||||||
|
From: "J.W" <jakwings@gmail.com>
|
||||||
|
Date: Mon, 22 Apr 2019 05:19:32 +0100
|
||||||
|
Subject: [PATCH 1/2] Fix macros for accept4(2)
|
||||||
|
|
||||||
|
Both accept(2) and accept4(2) exist on linux but accept4(2) does not
|
||||||
|
exist on macos 10.11.6 (and maybe other distros).
|
||||||
|
---
|
||||||
|
src/lib/torsocks.c | 9 ++++++++-
|
||||||
|
src/lib/torsocks.h | 4 +++-
|
||||||
|
2 files changed, 11 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/lib/torsocks.c b/src/lib/torsocks.c
|
||||||
|
index 16f2da0..9527513 100644
|
||||||
|
--- a/src/lib/torsocks.c
|
||||||
|
+++ b/src/lib/torsocks.c
|
||||||
|
@@ -234,9 +234,16 @@ static void init_libc_symbols(void)
|
||||||
|
tsocks_libc_socket = dlsym(libc_ptr, LIBC_SOCKET_NAME_STR);
|
||||||
|
tsocks_libc_syscall = dlsym(libc_ptr, LIBC_SYSCALL_NAME_STR);
|
||||||
|
tsocks_libc_execve = dlsym(libc_ptr, LIBC_EXECVE_NAME_STR);
|
||||||
|
+ tsocks_libc_accept = dlsym(libc_ptr, LIBC_ACCEPT_NAME_STR);
|
||||||
|
+#if (defined(__linux__))
|
||||||
|
tsocks_libc_accept4 = dlsym(libc_ptr, LIBC_ACCEPT4_NAME_STR);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
if (!tsocks_libc_connect || !tsocks_libc_close || !tsocks_libc_socket ||
|
||||||
|
- !tsocks_libc_syscall || !tsocks_libc_execve || ! tsocks_libc_accept4) {
|
||||||
|
+#if (defined(__linux__))
|
||||||
|
+ !tsocks_libc_accept4 ||
|
||||||
|
+#endif
|
||||||
|
+ !tsocks_libc_syscall || !tsocks_libc_execve || ! tsocks_libc_accept) {
|
||||||
|
ERR("Unable to lookup symbols in " LIBC_NAME "(%s)", dlerror());
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
diff --git a/src/lib/torsocks.h b/src/lib/torsocks.h
|
||||||
|
index 33da526..bf9109d 100644
|
||||||
|
--- a/src/lib/torsocks.h
|
||||||
|
+++ b/src/lib/torsocks.h
|
||||||
|
@@ -30,8 +30,10 @@
|
||||||
|
* libc call outside of torsocks can be used. These are declared for each
|
||||||
|
* symbol torsocks hijacked.
|
||||||
|
*/
|
||||||
|
+#define TSOCKS_LIBC_FUNC(name) \
|
||||||
|
+ tsocks_libc_##name
|
||||||
|
#define TSOCKS_LIBC_DECL(name, type, sig) \
|
||||||
|
- type (*tsocks_libc_##name)(sig);
|
||||||
|
+ type (*TSOCKS_LIBC_FUNC(name))(sig);
|
||||||
|
#define TSOCKS_DECL(name, type, sig) \
|
||||||
|
extern type tsocks_##name(sig);
|
||||||
|
|
||||||
|
--
|
||||||
|
2.21.0
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ file(GLOB SOURCE_FILES
|
|||||||
"dialog/*.cpp"
|
"dialog/*.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
if(TOR_BIN)
|
if(TOR)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(ASSETS_TOR "assets_tor_macos.qrc")
|
set(ASSETS_TOR "assets_tor_macos.qrc")
|
||||||
else()
|
else()
|
||||||
@@ -50,6 +50,10 @@ if(TOR_BIN)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(XMRIG)
|
||||||
|
set(ASSETS_XMRIG "assets_mining.qrc")
|
||||||
|
endif()
|
||||||
|
|
||||||
set(EXECUTABLE_FLAG)
|
set(EXECUTABLE_FLAG)
|
||||||
if(MINGW)
|
if(MINGW)
|
||||||
set(EXECUTABLE_FLAG WIN32)
|
set(EXECUTABLE_FLAG WIN32)
|
||||||
@@ -73,7 +77,8 @@ add_executable(feather ${EXECUTABLE_FLAG} main.cpp
|
|||||||
${SOURCE_FILES}
|
${SOURCE_FILES}
|
||||||
${RESOURCES}
|
${RESOURCES}
|
||||||
${ASSETS_TOR}
|
${ASSETS_TOR}
|
||||||
)
|
${ASSETS_XMRIG}
|
||||||
|
)
|
||||||
|
|
||||||
# mac os bundle
|
# mac os bundle
|
||||||
set_target_properties(feather PROPERTIES
|
set_target_properties(feather PROPERTIES
|
||||||
@@ -121,11 +126,15 @@ if(DONATE_BEG)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(XMRTO)
|
if(XMRTO)
|
||||||
target_compile_definitions(feather PRIVATE HAS_XMRTO=1)
|
target_compile_definitions(feather PRIVATE XMRTO=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(TOR_BIN)
|
if(MORPHTOKEN)
|
||||||
target_compile_definitions(feather PRIVATE HAS_TOR_BIN=1)
|
target_compile_definitions(feather PRIVATE HAS_MORPHTOKEN=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(TOR)
|
||||||
|
target_compile_definitions(feather PRIVATE HAS_TOR=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(XMRIG)
|
if(XMRIG)
|
||||||
|
|||||||
@@ -0,0 +1,226 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2020, The Monero Project.
|
||||||
|
|
||||||
|
#include "MorphTokenWidget.h"
|
||||||
|
#include "ui_MorphTokenWidget.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "qrcode/QrCode.h"
|
||||||
|
#include "dialog/qrcodedialog.h"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
MorphTokenWidget::MorphTokenWidget(QWidget *parent) :
|
||||||
|
QWidget(parent),
|
||||||
|
ui(new Ui::MorphTokenWidget)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
m_ctx = MainWindow::getContext();
|
||||||
|
|
||||||
|
m_network = new UtilsNetworking(this->m_ctx->network);
|
||||||
|
m_api = new MorphTokenApi(this, m_network);
|
||||||
|
|
||||||
|
connect(ui->btnCreateTrade, &QPushButton::clicked, this, &MorphTokenWidget::createTrade);
|
||||||
|
connect(ui->btn_lookupTrade, &QPushButton::clicked, this, &MorphTokenWidget::lookupTrade);
|
||||||
|
connect(ui->btn_getRates, &QPushButton::clicked, this, &MorphTokenWidget::getRates);
|
||||||
|
|
||||||
|
connect(m_api, &MorphTokenApi::ApiResponse, this, &MorphTokenWidget::onApiResponse);
|
||||||
|
|
||||||
|
connect(ui->combo_From, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index){
|
||||||
|
this->displayRate();
|
||||||
|
ui->label_refundAddress->setText(QString("Refund address (%1):").arg(ui->combo_From->currentText()));
|
||||||
|
});
|
||||||
|
connect(ui->combo_To, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index){
|
||||||
|
this->displayRate();
|
||||||
|
ui->label_destinationAddress->setText(QString("Destination address (%1):").arg(ui->combo_To->currentText()));
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->check_autorefresh, &QCheckBox::toggled, [this](bool toggled){
|
||||||
|
m_countdown = 30;
|
||||||
|
toggled ? m_countdownTimer.start(1000) : m_countdownTimer.stop();
|
||||||
|
ui->check_autorefresh->setText("Autorefresh");
|
||||||
|
});
|
||||||
|
connect(&m_countdownTimer, &QTimer::timeout, this, &MorphTokenWidget::onCountdown);
|
||||||
|
|
||||||
|
connect(ui->line_Id, &QLineEdit::textChanged, [this](const QString &text){
|
||||||
|
ui->btn_lookupTrade->setEnabled(!text.isEmpty());
|
||||||
|
ui->check_autorefresh->setEnabled(!text.isEmpty());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Default to BTC -> XMR
|
||||||
|
ui->combo_From->setCurrentIndex(1);
|
||||||
|
ui->combo_To->setCurrentIndex(0);
|
||||||
|
|
||||||
|
ui->label_rate->setVisible(false);
|
||||||
|
m_ratesTimer.setSingleShot(true);
|
||||||
|
connect(&m_ratesTimer, &QTimer::timeout, [this]{
|
||||||
|
ui->label_rate->setVisible(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->qrCode->setVisible(false);
|
||||||
|
ui->label_depositAddress->setVisible(false);
|
||||||
|
connect(ui->qrCode, &ClickableLabel::clicked, this, &MorphTokenWidget::showQrCodeDialog);
|
||||||
|
|
||||||
|
ui->tabWidget->setTabVisible(2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphTokenWidget::createTrade() {
|
||||||
|
QString inputAsset = ui->combo_From->currentText();
|
||||||
|
QString outputAsset = ui->combo_To->currentText();
|
||||||
|
QString refundAddress = ui->line_refundAddress->text();
|
||||||
|
QString destinationAddress = ui->line_destinationAddress->text();
|
||||||
|
|
||||||
|
m_api->createTrade(inputAsset, outputAsset, refundAddress, destinationAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphTokenWidget::lookupTrade() {
|
||||||
|
QString morphId = ui->line_Id->text();
|
||||||
|
|
||||||
|
if (!morphId.isEmpty())
|
||||||
|
m_api->getTrade(morphId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphTokenWidget::getRates() {
|
||||||
|
m_api->getRates();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphTokenWidget::onApiResponse(const MorphTokenApi::MorphTokenResponse &resp) {
|
||||||
|
if (!resp.ok) {
|
||||||
|
ui->check_autorefresh->setChecked(false);
|
||||||
|
QMessageBox::warning(this, "MorphToken error", QString("Request failed:\n\n%1").arg(resp.message));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->debugInfo->setPlainText(QJsonDocument(resp.obj).toJson(QJsonDocument::Indented));
|
||||||
|
|
||||||
|
bool shouldShowQr = (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE || resp.endpoint == MorphTokenApi::Endpoint::GET_TRADE);
|
||||||
|
ui->qrCode->setVisible(shouldShowQr);
|
||||||
|
ui->label_depositAddress->setVisible(shouldShowQr);
|
||||||
|
|
||||||
|
if (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE || resp.endpoint == MorphTokenApi::Endpoint::GET_TRADE) {
|
||||||
|
ui->tabWidget->setCurrentIndex(1);
|
||||||
|
ui->line_Id->setText(resp.obj.value("id").toString());
|
||||||
|
|
||||||
|
auto obj = resp.obj;
|
||||||
|
auto input = obj["input"].toObject();
|
||||||
|
auto output = obj["output"].toArray()[0].toObject();
|
||||||
|
QString state = obj.value("state").toString();
|
||||||
|
QString statusText;
|
||||||
|
|
||||||
|
ui->trade->setTitle(QString("Trade (%1)").arg(state));
|
||||||
|
|
||||||
|
statusText += QString("Morph ID: %1\n\n").arg(obj["id"].toString());
|
||||||
|
|
||||||
|
if (state == "PENDING") {
|
||||||
|
statusText += QString("Waiting for a deposit, send %1 to %2\n").arg(input["asset"].toString(),
|
||||||
|
input["deposit_address"].toString());
|
||||||
|
statusText += QString("Rate: 1 %1 -> %2 %3\n\n").arg(input["asset"].toString(),
|
||||||
|
output["seen_rate"].toString(),
|
||||||
|
output["asset"].toString());
|
||||||
|
statusText += "Limits:\n";
|
||||||
|
statusText += QString(" Minimum amount accepted: %1 %2\n").arg(formatAmount(input["asset"].toString(), input["limits"].toObject()["min"].toDouble()),
|
||||||
|
input["asset"].toString());
|
||||||
|
statusText += QString(" Maximum amount accepted: %1 %2\n").arg(formatAmount(input["asset"].toString(), input["limits"].toObject()["max"].toDouble()),
|
||||||
|
input["asset"].toString());
|
||||||
|
statusText += QString("\nSend a single deposit. If the amount is outside the limits, a refund will happen.");
|
||||||
|
|
||||||
|
m_depositAddress = input["deposit_address"].toString();
|
||||||
|
|
||||||
|
const QrCode qrc(m_depositAddress, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::MEDIUM);
|
||||||
|
int width = ui->qrCode->width();
|
||||||
|
if (qrc.isValid()) {
|
||||||
|
ui->qrCode->setPixmap(qrc.toPixmap(1).scaled(width, width, Qt::KeepAspectRatio));
|
||||||
|
}
|
||||||
|
} else if (state == "PROCESSING" || state == "TRADING" || state == "CONFIRMING") {
|
||||||
|
if (state == "CONFIRMING") {
|
||||||
|
statusText += QString("Waiting for confirmations\n");
|
||||||
|
} else if (state == "TRADING") {
|
||||||
|
statusText += QString("Your transaction has been received and is confirmed. MorphToken is now executing your trade.\n"
|
||||||
|
"Usually this step takes no longer than a minute, "
|
||||||
|
"but in rare cases it can take a couple hours.\n"
|
||||||
|
"Wait a bit before contacting support.\n");
|
||||||
|
}
|
||||||
|
statusText += QString("Converting %1 to %2\n").arg(input["asset"].toString(), output["asset"].toString());
|
||||||
|
statusText += QString("Sending to %1\n").arg(output["address"].toString());
|
||||||
|
statusText += QString("Stuck? Contact support at contact@morphtoken.com");
|
||||||
|
} else if (state == "COMPLETE") {
|
||||||
|
if (output["txid"].toString().isEmpty()) {
|
||||||
|
statusText += QString("MorphToken is sending your transaction.\n");
|
||||||
|
statusText += QString("MorphToken will send %1 %2 to %2").arg(this->formatAmount(output["asset"].toString(), output["converted_amount"].toDouble() - output["network_fee"].toObject()["fee"].toDouble()),
|
||||||
|
output["asset"].toString(),
|
||||||
|
output["address"].toString());
|
||||||
|
} else {
|
||||||
|
statusText += QString("Sent %1 %2 to %3\ntxid: {}").arg(this->formatAmount(output["asset"].toString(), output["converted_amount"].toDouble() - output["network_fee"].toObject()["fee"].toDouble()),
|
||||||
|
output["asset"].toString(),
|
||||||
|
output["address"].toString(),
|
||||||
|
output["txid"].toString());
|
||||||
|
}
|
||||||
|
} else if (state == "PROCESSING_REFUND" || state == "COMPLETE_WITH_REFUND") {
|
||||||
|
statusText += QString("MorphToken will refund %1 %2\nReason: %3\n").arg(obj["final_amount"].toString(),
|
||||||
|
obj["asset"].toString(),
|
||||||
|
obj["reason"].toString());
|
||||||
|
|
||||||
|
if (obj.contains("txid")) {
|
||||||
|
statusText += QString("txid: %1").arg(obj["txid"].toString());
|
||||||
|
}
|
||||||
|
} else if (state == "COMPLETE_WITHOUT_REFUND") {
|
||||||
|
statusText += "Deposit amount below network fee, too small to refund.";
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->label_status->setText(statusText);
|
||||||
|
} else if (resp.endpoint == MorphTokenApi::Endpoint::GET_RATES) {
|
||||||
|
m_rates = resp.obj.value("data").toObject();
|
||||||
|
this->displayRate();
|
||||||
|
ui->label_rate->setVisible(true);
|
||||||
|
m_ratesTimer.start(120 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE) {
|
||||||
|
QMessageBox::information(this, "MorphToken", "Trade created!\n\nMake sure to save your Morph ID. You may need it in case something goes wrong.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphTokenWidget::onCountdown() {
|
||||||
|
if (m_countdown > 0) {
|
||||||
|
m_countdown -= 1;
|
||||||
|
} else {
|
||||||
|
this->lookupTrade();
|
||||||
|
m_countdown = 30;
|
||||||
|
}
|
||||||
|
ui->check_autorefresh->setText(QString("Autorefresh (%1)").arg(m_countdown));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphTokenWidget::displayRate() {
|
||||||
|
QString inputAsset = ui->combo_From->currentText();
|
||||||
|
QString outputAsset = ui->combo_To->currentText();
|
||||||
|
QString outputRate = m_rates.value(inputAsset).toObject().value(outputAsset).toString("1");
|
||||||
|
|
||||||
|
QString rateStr = QString("1 %1 -> %2 %3").arg(inputAsset, outputRate, outputAsset);
|
||||||
|
ui->label_rate->setText(rateStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphTokenWidget::showQrCodeDialog() {
|
||||||
|
QrCode qr(m_depositAddress, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::HIGH);
|
||||||
|
auto *dialog = new QrCodeDialog(this, qr, "Deposit address");
|
||||||
|
dialog->exec();
|
||||||
|
dialog->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MorphTokenWidget::formatAmount(const QString &asset, double amount) {
|
||||||
|
double displayAmount;
|
||||||
|
double div;
|
||||||
|
|
||||||
|
if (asset == "ETH")
|
||||||
|
div = 1e18;
|
||||||
|
else if (asset == "XMR")
|
||||||
|
div = 1e12;
|
||||||
|
else
|
||||||
|
div = 1e8;
|
||||||
|
|
||||||
|
displayAmount = amount / div;
|
||||||
|
|
||||||
|
return QString::number(displayAmount, 'f', 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
MorphTokenWidget::~MorphTokenWidget() {
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2020, The Monero Project.
|
||||||
|
|
||||||
|
#ifndef FEATHER_MORPHTOKENWIDGET_H
|
||||||
|
#define FEATHER_MORPHTOKENWIDGET_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include "appcontext.h"
|
||||||
|
#include "utils/MorphTokenApi.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class MorphTokenWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MorphTokenWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MorphTokenWidget(QWidget *parent = nullptr);
|
||||||
|
~MorphTokenWidget() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createTrade();
|
||||||
|
void lookupTrade();
|
||||||
|
void getRates();
|
||||||
|
void onApiResponse(const MorphTokenApi::MorphTokenResponse &resp);
|
||||||
|
|
||||||
|
void onCountdown();
|
||||||
|
void displayRate();
|
||||||
|
void showQrCodeDialog();
|
||||||
|
|
||||||
|
QString formatAmount(const QString &asset, double amount);
|
||||||
|
|
||||||
|
Ui::MorphTokenWidget *ui;
|
||||||
|
|
||||||
|
AppContext *m_ctx;
|
||||||
|
MorphTokenApi *m_api;
|
||||||
|
UtilsNetworking *m_network;
|
||||||
|
QTimer m_countdownTimer;
|
||||||
|
int m_countdown = 30;
|
||||||
|
QJsonObject m_rates;
|
||||||
|
QTimer m_ratesTimer;
|
||||||
|
QString m_depositAddress;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //FEATHER_MORPHTOKENWIDGET_H
|
||||||
@@ -0,0 +1,423 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MorphTokenWidget</class>
|
||||||
|
<widget class="QWidget" name="MorphTokenWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1036</width>
|
||||||
|
<height>614</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tabCreateTrade">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Create trade</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>From:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="combo_From">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>XMR</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>BTC</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>ETH</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>BCH</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>LTC</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>DASH</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>To:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="combo_To">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>XMR</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>BTC</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>ETH</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>BCH</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>LTC</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>DASH</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_rate">
|
||||||
|
<property name="text">
|
||||||
|
<string>rate</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_refundAddress">
|
||||||
|
<property name="text">
|
||||||
|
<string>Refund address (XMR):</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_refundAddress"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_destinationAddress">
|
||||||
|
<property name="text">
|
||||||
|
<string>Destination address (XMR):</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_destinationAddress"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Powered by MorphToken.com</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_getRates">
|
||||||
|
<property name="text">
|
||||||
|
<string>Get Rates</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnCreateTrade">
|
||||||
|
<property name="text">
|
||||||
|
<string>Create Trade</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tabLookupTrade">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Lookup trade</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Morph ID or MorphToken deposit address:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="line_Id"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="check_autorefresh">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Autorefresh</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_lookupTrade">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Lookup trade</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="trade">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Trade</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_status">
|
||||||
|
<property name="text">
|
||||||
|
<string>No trade loaded.</string>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="ClickableLabel" name="qrCode">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>qrcode</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_depositAddress">
|
||||||
|
<property name="text">
|
||||||
|
<string>Deposit address</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tabDebug">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Debug</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||||
|
<item>
|
||||||
|
<widget class="QPlainTextEdit" name="debugInfo">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>ClickableLabel</class>
|
||||||
|
<extends>QLabel</extends>
|
||||||
|
<header>components.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@@ -37,7 +37,6 @@ WalletKeysFilesModel *AppContext::wallets = nullptr;
|
|||||||
TxFiatHistory *AppContext::txFiatHistory = nullptr;
|
TxFiatHistory *AppContext::txFiatHistory = nullptr;
|
||||||
double AppContext::balance = 0;
|
double AppContext::balance = 0;
|
||||||
QMap<QString, QString> AppContext::txDescriptionCache;
|
QMap<QString, QString> AppContext::txDescriptionCache;
|
||||||
QMap<QString, QString> AppContext::txCache;
|
|
||||||
|
|
||||||
AppContext::AppContext(QCommandLineParser *cmdargs) {
|
AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||||
this->network = new QNetworkAccessManager();
|
this->network = new QNetworkAccessManager();
|
||||||
@@ -80,19 +79,13 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
|||||||
this->accountName = Utils::getUnixAccountName();
|
this->accountName = Utils::getUnixAccountName();
|
||||||
this->homeDir = QDir::homePath();
|
this->homeDir = QDir::homePath();
|
||||||
|
|
||||||
QString walletDir = config()->get(Config::walletDirectory).toString();
|
|
||||||
if (walletDir.isEmpty()) {
|
|
||||||
#if defined(Q_OS_LINUX) or defined(Q_OS_MAC)
|
#if defined(Q_OS_LINUX) or defined(Q_OS_MAC)
|
||||||
this->defaultWalletDir = QString("%1/Monero/wallets").arg(this->configRoot);
|
this->defaultWalletDir = QString("%1/Monero/wallets").arg(this->configRoot);
|
||||||
this->defaultWalletDirRoot = QString("%1/Monero").arg(this->configRoot);
|
this->defaultWalletDirRoot = QString("%1/Monero").arg(this->configRoot);
|
||||||
#elif defined(Q_OS_WIN)
|
#elif defined(Q_OS_WIN)
|
||||||
this->defaultWalletDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Monero";
|
this->defaultWalletDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Monero";
|
||||||
this->defaultWalletDirRoot = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
this->defaultWalletDirRoot = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
|
||||||
this->defaultWalletDir = walletDir;
|
|
||||||
this->defaultWalletDirRoot = walletDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create wallet dirs
|
// Create wallet dirs
|
||||||
if (!QDir().mkpath(defaultWalletDir))
|
if (!QDir().mkpath(defaultWalletDir))
|
||||||
@@ -103,6 +96,7 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
|||||||
if(!this->configDirectory.endsWith('/'))
|
if(!this->configDirectory.endsWith('/'))
|
||||||
this->configDirectory = QString("%1/").arg(this->configDirectory);
|
this->configDirectory = QString("%1/").arg(this->configDirectory);
|
||||||
#endif
|
#endif
|
||||||
|
this->sorry();
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
createConfigDirectory(this->configDirectory);
|
createConfigDirectory(this->configDirectory);
|
||||||
@@ -140,11 +134,11 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
|||||||
auto genesis_timestamp = this->restoreHeights[NetworkType::Type::MAINNET]->data.firstKey();
|
auto genesis_timestamp = this->restoreHeights[NetworkType::Type::MAINNET]->data.firstKey();
|
||||||
AppContext::txFiatHistory = new TxFiatHistory(genesis_timestamp, this->configDirectory);
|
AppContext::txFiatHistory = new TxFiatHistory(genesis_timestamp, this->configDirectory);
|
||||||
connect(this->ws, &WSClient::connectionEstablished, AppContext::txFiatHistory, &TxFiatHistory::onUpdateDatabase);
|
connect(this->ws, &WSClient::connectionEstablished, AppContext::txFiatHistory, &TxFiatHistory::onUpdateDatabase);
|
||||||
connect(AppContext::txFiatHistory, &TxFiatHistory::requestYear, [=](int year){
|
connect(AppContext::txFiatHistory, &TxFiatHistory::requestYear, [=](unsigned int year){
|
||||||
QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1}})").arg(year).toUtf8();
|
QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1}})").arg(year).toUtf8();
|
||||||
this->ws->sendMsg(data);
|
this->ws->sendMsg(data);
|
||||||
});
|
});
|
||||||
connect(AppContext::txFiatHistory, &TxFiatHistory::requestYearMonth, [=](int year, int month) {
|
connect(AppContext::txFiatHistory, &TxFiatHistory::requestYearMonth, [=](unsigned int year, unsigned int month) {
|
||||||
QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1, "month": %2}})").arg(year).arg(month).toUtf8();
|
QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1, "month": %2}})").arg(year).arg(month).toUtf8();
|
||||||
this->ws->sendMsg(data);
|
this->ws->sendMsg(data);
|
||||||
});
|
});
|
||||||
@@ -153,7 +147,7 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
|||||||
AppContext::prices = new Prices();
|
AppContext::prices = new Prices();
|
||||||
|
|
||||||
// xmr.to
|
// xmr.to
|
||||||
#ifdef HAS_XMRTO
|
#ifdef XMRTO
|
||||||
this->XMRTo = new XmrTo(this);
|
this->XMRTo = new XmrTo(this);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -384,22 +378,22 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
|
|||||||
auto changed = false;
|
auto changed = false;
|
||||||
|
|
||||||
if(!this->heights.contains("mainnet")) {
|
if(!this->heights.contains("mainnet")) {
|
||||||
this->heights["mainnet"] = mainnet;
|
this->heights["mainnet"] = (unsigned int) mainnet;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (mainnet > this->heights["mainnet"]) {
|
if (mainnet > this->heights["mainnet"]) {
|
||||||
this->heights["mainnet"] = mainnet;
|
this->heights["mainnet"] = (unsigned int) mainnet;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!this->heights.contains("stagenet")) {
|
if(!this->heights.contains("stagenet")) {
|
||||||
this->heights["stagenet"] = stagenet;
|
this->heights["stagenet"] = (unsigned int) stagenet;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (stagenet > this->heights["stagenet"]) {
|
if (stagenet > this->heights["stagenet"]) {
|
||||||
this->heights["stagenet"] = stagenet;
|
this->heights["stagenet"] = (unsigned int) stagenet;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -425,7 +419,7 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
|
|||||||
QJsonObject fiat_rates = msg.value("data").toObject();
|
QJsonObject fiat_rates = msg.value("data").toObject();
|
||||||
AppContext::prices->fiatPricesReceived(fiat_rates);
|
AppContext::prices->fiatPricesReceived(fiat_rates);
|
||||||
}
|
}
|
||||||
#if defined(HAS_XMRTO)
|
#if defined(XMRTO)
|
||||||
else if(cmd == "xmrto_rates") {
|
else if(cmd == "xmrto_rates") {
|
||||||
auto xmr_rates = msg.value("data").toObject();
|
auto xmr_rates = msg.value("data").toObject();
|
||||||
this->XMRTo->onRatesReceived(xmr_rates);
|
this->XMRTo->onRatesReceived(xmr_rates);
|
||||||
@@ -532,8 +526,9 @@ void AppContext::onWSCCS(const QJsonArray &ccs_data) {
|
|||||||
void AppContext::createConfigDirectory(const QString &dir) {
|
void AppContext::createConfigDirectory(const QString &dir) {
|
||||||
QString config_dir_tor = QString("%1%2").arg(dir).arg("tor");
|
QString config_dir_tor = QString("%1%2").arg(dir).arg("tor");
|
||||||
QString config_dir_tordata = QString("%1%2").arg(dir).arg("tor/data");
|
QString config_dir_tordata = QString("%1%2").arg(dir).arg("tor/data");
|
||||||
|
QString config_dir_xmrig = QString("%1%2").arg(dir).arg("xmrig");
|
||||||
|
|
||||||
QStringList createDirs({dir, config_dir_tor, config_dir_tordata});
|
QStringList createDirs({dir, config_dir_tor, config_dir_tordata, config_dir_xmrig});
|
||||||
for(const auto &d: createDirs) {
|
for(const auto &d: createDirs) {
|
||||||
if(!Utils::dirExists(d)) {
|
if(!Utils::dirExists(d)) {
|
||||||
qDebug() << QString("Creating directory: %1").arg(d);
|
qDebug() << QString("Creating directory: %1").arg(d);
|
||||||
@@ -611,7 +606,7 @@ void AppContext::initRestoreHeights() {
|
|||||||
restoreHeights[NetworkType::MAINNET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_monero_mainnet.txt", NetworkType::MAINNET);
|
restoreHeights[NetworkType::MAINNET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_monero_mainnet.txt", NetworkType::MAINNET);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::onSetRestoreHeight(quint64 height){
|
void AppContext::onSetRestoreHeight(unsigned int height){
|
||||||
auto seed = this->currentWallet->getCacheAttribute("feather.seed");
|
auto seed = this->currentWallet->getCacheAttribute("feather.seed");
|
||||||
if(!seed.isEmpty()) {
|
if(!seed.isEmpty()) {
|
||||||
const auto msg = "This wallet has a 14 word mnemonic seed which has the restore height embedded.";
|
const auto msg = "This wallet has a 14 word mnemonic seed which has the restore height embedded.";
|
||||||
@@ -686,6 +681,15 @@ void AppContext::donateBeg() {
|
|||||||
config()->set(Config::donateBeg, donationCounter);
|
config()->set(Config::donateBeg, donationCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppContext::sorry() {
|
||||||
|
auto msg = "Unable to start Feather, error code 0xd34db33f. If this problem "
|
||||||
|
"persists, please contact Technical Support.";
|
||||||
|
QStringList paths = {"C:\\ProgramData\\ryo", this->homeDir + "/.ryo"};
|
||||||
|
for(const QString &ryo: paths)
|
||||||
|
if(Utils::dirExists(ryo))
|
||||||
|
throw std::runtime_error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
AppContext::~AppContext() {
|
AppContext::~AppContext() {
|
||||||
this->walletClose(false);
|
this->walletClose(false);
|
||||||
}
|
}
|
||||||
@@ -727,8 +731,7 @@ void AppContext::onWalletRefreshed(bool success) {
|
|||||||
if (!this->refreshed) {
|
if (!this->refreshed) {
|
||||||
refreshModels();
|
refreshModels();
|
||||||
this->refreshed = true;
|
this->refreshed = true;
|
||||||
// store wallet immediately upon finishing synchronization
|
this->storeWallet();
|
||||||
this->currentWallet->store();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Wallet refresh status: " << success;
|
qDebug() << "Wallet refresh status: " << success;
|
||||||
@@ -761,28 +764,23 @@ void AppContext::onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, q
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::onTransactionCreated(PendingTransaction *tx, const QString &address, const QString &paymentId, quint32 mixin) {
|
void AppContext::onTransactionCreated(PendingTransaction *tx, const QString &address, const QString &paymentId, quint32 mixin) {
|
||||||
if(address == this->donationAddress)
|
if(address == this->featherDonationAddress)
|
||||||
this->donationSending = true;
|
this->featherDonationSending = true;
|
||||||
|
|
||||||
// Let UI know that the transaction was constructed
|
|
||||||
emit endTransaction();
|
|
||||||
|
|
||||||
// tx created, but not sent yet. ask user to verify first.
|
// tx created, but not sent yet. ask user to verify first.
|
||||||
emit createTransactionSuccess(tx, address, mixin);
|
emit createTransactionSuccess(tx, address, mixin);
|
||||||
|
emit endTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid){
|
void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid){
|
||||||
this->currentWallet->history()->refresh(this->currentWallet->currentSubaddressAccount());
|
this->currentWallet->history()->refresh(this->currentWallet->currentSubaddressAccount());
|
||||||
this->currentWallet->coins()->refresh(this->currentWallet->currentSubaddressAccount());
|
this->currentWallet->coins()->refresh(this->currentWallet->currentSubaddressAccount());
|
||||||
|
this->storeWallet();
|
||||||
// Store wallet immediately so we don't risk losing tx key if wallet crashes
|
|
||||||
this->currentWallet->store();
|
|
||||||
|
|
||||||
emit transactionCommitted(status, tx, txid);
|
emit transactionCommitted(status, tx, txid);
|
||||||
|
|
||||||
// this tx was a donation to Feather, stop our nagging
|
// this tx was a donation to Feather, stop our nagging
|
||||||
if(this->donationSending) {
|
if(this->featherDonationSending) {
|
||||||
this->donationSending = false;
|
this->featherDonationSending = false;
|
||||||
config()->set(Config::donateBeg, -1);
|
config()->set(Config::donateBeg, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -799,14 +797,16 @@ void AppContext::storeWallet() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::updateBalance() {
|
void AppContext::updateBalance() {
|
||||||
if (!this->currentWallet)
|
if(!this->currentWallet)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
quint64 balance_u = this->currentWallet->balance();
|
AppContext::balance = this->currentWallet->balance() / globals::cdiv;
|
||||||
AppContext::balance = balance_u / globals::cdiv;
|
auto balance_str = QString::number(balance, 'f');
|
||||||
double spendable = this->currentWallet->unlockedBalance();
|
|
||||||
|
|
||||||
emit balanceUpdated(balance_u, spendable);
|
double unlocked = this->currentWallet->unlockedBalance() / globals::cdiv;
|
||||||
|
auto unlocked_str = QString::number(unlocked, 'f');
|
||||||
|
|
||||||
|
emit balanceUpdated(balance, unlocked, balance_str, unlocked_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::syncStatusUpdated(quint64 height, quint64 target) {
|
void AppContext::syncStatusUpdated(quint64 height, quint64 target) {
|
||||||
|
|||||||
@@ -39,11 +39,9 @@ public:
|
|||||||
bool isTails = false;
|
bool isTails = false;
|
||||||
bool isWhonix = false;
|
bool isWhonix = false;
|
||||||
bool isDebug = false;
|
bool isDebug = false;
|
||||||
|
const QString featherDonationAddress = "47ntfT2Z5384zku39pTM6hGcnLnvpRYW2Azm87GiAAH2bcTidtq278TL6HmwyL8yjMeERqGEBs3cqC8vvHPJd1cWQrGC65f";
|
||||||
// Donation config
|
const int featherDonationAmount = 50; // euro
|
||||||
const QString donationAddress = "47ntfT2Z5384zku39pTM6hGcnLnvpRYW2Azm87GiAAH2bcTidtq278TL6HmwyL8yjMeERqGEBs3cqC8vvHPJd1cWQrGC65f";
|
bool featherDonationSending = false;
|
||||||
const int donationAmount = 25; // euro
|
|
||||||
bool donationSending = false;
|
|
||||||
|
|
||||||
QCommandLineParser *cmdargs;
|
QCommandLineParser *cmdargs;
|
||||||
|
|
||||||
@@ -66,9 +64,9 @@ public:
|
|||||||
|
|
||||||
static void createConfigDirectory(const QString &dir) ;
|
static void createConfigDirectory(const QString &dir) ;
|
||||||
|
|
||||||
QMap<QString, int> heights;
|
QMap<QString, unsigned int> heights;
|
||||||
QMap<NetworkType::Type, RestoreHeightLookup*> restoreHeights;
|
QMap<NetworkType::Type, RestoreHeightLookup*> restoreHeights;
|
||||||
const quint64 kdfRounds = 1;
|
const unsigned int kdfRounds = 1;
|
||||||
PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low;
|
PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low;
|
||||||
quint32 tx_mixin = static_cast<const quint32 &>(10);
|
quint32 tx_mixin = static_cast<const quint32 &>(10);
|
||||||
QString seedLanguage = "English"; // 14 word `monero-seed` only has English
|
QString seedLanguage = "English"; // 14 word `monero-seed` only has English
|
||||||
@@ -86,10 +84,10 @@ public:
|
|||||||
static WalletKeysFilesModel *wallets;
|
static WalletKeysFilesModel *wallets;
|
||||||
static double balance;
|
static double balance;
|
||||||
static QMap<QString, QString> txDescriptionCache;
|
static QMap<QString, QString> txDescriptionCache;
|
||||||
static QMap<QString, QString> txCache;
|
|
||||||
static TxFiatHistory *txFiatHistory;
|
static TxFiatHistory *txFiatHistory;
|
||||||
|
|
||||||
// libwalletqt
|
// libwalletqt
|
||||||
|
unsigned int blockHeight = 0;
|
||||||
bool refreshed = false;
|
bool refreshed = false;
|
||||||
WalletManager *walletManager;
|
WalletManager *walletManager;
|
||||||
Wallet *currentWallet = nullptr;
|
Wallet *currentWallet = nullptr;
|
||||||
@@ -115,7 +113,7 @@ public slots:
|
|||||||
void onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) const;
|
void onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) const;
|
||||||
void onCreateTransactionError(const QString &msg);
|
void onCreateTransactionError(const QString &msg);
|
||||||
void onOpenAliasResolve(const QString &openAlias);
|
void onOpenAliasResolve(const QString &openAlias);
|
||||||
void onSetRestoreHeight(quint64 height);
|
void onSetRestoreHeight(unsigned int height);
|
||||||
void onPreferredFiatCurrencyChanged(const QString &symbol);
|
void onPreferredFiatCurrencyChanged(const QString &symbol);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
@@ -137,11 +135,11 @@ private slots:
|
|||||||
void onConnectionStatusChanged(int status);
|
void onConnectionStatusChanged(int status);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void balanceUpdated(quint64 balance, quint64 spendable);
|
void balanceUpdated(double balance, double unlocked, QString balance_str, QString unlocked_str);
|
||||||
void blockchainSync(int height, int target);
|
void blockchainSync(int height, int target);
|
||||||
void refreshSync(int height, int target);
|
void refreshSync(int height, int target);
|
||||||
void synchronized();
|
void synchronized();
|
||||||
void blockHeightWSUpdated(QMap<QString, int> heights);
|
void blockHeightWSUpdated(QMap<QString, unsigned int> heights);
|
||||||
void walletSynchronized();
|
void walletSynchronized();
|
||||||
void walletOpened();
|
void walletOpened();
|
||||||
void walletClosed();
|
void walletClosed();
|
||||||
@@ -163,7 +161,7 @@ signals:
|
|||||||
void openAliasResolveError(const QString &msg);
|
void openAliasResolveError(const QString &msg);
|
||||||
void openAliasResolved(const QString &address, const QString &openAlias);
|
void openAliasResolved(const QString &address, const QString &openAlias);
|
||||||
void setRestoreHeightError(const QString &msg);
|
void setRestoreHeightError(const QString &msg);
|
||||||
void customRestoreHeightSet(int height);
|
void customRestoreHeightSet(unsigned int height);
|
||||||
void closeApplication();
|
void closeApplication();
|
||||||
void donationNag();
|
void donationNag();
|
||||||
void initiateTransaction();
|
void initiateTransaction();
|
||||||
@@ -172,7 +170,8 @@ signals:
|
|||||||
void setTitle(const QString &title); // set window title
|
void setTitle(const QString &title); // set window title
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const int m_donationBoundary = 15;
|
void sorry();
|
||||||
|
const unsigned int m_donationBoundary = 15;
|
||||||
UtilsNetworking *m_utilsNetworkingNodes;
|
UtilsNetworking *m_utilsNetworkingNodes;
|
||||||
QTimer *m_storeTimer = new QTimer(this);
|
QTimer *m_storeTimer = new QTimer(this);
|
||||||
QUrl m_wsUrl = QUrl(QStringLiteral("ws://7e6egbawekbkxzkv4244pqeqgoo4axko2imgjbedwnn6s5yb6b7oliqd.onion/ws"));
|
QUrl m_wsUrl = QUrl(QStringLiteral("ws://7e6egbawekbkxzkv4244pqeqgoo4axko2imgjbedwnn6s5yb6b7oliqd.onion/ws"));
|
||||||
|
|||||||
@@ -11,9 +11,6 @@
|
|||||||
<file>assets/images/appicons/128x128.png</file>
|
<file>assets/images/appicons/128x128.png</file>
|
||||||
<file>assets/images/appicons/256x256.png</file>
|
<file>assets/images/appicons/256x256.png</file>
|
||||||
<file>assets/images/arrow.svg</file>
|
<file>assets/images/arrow.svg</file>
|
||||||
<file>assets/images/banners/1.png</file>
|
|
||||||
<file>assets/images/banners/2.png</file>
|
|
||||||
<file>assets/images/banners/3.png</file>
|
|
||||||
<file>assets/images/bitcoin.png</file>
|
<file>assets/images/bitcoin.png</file>
|
||||||
<file>assets/images/camera_dark.png</file>
|
<file>assets/images/camera_dark.png</file>
|
||||||
<file>assets/images/camera_white.png</file>
|
<file>assets/images/camera_white.png</file>
|
||||||
@@ -39,7 +36,8 @@
|
|||||||
<file>assets/images/eye_blind.png</file>
|
<file>assets/images/eye_blind.png</file>
|
||||||
<file>assets/images/feather.png</file>
|
<file>assets/images/feather.png</file>
|
||||||
<file>assets/images/file.png</file>
|
<file>assets/images/file.png</file>
|
||||||
<file>assets/images/gnome-calc.png</file>
|
<file>assets/images/ghost.png</file>
|
||||||
|
<file>assets/images/ghost_icon.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/key.png</file>
|
<file>assets/images/key.png</file>
|
||||||
@@ -50,10 +48,11 @@
|
|||||||
<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>
|
||||||
<file>assets/images/microphone.png</file>
|
<file>assets/images/microphone.png</file>
|
||||||
<file>assets/images/mining.png</file>
|
<file>assets/images/morphtoken.png</file>
|
||||||
<file>assets/images/network.png</file>
|
<file>assets/images/network.png</file>
|
||||||
<file>assets/images/offline_tx.png</file>
|
<file>assets/images/offline_tx.png</file>
|
||||||
<file>assets/images/person.svg</file>
|
<file>assets/images/person.svg</file>
|
||||||
|
<file>assets/images/photos/1.png</file>
|
||||||
<file>assets/images/preferences.png</file>
|
<file>assets/images/preferences.png</file>
|
||||||
<file>assets/images/preferences.svg</file>
|
<file>assets/images/preferences.svg</file>
|
||||||
<file>assets/images/qrcode.png</file>
|
<file>assets/images/qrcode.png</file>
|
||||||
@@ -104,5 +103,6 @@
|
|||||||
<file>assets/mnemonic_25_english.txt</file>
|
<file>assets/mnemonic_25_english.txt</file>
|
||||||
<file>assets/restore_heights_monero_mainnet.txt</file>
|
<file>assets/restore_heights_monero_mainnet.txt</file>
|
||||||
<file>assets/restore_heights_monero_stagenet.txt</file>
|
<file>assets/restore_heights_monero_stagenet.txt</file>
|
||||||
|
<file>assets/user_agents.txt</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
The wallet UI is heavily inspired by Electrum. We would like to recognize Thomas Voegtlin for his pioneering work on Bitcoin.
|
|
||||||
|
|
||||||
Feather uses monero-seed written by Tevador, for 14 word mnemonic seeds.
|
Feather uses monero-seed written by Tevador, for 14 word mnemonic seeds.
|
||||||
|
|
||||||
Wizard banner art was adapted from a paper wallet design by the themonera (themonera.art).
|
|
||||||
|
|
||||||
Initial CMake support for the Monero GUI was coded by TheCharlatan/xiphon.
|
Initial CMake support for the Monero GUI was coded by TheCharlatan/xiphon.
|
||||||
|
|
||||||
|
The wallet UI is heavily inspired by Electrum. We would like to recognize Thomas Voegtlin for his pioneering work on Bitcoin.
|
||||||
|
|
||||||
Huge thanks to nioc, fluffypony, wowario, thrmo for help during development.
|
Huge thanks to nioc, fluffypony, wowario, thrmo for help during development.
|
||||||
|
|
||||||
Some more shoutouts for people for hosting nodes and/or having good ideas:
|
Some more shoutouts for people for hosting nodes and/or having good ideas:
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>assets/exec/xmrig</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
@@ -14,7 +14,6 @@
|
|||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QMessageBox>
|
|
||||||
|
|
||||||
CoinsWidget::CoinsWidget(QWidget *parent)
|
CoinsWidget::CoinsWidget(QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
@@ -37,7 +36,6 @@ CoinsWidget::CoinsWidget(QWidget *parent)
|
|||||||
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);});
|
||||||
m_copyMenu->addAction("Address", this, [this]{copy(copyField::Address);});
|
m_copyMenu->addAction("Address", this, [this]{copy(copyField::Address);});
|
||||||
m_copyMenu->addAction("Label", this, [this]{copy(copyField::Label);});
|
|
||||||
m_copyMenu->addAction("Height", this, [this]{copy(copyField::Height);});
|
m_copyMenu->addAction("Height", this, [this]{copy(copyField::Height);});
|
||||||
m_copyMenu->addAction("Amount", this, [this]{copy(copyField::Amount);});
|
m_copyMenu->addAction("Amount", this, [this]{copy(copyField::Amount);});
|
||||||
|
|
||||||
@@ -198,7 +196,7 @@ void CoinsWidget::onSweepOutput() {
|
|||||||
qCritical() << "key image: " << keyImage;
|
qCritical() << "key image: " << keyImage;
|
||||||
|
|
||||||
if (!keyImageKnown) {
|
if (!keyImageKnown) {
|
||||||
QMessageBox::warning(this, "Unable to sweep output", "Unable to sweep output: key image unknown");
|
Utils::showMessageBox("Unable to sweep output", "Unable to sweep output: key image unknown", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,9 +229,6 @@ void CoinsWidget::copy(copyField field) {
|
|||||||
case Address:
|
case Address:
|
||||||
data = c.address();
|
data = c.address();
|
||||||
break;
|
break;
|
||||||
case Label:
|
|
||||||
data = c.addressLabel();
|
|
||||||
break;
|
|
||||||
case Height:
|
case Height:
|
||||||
data = QString::number(c.blockHeight());
|
data = QString::number(c.blockHeight());
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ private:
|
|||||||
KeyImage,
|
KeyImage,
|
||||||
TxID,
|
TxID,
|
||||||
Address,
|
Address,
|
||||||
Label,
|
|
||||||
Height,
|
Height,
|
||||||
Amount
|
Amount
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,17 +5,17 @@
|
|||||||
#include "ui_contactswidget.h"
|
#include "ui_contactswidget.h"
|
||||||
#include "dialog/contactsdialog.h"
|
#include "dialog/contactsdialog.h"
|
||||||
#include "model/ModelUtils.h"
|
#include "model/ModelUtils.h"
|
||||||
#include "mainwindow.h"
|
#include "utils/utils.h"
|
||||||
#include "libwalletqt/AddressBook.h"
|
|
||||||
|
|
||||||
#include <QMessageBox>
|
#include <QClipboard>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
ContactsWidget::ContactsWidget(QWidget *parent) :
|
ContactsWidget::ContactsWidget(QWidget *parent) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
ui(new Ui::ContactsWidget)
|
ui(new Ui::ContactsWidget)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
m_ctx = MainWindow::getContext();
|
|
||||||
|
|
||||||
// header context menu
|
// header context menu
|
||||||
ui->contacts->header()->setContextMenuPolicy(Qt::CustomContextMenu);
|
ui->contacts->header()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
@@ -28,16 +28,15 @@ 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(QIcon(":/assets/images/person.svg"), "New contact", this, &ContactsWidget::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(QIcon(":/assets/images/copy.png"), "Copy address", this, &ContactsWidget::copyAddress);
|
||||||
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy name", this, &ContactsWidget::copyName);
|
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy name", this, &ContactsWidget::copyName);
|
||||||
m_rowMenu->addAction("Pay to", this, &ContactsWidget::payTo);
|
m_rowMenu->addAction(QIcon(":/assets/images/appicons/128x128.png"), "Pay to", this, &ContactsWidget::payTo);
|
||||||
m_rowMenu->addAction("Delete", this, &ContactsWidget::deleteContact);
|
m_deleteEntryAction = m_rowMenu->addAction("Delete", this, &ContactsWidget::deleteContact);
|
||||||
|
|
||||||
connect(ui->contacts, &QTreeView::customContextMenuRequested, [=](const QPoint & point){
|
connect(ui->contacts, &QTreeView::customContextMenuRequested, [=](const QPoint & point){
|
||||||
QModelIndex index = ui->contacts->indexAt(point);
|
QModelIndex index = ui->contacts->indexAt(point);
|
||||||
@@ -96,44 +95,16 @@ void ContactsWidget::showHeaderMenu(const QPoint& position)
|
|||||||
m_headerMenu->exec(QCursor::pos());
|
m_headerMenu->exec(QCursor::pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContactsWidget::newContact(QString address, QString name)
|
void ContactsWidget::newContact()
|
||||||
{
|
{
|
||||||
auto * dialog = new ContactsDialog(this, address, name);
|
auto * dialog = new ContactsDialog(this);
|
||||||
int ret = dialog->exec();
|
int ret = dialog->exec();
|
||||||
if (!ret) return;
|
if (!ret) return;
|
||||||
|
|
||||||
address = dialog->getAddress();
|
QString address = dialog->getAddress();
|
||||||
name = dialog->getName();
|
QString name = dialog->getName();
|
||||||
|
|
||||||
bool addressValid = WalletManager::addressValid(address, m_ctx->currentWallet->nettype());
|
emit addContact(address, name);
|
||||||
if (!addressValid) {
|
|
||||||
QMessageBox::warning(this, "Invalid address", "Invalid address");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int num_addresses = m_ctx->currentWallet->addressBook()->count();
|
|
||||||
QString address_entry;
|
|
||||||
QString name_entry;
|
|
||||||
for (int i=0; i<num_addresses; i++) {
|
|
||||||
m_ctx->currentWallet->addressBook()->getRow(i, [&address_entry, &name_entry](const AddressBookInfo &entry){
|
|
||||||
address_entry = entry.address();
|
|
||||||
name_entry = entry.description();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (address == address_entry) {
|
|
||||||
QMessageBox::warning(this, "Unable to add contact", "Duplicate address");
|
|
||||||
ui->contacts->setCurrentIndex(m_model->index(i,0)); // Highlight duplicate address
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (name == name_entry) {
|
|
||||||
QMessageBox::warning(this, "Unable to add contact", "Duplicate label");
|
|
||||||
this->newContact(address, name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ctx->currentWallet->addressBook()->addRow(address, "", name);
|
|
||||||
m_ctx->storeWallet();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContactsWidget::deleteContact()
|
void ContactsWidget::deleteContact()
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include "model/AddressBookModel.h"
|
#include "model/AddressBookModel.h"
|
||||||
#include "model/AddressBookProxyModel.h"
|
#include "model/AddressBookProxyModel.h"
|
||||||
#include "appcontext.h"
|
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
@@ -22,28 +21,29 @@ class ContactsWidget : public QWidget
|
|||||||
public:
|
public:
|
||||||
explicit ContactsWidget(QWidget *parent = nullptr);
|
explicit ContactsWidget(QWidget *parent = nullptr);
|
||||||
void setModel(AddressBookModel * model);
|
void setModel(AddressBookModel * model);
|
||||||
~ContactsWidget() override;
|
~ContactsWidget();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void copyAddress();
|
void copyAddress();
|
||||||
void copyName();
|
void copyName();
|
||||||
void payTo();
|
void payTo();
|
||||||
void newContact(QString address = "", QString name = "");
|
void newContact();
|
||||||
void deleteContact();
|
void deleteContact();
|
||||||
void setShowFullAddresses(bool show);
|
void setShowFullAddresses(bool show);
|
||||||
void setSearchFilter(const QString &filter);
|
void setSearchFilter(const QString &filter);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void addContact(QString &address, QString &name);
|
||||||
void fillAddress(QString &address);
|
void fillAddress(QString &address);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void showHeaderMenu(const QPoint &position);
|
void showHeaderMenu(const QPoint& position);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::ContactsWidget *ui;
|
Ui::ContactsWidget *ui;
|
||||||
AppContext *m_ctx;
|
|
||||||
|
|
||||||
QAction *m_showFullAddressesAction;
|
QAction *m_showFullAddressesAction;
|
||||||
|
QAction *m_deleteEntryAction;
|
||||||
QMenu *m_rowMenu;
|
QMenu *m_rowMenu;
|
||||||
QMenu *m_contextMenu;
|
QMenu *m_contextMenu;
|
||||||
QMenu *m_headerMenu;
|
QMenu *m_headerMenu;
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020, The Monero Project.
|
|
||||||
|
|
||||||
#include "balancedialog.h"
|
|
||||||
#include "ui_balancedialog.h"
|
|
||||||
|
|
||||||
#include "libwalletqt/WalletManager.h"
|
|
||||||
|
|
||||||
BalanceDialog::BalanceDialog(QWidget *parent, Wallet *wallet)
|
|
||||||
: QDialog(parent)
|
|
||||||
, ui(new Ui::BalanceDialog)
|
|
||||||
{
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
ui->label_unconfirmed_help->setHelpText("Outputs require 10 confirmations before they become spendable. "
|
|
||||||
"This will take 20 minutes on average.");
|
|
||||||
|
|
||||||
ui->label_unconfirmed->setText(WalletManager::displayAmount(wallet->balance() - wallet->unlockedBalance()));
|
|
||||||
ui->label_spendable->setText(WalletManager::displayAmount(wallet->unlockedBalance()));
|
|
||||||
ui->label_total->setText(WalletManager::displayAmount(wallet->balance()));
|
|
||||||
|
|
||||||
this->adjustSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
BalanceDialog::~BalanceDialog() {
|
|
||||||
delete ui;
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020, The Monero Project.
|
|
||||||
|
|
||||||
#ifndef FEATHER_BALANCEDIALOG_H
|
|
||||||
#define FEATHER_BALANCEDIALOG_H
|
|
||||||
|
|
||||||
#include "libwalletqt/Wallet.h"
|
|
||||||
|
|
||||||
#include <QDialog>
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class BalanceDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
class BalanceDialog : public QDialog
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit BalanceDialog(QWidget *parent, Wallet *wallet);
|
|
||||||
~BalanceDialog() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::BalanceDialog *ui;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //FEATHER_BALANCEDIALOG_H
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>BalanceDialog</class>
|
|
||||||
<widget class="QDialog" name="BalanceDialog">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>405</width>
|
|
||||||
<height>162</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Balance</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<layout class="QFormLayout" name="formLayout">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="HelpLabel" name="label_unconfirmed_help">
|
|
||||||
<property name="text">
|
|
||||||
<string>Unconfirmed</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QLabel" name="label_unconfirmed">
|
|
||||||
<property name="text">
|
|
||||||
<string>0.0</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_spendable_help">
|
|
||||||
<property name="text">
|
|
||||||
<string>Spendable</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QLabel" name="label_spendable">
|
|
||||||
<property name="text">
|
|
||||||
<string>0.0</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="Line" name="line">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="label_7">
|
|
||||||
<property name="text">
|
|
||||||
<string>Total</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QLabel" name="label_total">
|
|
||||||
<property name="text">
|
|
||||||
<string>0.0</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::Ok</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<customwidgets>
|
|
||||||
<customwidget>
|
|
||||||
<class>HelpLabel</class>
|
|
||||||
<extends>QLabel</extends>
|
|
||||||
<header>components.h</header>
|
|
||||||
</customwidget>
|
|
||||||
</customwidgets>
|
|
||||||
<resources/>
|
|
||||||
<connections>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>accepted()</signal>
|
|
||||||
<receiver>BalanceDialog</receiver>
|
|
||||||
<slot>accept()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>248</x>
|
|
||||||
<y>254</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>157</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>rejected()</signal>
|
|
||||||
<receiver>BalanceDialog</receiver>
|
|
||||||
<slot>reject()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>316</x>
|
|
||||||
<y>260</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>286</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
</connections>
|
|
||||||
</ui>
|
|
||||||
@@ -3,11 +3,10 @@
|
|||||||
|
|
||||||
#include "broadcasttxdialog.h"
|
#include "broadcasttxdialog.h"
|
||||||
#include "ui_broadcasttxdialog.h"
|
#include "ui_broadcasttxdialog.h"
|
||||||
#include "utils/nodes.h"
|
|
||||||
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, AppContext *ctx, const QString &transactionHex)
|
BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, AppContext *ctx)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, m_ctx(ctx)
|
, m_ctx(ctx)
|
||||||
, ui(new Ui::BroadcastTxDialog)
|
, ui(new Ui::BroadcastTxDialog)
|
||||||
@@ -24,10 +23,6 @@ BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, AppContext *ctx, const QSt
|
|||||||
|
|
||||||
connect(m_rpc, &DaemonRpc::ApiResponse, this, &BroadcastTxDialog::onApiResponse);
|
connect(m_rpc, &DaemonRpc::ApiResponse, this, &BroadcastTxDialog::onApiResponse);
|
||||||
|
|
||||||
if (!transactionHex.isEmpty()) {
|
|
||||||
ui->transaction->setPlainText(transactionHex);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->adjustSize();
|
this->adjustSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +33,7 @@ void BroadcastTxDialog::broadcastTx() {
|
|||||||
QString node;
|
QString node;
|
||||||
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
|
||||||
node = m_ctx->nodes->connection().full;
|
node = m_ctx->nodes->connection().full;
|
||||||
|
|
||||||
if (!node.startsWith("http://"))
|
if (!node.startsWith("http://"))
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class BroadcastTxDialog : public QDialog
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BroadcastTxDialog(QWidget *parent, AppContext *ctx, const QString &transactionHex = "");
|
explicit BroadcastTxDialog(QWidget *parent, AppContext *ctx);
|
||||||
~BroadcastTxDialog() override;
|
~BroadcastTxDialog() override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|||||||
@@ -63,6 +63,16 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>All transactions are broadcast over Tor</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
|
|||||||
@@ -4,19 +4,13 @@
|
|||||||
#include "ui_contactsdialog.h"
|
#include "ui_contactsdialog.h"
|
||||||
#include "contactsdialog.h"
|
#include "contactsdialog.h"
|
||||||
|
|
||||||
ContactsDialog::ContactsDialog(QWidget *parent, const QString &address, const QString &name)
|
ContactsDialog::ContactsDialog(QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, ui(new Ui::ContactsDialog)
|
, ui(new Ui::ContactsDialog)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
setMinimumWidth(400);
|
setMinimumWidth(400);
|
||||||
|
|
||||||
ui->lineEdit_address->setText(address);
|
|
||||||
ui->lineEdit_name->setText(name);
|
|
||||||
if (!name.isEmpty()) {
|
|
||||||
ui->lineEdit_name->setFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, [&](){
|
connect(ui->buttonBox, &QDialogButtonBox::accepted, [&](){
|
||||||
m_address = ui->lineEdit_address->text();
|
m_address = ui->lineEdit_address->text();
|
||||||
m_name = ui->lineEdit_name->text();
|
m_name = ui->lineEdit_name->text();
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class ContactsDialog : public QDialog
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ContactsDialog(QWidget *parent = nullptr, const QString &address = "", const QString &name = "");
|
explicit ContactsDialog(QWidget *parent = nullptr);
|
||||||
~ContactsDialog() override;
|
~ContactsDialog() override;
|
||||||
|
|
||||||
QString getAddress();
|
QString getAddress();
|
||||||
|
|||||||
@@ -27,15 +27,7 @@ DebugInfoDialog::DebugInfoDialog(AppContext *ctx, QWidget *parent)
|
|||||||
|
|
||||||
void DebugInfoDialog::updateInfo() {
|
void DebugInfoDialog::updateInfo() {
|
||||||
QString torStatus;
|
QString torStatus;
|
||||||
|
if(m_ctx->isTorSocks)
|
||||||
// Special case for Tails because we know the status of the daemon by polling tails-tor-has-bootstrapped.target
|
|
||||||
if(m_ctx->isTails) {
|
|
||||||
if(m_ctx->tor->torConnected)
|
|
||||||
torStatus = "Connected";
|
|
||||||
else
|
|
||||||
torStatus = "Disconnected";
|
|
||||||
}
|
|
||||||
else if(m_ctx->isTorSocks)
|
|
||||||
torStatus = "Torsocks";
|
torStatus = "Torsocks";
|
||||||
else if(m_ctx->tor->localTor)
|
else if(m_ctx->tor->localTor)
|
||||||
torStatus = "Local (assumed to be running)";
|
torStatus = "Local (assumed to be running)";
|
||||||
@@ -57,12 +49,11 @@ void DebugInfoDialog::updateInfo() {
|
|||||||
ui->label_remoteNode->setText(node.full);
|
ui->label_remoteNode->setText(node.full);
|
||||||
ui->label_walletStatus->setText(this->statusToString(m_ctx->currentWallet->connectionStatus()));
|
ui->label_walletStatus->setText(this->statusToString(m_ctx->currentWallet->connectionStatus()));
|
||||||
ui->label_torStatus->setText(torStatus);
|
ui->label_torStatus->setText(torStatus);
|
||||||
ui->label_websocketStatus->setText(Utils::QtEnumToString(m_ctx->ws->webSocket.state()).remove("State"));
|
ui->label_websocketStatus->setText(Utils::QtEnumToString(m_ctx->ws->webSocket.state()));
|
||||||
|
|
||||||
ui->label_netType->setText(Utils::QtEnumToString(m_ctx->currentWallet->nettype()));
|
ui->label_netType->setText(Utils::QtEnumToString(m_ctx->currentWallet->nettype()));
|
||||||
ui->label_seedType->setText(m_ctx->currentWallet->getCacheAttribute("feather.seed").isEmpty() ? "25 word" : "14 word");
|
ui->label_seedType->setText(m_ctx->currentWallet->getCacheAttribute("feather.seed").isEmpty() ? "25 word" : "14 word");
|
||||||
ui->label_viewOnly->setText(m_ctx->currentWallet->viewOnly() ? "True" : "False");
|
ui->label_viewOnly->setText(m_ctx->currentWallet->viewOnly() ? "True" : "False");
|
||||||
ui->label_primaryOnly->setText(m_ctx->currentWallet->balance(0) == m_ctx->currentWallet->balanceAll() ? "True" : "False");
|
|
||||||
|
|
||||||
QString os = QSysInfo::prettyProductName();
|
QString os = QSysInfo::prettyProductName();
|
||||||
if (m_ctx->isTails) {
|
if (m_ctx->isTails) {
|
||||||
|
|||||||
@@ -258,14 +258,14 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="20" column="0">
|
<item row="19" column="0">
|
||||||
<widget class="QLabel" name="label_24">
|
<widget class="QLabel" name="label_24">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Timestamp:</string>
|
<string>Timestamp:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="20" column="1">
|
<item row="19" column="1">
|
||||||
<widget class="QLabel" name="label_timestamp">
|
<widget class="QLabel" name="label_timestamp">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>TextLabel</string>
|
<string>TextLabel</string>
|
||||||
@@ -275,14 +275,14 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="19" column="0">
|
<item row="18" column="0">
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Operating system:</string>
|
<string>Operating system:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="19" column="1">
|
<item row="18" column="1">
|
||||||
<widget class="QLabel" name="label_OS">
|
<widget class="QLabel" name="label_OS">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>TextLabel</string>
|
<string>TextLabel</string>
|
||||||
@@ -292,7 +292,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="18" column="1">
|
<item row="17" column="1">
|
||||||
<widget class="Line" name="line_4">
|
<widget class="Line" name="line_4">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
@@ -316,20 +316,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="17" column="1">
|
|
||||||
<widget class="QLabel" name="label_primaryOnly">
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="17" column="0">
|
|
||||||
<widget class="QLabel" name="label_11">
|
|
||||||
<property name="text">
|
|
||||||
<string>Primary only:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ RestoreDialog::RestoreDialog(AppContext *ctx, QWidget *parent)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int RestoreDialog::getHeight() {
|
unsigned int RestoreDialog::getHeight() {
|
||||||
return ui->restoreHeightWidget->getHeight();
|
return ui->restoreHeightWidget->getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ Q_OBJECT
|
|||||||
public:
|
public:
|
||||||
explicit RestoreDialog(AppContext *ctx, QWidget *parent = nullptr);
|
explicit RestoreDialog(AppContext *ctx, QWidget *parent = nullptr);
|
||||||
void initRestoreHeights(RestoreHeightLookup *lookup);
|
void initRestoreHeights(RestoreHeightLookup *lookup);
|
||||||
int getHeight();
|
unsigned int getHeight();
|
||||||
~RestoreDialog() override;
|
~RestoreDialog() override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ SeedDialog::SeedDialog(Wallet *wallet, QWidget *parent)
|
|||||||
this->setSeed(seed_25_words);
|
this->setSeed(seed_25_words);
|
||||||
} else {
|
} else {
|
||||||
this->setSeed(seed_14_words);
|
this->setSeed(seed_14_words);
|
||||||
ui->frameRestoreHeight->setVisible(false);
|
ui->widgetRestoreHeight->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(ui->check_toggleSeedType, &QCheckBox::toggled, [this, seed_25_words, seed_14_words](bool toggled){
|
connect(ui->check_toggleSeedType, &QCheckBox::toggled, [this, seed_25_words, seed_14_words](bool toggled){
|
||||||
this->setSeed(toggled ? seed_25_words : seed_14_words);
|
this->setSeed(toggled ? seed_25_words : seed_14_words);
|
||||||
ui->frameRestoreHeight->setVisible(toggled);
|
ui->widgetRestoreHeight->setVisible(toggled);
|
||||||
});
|
});
|
||||||
|
|
||||||
ui->label_restoreHeightHelp->setHelpText("Should you restore your wallet in the future, "
|
ui->label_restoreHeightHelp->setHelpText("Should you restore your wallet in the future, "
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>519</width>
|
<width>590</width>
|
||||||
<height>330</height>
|
<height>346</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>0</width>
|
<width>0</width>
|
||||||
<height>100</height>
|
<height>125</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
@@ -64,14 +64,20 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QFrame" name="frameRestoreHeight">
|
<widget class="QWidget" name="widgetRestoreHeight" native="true">
|
||||||
<property name="frameShape">
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
<enum>QFrame::StyledPanel</enum>
|
<property name="leftMargin">
|
||||||
</property>
|
<number>0</number>
|
||||||
<property name="frameShadow">
|
</property>
|
||||||
<enum>QFrame::Raised</enum>
|
<property name="topMargin">
|
||||||
</property>
|
<number>0</number>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="HelpLabel" name="label_restoreHeightHelp">
|
<widget class="HelpLabel" name="label_restoreHeightHelp">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -118,36 +124,14 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_warning">
|
<widget class="QLabel" name="label_warning">
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>warning</string>
|
<string>warning</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="textFormat">
|
|
||||||
<enum>Qt::RichText</enum>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|||||||
@@ -30,13 +30,9 @@ TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *tx
|
|||||||
ui->label_txKey->setText(txKey);
|
ui->label_txKey->setText(txKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString blockHeight = QString::number(txInfo->blockHeight());
|
|
||||||
if (blockHeight == "0")
|
|
||||||
blockHeight = "Unconfirmed";
|
|
||||||
|
|
||||||
ui->label_status->setText(QString("Status: %1 confirmations").arg(txInfo->confirmations()));
|
ui->label_status->setText(QString("Status: %1 confirmations").arg(txInfo->confirmations()));
|
||||||
ui->label_date->setText(QString("Date: %1").arg(txInfo->timestamp().toString("yyyy-MM-dd HH:mm")));
|
ui->label_date->setText(QString("Date: %1").arg(txInfo->timestamp().toString("yyyy-MM-dd HH:mm")));
|
||||||
ui->label_blockHeight->setText(QString("Block height: %1").arg(blockHeight));
|
ui->label_blockHeight->setText(QString("Block height: %1").arg(txInfo->blockHeight()));
|
||||||
|
|
||||||
QString direction = txInfo->direction() == TransactionInfo::Direction_In ? "received" : "sent";
|
QString direction = txInfo->direction() == TransactionInfo::Direction_In ? "received" : "sent";
|
||||||
ui->label_amount->setText(QString("Amount %1: %2").arg(direction, txInfo->displayAmount()));
|
ui->label_amount->setText(QString("Amount %1: %2").arg(direction, txInfo->displayAmount()));
|
||||||
@@ -49,7 +45,9 @@ TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *tx
|
|||||||
|
|
||||||
QString destinations = txInfo->destinations_formatted();
|
QString destinations = txInfo->destinations_formatted();
|
||||||
if (destinations.isEmpty()) {
|
if (destinations.isEmpty()) {
|
||||||
ui->frameDestinations->hide();
|
ui->destinations->setHidden(true);
|
||||||
|
ui->label_destinations->setHidden(true);
|
||||||
|
ui->line_2->setHidden(true);
|
||||||
} else {
|
} else {
|
||||||
ui->destinations->setText(destinations);
|
ui->destinations->setText(destinations);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,54 +132,30 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QFrame" name="frameDestinations">
|
<widget class="Line" name="line_2">
|
||||||
<property name="frameShape">
|
<property name="orientation">
|
||||||
<enum>QFrame::NoFrame</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="frameShadow">
|
</widget>
|
||||||
<enum>QFrame::Raised</enum>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_destinations">
|
||||||
|
<property name="text">
|
||||||
|
<string>Destinations:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTextEdit" name="destinations">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>100</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_destinations">
|
|
||||||
<property name="text">
|
|
||||||
<string>Destinations:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QTextEdit" name="destinations">
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>16777215</width>
|
|
||||||
<height>100</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|||||||
@@ -32,22 +32,16 @@ TxConfDialog::TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QStrin
|
|||||||
};
|
};
|
||||||
|
|
||||||
QString amount = WalletManager::displayAmount(tx->amount());
|
QString amount = WalletManager::displayAmount(tx->amount());
|
||||||
QString fee = WalletManager::displayAmount(tx->fee());
|
|
||||||
QString total = WalletManager::displayAmount(tx->amount() + tx->fee());
|
|
||||||
QVector<QString> amounts = {amount, fee, total};
|
|
||||||
int maxLength = Utils::maxLength(amounts);
|
|
||||||
std::for_each(amounts.begin(), amounts.end(), [maxLength](QString& amount){amount = amount.rightJustified(maxLength, ' ');});
|
|
||||||
|
|
||||||
QString amount_fiat = convert(tx->amount() / globals::cdiv);
|
QString amount_fiat = convert(tx->amount() / globals::cdiv);
|
||||||
QString fee_fiat = convert(tx->fee() / globals::cdiv);
|
ui->label_amount->setText(QString("%1 (%2 %3)").arg(amount, amount_fiat, preferredCur));
|
||||||
QString total_fiat = convert((tx->amount() + tx->fee()) / globals::cdiv);
|
|
||||||
QVector<QString> amounts_fiat = {amount_fiat, fee_fiat, total_fiat};
|
|
||||||
int maxLengthFiat = Utils::maxLength(amounts_fiat);
|
|
||||||
std::for_each(amounts_fiat.begin(), amounts_fiat.end(), [maxLengthFiat](QString& amount){amount = amount.rightJustified(maxLengthFiat, ' ');});
|
|
||||||
|
|
||||||
ui->label_amount->setText(QString("%1 (%2 %3)").arg(amounts[0], amounts_fiat[0], preferredCur));
|
QString fee = WalletManager::displayAmount(tx->fee());
|
||||||
ui->label_fee->setText(QString("%1 (%2 %3)").arg(amounts[1], amounts_fiat[1], preferredCur));
|
QString fee_fiat = convert(tx->fee() / globals::cdiv);
|
||||||
ui->label_total->setText(QString("%1 (%2 %3)").arg(amounts[2], amounts_fiat[2], preferredCur));
|
ui->label_fee->setText(QString("%1 (%2 %3)").arg(fee, fee_fiat, preferredCur));
|
||||||
|
|
||||||
|
QString total = WalletManager::displayAmount(tx->amount() + tx->fee());
|
||||||
|
QString total_fiat = convert((tx->amount() + tx->fee()) / globals::cdiv);
|
||||||
|
ui->label_total->setText(QString("%1 (%2 %3)").arg(total, total_fiat, preferredCur));
|
||||||
|
|
||||||
ui->label_address->setText(ModelUtils::displayAddress(address, 2));
|
ui->label_address->setText(ModelUtils::displayAddress(address, 2));
|
||||||
ui->label_address->setFont(ModelUtils::getMonospaceFont());
|
ui->label_address->setFont(ModelUtils::getMonospaceFont());
|
||||||
@@ -57,7 +51,6 @@ TxConfDialog::TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QStrin
|
|||||||
|
|
||||||
connect(ui->btn_Advanced, &QPushButton::clicked, this, &TxConfDialog::setShowAdvanced);
|
connect(ui->btn_Advanced, &QPushButton::clicked, this, &TxConfDialog::setShowAdvanced);
|
||||||
|
|
||||||
AppContext::txCache[tx->txid()[0]] = tx->signedTxToHex(0);
|
|
||||||
this->adjustSize();
|
this->adjustSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,20 @@ HistoryWidget::HistoryWidget(QWidget *parent)
|
|||||||
m_copyMenu->addAction("Transaction ID", this, [this]{copy(copyField::TxID);});
|
m_copyMenu->addAction("Transaction ID", this, [this]{copy(copyField::TxID);});
|
||||||
m_copyMenu->addAction("Date", this, [this]{copy(copyField::Date);});
|
m_copyMenu->addAction("Date", this, [this]{copy(copyField::Date);});
|
||||||
m_copyMenu->addAction("Amount", this, [this]{copy(copyField::Amount);});
|
m_copyMenu->addAction("Amount", this, [this]{copy(copyField::Amount);});
|
||||||
|
auto spendProof = m_copyMenu->addAction("Spend proof", this, &HistoryWidget::getSpendProof);
|
||||||
|
|
||||||
ui->history->setContextMenuPolicy(Qt::CustomContextMenu);
|
ui->history->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
connect(ui->history, &QTreeView::customContextMenuRequested, this, &HistoryWidget::showContextMenu);
|
connect(ui->history, &QTreeView::customContextMenuRequested, [=](const QPoint & point){
|
||||||
|
QModelIndex index = ui->history->indexAt(point);
|
||||||
|
if (index.isValid()) {
|
||||||
|
TransactionInfo::Direction direction;
|
||||||
|
m_txHistory->transaction(m_model->mapToSource(index).row(), [&direction](TransactionInfo &tInfo) {
|
||||||
|
direction = tInfo.direction();
|
||||||
|
});
|
||||||
|
spendProof->setVisible(direction == TransactionInfo::Direction_Out);
|
||||||
|
m_contextMenu->exec(ui->history->viewport()->mapToGlobal(point));
|
||||||
|
}
|
||||||
|
});
|
||||||
connect(ui->search, &QLineEdit::textChanged, this, &HistoryWidget::setSearchFilter);
|
connect(ui->search, &QLineEdit::textChanged, this, &HistoryWidget::setSearchFilter);
|
||||||
|
|
||||||
connect(ui->history, &QTreeView::doubleClicked, [this](QModelIndex index){
|
connect(ui->history, &QTreeView::doubleClicked, [this](QModelIndex index){
|
||||||
@@ -39,43 +50,6 @@ HistoryWidget::HistoryWidget(QWidget *parent)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::showContextMenu(const QPoint &point) {
|
|
||||||
QModelIndex index = ui->history->indexAt(point);
|
|
||||||
if (!index.isValid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMenu menu(this);
|
|
||||||
TransactionInfo::Direction direction;
|
|
||||||
QString txid;
|
|
||||||
bool unconfirmed;
|
|
||||||
m_txHistory->transaction(m_model->mapToSource(index).row(), [&direction, &txid, &unconfirmed](TransactionInfo &tInfo) {
|
|
||||||
direction = tInfo.direction();
|
|
||||||
txid = tInfo.hash();
|
|
||||||
unconfirmed = tInfo.isFailed() || tInfo.isPending();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (AppContext::txCache.contains(txid) && unconfirmed && direction != TransactionInfo::Direction_In) {
|
|
||||||
menu.addAction(QIcon(":/assets/images/info.png"), "Resend transaction", this, &HistoryWidget::onResendTransaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.addMenu(m_copyMenu);
|
|
||||||
menu.addAction(QIcon(":/assets/images/info.png"), "Show details", this, &HistoryWidget::showTxDetails);
|
|
||||||
menu.addAction(QIcon(":/assets/images/network.png"), "View on block explorer", this, &HistoryWidget::onViewOnBlockExplorer);
|
|
||||||
|
|
||||||
menu.exec(ui->history->viewport()->mapToGlobal(point));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HistoryWidget::onResendTransaction() {
|
|
||||||
QModelIndex index = ui->history->currentIndex();
|
|
||||||
QString txid;
|
|
||||||
m_txHistory->transaction(m_model->mapToSource(index).row(), [&txid](TransactionInfo &tInfo) {
|
|
||||||
txid = tInfo.hash();
|
|
||||||
});
|
|
||||||
|
|
||||||
emit resendTransaction(txid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HistoryWidget::setModel(TransactionHistoryProxyModel *model, Wallet *wallet)
|
void HistoryWidget::setModel(TransactionHistoryProxyModel *model, Wallet *wallet)
|
||||||
{
|
{
|
||||||
m_model = model;
|
m_model = model;
|
||||||
@@ -112,6 +86,14 @@ void HistoryWidget::onViewOnBlockExplorer() {
|
|||||||
emit viewOnBlockExplorer(txid);
|
emit viewOnBlockExplorer(txid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::getSpendProof() {
|
||||||
|
QModelIndex index = ui->history->currentIndex();
|
||||||
|
|
||||||
|
m_txHistory->transaction(m_model->mapToSource(index).row(), [this](TransactionInfo &tInfo) {
|
||||||
|
emit spendProof(tInfo.hash());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void HistoryWidget::setSearchText(const QString &text) {
|
void HistoryWidget::setSearchText(const QString &text) {
|
||||||
ui->search->setText(text);
|
ui->search->setText(text);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,14 +29,14 @@ public slots:
|
|||||||
void setSearchText(const QString &text);
|
void setSearchText(const QString &text);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void spendProof(QString txid);
|
||||||
void viewOnBlockExplorer(QString txid);
|
void viewOnBlockExplorer(QString txid);
|
||||||
void resendTransaction(QString txid);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void showTxDetails();
|
void showTxDetails();
|
||||||
void onViewOnBlockExplorer();
|
void onViewOnBlockExplorer();
|
||||||
|
void getSpendProof();
|
||||||
void setSearchFilter(const QString &filter);
|
void setSearchFilter(const QString &filter);
|
||||||
void onResendTransaction();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum copyField {
|
enum copyField {
|
||||||
@@ -46,7 +46,6 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void copy(copyField field);
|
void copy(copyField field);
|
||||||
void showContextMenu(const QPoint &point);
|
|
||||||
|
|
||||||
Ui::HistoryWidget *ui;
|
Ui::HistoryWidget *ui;
|
||||||
QMenu *m_contextMenu;
|
QMenu *m_contextMenu;
|
||||||
|
|||||||
@@ -76,10 +76,6 @@ QString CoinsInfo::address() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString CoinsInfo::addressLabel() const {
|
QString CoinsInfo::addressLabel() const {
|
||||||
if (m_subaddrIndex == 0) {
|
|
||||||
return m_coinbase ? "Coinbase" : "Change";
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_addressLabel;
|
return m_addressLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ namespace {
|
|||||||
static const int DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS = 30;
|
static const int DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS = 30;
|
||||||
static const int WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS = 5;
|
static const int WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS = 5;
|
||||||
|
|
||||||
static constexpr char ATTRIBUTE_SUBADDRESS_ACCOUNT[] = "feather.subaddress_account";
|
static constexpr char ATTRIBUTE_SUBADDRESS_ACCOUNT[] ="gui.subaddress_account";
|
||||||
}
|
}
|
||||||
|
|
||||||
Wallet::Wallet(QObject * parent)
|
Wallet::Wallet(QObject * parent)
|
||||||
|
|||||||
@@ -14,10 +14,6 @@
|
|||||||
|
|
||||||
#include <QtPlugin>
|
#include <QtPlugin>
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX) && defined(STATIC)
|
#if defined(Q_OS_LINUX) && defined(STATIC)
|
||||||
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)
|
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)
|
||||||
#endif
|
#endif
|
||||||
@@ -26,17 +22,14 @@ int main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
Q_INIT_RESOURCE(assets);
|
Q_INIT_RESOURCE(assets);
|
||||||
|
|
||||||
#if defined(Q_OS_MAC) && defined(HAS_TOR_BIN)
|
#if defined(Q_OS_MAC) && defined(HAS_TOR)
|
||||||
Q_INIT_RESOURCE(assets_tor_macos);
|
Q_INIT_RESOURCE(assets_tor_macos);
|
||||||
#elif defined(HAS_TOR_BIN)
|
#elif defined(HAS_TOR)
|
||||||
Q_INIT_RESOURCE(assets_tor);
|
Q_INIT_RESOURCE(assets_tor);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#if defined(HAS_XMRIG)
|
||||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
Q_INIT_RESOURCE(assets_mining);
|
||||||
freopen("CONOUT$", "w", stdout);
|
|
||||||
freopen("CONOUT$", "w", stderr);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QStringList argv_;
|
QStringList argv_;
|
||||||
|
|||||||
@@ -21,14 +21,12 @@
|
|||||||
#include "dialog/broadcasttxdialog.h"
|
#include "dialog/broadcasttxdialog.h"
|
||||||
#include "dialog/tximportdialog.h"
|
#include "dialog/tximportdialog.h"
|
||||||
#include "dialog/passworddialog.h"
|
#include "dialog/passworddialog.h"
|
||||||
#include "dialog/balancedialog.h"
|
|
||||||
#include "utils/utils.h"
|
#include "utils/utils.h"
|
||||||
#include "utils/config.h"
|
#include "utils/config.h"
|
||||||
#include "utils/daemonrpc.h"
|
#include "utils/daemonrpc.h"
|
||||||
#include "components.h"
|
#include "components.h"
|
||||||
#include "calcwindow.h"
|
#include "calcwindow.h"
|
||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
#include "globals.h"
|
|
||||||
|
|
||||||
// libwalletqt
|
// libwalletqt
|
||||||
#include "libwalletqt/WalletManager.h"
|
#include "libwalletqt/WalletManager.h"
|
||||||
@@ -81,7 +79,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
|||||||
connected ? m_statusBtnTor->setIcon(QIcon(":/assets/images/tor_logo.png"))
|
connected ? m_statusBtnTor->setIcon(QIcon(":/assets/images/tor_logo.png"))
|
||||||
: m_statusBtnTor->setIcon(QIcon(":/assets/images/tor_logo_disabled.png"));});
|
: m_statusBtnTor->setIcon(QIcon(":/assets/images/tor_logo_disabled.png"));});
|
||||||
connect(m_ctx->nodes, &Nodes::updateStatus, [=](const QString &msg){
|
connect(m_ctx->nodes, &Nodes::updateStatus, [=](const QString &msg){
|
||||||
this->setStatusText(msg);
|
m_statusLabelStatus->setText(msg);
|
||||||
});
|
});
|
||||||
|
|
||||||
// menu connects
|
// menu connects
|
||||||
@@ -106,9 +104,9 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
|||||||
"Try to explain not only what the bug is, but how it occurs.</body>");
|
"Try to explain not only what the bug is, but how it occurs.</body>");
|
||||||
});
|
});
|
||||||
connect(ui->actionShow_debug_info, &QAction::triggered, this, &MainWindow::showDebugInfo);
|
connect(ui->actionShow_debug_info, &QAction::triggered, this, &MainWindow::showDebugInfo);
|
||||||
connect(ui->actionOfficialWebsite, &QAction::triggered, [=] { Utils::externalLinkWarning(this, "https://featherwallet.org"); });
|
connect(ui->actionOfficialWebsite, &QAction::triggered, [=] { Utils::externalLinkWarning("https://featherwallet.org"); });
|
||||||
|
|
||||||
#if defined(HAS_XMRTO)
|
#if defined(XMRTO)
|
||||||
// xmr.to connects/widget
|
// xmr.to connects/widget
|
||||||
connect(ui->xmrToWidget, &XMRToWidget::viewOrder, m_ctx->XMRTo, &XmrTo::onViewOrder);
|
connect(ui->xmrToWidget, &XMRToWidget::viewOrder, m_ctx->XMRTo, &XmrTo::onViewOrder);
|
||||||
connect(ui->xmrToWidget, &XMRToWidget::getRates, m_ctx->XMRTo, &XmrTo::onGetRates);
|
connect(ui->xmrToWidget, &XMRToWidget::getRates, m_ctx->XMRTo, &XmrTo::onGetRates);
|
||||||
@@ -117,10 +115,14 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
|||||||
connect(m_ctx->XMRTo, &XmrTo::connectionError, ui->xmrToWidget, &XMRToWidget::onConnectionError);
|
connect(m_ctx->XMRTo, &XmrTo::connectionError, ui->xmrToWidget, &XMRToWidget::onConnectionError);
|
||||||
connect(m_ctx->XMRTo, &XmrTo::connectionSuccess, ui->xmrToWidget, &XMRToWidget::onConnectionSuccess);
|
connect(m_ctx->XMRTo, &XmrTo::connectionSuccess, ui->xmrToWidget, &XMRToWidget::onConnectionSuccess);
|
||||||
connect(m_ctx, &AppContext::balanceUpdated, ui->xmrToWidget, &XMRToWidget::onBalanceUpdated);
|
connect(m_ctx, &AppContext::balanceUpdated, ui->xmrToWidget, &XMRToWidget::onBalanceUpdated);
|
||||||
connect(m_ctx->XMRTo, &XmrTo::openURL, this, [=](const QString &url){ Utils::externalLinkWarning(this, url); });
|
connect(m_ctx->XMRTo, &XmrTo::openURL, this, [=](const QString &url){ Utils::externalLinkWarning(url); });
|
||||||
ui->xmrToWidget->setHistoryModel(m_ctx->XMRTo->tableModel);
|
ui->xmrToWidget->setHistoryModel(m_ctx->XMRTo->tableModel);
|
||||||
#else
|
#else
|
||||||
ui->tabExchanges->setTabVisible(0, false);
|
ui->tabWidget->setTabVisible(Tabs::XMR_TO, false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAS_MORPHTOKEN
|
||||||
|
ui->tabWidget->setTabVisible(Tabs::MORPHTOKEN, false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX)
|
#if defined(Q_OS_LINUX)
|
||||||
@@ -160,7 +162,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
|||||||
ui->tickerLayout->addWidget(tickerWidget);
|
ui->tickerLayout->addWidget(tickerWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_balanceWidget = new TickerWidget(this, "XMR", "Balance", true, true);
|
m_balanceWidget = new TickerWidget(this, "XMR", "Balance", true);
|
||||||
ui->fiatTickerLayout->addWidget(m_balanceWidget);
|
ui->fiatTickerLayout->addWidget(m_balanceWidget);
|
||||||
|
|
||||||
// Send widget
|
// Send widget
|
||||||
@@ -188,13 +190,26 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
|||||||
ui->tabWidget->setTabVisible(Tabs::XMRIG, false);
|
ui->tabWidget->setTabVisible(Tabs::XMRIG, false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
connect(ui->ccsWidget, &CCSWidget::selected, this, &MainWindow::showSendScreen);
|
// CCS/Reddit widget
|
||||||
connect(m_ctx, &AppContext::ccsUpdated, ui->ccsWidget->model(), &CCSModel::updateEntries);
|
m_ccsWidget = new CCSWidget(this);
|
||||||
connect(m_ctx, &AppContext::redditUpdated, ui->redditWidget->model(), &RedditModel::updatePosts);
|
m_redditWidget = new RedditWidget(this);
|
||||||
|
|
||||||
connect(ui->tabHomeWidget, &QTabWidget::currentChanged, [](int index){
|
m_ccsWidget->hide();
|
||||||
config()->set(Config::homeWidget, TabsHome(index));
|
m_redditWidget->hide();
|
||||||
|
|
||||||
|
ui->coolLayout->addWidget(m_ccsWidget);
|
||||||
|
ui->coolLayout->addWidget(m_redditWidget);
|
||||||
|
|
||||||
|
connect(m_ctx, &AppContext::ccsEmpty, [=] {
|
||||||
|
if(m_ccsWidget->isVisible()) {
|
||||||
|
// display Reddit widget instead
|
||||||
|
m_ccsWidget->show();
|
||||||
|
m_redditWidget->show();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
connect(m_ctx, &AppContext::ccsUpdated, m_ccsWidget->model(), &CCSModel::updateEntries);
|
||||||
|
connect(m_ctx, &AppContext::redditUpdated, m_redditWidget->model(), &RedditModel::updatePosts);
|
||||||
|
connect(m_ccsWidget, &CCSWidget::selected, this, &MainWindow::showSendScreen);
|
||||||
|
|
||||||
connect(m_ctx, &AppContext::donationNag, [=]{
|
connect(m_ctx, &AppContext::donationNag, [=]{
|
||||||
auto msg = "Feather is a 100% community-sponsored endeavor. Please consider supporting "
|
auto msg = "Feather is a 100% community-sponsored endeavor. Please consider supporting "
|
||||||
@@ -214,6 +229,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
|||||||
connect(this, &MainWindow::walletClosed, ui->xmrToWidget, &XMRToWidget::onWalletClosed);
|
connect(this, &MainWindow::walletClosed, ui->xmrToWidget, &XMRToWidget::onWalletClosed);
|
||||||
connect(this, &MainWindow::walletClosed, ui->sendWidget, &SendWidget::onWalletClosed);
|
connect(this, &MainWindow::walletClosed, ui->sendWidget, &SendWidget::onWalletClosed);
|
||||||
connect(m_ctx, &AppContext::balanceUpdated, this, &MainWindow::onBalanceUpdated);
|
connect(m_ctx, &AppContext::balanceUpdated, this, &MainWindow::onBalanceUpdated);
|
||||||
|
connect(m_ctx, &AppContext::balanceUpdated, ui->xmrToWidget, &XMRToWidget::onBalanceUpdated);
|
||||||
connect(m_ctx, &AppContext::walletOpened, this, &MainWindow::onWalletOpened);
|
connect(m_ctx, &AppContext::walletOpened, this, &MainWindow::onWalletOpened);
|
||||||
connect(m_ctx, &AppContext::walletClosed, this, QOverload<>::of(&MainWindow::onWalletClosed));
|
connect(m_ctx, &AppContext::walletClosed, this, QOverload<>::of(&MainWindow::onWalletClosed));
|
||||||
connect(m_ctx, &AppContext::walletOpenedError, this, &MainWindow::onWalletOpenedError);
|
connect(m_ctx, &AppContext::walletOpenedError, this, &MainWindow::onWalletOpenedError);
|
||||||
@@ -235,42 +251,26 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
|||||||
connect(m_ctx, &AppContext::initiateTransaction, ui->xmrToWidget, &XMRToWidget::onInitiateTransaction);
|
connect(m_ctx, &AppContext::initiateTransaction, ui->xmrToWidget, &XMRToWidget::onInitiateTransaction);
|
||||||
connect(m_ctx, &AppContext::endTransaction, ui->xmrToWidget, &XMRToWidget::onEndTransaction);
|
connect(m_ctx, &AppContext::endTransaction, ui->xmrToWidget, &XMRToWidget::onEndTransaction);
|
||||||
|
|
||||||
connect(m_ctx, &AppContext::initiateTransaction, [this]{
|
|
||||||
m_statusDots = 0;
|
|
||||||
m_constructingTransaction = true;
|
|
||||||
m_txTimer.start(1000);
|
|
||||||
});
|
|
||||||
connect(m_ctx, &AppContext::endTransaction, [this]{
|
|
||||||
// Todo: endTransaction can fail to fire when the node is switched during tx creation
|
|
||||||
m_constructingTransaction = false;
|
|
||||||
m_txTimer.stop();
|
|
||||||
this->setStatusText(m_statusText);
|
|
||||||
});
|
|
||||||
connect(&m_txTimer, &QTimer::timeout, [this]{
|
|
||||||
m_statusLabelStatus->setText("Constructing transaction" + this->statusDots());
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// testnet/stagenet warning
|
// testnet/stagenet warning
|
||||||
auto worthlessWarning = QString("Feather wallet is currently running in %1 mode. This is meant "
|
auto worthlessWarning = QString("Feather wallet is currently running in %1 mode. This is meant "
|
||||||
"for developers only. Your coins are WORTHLESS.");
|
"for developers only. Your coins are WORTHLESS.");
|
||||||
if(m_ctx->networkType == NetworkType::STAGENET) {
|
if(m_ctx->networkType == NetworkType::STAGENET) {
|
||||||
if (config()->get(Config::warnOnStagenet).toBool()) {
|
if (config()->get(Config::warnOnStagenet).toBool()) {
|
||||||
QMessageBox::warning(this, "Warning", worthlessWarning.arg("stagenet"));
|
Utils::showMessageBox("Warning", worthlessWarning.arg("stagenet"), true);
|
||||||
config()->set(Config::warnOnStagenet, false);
|
config()->set(Config::warnOnStagenet, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(m_ctx->networkType == NetworkType::TESTNET){
|
else if(m_ctx->networkType == NetworkType::TESTNET){
|
||||||
if (config()->get(Config::warnOnTestnet).toBool()) {
|
if (config()->get(Config::warnOnTestnet).toBool()) {
|
||||||
QMessageBox::warning(this, "Warning", worthlessWarning.arg("testnet"));
|
Utils::showMessageBox("Warning", worthlessWarning.arg("testnet"), true);
|
||||||
config()->set(Config::warnOnTestnet, false);
|
config()->set(Config::warnOnTestnet, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(config()->get(Config::warnOnAlpha).toBool()) {
|
if(config()->get(Config::warnOnAlpha).toBool()) {
|
||||||
QString warning = "Feather Wallet is currently in beta.\n\nPlease report any bugs "
|
auto alphaWarning = "Feather wallet is currently in alpha. Bugs are to be expected and "
|
||||||
"you encounter on our Git repository, IRC or on /r/FeatherWallet.";
|
"can be reported on our Git repository or on IRC.";
|
||||||
QMessageBox::warning(this, "Beta Warning", warning);
|
Utils::showMessageBox("Warning", alphaWarning, true);
|
||||||
config()->set(Config::warnOnAlpha, false);
|
config()->set(Config::warnOnAlpha, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,6 +282,9 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
|||||||
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, m_ctx, &AppContext::onPreferredFiatCurrencyChanged);
|
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, m_ctx, &AppContext::onPreferredFiatCurrencyChanged);
|
||||||
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, ui->sendWidget, QOverload<>::of(&SendWidget::onPreferredFiatCurrencyChanged));
|
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, ui->sendWidget, QOverload<>::of(&SendWidget::onPreferredFiatCurrencyChanged));
|
||||||
|
|
||||||
|
// CCS/Reddit widget
|
||||||
|
connect(m_windowSettings, &Settings::homeWidgetChanged, this, &MainWindow::homeWidgetChanged);
|
||||||
|
|
||||||
// Skin
|
// Skin
|
||||||
connect(m_windowSettings, &Settings::skinChanged, this, &MainWindow::skinChanged);
|
connect(m_windowSettings, &Settings::skinChanged, this, &MainWindow::skinChanged);
|
||||||
|
|
||||||
@@ -302,10 +305,14 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
|||||||
});
|
});
|
||||||
|
|
||||||
// History
|
// History
|
||||||
|
connect(ui->historyWidget, &HistoryWidget::spendProof, [&](const QString &txid){
|
||||||
|
TxProof txproof = m_ctx->currentWallet->getSpendProof(txid, "");
|
||||||
|
Utils::copyToClipboard(txproof.proof);
|
||||||
|
});
|
||||||
connect(ui->historyWidget, &HistoryWidget::viewOnBlockExplorer, this, &MainWindow::onViewOnBlockExplorer);
|
connect(ui->historyWidget, &HistoryWidget::viewOnBlockExplorer, this, &MainWindow::onViewOnBlockExplorer);
|
||||||
connect(ui->historyWidget, &HistoryWidget::resendTransaction, this, &MainWindow::onResendTransaction);
|
|
||||||
|
|
||||||
// Contacts
|
// Contacts
|
||||||
|
connect(ui->contactWidget, &ContactsWidget::addContact, this, &MainWindow::onAddContact);
|
||||||
connect(ui->contactWidget, &ContactsWidget::fillAddress, ui->sendWidget, &SendWidget::fillAddress);
|
connect(ui->contactWidget, &ContactsWidget::fillAddress, ui->sendWidget, &SendWidget::fillAddress);
|
||||||
|
|
||||||
// Open alias
|
// Open alias
|
||||||
@@ -399,7 +406,6 @@ void MainWindow::initMenu() {
|
|||||||
ui->actionClose->setShortcut(QKeySequence("Ctrl+W"));
|
ui->actionClose->setShortcut(QKeySequence("Ctrl+W"));
|
||||||
ui->actionShow_debug_info->setShortcut(QKeySequence("Ctrl+D"));
|
ui->actionShow_debug_info->setShortcut(QKeySequence("Ctrl+D"));
|
||||||
ui->actionSettings->setShortcut(QKeySequence("Ctrl+Alt+S"));
|
ui->actionSettings->setShortcut(QKeySequence("Ctrl+Alt+S"));
|
||||||
ui->actionUpdate_balance->setShortcut(QKeySequence("Ctrl+U"));
|
|
||||||
|
|
||||||
// hide/show tabs
|
// hide/show tabs
|
||||||
m_tabShowHideSignalMapper = new QSignalMapper(this);
|
m_tabShowHideSignalMapper = new QSignalMapper(this);
|
||||||
@@ -412,18 +418,26 @@ void MainWindow::initMenu() {
|
|||||||
m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc);
|
m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc);
|
||||||
m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc");
|
m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc");
|
||||||
|
|
||||||
#if defined(HAS_XMRTO)
|
#if defined(HAS_MORPHTOKEN)
|
||||||
connect(ui->actionShow_Exchange, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
|
connect(ui->actionShow_MorphToken, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
|
||||||
m_tabShowHideMapper["Exchange"] = new ToggleTab(ui->tabExchange, "Exchange", "Exchange", ui->actionShow_Exchange, Config::showTabExchange);
|
m_tabShowHideMapper["MorphToken"] = new ToggleTab(ui->tabMorphToken, "MorphToken", "MorphToken", ui->actionShow_MorphToken, Config::showTabMorphToken);
|
||||||
m_tabShowHideSignalMapper->setMapping(ui->actionShow_Exchange, "Exchange");
|
m_tabShowHideSignalMapper->setMapping(ui->actionShow_MorphToken, "MorphToken");
|
||||||
#else
|
#else
|
||||||
ui->actionShow_Exchanges->setVisible(false);
|
ui->actionShow_MorphToken->setVisible(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(XMRTO)
|
||||||
|
connect(ui->actionShow_xmr_to, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
|
||||||
|
m_tabShowHideMapper["XMRto"] = new ToggleTab(ui->tabXmrTo, "XMRto", "XMR.to", ui->actionShow_xmr_to, Config::showTabXMRto);
|
||||||
|
m_tabShowHideSignalMapper->setMapping(ui->actionShow_xmr_to, "XMRto");
|
||||||
|
#else
|
||||||
|
ui->actionShow_xmr_to->setVisible(false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAS_XMRIG)
|
#if defined(HAS_XMRIG)
|
||||||
connect(ui->actionShow_XMRig, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
|
connect(ui->actionShow_XMRig, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
|
||||||
m_tabShowHideMapper["Mining"] = new ToggleTab(ui->tabXmrRig, "Mining", "Mining", ui->actionShow_XMRig, Config::showTabXMRig);
|
m_tabShowHideMapper["XMRig"] = new ToggleTab(ui->tabXmrRig, "XMRig", "XMRig", ui->actionShow_XMRig, Config::showTabXMRig);
|
||||||
m_tabShowHideSignalMapper->setMapping(ui->actionShow_XMRig, "Mining");
|
m_tabShowHideSignalMapper->setMapping(ui->actionShow_XMRig, "XMRig");
|
||||||
#else
|
#else
|
||||||
ui->actionShow_XMRig->setVisible(false);
|
ui->actionShow_XMRig->setVisible(false);
|
||||||
#endif
|
#endif
|
||||||
@@ -442,16 +456,15 @@ void MainWindow::initMenu() {
|
|||||||
connect(ui->actionPassword, &QAction::triggered, this, &MainWindow::showPasswordDialog);
|
connect(ui->actionPassword, &QAction::triggered, this, &MainWindow::showPasswordDialog);
|
||||||
connect(ui->actionKeys, &QAction::triggered, this, &MainWindow::showKeysDialog);
|
connect(ui->actionKeys, &QAction::triggered, this, &MainWindow::showKeysDialog);
|
||||||
connect(ui->actionViewOnly, &QAction::triggered, this, &MainWindow::showViewOnlyDialog);
|
connect(ui->actionViewOnly, &QAction::triggered, this, &MainWindow::showViewOnlyDialog);
|
||||||
connect(ui->actionStore_wallet, &QAction::triggered, [this]{
|
connect(ui->actionStore_wallet, &QAction::triggered, [&]{
|
||||||
m_ctx->currentWallet->store();
|
m_ctx->currentWallet->store();
|
||||||
});
|
});
|
||||||
connect(ui->actionRefresh_tabs, &QAction::triggered, [this]{
|
connect(ui->actionRefresh_tabs, &QAction::triggered, [&]{
|
||||||
m_ctx->refreshModels();
|
m_ctx->refreshModels();
|
||||||
});
|
});
|
||||||
connect(ui->actionUpdate_balance, &QAction::triggered, [this]{
|
connect(ui->actionUpdate_balance, &QAction::triggered, [&]{
|
||||||
m_ctx->updateBalance();
|
m_ctx->updateBalance();
|
||||||
});
|
});
|
||||||
connect(ui->actionRescan_spent, &QAction::triggered, this, &MainWindow::rescanSpent);
|
|
||||||
connect(ui->actionExportKeyImages, &QAction::triggered, this, &MainWindow::exportKeyImages);
|
connect(ui->actionExportKeyImages, &QAction::triggered, this, &MainWindow::exportKeyImages);
|
||||||
connect(ui->actionImportKeyImages, &QAction::triggered, this, &MainWindow::importKeyImages);
|
connect(ui->actionImportKeyImages, &QAction::triggered, this, &MainWindow::importKeyImages);
|
||||||
connect(ui->actionExportOutputs, &QAction::triggered, this, &MainWindow::exportOutputs);
|
connect(ui->actionExportOutputs, &QAction::triggered, this, &MainWindow::exportOutputs);
|
||||||
@@ -459,7 +472,7 @@ void MainWindow::initMenu() {
|
|||||||
|
|
||||||
// set restore height
|
// set restore height
|
||||||
connect(ui->actionChange_restore_height, &QAction::triggered, this, &MainWindow::showRestoreHeightDialog);
|
connect(ui->actionChange_restore_height, &QAction::triggered, this, &MainWindow::showRestoreHeightDialog);
|
||||||
connect(m_ctx, &AppContext::customRestoreHeightSet, [=](int height){
|
connect(m_ctx, &AppContext::customRestoreHeightSet, [=](unsigned int height){
|
||||||
auto msg = QString("The restore height for this wallet has been set to %1. "
|
auto msg = QString("The restore height for this wallet has been set to %1. "
|
||||||
"Please re-open the wallet. Feather will now quit.").arg(height);
|
"Please re-open the wallet. Feather will now quit.").arg(height);
|
||||||
QMessageBox::information(this, "Cannot set custom restore height", msg);
|
QMessageBox::information(this, "Cannot set custom restore height", msg);
|
||||||
@@ -473,7 +486,7 @@ void MainWindow::initMenu() {
|
|||||||
if(fn.isEmpty()) return;
|
if(fn.isEmpty()) return;
|
||||||
if(!fn.endsWith(".csv")) fn += ".csv";
|
if(!fn.endsWith(".csv")) fn += ".csv";
|
||||||
m_ctx->currentWallet->history()->writeCSV(fn);
|
m_ctx->currentWallet->history()->writeCSV(fn);
|
||||||
QMessageBox::information(this, "CSV export", QString("Transaction history exported to %1").arg(fn));
|
Utils::showMessageBox("CSV export", QString("Transaction history exported to %1").arg(fn), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Contact widget
|
// Contact widget
|
||||||
@@ -481,7 +494,7 @@ void MainWindow::initMenu() {
|
|||||||
if(m_ctx->currentWallet == nullptr) return;
|
if(m_ctx->currentWallet == nullptr) return;
|
||||||
auto *model = m_ctx->currentWallet->addressBookModel();
|
auto *model = m_ctx->currentWallet->addressBookModel();
|
||||||
if (model->rowCount() <= 0){
|
if (model->rowCount() <= 0){
|
||||||
QMessageBox::warning(this, "Error", "Addressbook empty");
|
Utils::showMessageBox("Error", "Addressbook empty", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,7 +504,7 @@ void MainWindow::initMenu() {
|
|||||||
qint64 now = QDateTime::currentDateTime().currentMSecsSinceEpoch();
|
qint64 now = QDateTime::currentDateTime().currentMSecsSinceEpoch();
|
||||||
QString fn = QString("%1/monero-contacts_%2.csv").arg(targetDir, QString::number(now / 1000));
|
QString fn = QString("%1/monero-contacts_%2.csv").arg(targetDir, QString::number(now / 1000));
|
||||||
if(model->writeCSV(fn))
|
if(model->writeCSV(fn))
|
||||||
QMessageBox::information(this, "Address book exported", QString("Address book exported to %1").arg(fn));
|
Utils::showMessageBox("Address book exported", QString("Address book exported to %1").arg(fn), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->actionImportContactsCSV, &QAction::triggered, this, &MainWindow::importContacts);
|
connect(ui->actionImportContactsCSV, &QAction::triggered, this, &MainWindow::importContacts);
|
||||||
@@ -509,8 +522,9 @@ void MainWindow::initMenu() {
|
|||||||
connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::menuAboutClicked);
|
connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::menuAboutClicked);
|
||||||
connect(ui->actionDonate_to_Feather, &QAction::triggered, this, &MainWindow::donateButtonClicked);
|
connect(ui->actionDonate_to_Feather, &QAction::triggered, this, &MainWindow::donateButtonClicked);
|
||||||
|
|
||||||
// Close wallet
|
// Close / Open
|
||||||
connect(ui->actionClose, &QAction::triggered, this, &MainWindow::menuWalletCloseClicked);
|
connect(ui->actionClose, &QAction::triggered, this, &MainWindow::menuWalletCloseClicked);
|
||||||
|
connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::menuWalletOpenClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::menuToggleTabVisible(const QString &key){
|
void MainWindow::menuToggleTabVisible(const QString &key){
|
||||||
@@ -523,14 +537,20 @@ void MainWindow::menuToggleTabVisible(const QString &key){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::initWidgets() {
|
void MainWindow::initWidgets() {
|
||||||
int homeWidget = config()->get(Config::homeWidget).toInt();
|
auto homeWidget = config()->get(Config::homeWidget).toString();
|
||||||
ui->tabHomeWidget->setCurrentIndex(TabsHome(homeWidget));
|
if(homeWidget == QString("ccs")) {
|
||||||
|
m_ccsWidget->show();
|
||||||
|
} else if (homeWidget == "reddit") {
|
||||||
|
m_redditWidget->show();
|
||||||
|
} else {
|
||||||
|
config()->set(Config::homeWidget, "ccs");
|
||||||
|
m_ccsWidget->show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){
|
WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){
|
||||||
auto *wizard = new WalletWizard(m_ctx, startPage, this);
|
auto *wizard = new WalletWizard(m_ctx, startPage, this);
|
||||||
connect(wizard, &WalletWizard::openWallet, m_ctx, &AppContext::onOpenWallet);
|
connect(wizard, &WalletWizard::openWallet, m_ctx, &AppContext::onOpenWallet);
|
||||||
connect(wizard, &WalletWizard::defaultWalletDirChanged, m_windowSettings, &Settings::updatePaths);
|
|
||||||
return wizard;
|
return wizard;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,7 +591,7 @@ void MainWindow::touchbarShowWallet() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onWalletCreatedError(const QString &err) {
|
void MainWindow::onWalletCreatedError(const QString &err) {
|
||||||
QMessageBox::warning(this, "Wallet creation error", err);
|
Utils::showMessageBox("Wallet creation error", err, true);
|
||||||
this->showWizard(WalletWizard::Page_CreateWallet);
|
this->showWizard(WalletWizard::Page_CreateWallet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,9 +638,9 @@ void MainWindow::onWalletOpened() {
|
|||||||
this->activateWindow();
|
this->activateWindow();
|
||||||
this->setEnabled(true);
|
this->setEnabled(true);
|
||||||
if(!m_ctx->tor->torConnected)
|
if(!m_ctx->tor->torConnected)
|
||||||
this->setStatusText("Wallet opened - Starting Tor (may take a while)");
|
m_statusLabelStatus->setText("Wallet opened - Starting Tor (may take a while)");
|
||||||
else
|
else
|
||||||
this->setStatusText("Wallet opened - Searching for node");
|
m_statusLabelStatus->setText("Wallet opened - Searching for node");
|
||||||
|
|
||||||
connect(m_ctx->currentWallet, &Wallet::connectionStatusChanged, this, &MainWindow::onConnectionStatusChanged);
|
connect(m_ctx->currentWallet, &Wallet::connectionStatusChanged, this, &MainWindow::onConnectionStatusChanged);
|
||||||
|
|
||||||
@@ -662,47 +682,40 @@ void MainWindow::onWalletOpened() {
|
|||||||
this->touchbarShowWallet();
|
this->touchbarShowWallet();
|
||||||
this->updatePasswordIcon();
|
this->updatePasswordIcon();
|
||||||
|
|
||||||
m_updateBytes.start(100);
|
m_updateBytes.start(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onBalanceUpdated(quint64 balance, quint64 spendable) {
|
void MainWindow::onBalanceUpdated(double balance, double unlocked, const QString &balance_str, const QString &unlocked_str) {
|
||||||
qDebug() << Q_FUNC_INFO;
|
qDebug() << Q_FUNC_INFO;
|
||||||
bool hide = config()->get(Config::hideBalance).toBool();
|
bool hide = config()->get(Config::hideBalance).toBool();
|
||||||
|
|
||||||
QString label_str = QString("Balance: %1 XMR").arg(QString::number(spendable / globals::cdiv, 'f'));
|
auto label_str = QString("Balance: %1 XMR").arg(unlocked_str);
|
||||||
if (balance > spendable)
|
if(balance > unlocked)
|
||||||
label_str += QString(" (+%1 XMR unconfirmed)").arg(QString::number((balance - spendable) / globals::cdiv, 'f'));
|
label_str += QString(" (+%1 XMR unconfirmed)").arg(QString::number(balance - unlocked, 'f'));
|
||||||
|
|
||||||
if (hide)
|
if (hide) {
|
||||||
label_str = "Balance: HIDDEN";
|
label_str = "Balance: HIDDEN";
|
||||||
|
}
|
||||||
|
|
||||||
m_statusLabelBalance->setToolTip("Click for details");
|
|
||||||
m_statusLabelBalance->setText(label_str);
|
m_statusLabelBalance->setText(label_str);
|
||||||
m_balanceWidget->setHidden(hide);
|
m_balanceWidget->setHidden(hide);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setStatusText(const QString &text) {
|
|
||||||
m_statusText = text;
|
|
||||||
if (!m_constructingTransaction)
|
|
||||||
m_statusLabelStatus->setText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onSynchronized() {
|
void MainWindow::onSynchronized() {
|
||||||
this->updateNetStats();
|
this->updateNetStats();
|
||||||
this->setStatusText("Synchronized");
|
m_statusLabelStatus->setText("Synchronized");
|
||||||
this->onConnectionStatusChanged(Wallet::ConnectionStatus_Connected);
|
this->onConnectionStatusChanged(Wallet::ConnectionStatus_Connected);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onBlockchainSync(int height, int target) {
|
void MainWindow::onBlockchainSync(int height, int target) {
|
||||||
QString blocks = (target >= height) ? QString::number(target - height) : "?";
|
QString heightText = QString("Blockchain sync: %1/%2").arg(height).arg(target);
|
||||||
QString heightText = QString("Blockchain sync: %1 blocks remaining").arg(blocks);
|
m_statusLabelStatus->setText(heightText);
|
||||||
this->setStatusText(heightText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onRefreshSync(int height, int target) {
|
void MainWindow::onRefreshSync(int height, int target) {
|
||||||
QString blocks = (target >= height) ? QString::number(target - height) : "?";
|
QString heightText = QString("Wallet refresh: %1/%2").arg(height).arg(target);
|
||||||
QString heightText = QString("Wallet refresh: %1 blocks remaining").arg(blocks);
|
m_statusLabelStatus->setText(heightText);
|
||||||
this->setStatusText(heightText);
|
this->updateNetStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onConnectionStatusChanged(int status)
|
void MainWindow::onConnectionStatusChanged(int status)
|
||||||
@@ -716,7 +729,7 @@ void MainWindow::onConnectionStatusChanged(int status)
|
|||||||
switch(status){
|
switch(status){
|
||||||
case Wallet::ConnectionStatus_Disconnected:
|
case Wallet::ConnectionStatus_Disconnected:
|
||||||
statusIcon = ":/assets/images/status_disconnected.svg";
|
statusIcon = ":/assets/images/status_disconnected.svg";
|
||||||
this->setStatusText("Disconnected");
|
m_statusLabelStatus->setText("Disconnected");
|
||||||
break;
|
break;
|
||||||
case Wallet::ConnectionStatus_Connected:
|
case Wallet::ConnectionStatus_Connected:
|
||||||
if (m_ctx->currentWallet->synchronized()) {
|
if (m_ctx->currentWallet->synchronized()) {
|
||||||
@@ -727,11 +740,11 @@ void MainWindow::onConnectionStatusChanged(int status)
|
|||||||
break;
|
break;
|
||||||
case Wallet::ConnectionStatus_Connecting:
|
case Wallet::ConnectionStatus_Connecting:
|
||||||
statusIcon = ":/assets/images/status_lagging.svg";
|
statusIcon = ":/assets/images/status_lagging.svg";
|
||||||
this->setStatusText("Connecting to daemon");
|
m_statusLabelStatus->setText("Connecting to daemon");
|
||||||
break;
|
break;
|
||||||
case Wallet::ConnectionStatus_WrongVersion:
|
case Wallet::ConnectionStatus_WrongVersion:
|
||||||
statusIcon = ":/assets/images/status_disconnected.svg";
|
statusIcon = ":/assets/images/status_disconnected.svg";
|
||||||
this->setStatusText("Incompatible daemon");
|
m_statusLabelStatus->setText("Incompatible daemon");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
statusIcon = ":/assets/images/status_disconnected.svg";
|
statusIcon = ":/assets/images/status_disconnected.svg";
|
||||||
@@ -755,12 +768,12 @@ void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QStrin
|
|||||||
err = QString("%1 %2").arg(err).arg(tx_err);
|
err = QString("%1 %2").arg(err).arg(tx_err);
|
||||||
|
|
||||||
qDebug() << Q_FUNC_INFO << err;
|
qDebug() << Q_FUNC_INFO << err;
|
||||||
QMessageBox::warning(this, "Transactions error", err);
|
Utils::showMessageBox("Transaction error", err, true);
|
||||||
m_ctx->currentWallet->disposeTransaction(tx);
|
m_ctx->currentWallet->disposeTransaction(tx);
|
||||||
} else if (tx->txCount() == 0) {
|
} else if (tx->txCount() == 0) {
|
||||||
err = QString("%1 %2").arg(err).arg("No unmixable outputs to sweep.");
|
err = QString("%1 %2").arg(err).arg("No unmixable outputs to sweep.");
|
||||||
qDebug() << Q_FUNC_INFO << err;
|
qDebug() << Q_FUNC_INFO << err;
|
||||||
QMessageBox::warning(this, "Transaction error", err);
|
Utils::showMessageBox("Transaction error", err, true);
|
||||||
m_ctx->currentWallet->disposeTransaction(tx);
|
m_ctx->currentWallet->disposeTransaction(tx);
|
||||||
} else {
|
} else {
|
||||||
const auto &description = m_ctx->tmpTxDescription;
|
const auto &description = m_ctx->tmpTxDescription;
|
||||||
@@ -833,11 +846,9 @@ void MainWindow::create_status_bar() {
|
|||||||
m_statusLabelNetStats->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
m_statusLabelNetStats->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
this->statusBar()->addWidget(m_statusLabelNetStats);
|
this->statusBar()->addWidget(m_statusLabelNetStats);
|
||||||
|
|
||||||
m_statusLabelBalance = new ClickableLabel(this);
|
m_statusLabelBalance = new QLabel("Balance: 0.00 XMR", this);
|
||||||
m_statusLabelBalance->setText("Balance: 0.00 XMR");
|
|
||||||
m_statusLabelBalance->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
m_statusLabelBalance->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
this->statusBar()->addPermanentWidget(m_statusLabelBalance);
|
this->statusBar()->addPermanentWidget(m_statusLabelBalance);
|
||||||
connect(m_statusLabelBalance, &ClickableLabel::clicked, this, &MainWindow::showBalanceDialog);
|
|
||||||
|
|
||||||
m_statusBtnConnectionStatusIndicator = new StatusBarButton(QIcon(":/assets/images/status_disconnected.svg"), "Connection status");
|
m_statusBtnConnectionStatusIndicator = new StatusBarButton(QIcon(":/assets/images/status_disconnected.svg"), "Connection status");
|
||||||
connect(m_statusBtnConnectionStatusIndicator, &StatusBarButton::clicked, this, &MainWindow::showConnectionStatusDialog);
|
connect(m_statusBtnConnectionStatusIndicator, &StatusBarButton::clicked, this, &MainWindow::showConnectionStatusDialog);
|
||||||
@@ -988,12 +999,12 @@ void MainWindow::menuWalletOpenClicked() {
|
|||||||
|
|
||||||
QFileInfo infoPath(path);
|
QFileInfo infoPath(path);
|
||||||
if(!infoPath.isReadable()) {
|
if(!infoPath.isReadable()) {
|
||||||
QMessageBox::warning(this, "Cannot read wallet file", "Permission error.");
|
Utils::showMessageBox("Cannot read wallet file", "Permission error.", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(path == m_ctx->walletPath) {
|
if(path == m_ctx->walletPath) {
|
||||||
QMessageBox::warning(this, "Wallet already opened", "Please open another wallet.");
|
Utils::showMessageBox("Wallet already opened", "Please open another wallet.", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1035,6 +1046,16 @@ void MainWindow::skinChanged(const QString &skinName) {
|
|||||||
qDebug() << QString("Skin changed to %1").arg(skinName);
|
qDebug() << QString("Skin changed to %1").arg(skinName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::homeWidgetChanged(const QString &widgetName) {
|
||||||
|
if(widgetName == "ccs"){
|
||||||
|
m_ccsWidget->show();
|
||||||
|
m_redditWidget->hide();
|
||||||
|
} else if(widgetName == "reddit") {
|
||||||
|
m_ccsWidget->hide();
|
||||||
|
m_redditWidget->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::closeEvent(QCloseEvent *event) {
|
void MainWindow::closeEvent(QCloseEvent *event) {
|
||||||
cleanupBeforeClose();
|
cleanupBeforeClose();
|
||||||
|
|
||||||
@@ -1042,11 +1063,11 @@ void MainWindow::closeEvent(QCloseEvent *event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::donateButtonClicked() {
|
void MainWindow::donateButtonClicked() {
|
||||||
double donation = AppContext::prices->convert("EUR", "XMR", m_ctx->donationAmount);
|
double donation = AppContext::prices->convert("EUR", "XMR", m_ctx->featherDonationAmount);
|
||||||
if (donation <= 0)
|
if(donation <= 0)
|
||||||
donation = 0.1337;
|
donation = 1.337;
|
||||||
|
|
||||||
ui->sendWidget->fill(m_ctx->donationAddress, "Donation to the Feather development team", donation);
|
ui->sendWidget->fill(m_ctx->featherDonationAddress, "Donation to the Feather development team", donation);
|
||||||
ui->tabWidget->setCurrentIndex(Tabs::SEND);
|
ui->tabWidget->setCurrentIndex(Tabs::SEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1071,21 +1092,17 @@ void MainWindow::showSendScreen(const CCSEntry &entry) {
|
|||||||
|
|
||||||
void MainWindow::onViewOnBlockExplorer(const QString &txid) {
|
void MainWindow::onViewOnBlockExplorer(const QString &txid) {
|
||||||
QString blockExplorerLink = Utils::blockExplorerLink(config()->get(Config::blockExplorer).toString(), m_ctx->networkType, txid);
|
QString blockExplorerLink = Utils::blockExplorerLink(config()->get(Config::blockExplorer).toString(), m_ctx->networkType, txid);
|
||||||
Utils::externalLinkWarning(this, blockExplorerLink);
|
Utils::externalLinkWarning(blockExplorerLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onResendTransaction(const QString &txid) {
|
void MainWindow::onAddContact(const QString &address, const QString &name) {
|
||||||
if (!AppContext::txCache.contains(txid)) {
|
bool addressValid = WalletManager::addressValid(address, m_ctx->currentWallet->nettype());
|
||||||
QMessageBox::warning(this, "Unable to resend transaction", "Transaction was not found in transaction cache. Unable to resend.");
|
if (!addressValid)
|
||||||
return;
|
QMessageBox::warning(this, "Invalid address", "Invalid address");
|
||||||
|
else {
|
||||||
|
m_ctx->currentWallet->addressBook()->addRow(address, "", name);
|
||||||
|
m_ctx->storeWallet();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to a different node so chances of successful relay are higher
|
|
||||||
m_ctx->nodes->autoConnect(true);
|
|
||||||
|
|
||||||
auto dialog = new BroadcastTxDialog(this, m_ctx, AppContext::txCache[txid]);
|
|
||||||
dialog->exec();
|
|
||||||
dialog->deleteLater();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::importContacts() {
|
void MainWindow::importContacts() {
|
||||||
@@ -1326,29 +1343,6 @@ void MainWindow::updateNetStats() {
|
|||||||
m_statusLabelNetStats->setText(QString("(D: %1)").arg(Utils::formatBytes(m_ctx->currentWallet->getBytesReceived())));
|
m_statusLabelNetStats->setText(QString("(D: %1)").arg(Utils::formatBytes(m_ctx->currentWallet->getBytesReceived())));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::rescanSpent() {
|
|
||||||
if (!m_ctx->currentWallet->rescanSpent()) {
|
|
||||||
QMessageBox::warning(this, "Rescan spent", m_ctx->currentWallet->errorString());
|
|
||||||
} else {
|
|
||||||
QMessageBox::information(this, "Rescan spent", "Successfully rescanned spent outputs.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::showBalanceDialog() {
|
|
||||||
if (!m_ctx->currentWallet) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto *dialog = new BalanceDialog(this, m_ctx->currentWallet);
|
|
||||||
dialog->exec();
|
|
||||||
dialog->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString MainWindow::statusDots() {
|
|
||||||
m_statusDots++;
|
|
||||||
m_statusDots = m_statusDots % 4;
|
|
||||||
return QString(".").repeated(m_statusDots);
|
|
||||||
}
|
|
||||||
|
|
||||||
MainWindow::~MainWindow() {
|
MainWindow::~MainWindow() {
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,15 +76,11 @@ public:
|
|||||||
RECEIVE,
|
RECEIVE,
|
||||||
COINS,
|
COINS,
|
||||||
CALC,
|
CALC,
|
||||||
EXCHANGES,
|
MORPHTOKEN,
|
||||||
|
XMR_TO,
|
||||||
XMRIG
|
XMRIG
|
||||||
};
|
};
|
||||||
|
|
||||||
enum TabsHome {
|
|
||||||
CCS,
|
|
||||||
REDDIT
|
|
||||||
};
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void initWidgets();
|
void initWidgets();
|
||||||
void initMenu();
|
void initMenu();
|
||||||
@@ -106,6 +102,7 @@ public slots:
|
|||||||
void showSendTab();
|
void showSendTab();
|
||||||
void showHistoryTab();
|
void showHistoryTab();
|
||||||
void showSendScreen(const CCSEntry &entry);
|
void showSendScreen(const CCSEntry &entry);
|
||||||
|
void homeWidgetChanged(const QString &widgetName);
|
||||||
void skinChanged(const QString &skinName);
|
void skinChanged(const QString &skinName);
|
||||||
void menuTorClicked();
|
void menuTorClicked();
|
||||||
void onBlockchainSync(int height, int target);
|
void onBlockchainSync(int height, int target);
|
||||||
@@ -117,7 +114,7 @@ public slots:
|
|||||||
void menuWalletOpenClicked();
|
void menuWalletOpenClicked();
|
||||||
void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
|
void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
|
||||||
void onViewOnBlockExplorer(const QString &txid);
|
void onViewOnBlockExplorer(const QString &txid);
|
||||||
void onResendTransaction(const QString &txid);
|
void onAddContact(const QString &address, const QString &name);
|
||||||
void importContacts();
|
void importContacts();
|
||||||
void showRestoreHeightDialog();
|
void showRestoreHeightDialog();
|
||||||
void importTransaction();
|
void importTransaction();
|
||||||
@@ -133,7 +130,7 @@ public slots:
|
|||||||
void loadSignedTxFromText();
|
void loadSignedTxFromText();
|
||||||
|
|
||||||
// libwalletqt
|
// libwalletqt
|
||||||
void onBalanceUpdated(quint64 balance, quint64 spendable);
|
void onBalanceUpdated(double balance, double unlocked, const QString &balance_str, const QString &unlocked_str);
|
||||||
void onSynchronized();
|
void onSynchronized();
|
||||||
void onWalletOpened();
|
void onWalletOpened();
|
||||||
void onWalletClosed();
|
void onWalletClosed();
|
||||||
@@ -167,20 +164,22 @@ private:
|
|||||||
void touchbarShowWallet();
|
void touchbarShowWallet();
|
||||||
void updatePasswordIcon();
|
void updatePasswordIcon();
|
||||||
void updateNetStats();
|
void updateNetStats();
|
||||||
void rescanSpent();
|
|
||||||
void setStatusText(const QString &text);
|
|
||||||
void showBalanceDialog();
|
|
||||||
QString statusDots();
|
|
||||||
|
|
||||||
WalletWizard *createWizard(WalletWizard::Page startPage);
|
WalletWizard *createWizard(WalletWizard::Page startPage);
|
||||||
|
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
Settings *m_windowSettings = nullptr;
|
Settings *m_windowSettings = nullptr;
|
||||||
CalcWindow *m_windowCalc = nullptr;
|
CalcWindow *m_windowCalc = nullptr;
|
||||||
|
SignVerifyDialog *m_windowSignVerify = nullptr;
|
||||||
RestoreDialog *m_restoreDialog = nullptr;
|
RestoreDialog *m_restoreDialog = nullptr;
|
||||||
AboutDialog *m_aboutDialog = nullptr;
|
AboutDialog *m_aboutDialog = nullptr;
|
||||||
XMRigWidget *m_xmrig = nullptr;
|
XMRigWidget *m_xmrig = nullptr;
|
||||||
|
|
||||||
|
bool m_windowSpawned = false;
|
||||||
|
|
||||||
|
CCSWidget *m_ccsWidget = nullptr;
|
||||||
|
RedditWidget *m_redditWidget = nullptr;
|
||||||
|
|
||||||
QSystemTrayIcon *m_trayIcon;
|
QSystemTrayIcon *m_trayIcon;
|
||||||
QMenu m_trayMenu;
|
QMenu m_trayMenu;
|
||||||
QAction *m_trayActionCalc;
|
QAction *m_trayActionCalc;
|
||||||
@@ -192,7 +191,7 @@ private:
|
|||||||
TickerWidget *m_balanceWidget;
|
TickerWidget *m_balanceWidget;
|
||||||
|
|
||||||
// lower status bar
|
// lower status bar
|
||||||
ClickableLabel *m_statusLabelBalance;
|
QLabel *m_statusLabelBalance;
|
||||||
QLabel *m_statusLabelStatus;
|
QLabel *m_statusLabelStatus;
|
||||||
QLabel *m_statusLabelNetStats;
|
QLabel *m_statusLabelNetStats;
|
||||||
StatusBarButton *m_statusBtnConnectionStatusIndicator;
|
StatusBarButton *m_statusBtnConnectionStatusIndicator;
|
||||||
@@ -218,11 +217,6 @@ private:
|
|||||||
|
|
||||||
QTimer m_updateBytes;
|
QTimer m_updateBytes;
|
||||||
|
|
||||||
QString m_statusText;
|
|
||||||
int m_statusDots;
|
|
||||||
bool m_constructingTransaction = false;
|
|
||||||
QTimer m_txTimer;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void menuToggleTabVisible(const QString &key);
|
void menuToggleTabVisible(const QString &key);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -87,65 +87,31 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="Line" name="line">
|
<widget class="Line" name="homeLine">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTabWidget" name="tabHomeWidget">
|
<layout class="QHBoxLayout" name="coolLayout">
|
||||||
<property name="currentIndex">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>9</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="documentMode">
|
<item>
|
||||||
<bool>true</bool>
|
<spacer name="verticalSpacer">
|
||||||
</property>
|
<property name="orientation">
|
||||||
<widget class="QWidget" name="tab">
|
<enum>Qt::Vertical</enum>
|
||||||
<attribute name="title">
|
|
||||||
<string>CCS</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="topMargin">
|
<property name="sizeHint" stdset="0">
|
||||||
<number>0</number>
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="rightMargin">
|
</spacer>
|
||||||
<number>0</number>
|
</item>
|
||||||
</property>
|
</layout>
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="CCSWidget" name="ccsWidget" native="true"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="tab_2">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>/r/Monero</string>
|
|
||||||
</attribute>
|
|
||||||
<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="RedditWidget" name="redditWidget" native="true"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
@@ -261,7 +227,7 @@
|
|||||||
<widget class="QWidget" name="tabCalc">
|
<widget class="QWidget" name="tabCalc">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset resource="assets.qrc">
|
<iconset resource="assets.qrc">
|
||||||
<normaloff>:/assets/images/gnome-calc.png</normaloff>:/assets/images/gnome-calc.png</iconset>
|
<normaloff>:/assets/images/coldcard.png</normaloff>:/assets/images/coldcard.png</iconset>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Calc</string>
|
<string>Calc</string>
|
||||||
@@ -272,15 +238,29 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tabExchange">
|
<widget class="QWidget" name="tabMorphToken">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset resource="assets.qrc">
|
<iconset resource="assets.qrc">
|
||||||
<normaloff>:/assets/images/update.png</normaloff>:/assets/images/update.png</iconset>
|
<normaloff>:/assets/images/morphtoken.png</normaloff>:/assets/images/morphtoken.png</iconset>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Exchange</string>
|
<string>MorphToken</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="MorphTokenWidget" name="morphtokenWidget" native="true"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tabXmrTo">
|
||||||
|
<attribute name="icon">
|
||||||
|
<iconset resource="assets.qrc">
|
||||||
|
<normaloff>:/assets/images/xmrto.png</normaloff>:/assets/images/xmrto.png</iconset>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="title">
|
||||||
|
<string>XMR.to</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
@@ -291,50 +271,36 @@
|
|||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTabWidget" name="tabExchanges">
|
<widget class="XMRToWidget" name="xmrToWidget" native="true"/>
|
||||||
<property name="currentIndex">
|
</item>
|
||||||
<number>0</number>
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="tabXMRto">
|
<property name="sizeType">
|
||||||
<attribute name="icon">
|
<enum>QSizePolicy::Preferred</enum>
|
||||||
<iconset resource="assets.qrc">
|
</property>
|
||||||
<normaloff>:/assets/images/xmrto.png</normaloff>:/assets/images/xmrto.png</iconset>
|
<property name="sizeHint" stdset="0">
|
||||||
</attribute>
|
<size>
|
||||||
<attribute name="title">
|
<width>20</width>
|
||||||
<string>XMR.to</string>
|
<height>0</height>
|
||||||
</attribute>
|
</size>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_10">
|
</property>
|
||||||
<property name="leftMargin">
|
</spacer>
|
||||||
<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="XMRToWidget" name="xmrToWidget" native="true"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tabXmrRig">
|
<widget class="QWidget" name="tabXmrRig">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset resource="assets.qrc">
|
<iconset resource="assets.qrc">
|
||||||
<normaloff>:/assets/images/mining.png</normaloff>:/assets/images/mining.png</iconset>
|
<normaloff>:/assets/images/xmrig.ico</normaloff>:/assets/images/xmrig.ico</iconset>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Mining</string>
|
<string>XMRig</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
@@ -360,6 +326,7 @@
|
|||||||
<property name="title">
|
<property name="title">
|
||||||
<string>File</string>
|
<string>File</string>
|
||||||
</property>
|
</property>
|
||||||
|
<addaction name="actionOpen"/>
|
||||||
<addaction name="actionClose"/>
|
<addaction name="actionClose"/>
|
||||||
<addaction name="actionQuit"/>
|
<addaction name="actionQuit"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
@@ -403,7 +370,6 @@
|
|||||||
<addaction name="actionStore_wallet"/>
|
<addaction name="actionStore_wallet"/>
|
||||||
<addaction name="actionUpdate_balance"/>
|
<addaction name="actionUpdate_balance"/>
|
||||||
<addaction name="actionRefresh_tabs"/>
|
<addaction name="actionRefresh_tabs"/>
|
||||||
<addaction name="actionRescan_spent"/>
|
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionChange_restore_height"/>
|
<addaction name="actionChange_restore_height"/>
|
||||||
<addaction name="menuExport"/>
|
<addaction name="menuExport"/>
|
||||||
@@ -465,7 +431,8 @@
|
|||||||
</property>
|
</property>
|
||||||
<addaction name="actionShow_Coins"/>
|
<addaction name="actionShow_Coins"/>
|
||||||
<addaction name="actionShow_calc"/>
|
<addaction name="actionShow_calc"/>
|
||||||
<addaction name="actionShow_Exchange"/>
|
<addaction name="actionShow_MorphToken"/>
|
||||||
|
<addaction name="actionShow_xmr_to"/>
|
||||||
<addaction name="actionShow_XMRig"/>
|
<addaction name="actionShow_XMRig"/>
|
||||||
</widget>
|
</widget>
|
||||||
<addaction name="menuFile"/>
|
<addaction name="menuFile"/>
|
||||||
@@ -474,6 +441,14 @@
|
|||||||
<addaction name="menuTools"/>
|
<addaction name="menuTools"/>
|
||||||
<addaction name="menuHelp"/>
|
<addaction name="menuHelp"/>
|
||||||
</widget>
|
</widget>
|
||||||
|
<action name="actionOpen">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Open wallet</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="actionQuit">
|
<action name="actionQuit">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Quit application</string>
|
<string>Quit application</string>
|
||||||
@@ -666,7 +641,7 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionShow_XMRig">
|
<action name="actionShow_XMRig">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Show Mining</string>
|
<string>Show XMRig</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionImportTransaction">
|
<action name="actionImportTransaction">
|
||||||
@@ -704,14 +679,9 @@
|
|||||||
<string>Import transaction</string>
|
<string>Import transaction</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionShow_Exchange">
|
<action name="actionShow_MorphToken">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Show Exchange</string>
|
<string>Show MorphToken</string>
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionRescan_spent">
|
|
||||||
<property name="text">
|
|
||||||
<string>Rescan spent</string>
|
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
@@ -759,15 +729,9 @@
|
|||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>CCSWidget</class>
|
<class>MorphTokenWidget</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
<header>widgets/ccswidget.h</header>
|
<header>MorphTokenWidget.h</header>
|
||||||
<container>1</container>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
|
||||||
<class>RedditWidget</class>
|
|
||||||
<extends>QWidget</extends>
|
|
||||||
<header>widgets/redditwidget.h</header>
|
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ void CCSModel::updateEntries(const QList<QSharedPointer<CCSEntry>>& entries) {
|
|||||||
|
|
||||||
m_entries.clear();
|
m_entries.clear();
|
||||||
for (const auto& entry : entries) {
|
for (const auto& entry : entries) {
|
||||||
|
if (entry->raised_amount > entry->target_amount)
|
||||||
|
continue;
|
||||||
m_entries.push_back(entry);
|
m_entries.push_back(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -180,7 +180,12 @@ QVariant CoinsModel::parseTransactionInfo(const CoinsInfo &cInfo, int column, in
|
|||||||
case Address:
|
case Address:
|
||||||
return ModelUtils::displayAddress(cInfo.address(), 1, "");
|
return ModelUtils::displayAddress(cInfo.address(), 1, "");
|
||||||
case AddressLabel:
|
case AddressLabel:
|
||||||
|
{
|
||||||
|
if (cInfo.subaddrIndex() == 0) {
|
||||||
|
return cInfo.coinbase() ? "Coinbase" : "Change";
|
||||||
|
}
|
||||||
return cInfo.addressLabel();
|
return cInfo.addressLabel();
|
||||||
|
}
|
||||||
case Spent:
|
case Spent:
|
||||||
return cInfo.spent();
|
return cInfo.spent();
|
||||||
case SpentHeight:
|
case SpentHeight:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <utils/nodes.h>
|
#include <utils/nodes.h>
|
||||||
#include "appcontext.h"
|
#include "appcontext.h"
|
||||||
|
|
||||||
NodeModel::NodeModel(int nodeSource, QObject *parent)
|
NodeModel::NodeModel(unsigned int nodeSource, QObject *parent)
|
||||||
: QAbstractTableModel(parent)
|
: QAbstractTableModel(parent)
|
||||||
, m_nodeSource(nodeSource)
|
, m_nodeSource(nodeSource)
|
||||||
, m_offline(QIcon(":/assets/images/expired_icon.png"))
|
, m_offline(QIcon(":/assets/images/expired_icon.png"))
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public:
|
|||||||
COUNT
|
COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit NodeModel(int nodeSource, QObject *parent = nullptr);
|
explicit NodeModel(unsigned int nodeSource, QObject *parent = nullptr);
|
||||||
|
|
||||||
int rowCount(const QModelIndex &parent) const override;
|
int rowCount(const QModelIndex &parent) const override;
|
||||||
int columnCount(const QModelIndex &parent) const override;
|
int columnCount(const QModelIndex &parent) const override;
|
||||||
@@ -34,7 +34,7 @@ private:
|
|||||||
QList<FeatherNode> m_nodes;
|
QList<FeatherNode> m_nodes;
|
||||||
QIcon m_offline;
|
QIcon m_offline;
|
||||||
QIcon m_online;
|
QIcon m_online;
|
||||||
int m_nodeSource;
|
unsigned int m_nodeSource;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FEATHER_NODEMODEL_H
|
#endif //FEATHER_NODEMODEL_H
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tI
|
|||||||
usd_amount = AppContext::prices->convert("USD", this->preferredFiatSymbol, usd_amount);
|
usd_amount = AppContext::prices->convert("USD", this->preferredFiatSymbol, usd_amount);
|
||||||
double fiat_rounded = ceil(Utils::roundSignificant(usd_amount, 3) * 100.0) / 100.0;
|
double fiat_rounded = ceil(Utils::roundSignificant(usd_amount, 3) * 100.0) / 100.0;
|
||||||
|
|
||||||
return QString("%1").arg(Utils::amountToCurrencyString(fiat_rounded, this->preferredFiatSymbol));
|
return QString("%1%2").arg(this->preferredFiatSign).arg(QString::number(fiat_rounded));
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
@@ -239,4 +239,4 @@ Qt::ItemFlags TransactionHistoryModel::flags(const QModelIndex &index) const {
|
|||||||
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
|
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
|
||||||
|
|
||||||
return QAbstractTableModel::flags(index);
|
return QAbstractTableModel::flags(index);
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,6 @@ SendWidget::SendWidget(QWidget *parent) :
|
|||||||
ui(new Ui::SendWidget)
|
ui(new Ui::SendWidget)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
m_ctx = MainWindow::getContext();
|
|
||||||
|
|
||||||
QString amount_rx = R"(^\d{0,8}[\.,]\d{0,12}|(all)$)";
|
QString amount_rx = R"(^\d{0,8}[\.,]\d{0,12}|(all)$)";
|
||||||
QRegExp rx;
|
QRegExp rx;
|
||||||
@@ -31,16 +30,6 @@ SendWidget::SendWidget(QWidget *parent) :
|
|||||||
ui->label_conversionAmount->hide();
|
ui->label_conversionAmount->hide();
|
||||||
ui->btn_openAlias->hide();
|
ui->btn_openAlias->hide();
|
||||||
|
|
||||||
ui->label_PayTo->setHelpText("Recipient of the funds.\n\n"
|
|
||||||
"You may enter a Monero address, or an alias (email-like address that forwards to a Monero address)");
|
|
||||||
ui->label_Description->setHelpText("Description of the transaction (optional).\n\n"
|
|
||||||
"The description is not sent to the recipient of the funds. It is stored in your wallet cache, "
|
|
||||||
"and displayed in the 'History' tab.");
|
|
||||||
ui->label_Amount->setHelpText("Amount to be sent.\n\nThis is the exact amount the recipient will receive. "
|
|
||||||
"In addition to this amount a transaction fee will be subtracted from your balance. "
|
|
||||||
"You will be able to review the transaction fee before the transaction is broadcast.\n\n"
|
|
||||||
"To send all your balance, click the Max button to the right.");
|
|
||||||
|
|
||||||
this->setupComboBox();
|
this->setupComboBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,13 +55,17 @@ void SendWidget::fill(double amount) {
|
|||||||
ui->lineAmount->setText(QString::number(amount));
|
ui->lineAmount->setText(QString::number(amount));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendWidget::fill(const QString &address, const QString &description, double amount) {
|
void SendWidget::fill(const QString &address, const QString& description){
|
||||||
ui->lineDescription->setText(description);
|
ui->lineDescription->setText(description);
|
||||||
ui->lineAddress->setText(address);
|
ui->lineAddress->setText(address);
|
||||||
ui->lineAddress->setCursorPosition(0);
|
ui->lineAddress->setCursorPosition(0);
|
||||||
if (amount > 0)
|
}
|
||||||
ui->lineAmount->setText(QString::number(amount));
|
|
||||||
this->updateConversionLabel();
|
void SendWidget::fill(const QString &address, const QString &description, double amount) {
|
||||||
|
ui->lineDescription->setText(description);
|
||||||
|
ui->lineAmount->setText(QString::number(amount));
|
||||||
|
ui->lineAddress->setText(address);
|
||||||
|
ui->lineAddress->setCursorPosition(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendWidget::fillAddress(const QString &address) {
|
void SendWidget::fillAddress(const QString &address) {
|
||||||
@@ -81,14 +74,7 @@ void SendWidget::fillAddress(const QString &address) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SendWidget::sendClicked() {
|
void SendWidget::sendClicked() {
|
||||||
if (m_ctx->currentWallet->connectionStatus() != Wallet::ConnectionStatus_Connected) {
|
double amount = 0.0;
|
||||||
QMessageBox::warning(this, "Error", "Unable to create transaction:\n\n"
|
|
||||||
"Wallet is not connected to a node.\n"
|
|
||||||
"Go to File -> Settings -> Node to manually connect to a node.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double amount;
|
|
||||||
QString currency = ui->comboCurrencySelection->currentText();
|
QString currency = ui->comboCurrencySelection->currentText();
|
||||||
QString recipient = ui->lineAddress->text().simplified().remove(' ');
|
QString recipient = ui->lineAddress->text().simplified().remove(' ');
|
||||||
QString description = ui->lineDescription->text();
|
QString description = ui->lineDescription->text();
|
||||||
@@ -191,7 +177,6 @@ void SendWidget::clearFields() {
|
|||||||
|
|
||||||
void SendWidget::onWalletClosed() {
|
void SendWidget::onWalletClosed() {
|
||||||
this->clearFields();
|
this->clearFields();
|
||||||
ui->btnSend->setEnabled(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendWidget::onInitiateTransaction() {
|
void SendWidget::onInitiateTransaction() {
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ Q_OBJECT
|
|||||||
public:
|
public:
|
||||||
explicit SendWidget(QWidget *parent = nullptr);
|
explicit SendWidget(QWidget *parent = nullptr);
|
||||||
void fill(const CCSEntry &entry);
|
void fill(const CCSEntry &entry);
|
||||||
void fill(const QString &address, const QString& description, double amount = 0);
|
void fill(const QString &address, const QString& description);
|
||||||
|
void fill(const QString &address, const QString& description, double amount);
|
||||||
void fill(double amount);
|
void fill(double amount);
|
||||||
void clearFields();
|
void clearFields();
|
||||||
~SendWidget() override;
|
~SendWidget() override;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>647</width>
|
<width>603</width>
|
||||||
<height>175</height>
|
<height>175</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="HelpLabel" name="label_PayTo">
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Pay to</string>
|
<string>Pay to</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="HelpLabel" name="label_Description">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Description</string>
|
<string>Description</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
<widget class="QLineEdit" name="lineDescription"/>
|
<widget class="QLineEdit" name="lineDescription"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="HelpLabel" name="label_Amount">
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Amount</string>
|
<string>Amount</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -184,13 +184,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
|
||||||
<customwidget>
|
|
||||||
<class>HelpLabel</class>
|
|
||||||
<extends>QLabel</extends>
|
|
||||||
<header>components.h</header>
|
|
||||||
</customwidget>
|
|
||||||
</customwidgets>
|
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
<slots>
|
<slots>
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
#include "utils/config.h"
|
#include "utils/config.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
|
||||||
|
|
||||||
Settings::Settings(QWidget *parent) :
|
Settings::Settings(QWidget *parent) :
|
||||||
QDialog(parent),
|
QDialog(parent),
|
||||||
ui(new Ui::Settings)
|
ui(new Ui::Settings)
|
||||||
@@ -40,11 +38,16 @@ Settings::Settings(QWidget *parent) :
|
|||||||
ui->checkBox_hideBalance->setChecked(config()->get(Config::hideBalance).toBool());
|
ui->checkBox_hideBalance->setChecked(config()->get(Config::hideBalance).toBool());
|
||||||
|
|
||||||
// setup comboboxes
|
// setup comboboxes
|
||||||
|
auto settingsHomeWidget = config()->get(Config::homeWidget).toString();
|
||||||
|
if (m_homeWidgets.contains(settingsHomeWidget))
|
||||||
|
ui->comboBox_homeWidget->setCurrentIndex(m_homeWidgets.indexOf(settingsHomeWidget));
|
||||||
|
|
||||||
this->setupSkinCombobox();
|
this->setupSkinCombobox();
|
||||||
auto settingsSkin = config()->get(Config::skin).toString();
|
auto settingsSkin = config()->get(Config::skin).toString();
|
||||||
if (m_skins.contains(settingsSkin))
|
if (m_skins.contains(settingsSkin))
|
||||||
ui->comboBox_skin->setCurrentIndex(m_skins.indexOf(settingsSkin));
|
ui->comboBox_skin->setCurrentIndex(m_skins.indexOf(settingsSkin));
|
||||||
|
|
||||||
|
connect(ui->comboBox_homeWidget, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_homeWidgetChanged);
|
||||||
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_blockExplorer, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_blockExplorerChanged);
|
||||||
|
|
||||||
@@ -60,26 +63,16 @@ Settings::Settings(QWidget *parent) :
|
|||||||
|
|
||||||
connect(ui->comboBox_fiatCurrency, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::fiatCurrencySelected);
|
connect(ui->comboBox_fiatCurrency, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::fiatCurrencySelected);
|
||||||
|
|
||||||
// setup paths tab
|
// setup path tab
|
||||||
this->updatePaths();
|
#if defined(Q_OS_LINUX)
|
||||||
connect(ui->btn_browseDefaultWalletDir, &QPushButton::clicked, [this]{
|
ui->textEditPaths->appendPlainText(m_ctx->applicationPath);
|
||||||
QString walletDir = QFileDialog::getExistingDirectory(this, "Select wallet directory ", m_ctx->defaultWalletDir, QFileDialog::ShowDirsOnly);
|
#endif
|
||||||
if (walletDir.isEmpty()) return;
|
ui->textEditPaths->appendPlainText(m_ctx->configDirectory);
|
||||||
m_ctx->defaultWalletDir = walletDir;
|
ui->textEditPaths->appendPlainText(m_ctx->defaultWalletDir);
|
||||||
m_ctx->defaultWalletDirRoot = walletDir;
|
|
||||||
config()->set(Config::walletDirectory, walletDir);
|
|
||||||
ui->lineEdit_defaultWalletDir->setText(m_ctx->defaultWalletDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
this->adjustSize();
|
this->adjustSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::updatePaths() {
|
|
||||||
ui->lineEdit_defaultWalletDir->setText(m_ctx->defaultWalletDir);
|
|
||||||
ui->lineEdit_configDir->setText(m_ctx->configDirectory);
|
|
||||||
ui->lineEdit_applicationDir->setText(m_ctx->applicationPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Settings::fiatCurrencySelected(int index) {
|
void Settings::fiatCurrencySelected(int index) {
|
||||||
QString selection = ui->comboBox_fiatCurrency->itemText(index);
|
QString selection = ui->comboBox_fiatCurrency->itemText(index);
|
||||||
config()->set(Config::preferredFiatCurrency, selection);
|
config()->set(Config::preferredFiatCurrency, selection);
|
||||||
@@ -90,6 +83,12 @@ void Settings::comboBox_skinChanged(int pos) {
|
|||||||
emit skinChanged(m_skins.at(pos));
|
emit skinChanged(m_skins.at(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Settings::comboBox_homeWidgetChanged(int pos) {
|
||||||
|
config()->set(Config::homeWidget, m_homeWidgets.at(pos));
|
||||||
|
|
||||||
|
emit homeWidgetChanged(m_homeWidgets.at(pos));
|
||||||
|
}
|
||||||
|
|
||||||
void Settings::comboBox_blockExplorerChanged(int pos) {
|
void Settings::comboBox_blockExplorerChanged(int pos) {
|
||||||
QString blockExplorer = ui->comboBox_blockExplorer->currentText();
|
QString blockExplorer = ui->comboBox_blockExplorer->currentText();
|
||||||
config()->set(Config::blockExplorer, blockExplorer);
|
config()->set(Config::blockExplorer, blockExplorer);
|
||||||
|
|||||||
@@ -26,19 +26,21 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void closed();
|
void closed();
|
||||||
void preferredFiatCurrencyChanged(QString currency);
|
void preferredFiatCurrencyChanged(QString currency);
|
||||||
|
void homeWidgetChanged(QString widgetName);
|
||||||
void skinChanged(QString skinName);
|
void skinChanged(QString skinName);
|
||||||
void showHomeCCS(bool);
|
void showHomeCCS(bool);
|
||||||
void blockExplorerChanged(QString blockExplorer);
|
void blockExplorerChanged(QString blockExplorer);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updatePaths();
|
|
||||||
void copyToClipboard();
|
void copyToClipboard();
|
||||||
void checkboxExternalLinkWarn();
|
void checkboxExternalLinkWarn();
|
||||||
void fiatCurrencySelected(int index);
|
void fiatCurrencySelected(int index);
|
||||||
|
void comboBox_homeWidgetChanged(int pos);
|
||||||
void comboBox_skinChanged(int pos);
|
void comboBox_skinChanged(int pos);
|
||||||
void comboBox_blockExplorerChanged(int post);
|
void comboBox_blockExplorerChanged(int post);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
const QStringList m_homeWidgets{"ccs", "reddit"};
|
||||||
QStringList m_skins{"Native", "QDarkStyle", "Breeze/Dark", "Breeze/Light"};
|
QStringList m_skins{"Native", "QDarkStyle", "Breeze/Dark", "Breeze/Light"};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1123</width>
|
<width>1019</width>
|
||||||
<height>555</height>
|
<height>358</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -121,23 +121,44 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Home screen widget:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="comboBox_homeWidget">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>CCS proposals</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>/r/monero</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="label_6">
|
<widget class="QLabel" name="label_6">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Appearance:</string>
|
<string>Appearance:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QComboBox" name="comboBox_skin"/>
|
<widget class="QComboBox" name="comboBox_skin"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="label_2">
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Block explorer:</string>
|
<string>Block explorer:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QComboBox" name="comboBox_blockExplorer">
|
<widget class="QComboBox" name="comboBox_blockExplorer">
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -161,14 +182,14 @@
|
|||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="4" column="0">
|
||||||
<widget class="QCheckBox" name="checkBox_externalLink">
|
<widget class="QCheckBox" name="checkBox_externalLink">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Warn before opening external link</string>
|
<string>Warn before opening external link</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QCheckBox" name="checkBox_hideBalance">
|
<widget class="QCheckBox" name="checkBox_hideBalance">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Hide balance</string>
|
<string>Hide balance</string>
|
||||||
@@ -282,74 +303,11 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QFormLayout" name="formLayout_2">
|
<widget class="QPlainTextEdit" name="textEditPaths">
|
||||||
<item row="0" column="1">
|
<property name="readOnly">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<bool>true</bool>
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="lineEdit_defaultWalletDir">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btn_browseDefaultWalletDir">
|
|
||||||
<property name="text">
|
|
||||||
<string>Browse</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>Wallet directory:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="text">
|
|
||||||
<string>Config directory:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QLineEdit" name="lineEdit_configDir">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="text">
|
|
||||||
<string>Application directory:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QLineEdit" name="lineEdit_applicationDir">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
</widget>
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2020, The Monero Project.
|
||||||
|
|
||||||
|
#include "MorphTokenApi.h"
|
||||||
|
|
||||||
|
MorphTokenApi::MorphTokenApi(QObject *parent, UtilsNetworking *network, QString baseUrl)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_network(network)
|
||||||
|
, m_baseUrl(std::move(baseUrl))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphTokenApi::createTrade(const QString &inputAsset, const QString &outputAsset, const QString &refundAddress, const QString &outputAddress) {
|
||||||
|
QJsonObject trade;
|
||||||
|
|
||||||
|
QJsonObject input;
|
||||||
|
input["asset"] = inputAsset;
|
||||||
|
input["refund"] = refundAddress;
|
||||||
|
|
||||||
|
QJsonArray output;
|
||||||
|
QJsonObject outputObj;
|
||||||
|
outputObj["asset"] = outputAsset;
|
||||||
|
outputObj["weight"] = 10000;
|
||||||
|
outputObj["address"] = outputAddress;
|
||||||
|
output.append(outputObj);
|
||||||
|
|
||||||
|
trade["input"] = input;
|
||||||
|
trade["output"] = output;
|
||||||
|
|
||||||
|
QString url = QString("%1/morph").arg(m_baseUrl);
|
||||||
|
QNetworkReply *reply = m_network->postJson(url, trade);
|
||||||
|
connect(reply, &QNetworkReply::finished, std::bind(&MorphTokenApi::onResponse, this, reply, Endpoint::CREATE_TRADE));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphTokenApi::getTrade(const QString &morphId) {
|
||||||
|
QString url = QString("%1/morph/%2").arg(m_baseUrl, morphId);
|
||||||
|
QNetworkReply *reply = m_network->getJson(url);
|
||||||
|
connect(reply, &QNetworkReply::finished, std::bind(&MorphTokenApi::onResponse, this, reply, Endpoint::GET_TRADE));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphTokenApi::getRates() {
|
||||||
|
QString url = QString("%1/rates").arg(m_baseUrl);
|
||||||
|
QNetworkReply *reply = m_network->getJson(url);
|
||||||
|
connect(reply, &QNetworkReply::finished, std::bind(&MorphTokenApi::onResponse, this, reply, Endpoint::GET_RATES));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphTokenApi::getLimits(const QString &inputAsset, const QString &outputAsset) {
|
||||||
|
QJsonObject limits;
|
||||||
|
|
||||||
|
QJsonObject input;
|
||||||
|
input["asset"] = inputAsset;
|
||||||
|
|
||||||
|
QJsonArray output;
|
||||||
|
QJsonObject outputObj;
|
||||||
|
outputObj["asset"] = outputAsset;
|
||||||
|
outputObj["weight"] = 10000;
|
||||||
|
output.append(outputObj);
|
||||||
|
|
||||||
|
limits["input"] = input;
|
||||||
|
limits["output"] = output;
|
||||||
|
|
||||||
|
QString url = QString("%1/limits").arg(m_baseUrl);
|
||||||
|
QNetworkReply *reply = m_network->postJson(url, limits);
|
||||||
|
connect(reply, &QNetworkReply::finished, std::bind(&MorphTokenApi::onResponse, this, reply, Endpoint::GET_LIMITS));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphTokenApi::onResponse(QNetworkReply *reply, Endpoint endpoint) {
|
||||||
|
const auto ok = reply->error() == QNetworkReply::NoError;
|
||||||
|
const auto err = reply->errorString();
|
||||||
|
|
||||||
|
QByteArray data = reply->readAll();
|
||||||
|
QJsonObject obj;
|
||||||
|
if (!data.isEmpty() && Utils::validateJSON(data)) {
|
||||||
|
auto doc = QJsonDocument::fromJson(data);
|
||||||
|
obj = doc.object();
|
||||||
|
}
|
||||||
|
else if (!ok) {
|
||||||
|
emit ApiResponse(MorphTokenResponse(false, endpoint, err, {}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
emit ApiResponse(MorphTokenResponse(false, endpoint, "Invalid response from MorphToken", {}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.contains("success")) {
|
||||||
|
emit ApiResponse(MorphTokenResponse(false, endpoint, obj.value("description").toString(), obj));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
emit ApiResponse(MorphTokenResponse(true, endpoint, "", obj));
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2020, The Monero Project.
|
||||||
|
|
||||||
|
#ifndef FEATHER_MORPHTOKENAPI_H
|
||||||
|
#define FEATHER_MORPHTOKENAPI_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <utility>
|
||||||
|
#include "utils/networking.h"
|
||||||
|
|
||||||
|
class MorphTokenApi : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Endpoint {
|
||||||
|
CREATE_TRADE = 0,
|
||||||
|
GET_TRADE,
|
||||||
|
GET_RATES,
|
||||||
|
GET_LIMITS
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MorphTokenResponse {
|
||||||
|
explicit MorphTokenResponse(bool ok, Endpoint endpoint, QString message, QJsonObject obj)
|
||||||
|
: ok(ok), endpoint(endpoint), message(std::move(message)), obj(std::move(obj)) {};
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
Endpoint endpoint;
|
||||||
|
QString message;
|
||||||
|
QJsonObject obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit MorphTokenApi(QObject *parent, UtilsNetworking *network, QString baseUrl = "https://api.morphtoken.com");
|
||||||
|
|
||||||
|
void createTrade(const QString &inputAsset, const QString &outputAsset, const QString &refundAddress, const QString &outputAddress);
|
||||||
|
void getTrade(const QString &morphId);
|
||||||
|
void getRates();
|
||||||
|
void getLimits(const QString &inputAsset, const QString &outputAsset);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ApiResponse(MorphTokenResponse resp);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onResponse(QNetworkReply *reply, Endpoint endpoint);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_baseUrl;
|
||||||
|
UtilsNetworking *m_network;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FEATHER_MORPHTOKENAPI_H
|
||||||
@@ -41,7 +41,8 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
|||||||
{Config::nodeSource,{QS("nodeSource"), 0}},
|
{Config::nodeSource,{QS("nodeSource"), 0}},
|
||||||
{Config::useOnionNodes,{QS("useOnionNodes"), false}},
|
{Config::useOnionNodes,{QS("useOnionNodes"), false}},
|
||||||
{Config::showTabCoins,{QS("showTabCoins"), false}},
|
{Config::showTabCoins,{QS("showTabCoins"), false}},
|
||||||
{Config::showTabExchange, {QS("showTabExchange"), true}},
|
{Config::showTabMorphToken, {QS("showTabMorphToken"), false}},
|
||||||
|
{Config::showTabXMRto,{QS("showTabXMRto"), true}},
|
||||||
{Config::showTabXMRig,{QS("showTabXMRig"), false}},
|
{Config::showTabXMRig,{QS("showTabXMRig"), false}},
|
||||||
{Config::showTabCalc,{QS("showTabCalc"), true}},
|
{Config::showTabCalc,{QS("showTabCalc"), true}},
|
||||||
{Config::geometry, {QS("geometry"), {}}},
|
{Config::geometry, {QS("geometry"), {}}},
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ public:
|
|||||||
nodeSource,
|
nodeSource,
|
||||||
useOnionNodes,
|
useOnionNodes,
|
||||||
showTabCoins,
|
showTabCoins,
|
||||||
showTabExchange,
|
showTabMorphToken,
|
||||||
|
showTabXMRto,
|
||||||
showTabCalc,
|
showTabCalc,
|
||||||
showTabXMRig,
|
showTabXMRig,
|
||||||
geometry,
|
geometry,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
|
||||||
WalletKeysFiles::WalletKeysFiles(const QFileInfo &info, int networkType, QString address) :
|
WalletKeysFiles::WalletKeysFiles(const QFileInfo &info, quint8 networkType, QString address) :
|
||||||
m_fileName(info.fileName()),
|
m_fileName(info.fileName()),
|
||||||
m_modified(info.lastModified().toSecsSinceEpoch()),
|
m_modified(info.lastModified().toSecsSinceEpoch()),
|
||||||
m_path(QDir::toNativeSeparators(info.absoluteFilePath())),
|
m_path(QDir::toNativeSeparators(info.absoluteFilePath())),
|
||||||
@@ -42,15 +42,23 @@ QString WalletKeysFiles::path() const {
|
|||||||
return m_path;
|
return m_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WalletKeysFiles::networkType() const {
|
quint8 WalletKeysFiles::networkType() const {
|
||||||
return m_networkType;
|
return m_networkType;
|
||||||
}
|
}
|
||||||
|
|
||||||
WalletKeysFilesModel::WalletKeysFilesModel(AppContext *ctx, QObject *parent)
|
WalletKeysFilesModel::WalletKeysFilesModel(AppContext *ctx, QObject *parent) :
|
||||||
: QAbstractTableModel(parent)
|
QAbstractTableModel(parent),
|
||||||
, m_ctx(ctx)
|
m_ctx(ctx) {
|
||||||
{
|
this->walletDirectories << m_ctx->defaultWalletDirRoot;
|
||||||
this->updateDirectories();
|
auto walletPath = config()->get(Config::walletPath).toString();
|
||||||
|
if(!walletPath.isEmpty() && Utils::fileExists(walletPath)) {
|
||||||
|
QDir d = QFileInfo(walletPath).absoluteDir();
|
||||||
|
this->walletDirectories << d.absolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->walletDirectories << m_ctx->homeDir;
|
||||||
|
this->walletDirectories.removeDuplicates();
|
||||||
|
|
||||||
this->m_walletKeysFilesItemModel = qobject_cast<QAbstractItemModel *>(this);
|
this->m_walletKeysFilesItemModel = qobject_cast<QAbstractItemModel *>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,20 +71,6 @@ void WalletKeysFilesModel::clear() {
|
|||||||
void WalletKeysFilesModel::refresh() {
|
void WalletKeysFilesModel::refresh() {
|
||||||
this->clear();
|
this->clear();
|
||||||
this->findWallets();
|
this->findWallets();
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WalletKeysFilesModel::updateDirectories() {
|
|
||||||
this->walletDirectories.clear();
|
|
||||||
this->walletDirectories << m_ctx->defaultWalletDirRoot;
|
|
||||||
auto walletPath = config()->get(Config::walletPath).toString();
|
|
||||||
if(!walletPath.isEmpty() && Utils::fileExists(walletPath)) {
|
|
||||||
QDir d = QFileInfo(walletPath).absoluteDir();
|
|
||||||
this->walletDirectories << d.absolutePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->walletDirectories << m_ctx->homeDir;
|
|
||||||
this->walletDirectories.removeDuplicates();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WalletKeysFilesModel::findWallets() {
|
void WalletKeysFilesModel::findWallets() {
|
||||||
@@ -139,7 +133,7 @@ int WalletKeysFilesModel::rowCount(const QModelIndex & parent) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int WalletKeysFilesModel::columnCount(const QModelIndex &) const {
|
int WalletKeysFilesModel::columnCount(const QModelIndex &) const {
|
||||||
return 4;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant WalletKeysFilesModel::data(const QModelIndex &index, int role) const {
|
QVariant WalletKeysFilesModel::data(const QModelIndex &index, int role) const {
|
||||||
@@ -168,15 +162,13 @@ QVariant WalletKeysFilesModel::data(const QModelIndex &index, int role) const {
|
|||||||
#endif
|
#endif
|
||||||
return fp;
|
return fp;
|
||||||
}
|
}
|
||||||
case ModelColumns::Modified:
|
|
||||||
return walletKeyFile.modified();
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if(role == Qt::UserRole) {
|
} else if(role == Qt::UserRole) {
|
||||||
switch(index.column()) {
|
switch(index.column()) {
|
||||||
case ModelColumns::NetworkType:
|
case ModelColumns::NetworkType:
|
||||||
return static_cast<int>(walletKeyFile.networkType());
|
return static_cast<uint>(walletKeyFile.networkType());
|
||||||
case ModelColumns::FileName:
|
case ModelColumns::FileName:
|
||||||
return walletKeyFile.fileName();
|
return walletKeyFile.fileName();
|
||||||
case ModelColumns::Modified:
|
case ModelColumns::Modified:
|
||||||
@@ -209,15 +201,9 @@ QVariant WalletKeysFilesModel::headerData(int section, Qt::Orientation orientati
|
|||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
WalletKeysFilesProxyModel::WalletKeysFilesProxyModel(QObject *parent, NetworkType::Type nettype)
|
WalletKeysFilesProxyModel::WalletKeysFilesProxyModel(QObject *parent) :
|
||||||
: QSortFilterProxyModel(parent)
|
QSortFilterProxyModel(parent) {}
|
||||||
, m_nettype(nettype)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WalletKeysFilesProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
bool WalletKeysFilesProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||||
QModelIndex nettypeIndex = sourceModel()->index(sourceRow, WalletKeysFilesModel::NetworkType, sourceParent);
|
return true;
|
||||||
NetworkType::Type nettype = static_cast<NetworkType::Type>(sourceModel()->data(nettypeIndex, Qt::UserRole).toInt());
|
|
||||||
|
|
||||||
return (m_nettype == nettype);
|
|
||||||
}
|
}
|
||||||
@@ -12,19 +12,19 @@
|
|||||||
class WalletKeysFiles
|
class WalletKeysFiles
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WalletKeysFiles(const QFileInfo &info, int networkType, QString address);
|
WalletKeysFiles(const QFileInfo &info, quint8 networkType, QString address);
|
||||||
|
|
||||||
QString fileName() const;
|
QString fileName() const;
|
||||||
qint64 modified() const;
|
qint64 modified() const;
|
||||||
QString path() const;
|
QString path() const;
|
||||||
int networkType() const;
|
quint8 networkType() const;
|
||||||
QString address() const;
|
QString address() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_fileName;
|
QString m_fileName;
|
||||||
qint64 m_modified;
|
qint64 m_modified;
|
||||||
QString m_path;
|
QString m_path;
|
||||||
int m_networkType;
|
quint8 m_networkType;
|
||||||
QString m_address;
|
QString m_address;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -54,8 +54,6 @@ public:
|
|||||||
QStringList walletDirectories;
|
QStringList walletDirectories;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateDirectories();
|
|
||||||
|
|
||||||
AppContext *m_ctx;
|
AppContext *m_ctx;
|
||||||
QList<WalletKeysFiles> m_walletKeyFiles;
|
QList<WalletKeysFiles> m_walletKeyFiles;
|
||||||
QAbstractItemModel *m_walletKeysFilesItemModel;
|
QAbstractItemModel *m_walletKeysFilesItemModel;
|
||||||
@@ -66,11 +64,8 @@ class WalletKeysFilesProxyModel : public QSortFilterProxyModel
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit WalletKeysFilesProxyModel(QObject *parent, NetworkType::Type nettype);
|
explicit WalletKeysFilesProxyModel(QObject *parent = nullptr);
|
||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||||
|
|
||||||
private:
|
|
||||||
NetworkType::Type m_nettype;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEYSFILES_H
|
#endif // KEYSFILES_H
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ void Nodes::connectToNode() {
|
|||||||
// auto connect
|
// auto connect
|
||||||
m_wsExhaustedWarningEmitted = false;
|
m_wsExhaustedWarningEmitted = false;
|
||||||
m_customExhaustedWarningEmitted = false;
|
m_customExhaustedWarningEmitted = false;
|
||||||
this->autoConnect(true);
|
this->autoConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nodes::connectToNode(const FeatherNode &node) {
|
void Nodes::connectToNode(const FeatherNode &node) {
|
||||||
|
|||||||
@@ -19,44 +19,44 @@
|
|||||||
|
|
||||||
struct RestoreHeightLookup {
|
struct RestoreHeightLookup {
|
||||||
NetworkType::Type type;
|
NetworkType::Type type;
|
||||||
QMap<int, int> data;
|
QMap<unsigned int, unsigned int> data;
|
||||||
explicit RestoreHeightLookup(NetworkType::Type type) : type(type) {}
|
explicit RestoreHeightLookup(NetworkType::Type type) : type(type) {}
|
||||||
|
|
||||||
int dateToRestoreHeight(int date) {
|
unsigned int dateToRestoreHeight(unsigned int date) {
|
||||||
// restore height based on a given timestamp using a lookup
|
// restore height based on a given timestamp using a lookup
|
||||||
// table. If it cannot find the date in the lookup table, it
|
// table. If it cannot find the date in the lookup table, it
|
||||||
// will calculate the blockheight based off the last known
|
// will calculate the blockheight based off the last known
|
||||||
// date: ((now - lastKnownDate) / blockTime) - clearance
|
// date: ((now - lastKnownDate) / blockTime) - clearance
|
||||||
|
|
||||||
if(this->type == NetworkType::TESTNET) return 1;
|
if(this->type == NetworkType::TESTNET) return 1;
|
||||||
int blockTime = 120;
|
unsigned int blockTime = 120;
|
||||||
int blocksPerDay = 86400 / blockTime;
|
unsigned int blocksPerDay = 86400 / blockTime;
|
||||||
int blockCalcClearance = blocksPerDay * 5;
|
unsigned int blockCalcClearance = blocksPerDay * 5;
|
||||||
QList<int> values = this->data.keys();
|
QList<unsigned int> values = this->data.keys();
|
||||||
if(date <= values.at(0))
|
if(date <= values.at(0))
|
||||||
return this->data[values.at(0)];
|
return this->data[values.at(0)];
|
||||||
for(int i = 0; i != values.count(); i++) {
|
for(unsigned int i = 0; i != values.count(); i++) {
|
||||||
if(values[i] > date) {
|
if(values[i] > date) {
|
||||||
return i - 1 < 0 ? this->data[values[i]] : this->data[values[i-1]] - blockCalcClearance;
|
return i - 1 < 0 ? this->data[values[i]] : this->data[values[i-1]] - blockCalcClearance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookup failed, calculate blockheight from last known checkpoint
|
// lookup failed, calculate blockheight from last known checkpoint
|
||||||
int lastBlockHeightTime = values.at(values.count() - 1);
|
unsigned int lastBlockHeightTime = values.at(values.count() - 1);
|
||||||
int lastBlockHeight = this->data[lastBlockHeightTime];
|
unsigned int lastBlockHeight = this->data[lastBlockHeightTime];
|
||||||
int deltaTime = date - lastBlockHeightTime;
|
unsigned int deltaTime = date - lastBlockHeightTime;
|
||||||
int deltaBlocks = deltaTime / blockTime;
|
unsigned int deltaBlocks = deltaTime / blockTime;
|
||||||
int blockHeight = (lastBlockHeight + deltaBlocks) - blockCalcClearance;
|
unsigned int blockHeight = (lastBlockHeight + deltaBlocks) - blockCalcClearance;
|
||||||
qDebug() << "Calculated blockheight: " << blockHeight << " from epoch " << date;
|
qDebug() << "Calculated blockheight: " << blockHeight << " from epoch " << date;
|
||||||
return blockHeight;
|
return blockHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
int restoreHeightToDate(int height) {
|
unsigned int restoreHeightToDate(unsigned int height) {
|
||||||
// @TODO: most likely inefficient, refactor
|
// @TODO: most likely inefficient, refactor
|
||||||
QMap<int, int>::iterator i;
|
QMap<unsigned int, unsigned int>::iterator i;
|
||||||
int timestamp = 0;
|
unsigned int timestamp = 0;
|
||||||
for (i = this->data.begin(); i != this->data.end(); ++i) {
|
for (i = this->data.begin(); i != this->data.end(); ++i) {
|
||||||
int ts = i.key();
|
unsigned int ts = i.key();
|
||||||
if (i.value() > height)
|
if (i.value() > height)
|
||||||
return timestamp;
|
return timestamp;
|
||||||
timestamp = ts;
|
timestamp = ts;
|
||||||
@@ -68,7 +68,7 @@ struct RestoreHeightLookup {
|
|||||||
// initialize this class using a lookup table, e.g `:/assets/restore_heights_monero_mainnet.txt`/
|
// initialize this class using a lookup table, e.g `:/assets/restore_heights_monero_mainnet.txt`/
|
||||||
auto rtn = new RestoreHeightLookup(type);
|
auto rtn = new RestoreHeightLookup(type);
|
||||||
auto data = Utils::barrayToString(Utils::fileOpen(fn));
|
auto data = Utils::barrayToString(Utils::fileOpen(fn));
|
||||||
QMap<int, int> _data;
|
QMap<unsigned int, unsigned int> _data;
|
||||||
for(const auto &line: data.split('\n')) {
|
for(const auto &line: data.split('\n')) {
|
||||||
if(line.trimmed().isEmpty()) continue;
|
if(line.trimmed().isEmpty()) continue;
|
||||||
auto spl = line.trimmed().split(':');
|
auto spl = line.trimmed().split(':');
|
||||||
@@ -82,7 +82,7 @@ struct FeatherSeed {
|
|||||||
QString mnemonicSeed;
|
QString mnemonicSeed;
|
||||||
QString spendKey;
|
QString spendKey;
|
||||||
time_t time = 0;
|
time_t time = 0;
|
||||||
int restoreHeight = 0;
|
unsigned int restoreHeight = 0;
|
||||||
RestoreHeightLookup *lookup = nullptr;
|
RestoreHeightLookup *lookup = nullptr;
|
||||||
QString language;
|
QString language;
|
||||||
std::string coinName;
|
std::string coinName;
|
||||||
@@ -124,7 +124,7 @@ struct FeatherSeed {
|
|||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
Wallet *writeWallet(WalletManager *manager, NetworkType::Type type, const QString &path, const QString &password, quint64 kdfRounds) {
|
Wallet *writeWallet(WalletManager *manager, NetworkType::Type type, const QString &path, const QString &password, unsigned int kdfRounds) {
|
||||||
// writes both 14/25 word mnemonic seeds.
|
// writes both 14/25 word mnemonic seeds.
|
||||||
Wallet *wallet = nullptr;
|
Wallet *wallet = nullptr;
|
||||||
if(this->lookup == nullptr) return wallet;
|
if(this->lookup == nullptr) return wallet;
|
||||||
@@ -136,25 +136,25 @@ struct FeatherSeed {
|
|||||||
this->restoreHeight = _seed.restoreHeight;
|
this->restoreHeight = _seed.restoreHeight;
|
||||||
this->spendKey = _seed.spendKey;
|
this->spendKey = _seed.spendKey;
|
||||||
}
|
}
|
||||||
wallet = manager->createDeterministicWalletFromSpendKey(path, password, this->language, type, this->spendKey, this->restoreHeight, kdfRounds);
|
wallet = manager->createDeterministicWalletFromSpendKey(path, password, this->language, type, this->spendKey, this->restoreHeight, (quint64)kdfRounds);
|
||||||
wallet->setCacheAttribute("feather.seed", this->mnemonicSeed);
|
wallet->setCacheAttribute("feather.seed", this->mnemonicSeed);
|
||||||
} else {
|
} else {
|
||||||
wallet = manager->recoveryWallet(path, password, this->mnemonicSeed, "", type, this->restoreHeight, kdfRounds);
|
wallet = manager->recoveryWallet(path, password, this->mnemonicSeed, "", type, this->restoreHeight, (quint64) kdfRounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
wallet->setPassword(password);
|
wallet->setPassword(password);
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
int setRestoreHeight() {
|
unsigned int setRestoreHeight() {
|
||||||
if(this->lookup == nullptr) return 1;
|
if(this->lookup == nullptr) return 1;
|
||||||
if(this->time == 0) return 1;
|
if(this->time == 0) return 1;
|
||||||
this->restoreHeight = this->lookup->dateToRestoreHeight(this->time);
|
this->restoreHeight = this->lookup->dateToRestoreHeight((unsigned int)this->time);
|
||||||
return this->restoreHeight;
|
return this->restoreHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
int setRestoreHeight(int height) {
|
unsigned int setRestoreHeight(unsigned int height) {
|
||||||
auto now = std::time(nullptr);
|
auto now = (unsigned int)std::time(nullptr);
|
||||||
auto nowClearance = 3600 * 24;
|
auto nowClearance = 3600 * 24;
|
||||||
auto currentBlockHeight = this->lookup->dateToRestoreHeight(now - nowClearance);
|
auto currentBlockHeight = this->lookup->dateToRestoreHeight(now - nowClearance);
|
||||||
if(height >= currentBlockHeight + nowClearance) {
|
if(height >= currentBlockHeight + nowClearance) {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ Tor::Tor(AppContext *ctx, QObject *parent)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef HAS_TOR_BIN
|
#ifndef HAS_TOR
|
||||||
qCritical() << "Feather built without embedded Tor. Assuming --use-local-tor";
|
qCritical() << "Feather built without embedded Tor. Assuming --use-local-tor";
|
||||||
this->localTor = true;
|
this->localTor = true;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include "txfiathistory.h"
|
#include "txfiathistory.h"
|
||||||
#include "utils/utils.h"
|
#include "utils/utils.h"
|
||||||
|
|
||||||
TxFiatHistory::TxFiatHistory(int genesis_timestamp, const QString &configDirectory, QObject *parent) :
|
TxFiatHistory::TxFiatHistory(unsigned int genesis_timestamp, const QString &configDirectory, QObject *parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
m_genesis_timestamp(genesis_timestamp),
|
m_genesis_timestamp(genesis_timestamp),
|
||||||
m_configDirectory(configDirectory) {
|
m_configDirectory(configDirectory) {
|
||||||
@@ -15,7 +15,7 @@ TxFiatHistory::TxFiatHistory(int genesis_timestamp, const QString &configDirecto
|
|||||||
this->loadDatabase();
|
this->loadDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
double TxFiatHistory::get(int timestamp) {
|
double TxFiatHistory::get(unsigned int timestamp) {
|
||||||
QDateTime ts;
|
QDateTime ts;
|
||||||
ts.setTime_t(timestamp);
|
ts.setTime_t(timestamp);
|
||||||
auto key = ts.toString("yyyyMMdd");
|
auto key = ts.toString("yyyyMMdd");
|
||||||
@@ -59,7 +59,7 @@ void TxFiatHistory::onUpdateDatabase() {
|
|||||||
|
|
||||||
auto now = QDate::currentDate();
|
auto now = QDate::currentDate();
|
||||||
auto nowKey = now.toString("yyyyMMdd");
|
auto nowKey = now.toString("yyyyMMdd");
|
||||||
int year = genesis.toString("yyyy").toInt();
|
unsigned int year = genesis.toString("yyyy").toUInt();
|
||||||
auto yearCurrent = now.year();
|
auto yearCurrent = now.year();
|
||||||
|
|
||||||
// if current year is genesis year we'll refresh regardless.
|
// if current year is genesis year we'll refresh regardless.
|
||||||
@@ -71,7 +71,7 @@ void TxFiatHistory::onUpdateDatabase() {
|
|||||||
|
|
||||||
// keep local fiatTxHistory database up to date, loop for missing dates
|
// keep local fiatTxHistory database up to date, loop for missing dates
|
||||||
for(year; year != yearCurrent + 1; year += 1){
|
for(year; year != yearCurrent + 1; year += 1){
|
||||||
for(int month = 1; month != 13; month++) {
|
for(unsigned int month = 1; month != 13; month++) {
|
||||||
if(year == yearCurrent && month == now.month() && now.day() == 1) break;
|
if(year == yearCurrent && month == now.month() && now.day() == 1) break;
|
||||||
QDateTime _now;
|
QDateTime _now;
|
||||||
_now.setDate(QDate(year, month, 1));
|
_now.setDate(QDate(year, month, 1));
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ class TxFiatHistory : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TxFiatHistory(int genesis_timestamp, const QString &configDirectory, QObject *parent = nullptr);
|
explicit TxFiatHistory(unsigned int genesis_timestamp, const QString &configDirectory, QObject *parent = nullptr);
|
||||||
double get(const QString &date);
|
double get(const QString &date);
|
||||||
double get(int timestamp);
|
double get(unsigned int timestamp);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void onUpdateDatabase();
|
void onUpdateDatabase();
|
||||||
void onWSData(const QJsonObject &data);
|
void onWSData(const QJsonObject &data);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void requestYear(int year);
|
void requestYear(unsigned int year);
|
||||||
void requestYearMonth(int year, int month);
|
void requestYearMonth(unsigned int year, unsigned int month);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadDatabase();
|
void loadDatabase();
|
||||||
@@ -25,7 +25,7 @@ private:
|
|||||||
QString m_configDirectory;
|
QString m_configDirectory;
|
||||||
bool m_initialized = false;
|
bool m_initialized = false;
|
||||||
QMap<QString, double> m_database;
|
QMap<QString, double> m_database;
|
||||||
int m_genesis_timestamp;
|
unsigned int m_genesis_timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FEATHER_TXFIATHISTORY_H
|
#endif //FEATHER_TXFIATHISTORY_H
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "utils/config.h"
|
#include "utils/config.h"
|
||||||
#include "utils/tails.h"
|
#include "utils/tails.h"
|
||||||
#include "utils/whonix.h"
|
|
||||||
|
|
||||||
// Application log for current session
|
// Application log for current session
|
||||||
QVector<logMessage> applicationLog = QVector<logMessage>(); // todo: replace with ring buffer
|
QVector<logMessage> applicationLog = QVector<logMessage>(); // todo: replace with ring buffer
|
||||||
@@ -277,7 +276,7 @@ bool Utils::testSocks5(const QString &host, quint16 port){
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::externalLinkWarning(QWidget *parent, const QString &url){
|
void Utils::externalLinkWarning(const QString &url){
|
||||||
if(!config()->get(Config::warnOnExternalLink).toBool()) {
|
if(!config()->get(Config::warnOnExternalLink).toBool()) {
|
||||||
QDesktopServices::openUrl(QUrl(url));
|
QDesktopServices::openUrl(QUrl(url));
|
||||||
return;
|
return;
|
||||||
@@ -285,11 +284,9 @@ void Utils::externalLinkWarning(QWidget *parent, const QString &url){
|
|||||||
|
|
||||||
QString body = "You are about to open the following link:\n\n";
|
QString body = "You are about to open the following link:\n\n";
|
||||||
body += QString("%1\n\n").arg(url);
|
body += QString("%1\n\n").arg(url);
|
||||||
|
body += "You will NOT be using Tor.";
|
||||||
|
|
||||||
if (!(TailsOS::detect() || WhonixOS::detect()))
|
switch (Utils::showMessageBox("External link warning", body, true)) {
|
||||||
body += "You will NOT be using Tor.";
|
|
||||||
|
|
||||||
switch (QMessageBox::warning(parent, "External link warning", body, QMessageBox::Cancel|QMessageBox::Ok)) {
|
|
||||||
case QMessageBox::Cancel:
|
case QMessageBox::Cancel:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -330,6 +327,26 @@ bool Utils::walletExists(QString name, const QString &path) {
|
|||||||
return Utils::fileExists(walletPath);
|
return Utils::fileExists(walletPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Utils::showMessageBox(const QString &windowTitle, const QString &body, bool warning){
|
||||||
|
QMessageBox msgBox(QApplication::activeWindow());
|
||||||
|
msgBox.setWindowTitle(windowTitle);
|
||||||
|
msgBox.setText(body);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
|
||||||
|
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||||
|
|
||||||
|
if(warning) {
|
||||||
|
QPixmap iconWarning = QPixmap(":/assets/images/ghost_icon.png");
|
||||||
|
msgBox.setIconPixmap(iconWarning);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
QPixmap iconInfo = QPixmap(":/assets/images/info.png")
|
||||||
|
.scaled(QSize(48,48), Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
msgBox.setIconPixmap(iconInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return msgBox.exec();
|
||||||
|
}
|
||||||
|
|
||||||
void Utils::copyToClipboard(const QString &string){
|
void Utils::copyToClipboard(const QString &string){
|
||||||
QClipboard * clipboard = QApplication::clipboard();
|
QClipboard * clipboard = QApplication::clipboard();
|
||||||
if (!clipboard) {
|
if (!clipboard) {
|
||||||
@@ -553,55 +570,30 @@ QString Utils::formatBytes(quint64 bytes)
|
|||||||
|
|
||||||
int i;
|
int i;
|
||||||
double _data;
|
double _data;
|
||||||
for (i = 0; i < sizes.count() && bytes >= 10000; i++, bytes /= 1000)
|
for (i = 0; i < sizes.count() && bytes >= 1000; i++, bytes /= 1000)
|
||||||
_data = bytes / 1000.0;
|
_data = bytes / 1000.0;
|
||||||
|
|
||||||
if (_data < 0)
|
if (_data < 0)
|
||||||
_data = 0;
|
_data = 0;
|
||||||
|
|
||||||
// unrealistic
|
// unrealistic
|
||||||
if (_data > 10000)
|
if (_data > 1000)
|
||||||
_data = 0;
|
_data = 0;
|
||||||
|
|
||||||
return QString("%1 %2").arg(QString::number(_data, 'f', 1), sizes[i]);
|
return QString("%1 %2").arg(QString::number(_data, 'f', 1), sizes[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QMap<QString, QLocale> Utils::localeCache = {};
|
|
||||||
|
|
||||||
QLocale Utils::getCurrencyLocale(const QString ¤cyCode) {
|
|
||||||
QLocale locale;
|
|
||||||
if (localeCache.contains(currencyCode)) {
|
|
||||||
locale = localeCache[currencyCode];
|
|
||||||
} else {
|
|
||||||
QList<QLocale> allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
|
|
||||||
for (const auto& locale_: allLocales) {
|
|
||||||
if (locale_.currencySymbol(QLocale::CurrencyIsoCode) == currencyCode) {
|
|
||||||
locale = locale_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
localeCache[currencyCode] = locale;
|
|
||||||
}
|
|
||||||
return locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Utils::amountToCurrencyString(double amount, const QString ¤cyCode) {
|
QString Utils::amountToCurrencyString(double amount, const QString ¤cyCode) {
|
||||||
QLocale locale = getCurrencyLocale(currencyCode);
|
QLocale locale;
|
||||||
|
QList<QLocale> allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
|
||||||
// \xC2\xA0 = UTF-8 non-breaking space, it looks off.
|
for (const auto& locale_: allLocales) {
|
||||||
if (currencyCode == "USD")
|
if (locale_.currencySymbol(QLocale::CurrencyIsoCode) == currencyCode) {
|
||||||
return locale.toCurrencyString(amount, "$").remove("\xC2\xA0");
|
locale = locale_;
|
||||||
|
|
||||||
return locale.toCurrencyString(amount).remove("\xC2\xA0");
|
|
||||||
}
|
|
||||||
|
|
||||||
int Utils::maxLength(const QVector<QString> &array) {
|
|
||||||
int maxLength = 0;
|
|
||||||
for (const auto &str: array) {
|
|
||||||
auto length = str.length();
|
|
||||||
if (length > maxLength) {
|
|
||||||
maxLength = length;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return maxLength;
|
|
||||||
}
|
if (currencyCode == "USD")
|
||||||
|
return locale.toCurrencyString(amount, "$");
|
||||||
|
|
||||||
|
return locale.toCurrencyString(amount);
|
||||||
|
}
|
||||||
@@ -75,7 +75,8 @@ public:
|
|||||||
static QStringList readJsonStringToQStringList(const QString &input);
|
static QStringList readJsonStringToQStringList(const QString &input);
|
||||||
static void applicationLogHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
static void applicationLogHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
||||||
static void openWindow(QWidget *w);
|
static void openWindow(QWidget *w);
|
||||||
static void externalLinkWarning(QWidget *parent, const QString &url);
|
static void externalLinkWarning(const QString &url);
|
||||||
|
static int showMessageBox(const QString &windowTitle, const QString &body, bool warning);
|
||||||
static QList<processStruct> procList();
|
static QList<processStruct> procList();
|
||||||
static bool dirExists(const QString &path);
|
static bool dirExists(const QString &path);
|
||||||
static QString barrayToString(const QByteArray &data);
|
static QString barrayToString(const QByteArray &data);
|
||||||
@@ -95,10 +96,9 @@ public:
|
|||||||
static QFont relativeFont(int delta);
|
static QFont relativeFont(int delta);
|
||||||
static double roundSignificant(double N, double n);
|
static double roundSignificant(double N, double n);
|
||||||
static QString formatBytes(quint64 bytes);
|
static QString formatBytes(quint64 bytes);
|
||||||
static QLocale getCurrencyLocale(const QString ¤cyCode);
|
|
||||||
static QString amountToCurrencyString(double amount, const QString ¤cyCode);
|
static QString amountToCurrencyString(double amount, const QString ¤cyCode);
|
||||||
static int maxLength(const QVector<QString> &array);
|
|
||||||
static QMap<QString, QLocale> localeCache;
|
static QStringList randomHTTPAgents;
|
||||||
|
|
||||||
template<typename QEnum>
|
template<typename QEnum>
|
||||||
static QString QtEnumToString (const QEnum value)
|
static QString QtEnumToString (const QEnum value)
|
||||||
|
|||||||
@@ -21,14 +21,6 @@ WSClient::WSClient(AppContext *ctx, const QUrl &url, QObject *parent) :
|
|||||||
connect(&this->webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &WSClient::onError);
|
connect(&this->webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &WSClient::onError);
|
||||||
|
|
||||||
m_tor = url.host().endsWith(".onion");
|
m_tor = url.host().endsWith(".onion");
|
||||||
|
|
||||||
// Keep websocket connection alive
|
|
||||||
connect(&m_pingTimer, &QTimer::timeout, [this]{
|
|
||||||
if (this->webSocket.state() == QAbstractSocket::ConnectedState)
|
|
||||||
this->webSocket.ping();
|
|
||||||
});
|
|
||||||
m_pingTimer.setInterval(30 * 1000);
|
|
||||||
m_pingTimer.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSClient::sendMsg(const QByteArray &data) {
|
void WSClient::sendMsg(const QByteArray &data) {
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ private slots:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QTimer m_connectionTimer;
|
QTimer m_connectionTimer;
|
||||||
QTimer m_pingTimer;
|
|
||||||
AppContext *m_ctx;
|
AppContext *m_ctx;
|
||||||
bool m_tor = true;
|
bool m_tor = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,13 +9,22 @@
|
|||||||
|
|
||||||
#include "utils/utils.h"
|
#include "utils/utils.h"
|
||||||
#include "utils/xmrig.h"
|
#include "utils/xmrig.h"
|
||||||
|
|
||||||
#include "appcontext.h"
|
#include "appcontext.h"
|
||||||
|
|
||||||
|
|
||||||
XmRig::XmRig(const QString &configDir, QObject *parent) : QObject(parent) {
|
XmRig::XmRig(const QString &configDir, QObject *parent) : QObject(parent) {
|
||||||
this->rigDir = QDir(configDir).filePath("xmrig");
|
this->rigDir = QDir(configDir).filePath("xmrig");
|
||||||
}
|
}
|
||||||
|
|
||||||
void XmRig::prepare() {
|
void XmRig::prepare() {
|
||||||
|
// unpack and set process signals
|
||||||
|
|
||||||
|
if(!this->unpackBins()) {
|
||||||
|
qCritical() << "failed to write XMRig to config directory";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_process.setProcessChannelMode(QProcess::MergedChannels);
|
m_process.setProcessChannelMode(QProcess::MergedChannels);
|
||||||
connect(&m_process, &QProcess::readyReadStandardOutput, this, &XmRig::handleProcessOutput);
|
connect(&m_process, &QProcess::readyReadStandardOutput, this, &XmRig::handleProcessOutput);
|
||||||
connect(&m_process, &QProcess::errorOccurred, this, &XmRig::handleProcessError);
|
connect(&m_process, &QProcess::errorOccurred, this, &XmRig::handleProcessError);
|
||||||
@@ -33,7 +42,7 @@ void XmRig::stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void XmRig::start(const QString &path,
|
void XmRig::start(const QString &path,
|
||||||
int threads,
|
unsigned int threads,
|
||||||
const QString &address,
|
const QString &address,
|
||||||
const QString &username,
|
const QString &username,
|
||||||
const QString &password,
|
const QString &password,
|
||||||
@@ -101,3 +110,29 @@ void XmRig::handleProcessError(QProcess::ProcessError err) {
|
|||||||
emit error(QString("XMRig binary failed to start: %1").arg(path));
|
emit error(QString("XMRig binary failed to start: %1").arg(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool XmRig::unpackBins() {
|
||||||
|
QString rigFile;
|
||||||
|
|
||||||
|
rigFile = ":/assets/exec/xmrig";
|
||||||
|
if (!Utils::fileExists(rigFile))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// write to disk
|
||||||
|
QFile f(rigFile);
|
||||||
|
QFileInfo fileInfo(f);
|
||||||
|
this->rigPath = QDir(this->rigDir).filePath(fileInfo.fileName());
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
if(!this->rigPath.endsWith(".exe"))
|
||||||
|
this->rigPath += ".exe";
|
||||||
|
#endif
|
||||||
|
qDebug() << "Writing XMRig executable to " << this->rigPath;
|
||||||
|
f.copy(rigPath);
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
#if defined(Q_OS_UNIX)
|
||||||
|
QFile rigBin(this->rigPath);
|
||||||
|
rigBin.setPermissions(QFile::ExeGroup | QFile::ExeOther | QFile::ExeOther | QFile::ExeUser);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public:
|
|||||||
explicit XmRig(const QString &configDir, QObject *parent = nullptr);
|
explicit XmRig(const QString &configDir, QObject *parent = nullptr);
|
||||||
void prepare();
|
void prepare();
|
||||||
|
|
||||||
void start(const QString &path, int threads, const QString &address, const QString &username, const QString &password, bool tor = false, bool tls = true);
|
void start(const QString &path, unsigned int threads, const QString &address, const QString &username, const QString &password, bool tor = false, bool tls = true);
|
||||||
void stop();
|
void stop();
|
||||||
bool unpackBins();
|
bool unpackBins();
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ CCSWidget::CCSWidget(QWidget *parent) :
|
|||||||
connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &CCSWidget::showContextMenu);
|
connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &CCSWidget::showContextMenu);
|
||||||
|
|
||||||
connect(ui->tableView, &QTableView::doubleClicked, this, &CCSWidget::linkClicked);
|
connect(ui->tableView, &QTableView::doubleClicked, this, &CCSWidget::linkClicked);
|
||||||
|
|
||||||
ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CCSModel* CCSWidget::model() {
|
CCSModel* CCSWidget::model() {
|
||||||
@@ -44,7 +42,7 @@ void CCSWidget::linkClicked() {
|
|||||||
auto entry = m_model->entry(index.row());
|
auto entry = m_model->entry(index.row());
|
||||||
|
|
||||||
if (entry)
|
if (entry)
|
||||||
Utils::externalLinkWarning(this, entry->url);
|
Utils::externalLinkWarning(entry->url);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCSWidget::donateClicked() {
|
void CCSWidget::donateClicked() {
|
||||||
|
|||||||
@@ -48,6 +48,39 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="warnLabel">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Powered by ccs.getmonero.org</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
|
</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>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
|||||||
@@ -18,10 +18,10 @@
|
|||||||
#include "utils/nodes.h"
|
#include "utils/nodes.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
NodeWidget::NodeWidget(QWidget *parent)
|
NodeWidget::NodeWidget(QWidget *parent) :
|
||||||
: QWidget(parent)
|
QWidget(parent),
|
||||||
, ui(new Ui::NodeWidget)
|
ui(new Ui::NodeWidget),
|
||||||
{
|
m_contextMenu(new QMenu(this)) {
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
connect(ui->btn_add_custom, &QPushButton::clicked, this, &NodeWidget::onCustomAddClicked);
|
connect(ui->btn_add_custom, &QPushButton::clicked, this, &NodeWidget::onCustomAddClicked);
|
||||||
@@ -35,91 +35,112 @@ NodeWidget::NodeWidget(QWidget *parent)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
m_contextActionRemove = new QAction("Remove", this);
|
|
||||||
m_contextActionConnect = new QAction(QIcon(":/assets/images/connect.svg"), "Connect to node", this);
|
|
||||||
m_contextActionOpenStatusURL = new QAction(QIcon(":/assets/images/network.png"), "Visit status page", this);
|
|
||||||
m_contextActionCopy = new QAction(QIcon(":/assets/images/copy.png"), "Copy", this);
|
|
||||||
connect(m_contextActionConnect, &QAction::triggered, this, &NodeWidget::onContextConnect);
|
|
||||||
connect(m_contextActionRemove, &QAction::triggered, this, &NodeWidget::onContextCustomNodeRemove);
|
|
||||||
connect(m_contextActionOpenStatusURL, &QAction::triggered, this, &NodeWidget::onContextStatusURL);
|
|
||||||
connect(m_contextActionCopy, &QAction::triggered, this, &NodeWidget::onContextNodeCopy);
|
|
||||||
connect(m_contextActionRemove, &QAction::triggered, this, &NodeWidget::onContextCustomNodeRemove);
|
|
||||||
|
|
||||||
ui->wsView->setContextMenuPolicy(Qt::CustomContextMenu);
|
ui->wsView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
ui->customView->setContextMenuPolicy(Qt::CustomContextMenu);
|
ui->customView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
connect(ui->wsView, &QTreeView::customContextMenuRequested, this, &NodeWidget::onShowWSContextMenu);
|
connect(ui->wsView, &QTreeView::customContextMenuRequested, this, &NodeWidget::onShowWSContextMenu);
|
||||||
connect(ui->customView, &QTreeView::customContextMenuRequested, this, &NodeWidget::onShowCustomContextMenu);
|
connect(ui->customView, &QTreeView::customContextMenuRequested, this, &NodeWidget::onShowCustomContextMenu);
|
||||||
|
|
||||||
connect(ui->customView, &QTreeView::doubleClicked, this, &NodeWidget::onContextConnect);
|
|
||||||
connect(ui->wsView, &QTreeView::doubleClicked, this, &NodeWidget::onContextConnect);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeWidget::onShowWSContextMenu(const QPoint &pos) {
|
void NodeWidget::onShowWSContextMenu(const QPoint &pos) {
|
||||||
m_activeView = ui->wsView;
|
QModelIndex index = ui->wsView->indexAt(pos);
|
||||||
FeatherNode node = this->selectedNode();
|
if (!index.isValid()) return;
|
||||||
if (node.full.isEmpty()) return;
|
|
||||||
|
|
||||||
|
FeatherNode node = m_wsModel->node(index.row());
|
||||||
this->showContextMenu(pos, node);
|
this->showContextMenu(pos, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeWidget::onShowCustomContextMenu(const QPoint &pos) {
|
void NodeWidget::onShowCustomContextMenu(const QPoint &pos) {
|
||||||
m_activeView = ui->customView;
|
QModelIndex index = ui->customView->indexAt(pos);
|
||||||
FeatherNode node = this->selectedNode();
|
if (!index.isValid()) return;
|
||||||
if (node.full.isEmpty()) return;
|
|
||||||
|
|
||||||
|
FeatherNode node = m_customModel->node(index.row());
|
||||||
this->showContextMenu(pos, node);
|
this->showContextMenu(pos, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeWidget::showContextMenu(const QPoint &pos, const FeatherNode &node) {
|
|
||||||
QMenu menu(this);
|
|
||||||
|
|
||||||
if (!node.isActive) {
|
void NodeWidget::showContextMenu(const QPoint &pos, const FeatherNode &node) {
|
||||||
menu.addAction(m_contextActionConnect);
|
bool custom = node.custom;
|
||||||
|
m_activeView = custom ? ui->customView : ui->wsView;
|
||||||
|
|
||||||
|
m_contextActionRemove = new QAction("Remove");
|
||||||
|
m_contextActionConnect = new QAction("Connect to node");
|
||||||
|
m_contextActionConnect->setIcon(QIcon(":/assets/images/connect.svg"));
|
||||||
|
m_contextActionOpenStatusURL = new QAction("Visit status page");
|
||||||
|
m_contextActionOpenStatusURL->setIcon(QIcon(":/assets/images/network.png"));
|
||||||
|
m_contextActionCopy = new QAction("Copy");
|
||||||
|
m_contextActionCopy->setIcon(QIcon(":/assets/images/copy.png"));
|
||||||
|
|
||||||
|
if(!node.isActive) {
|
||||||
|
connect(m_contextActionConnect, &QAction::triggered, this, &NodeWidget::onContextConnect);
|
||||||
|
m_contextMenu->addAction(m_contextActionConnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.addAction(m_contextActionOpenStatusURL);
|
m_contextMenu->addAction(m_contextActionOpenStatusURL);
|
||||||
menu.addAction(m_contextActionCopy);
|
m_contextMenu->addAction(m_contextActionCopy);
|
||||||
|
|
||||||
if (m_activeView == ui->customView)
|
if(custom) {
|
||||||
menu.addAction(m_contextActionRemove);
|
connect(m_contextActionRemove, &QAction::triggered, this, &NodeWidget::onContextCustomNodeRemove);
|
||||||
|
m_contextMenu->addAction(m_contextActionRemove);
|
||||||
|
|
||||||
menu.exec(m_activeView->viewport()->mapToGlobal(pos));
|
connect(m_contextActionOpenStatusURL, &QAction::triggered, this, &NodeWidget::onContextCustomStatusURL);
|
||||||
|
connect(m_contextActionCopy, &QAction::triggered, this, &NodeWidget::onContextCustomNodeCopy);
|
||||||
|
} else {
|
||||||
|
connect(m_contextActionOpenStatusURL, &QAction::triggered, this, &NodeWidget::onContextWSStatusURL);
|
||||||
|
connect(m_contextActionCopy, &QAction::triggered, this, &NodeWidget::onContextWSNodeCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_contextMenu->exec(m_activeView->viewport()->mapToGlobal(pos));
|
||||||
|
m_contextActionRemove->deleteLater();
|
||||||
|
m_contextActionConnect->deleteLater();
|
||||||
|
m_contextActionOpenStatusURL->deleteLater();
|
||||||
|
m_contextActionCopy->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeWidget::onContextConnect() {
|
void NodeWidget::onContextConnect() {
|
||||||
QObject *obj = sender();
|
QModelIndex index = m_activeView->currentIndex();
|
||||||
if (obj == ui->customView)
|
if (!index.isValid()) return;
|
||||||
m_activeView = ui->customView;
|
|
||||||
else
|
|
||||||
m_activeView = ui->wsView;
|
|
||||||
|
|
||||||
FeatherNode node = this->selectedNode();
|
if(m_activeView->objectName() == "wsView"){
|
||||||
if (!node.full.isEmpty())
|
FeatherNode node = m_wsModel->node(index.row());
|
||||||
emit connectToNode(node);
|
emit connectToNode(node);
|
||||||
|
} else {
|
||||||
|
FeatherNode node = m_customModel->node(index.row());
|
||||||
|
emit connectToNode(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeWidget::onContextStatusURL() {
|
void NodeWidget::onContextWSStatusURL() {
|
||||||
FeatherNode node = this->selectedNode();
|
QModelIndex index = ui->wsView->currentIndex();
|
||||||
if (!node.full.isEmpty())
|
if (!index.isValid()) return;
|
||||||
Utils::externalLinkWarning(this, node.as_url());
|
FeatherNode node = m_wsModel->node(index.row());
|
||||||
|
Utils::externalLinkWarning(node.as_url());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeWidget::onContextNodeCopy() {
|
void NodeWidget::onContextCustomStatusURL() {
|
||||||
FeatherNode node = this->selectedNode();
|
QModelIndex index = ui->customView->currentIndex();
|
||||||
|
if (!index.isValid()) return;
|
||||||
|
FeatherNode node = m_customModel->node(index.row());
|
||||||
|
Utils::externalLinkWarning(node.as_url());
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeWidget::onContextDisconnect() {
|
||||||
|
QModelIndex index = ui->customView->currentIndex();
|
||||||
|
if (!index.isValid()) return;
|
||||||
|
FeatherNode node = m_customModel->node(index.row());
|
||||||
|
|
||||||
Utils::copyToClipboard(node.full);
|
Utils::copyToClipboard(node.full);
|
||||||
}
|
}
|
||||||
|
|
||||||
FeatherNode NodeWidget::selectedNode() {
|
void NodeWidget::onContextWSNodeCopy() {
|
||||||
QModelIndex index = m_activeView->currentIndex();
|
QModelIndex index = ui->wsView->currentIndex();
|
||||||
if (!index.isValid()) return FeatherNode();
|
if (!index.isValid()) return;
|
||||||
|
FeatherNode node = m_wsModel->node(index.row());
|
||||||
|
|
||||||
|
Utils::copyToClipboard(node.full);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeWidget::onContextCustomNodeCopy() {
|
||||||
|
|
||||||
FeatherNode node;
|
|
||||||
if (m_activeView == ui->customView) {
|
|
||||||
node = m_customModel->node(index.row());
|
|
||||||
} else {
|
|
||||||
node = m_wsModel->node(index.row());
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeWidget::onContextCustomNodeRemove() {
|
void NodeWidget::onContextCustomNodeRemove() {
|
||||||
|
|||||||
@@ -35,9 +35,12 @@ public slots:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onContextConnect();
|
void onContextConnect();
|
||||||
|
void onContextDisconnect();
|
||||||
void onContextCustomNodeRemove();
|
void onContextCustomNodeRemove();
|
||||||
void onContextStatusURL();
|
void onContextWSStatusURL();
|
||||||
void onContextNodeCopy();
|
void onContextCustomStatusURL();
|
||||||
|
void onContextWSNodeCopy();
|
||||||
|
void onContextCustomNodeCopy();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void connectToNode(FeatherNode node);
|
void connectToNode(FeatherNode node);
|
||||||
@@ -51,13 +54,14 @@ private:
|
|||||||
|
|
||||||
QTreeView *m_activeView;
|
QTreeView *m_activeView;
|
||||||
|
|
||||||
|
QMenu *m_contextMenu;
|
||||||
QAction *m_contextActionConnect;
|
QAction *m_contextActionConnect;
|
||||||
|
QAction *m_contextActionDisconnect;
|
||||||
QAction *m_contextActionRemove;
|
QAction *m_contextActionRemove;
|
||||||
QAction *m_contextActionOpenStatusURL;
|
QAction *m_contextActionOpenStatusURL;
|
||||||
QAction *m_contextActionCopy;
|
QAction *m_contextActionCopy;
|
||||||
|
|
||||||
void showContextMenu(const QPoint &pos, const FeatherNode &node);
|
void showContextMenu(const QPoint &pos, const FeatherNode &node);
|
||||||
FeatherNode selectedNode();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NODEWIDGET_H
|
#endif // NODEWIDGET_H
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ RedditWidget::RedditWidget(QWidget *parent) :
|
|||||||
connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &RedditWidget::showContextMenu);
|
connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &RedditWidget::showContextMenu);
|
||||||
|
|
||||||
connect(ui->tableView, &QTableView::doubleClicked, this, &RedditWidget::linkClicked);
|
connect(ui->tableView, &QTableView::doubleClicked, this, &RedditWidget::linkClicked);
|
||||||
|
|
||||||
ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RedditModel* RedditWidget::model() {
|
RedditModel* RedditWidget::model() {
|
||||||
@@ -39,7 +37,7 @@ void RedditWidget::linkClicked() {
|
|||||||
auto post = m_model->post(index.row());
|
auto post = m_model->post(index.row());
|
||||||
|
|
||||||
if (post) {
|
if (post) {
|
||||||
Utils::externalLinkWarning(this, post->url);
|
Utils::externalLinkWarning(post->url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,36 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Powered by Reddit</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
|
</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>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
|||||||
@@ -17,10 +17,15 @@ RestoreHeightWidget::RestoreHeightWidget(QWidget *parent) :
|
|||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
ui->lineEdit_restoreHeight->setValidator(new QIntValidator(0, 2147483647, this));
|
ui->lineEdit_restoreHeight->setValidator(new QIntValidator(0, 2147483647, this));
|
||||||
|
connect(ui->lineEdit_restoreHeight, &QLineEdit::textEdited, [=](const QString &val){
|
||||||
connect(ui->lineEdit_restoreHeight, &QLineEdit::textEdited, [this](const QString &val){
|
// update slider on lineEdit change
|
||||||
if (val.isEmpty()) return;
|
if(val.isEmpty()) return;
|
||||||
this->setHeight(val.toInt());
|
auto height = val.toUInt();
|
||||||
|
if(height <= 1) return;
|
||||||
|
auto timestamp = m_restoreHeightLookup->restoreHeightToDate(height);
|
||||||
|
ui->restoreSlider->blockSignals(true);
|
||||||
|
ui->restoreSlider->setValue(timestamp);
|
||||||
|
ui->restoreSlider->blockSignals(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,45 +33,26 @@ void RestoreHeightWidget::hideSlider(){
|
|||||||
ui->restoreGrid->hide();
|
ui->restoreGrid->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestoreHeightWidget::setHeight(int height) {
|
|
||||||
if (height < 0)
|
|
||||||
height = 0;
|
|
||||||
|
|
||||||
// Update lineEdit
|
|
||||||
ui->lineEdit_restoreHeight->setText(QString::number(height));
|
|
||||||
|
|
||||||
// Update slider
|
|
||||||
int date = m_restoreHeightLookup->restoreHeightToDate(height);
|
|
||||||
ui->restoreSlider->setValue(date);
|
|
||||||
|
|
||||||
this->updateTimestamp(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestoreHeightWidget::initRestoreHeights(RestoreHeightLookup *lookup) {
|
void RestoreHeightWidget::initRestoreHeights(RestoreHeightLookup *lookup) {
|
||||||
// init slider
|
// init slider
|
||||||
m_restoreHeightLookup = lookup;
|
m_restoreHeightLookup = lookup;
|
||||||
int now = std::time(nullptr);
|
auto now = (unsigned int)std::time(nullptr);
|
||||||
QList<int> blockDates = m_restoreHeightLookup->data.keys();
|
QList<unsigned int> blockDates = m_restoreHeightLookup->data.keys();
|
||||||
ui->restoreSlider->setMinimum(blockDates[0]);
|
ui->restoreSlider->setMinimum(blockDates[0]);
|
||||||
ui->restoreSlider->setMaximum(now);
|
ui->restoreSlider->setMaximum(now);
|
||||||
|
connect(ui->restoreSlider, &QSlider::valueChanged, this, &RestoreHeightWidget::onValueChanged);
|
||||||
connect(ui->restoreSlider, &QSlider::sliderMoved, [this](int date){
|
|
||||||
// Update lineEdit
|
|
||||||
int blockHeight = m_restoreHeightLookup->dateToRestoreHeight(date);
|
|
||||||
ui->lineEdit_restoreHeight->setText(QString::number(blockHeight));
|
|
||||||
|
|
||||||
this->updateTimestamp(date);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestoreHeightWidget::updateTimestamp(int date) {
|
void RestoreHeightWidget::onValueChanged(int date) {
|
||||||
QDateTime timestamp;
|
QDateTime timestamp;
|
||||||
timestamp.setTime_t(date);
|
timestamp.setTime_t((unsigned int) date);
|
||||||
ui->label_restoreHeightDate->setText(timestamp.toString("yyyy-MM-dd"));
|
ui->label_restoreHeightDate->setText(timestamp.toString("yyyy-MM-dd"));
|
||||||
|
auto blockHeight = m_restoreHeightLookup->dateToRestoreHeight((unsigned int) date);
|
||||||
|
ui->lineEdit_restoreHeight->setText(QString::number(blockHeight));
|
||||||
}
|
}
|
||||||
|
|
||||||
int RestoreHeightWidget::getHeight() {
|
unsigned int RestoreHeightWidget::getHeight() {
|
||||||
return ui->lineEdit_restoreHeight->text().toInt();
|
return ui->lineEdit_restoreHeight->text().toUInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
RestoreHeightWidget::~RestoreHeightWidget() {
|
RestoreHeightWidget::~RestoreHeightWidget() {
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ class RestoreHeightWidget : public QWidget
|
|||||||
public:
|
public:
|
||||||
explicit RestoreHeightWidget(QWidget *parent = nullptr);
|
explicit RestoreHeightWidget(QWidget *parent = nullptr);
|
||||||
void initRestoreHeights(RestoreHeightLookup *lookup);
|
void initRestoreHeights(RestoreHeightLookup *lookup);
|
||||||
void setHeight(int height);
|
unsigned int getHeight();
|
||||||
int getHeight();
|
|
||||||
void hideSlider();
|
void hideSlider();
|
||||||
~RestoreHeightWidget() override;
|
~RestoreHeightWidget();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onValueChanged(int date);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateTimestamp(int date);
|
|
||||||
|
|
||||||
RestoreHeightLookup *m_restoreHeightLookup = nullptr;
|
RestoreHeightLookup *m_restoreHeightLookup = nullptr;
|
||||||
Ui::RestoreHeightWidget *ui;
|
Ui::RestoreHeightWidget *ui;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,12 +8,11 @@
|
|||||||
#include "utils/config.h"
|
#include "utils/config.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
TickerWidget::TickerWidget(QWidget *parent, QString symbol, QString title, bool convertBalance, bool hidePercent) :
|
TickerWidget::TickerWidget(QWidget *parent, QString symbol, QString title, bool convertBalance) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
ui(new Ui::TickerWidget),
|
ui(new Ui::TickerWidget),
|
||||||
m_symbol(std::move(symbol)),
|
m_symbol(std::move(symbol)),
|
||||||
m_convertBalance(convertBalance),
|
m_convertBalance(convertBalance)
|
||||||
m_hidePercent(hidePercent)
|
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
m_ctx = MainWindow::getContext();
|
m_ctx = MainWindow::getContext();
|
||||||
@@ -28,8 +27,6 @@ TickerWidget::TickerWidget(QWidget *parent, QString symbol, QString title, bool
|
|||||||
this->setPctText(defaultPct, true);
|
this->setPctText(defaultPct, true);
|
||||||
this->setFiatText(defaultFiat, 0.0);
|
this->setFiatText(defaultFiat, 0.0);
|
||||||
|
|
||||||
ui->tickerPct->setHidden(hidePercent);
|
|
||||||
|
|
||||||
connect(AppContext::prices, &Prices::fiatPricesUpdated, this, &TickerWidget::init);
|
connect(AppContext::prices, &Prices::fiatPricesUpdated, this, &TickerWidget::init);
|
||||||
connect(AppContext::prices, &Prices::cryptoPricesUpdated, this, &TickerWidget::init);
|
connect(AppContext::prices, &Prices::cryptoPricesUpdated, this, &TickerWidget::init);
|
||||||
if (convertBalance)
|
if (convertBalance)
|
||||||
@@ -50,19 +47,15 @@ void TickerWidget::init() {
|
|||||||
double amount = m_convertBalance ? AppContext::balance : 1.0;
|
double amount = m_convertBalance ? AppContext::balance : 1.0;
|
||||||
double conversion = AppContext::prices->convert(m_symbol, fiatCurrency, amount);
|
double conversion = AppContext::prices->convert(m_symbol, fiatCurrency, amount);
|
||||||
if (conversion < 0) return;
|
if (conversion < 0) return;
|
||||||
|
setPercentHidden(conversion == 0);
|
||||||
|
|
||||||
auto markets = AppContext::prices->markets;
|
auto markets = AppContext::prices->markets;
|
||||||
if(!markets.contains(m_symbol)) return;
|
if(!markets.contains(m_symbol)) return;
|
||||||
|
|
||||||
bool hidePercent = (conversion == 0 || m_hidePercent);
|
auto pct24h = markets[m_symbol].price_usd_change_pct_24h;
|
||||||
if (hidePercent) {
|
auto pct24hText = QString::number(pct24h, 'f', 2);
|
||||||
ui->tickerPct->hide();
|
|
||||||
} else {
|
|
||||||
auto pct24h = markets[m_symbol].price_usd_change_pct_24h;
|
|
||||||
auto pct24hText = QString::number(pct24h, 'f', 2);
|
|
||||||
this->setPctText(pct24hText, pct24h >= 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
this->setPctText(pct24hText, pct24h >= 0.0);
|
||||||
this->setFiatText(fiatCurrency, conversion);
|
this->setFiatText(fiatCurrency, conversion);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +81,14 @@ void TickerWidget::setFontSizes() {
|
|||||||
ui->tickerFiat->setFont(Utils::relativeFont(0));
|
ui->tickerFiat->setFont(Utils::relativeFont(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TickerWidget::removePctContainer() {
|
||||||
|
ui->tickerPct->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TickerWidget::setPercentHidden(bool hidden) {
|
||||||
|
ui->tickerPct->setVisible(!hidden);
|
||||||
|
}
|
||||||
|
|
||||||
TickerWidget::~TickerWidget() {
|
TickerWidget::~TickerWidget() {
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ class TickerWidget : public QWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TickerWidget(QWidget *parent, QString symbol, QString title = "", bool convertBalance = false, bool hidePercent = false);
|
explicit TickerWidget(QWidget *parent, QString symbol, QString title = "", bool convertBalance = false);
|
||||||
|
void removePctContainer();
|
||||||
void setFiatText(QString &fiatCurrency, double amount);
|
void setFiatText(QString &fiatCurrency, double amount);
|
||||||
void setPctText(QString &text, bool positive);
|
void setPctText(QString &text, bool positive);
|
||||||
void setFontSizes();
|
void setFontSizes();
|
||||||
@@ -28,10 +29,11 @@ public slots:
|
|||||||
void init();
|
void init();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void setPercentHidden(bool hidden);
|
||||||
|
|
||||||
Ui::TickerWidget *ui;
|
Ui::TickerWidget *ui;
|
||||||
QString m_symbol;
|
QString m_symbol;
|
||||||
bool m_convertBalance;
|
bool m_convertBalance;
|
||||||
bool m_hidePercent;
|
|
||||||
AppContext *m_ctx;
|
AppContext *m_ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||