diff --git a/BasicPanel.qml b/BasicPanel.qml index 07aeb080..12a257bf 100644 --- a/BasicPanel.qml +++ b/BasicPanel.qml @@ -31,6 +31,8 @@ import QtGraphicalEffects 1.0 import "components" import "pages" +// mbg033 @ 2016-10-08: Not used anymore, to be deleted + Rectangle { id: root width: 470 diff --git a/LeftPanel.qml b/LeftPanel.qml index 851db047..7209a6dc 100644 --- a/LeftPanel.qml +++ b/LeftPanel.qml @@ -36,6 +36,7 @@ Rectangle { property alias unlockedBalanceText: unlockedBalanceText.text property alias balanceText: balanceText.text property alias networkStatus : networkStatus + property alias daemonProgress : daemonProgress signal dashboardClicked() signal historyClicked() @@ -252,17 +253,17 @@ Rectangle { panel.receiveClicked() } } - /* + Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.leftMargin: 16 color: transferButton.checked || historyButton.checked ? "#1C1C1C" : "#505050" height: 1 - }*/ + } // ------------- History tab --------------- - /* + MenuButton { id: historyButton anchors.left: parent.left @@ -276,7 +277,7 @@ Rectangle { panel.historyClicked() } } - + /* Rectangle { anchors.left: parent.left anchors.right: parent.right @@ -330,7 +331,7 @@ Rectangle { color: miningButton.checked || settingsButton.checked ? "#1C1C1C" : "#505050" height: 1 } - + */ // ------------- Settings tab --------------- MenuButton { id: settingsButton @@ -345,16 +346,23 @@ Rectangle { panel.settingsClicked() } } - */ + } NetworkStatusItem { id: networkStatus anchors.left: parent.left anchors.right: parent.right - anchors.bottom: parent.bottom + anchors.bottom: (daemonProgress.visible)? daemonProgress.top : parent.bottom; connected: false } + + DaemonProgress { + id: daemonProgress + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + } } // indicate disabled state Desaturate { diff --git a/MiddlePanel.qml b/MiddlePanel.qml index a2cabc1c..9160a359 100644 --- a/MiddlePanel.qml +++ b/MiddlePanel.qml @@ -27,44 +27,103 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import QtQuick 2.2 +import QtQml 2.0 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.1 import QtGraphicalEffects 1.0 +import "pages" Rectangle { id: root - color: "#F0EEEE" + + property Item currentView + property bool basicMode : false + property string balanceText + property string unlockedBalanceText + + property Transfer transferView: Transfer { } + property Receive receiveView: Receive { } + property History historyView: History { } + property Settings settingsView: Settings { } + + signal paymentClicked(string address, string paymentId, double amount, int mixinCount, int priority) signal generatePaymentIdInvoked() - states: [ - State { - name: "Dashboard" - PropertyChanges { target: loader; source: "pages/Dashboard.qml" } - }, State { - name: "History" - PropertyChanges { target: loader; source: "pages/History.qml" } - }, State { - name: "Transfer" - PropertyChanges { target: loader; source: "pages/Transfer.qml" } - }, State { - name: "Receive" - PropertyChanges { target: loader; source: "pages/Receive.qml" } - }, State { - name: "AddressBook" - PropertyChanges { target: loader; source: "pages/AddressBook.qml" } - }, State { - name: "Settings" - PropertyChanges { target: loader; source: "pages/Settings.qml" } - }, State { - name: "Mining" - PropertyChanges { target: loader; source: "pages/Mining.qml" } - } - ] + color: "#F0EEEE" + onCurrentViewChanged: { + if (currentView) { + stackView.replace(currentView) + + // Component.onCompleted is called before wallet is initilized + if (typeof currentView.onPageCompleted === "function") { + currentView.onPageCompleted(); + } + } + } + + + // XXX: just for memo, to be removed + // states: [ + // State { + // name: "Dashboard" + // PropertyChanges { target: loader; source: "pages/Dashboard.qml" } + // }, State { + // name: "History" + // PropertyChanges { target: loader; source: "pages/History.qml" } + // }, State { + // name: "Transfer" + // PropertyChanges { target: loader; source: "pages/Transfer.qml" } + // }, State { + // name: "Receive" + // PropertyChanges { target: loader; source: "pages/Receive.qml" } + // }, State { + // name: "AddressBook" + // PropertyChanges { target: loader; source: "pages/AddressBook.qml" } + // }, State { + // name: "Settings" + // PropertyChanges { target: loader; source: "pages/Settings.qml" } + // }, State { + // name: "Mining" + // PropertyChanges { target: loader; source: "pages/Mining.qml" } + // } + // ] + + states: [ + State { + name: "Dashboard" + PropertyChanges { } + }, State { + name: "History" + PropertyChanges { target: root; currentView: historyView } + PropertyChanges { target: historyView; model: appWindow.currentWallet.historyModel } + }, State { + name: "Transfer" + PropertyChanges { target: root; currentView: transferView } + }, State { + name: "Receive" + PropertyChanges { target: root; currentView: receiveView } + }, State { + name: "AddressBook" + PropertyChanges { /*TODO*/ } + }, State { + name: "Settings" + PropertyChanges { target: root; currentView: settingsView } + }, State { + name: "Mining" + PropertyChanges { /*TODO*/ } + } + ] + + // color stripe at the top Row { id: styledRow + height: 4 + anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right - anchors.top: parent.top + Rectangle { height: 4; width: parent.width / 5; color: "#FFE00A" } Rectangle { height: 4; width: parent.width / 5; color: "#6B0072" } @@ -73,28 +132,127 @@ Rectangle { Rectangle { height: 4; width: parent.width / 5; color: "#FF4F41" } } - Loader { - id: loader - anchors.left: parent.left - anchors.right: parent.right - anchors.top: styledRow.bottom - anchors.bottom: parent.bottom - onLoaded: { - console.log("Loaded " + item); + ColumnLayout { + anchors.fill: parent + anchors.margins: 2 + anchors.topMargin: 30 + spacing: 0 + + + // BasicPanel header + Rectangle { + id: header + anchors.leftMargin: 1 + anchors.rightMargin: 1 + Layout.fillWidth: true + Layout.preferredHeight: 64 + color: "#FFFFFF" + visible: basicMode + + Image { + id: logo + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: -5 + anchors.left: parent.left + anchors.leftMargin: 20 + source: "images/moneroLogo2.png" + } + + Grid { + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + width: 256 + columns: 3 + + Text { + + width: 116 + height: 20 + font.family: "Arial" + font.pixelSize: 12 + font.letterSpacing: -1 + elide: Text.ElideRight + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignBottom + color: "#535353" + text: qsTr("Balance:") + } + + Text { + id: balanceText + width: 110 + height: 20 + font.family: "Arial" + font.pixelSize: 18 + font.letterSpacing: -1 + elide: Text.ElideRight + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignBottom + color: "#000000" + text: root.balanceText + } + + Item { + height: 20 + width: 20 + + Image { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + source: "images/lockIcon.png" + } + } + + Text { + width: 116 + height: 20 + font.family: "Arial" + font.pixelSize: 12 + font.letterSpacing: -1 + elide: Text.ElideRight + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignBottom + color: "#535353" + text: qsTr("Unlocked Balance:") + } + + Text { + id: availableBalanceText + width: 110 + height: 20 + font.family: "Arial" + font.pixelSize: 14 + font.letterSpacing: -1 + elide: Text.ElideRight + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignBottom + color: "#000000" + text: root.unlockedBalanceText + } + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: 1 + color: "#DBDBDB" + } } - } - - /* connect "payment" click */ - Connections { - ignoreUnknownSignals: false - target: loader.item - onPaymentClicked : { - console.log("MiddlePanel: paymentClicked") - paymentClicked(address, paymentId, amount, mixinCount, priority) + // Views container + StackView { + id: stackView + initialItem: transferView + anchors.topMargin: 30 + Layout.fillWidth: true + Layout.fillHeight: true + anchors.top: styledRow.bottom + anchors.margins: 4 + clip: true // otherwise animation will affect left panel } } - + // border Rectangle { anchors.top: styledRow.bottom anchors.bottom: parent.bottom @@ -117,12 +275,25 @@ Rectangle { anchors.bottom: parent.bottom height: 1 color: "#DBDBDB" + } - // indicate disabled state + + // indicates disabled state Desaturate { anchors.fill: parent source: parent desaturation: root.enabled ? 0.0 : 1.0 } + + + /* connect "payment" click */ + Connections { + ignoreUnknownSignals: false + target: transferView + onPaymentClicked : { + console.log("MiddlePanel: paymentClicked") + paymentClicked(address, paymentId, amount, mixinCount, priority) + } + } } diff --git a/build_libwallet_api.sh b/build_libwallet_api.sh new file mode 100755 index 00000000..832b48d2 --- /dev/null +++ b/build_libwallet_api.sh @@ -0,0 +1,52 @@ +#!/bin/bash + + +# MONERO_URL=https://github.com/monero-project/monero.git +# MONERO_BRANCH=master +CPU_CORE_COUNT=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || sysctl -n hw.ncpu) +pushd $(pwd) +ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +source $ROOT_DIR/utils.sh + + +INSTALL_DIR=$ROOT_DIR/wallet +MONERO_DIR=$ROOT_DIR/monero + + +mkdir -p $MONERO_DIR/build/release +pushd $MONERO_DIR/build/release + +# reusing function from "utils.sh" +platform=$(get_platform) + +pushd $MONERO_DIR/build/release/src/wallet +make -j$CPU_CORE_COUNT +make install -j$CPU_CORE_COUNT +popd + +# unbound is one more dependency. can't be merged to the wallet_merged +# since filename conflict (random.c.obj) +# for Linux, we use libunbound shipped with the system, so we don't need to build it + +if [ "$platform" != "linux" ]; then + echo "Building libunbound..." + pushd $MONERO_DIR/build/release/external/unbound + # no need to make, it was already built as dependency for libwallet + # make -j$CPU_CORE_COUNT + make install -j$CPU_CORE_COUNT + popd +fi + +popd + + + + + + + + + + + diff --git a/components/AddressBookTable.qml b/components/AddressBookTable.qml index 756445f7..2ad9d2ff 100644 --- a/components/AddressBookTable.qml +++ b/components/AddressBookTable.qml @@ -27,7 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import QtQuick 2.0 -import moneroComponents 1.0 +import moneroComponents.Clipboard 1.0 ListView { id: listView diff --git a/components/DaemonProgress.qml b/components/DaemonProgress.qml new file mode 100644 index 00000000..1ed533e3 --- /dev/null +++ b/components/DaemonProgress.qml @@ -0,0 +1,90 @@ +// Copyright (c) 2014-2015, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import QtQuick 2.0 + +Item { + id: item + property int fillLevel: 0 + height: 44 + anchors.margins: 10 + visible: false + //clip: true + + function updateProgress(currentBlock,targetBlock){ + if(targetBlock > 0) { + var progressLevel = ((currentBlock/targetBlock) * 100).toFixed(0); + fillLevel = progressLevel + console.log("target block: ",progressLevel) + progressText.text = qsTr("Synchronizing blocks %1/%2").arg(currentBlock.toFixed(0)).arg(targetBlock.toFixed(0)); + console.log("Progress text: " + progressText.text); + + // TODO: lower daemon block height cache, ttl and refresh interval? + + item.visible = (currentBlock < targetBlock) + + } + } + + Rectangle { + id: bar + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + height: 18 + //radius: 4 + color: "#FFFFFF" + + Rectangle { + id: fillRect + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.margins: 2 + property int maxWidth: parent.width - 4 + width: (maxWidth * fillLevel) / 100 + + color: { + if(item.fillLevel < 99) return "#FF6C3C" + //if(item.fillLevel < 99) return "#FFE00A" + return "#36B25C" + } + + } + } + + Text { + id:progressText + anchors.bottom: parent.bottom + font.family: "Arial" + font.pixelSize: 12 + color: "#545454" + text: qsTr("Synchronizing blocks") + } + +} diff --git a/components/DashboardTable.qml b/components/DashboardTable.qml index 05b23c6e..1dc0d3b4 100644 --- a/components/DashboardTable.qml +++ b/components/DashboardTable.qml @@ -27,7 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import QtQuick 2.0 -import moneroComponents 1.0 +import moneroComponents.Clipboard 1.0 ListView { id: listView diff --git a/components/DatePicker.qml b/components/DatePicker.qml index 442ecdb0..9d0cbf20 100644 --- a/components/DatePicker.qml +++ b/components/DatePicker.qml @@ -33,12 +33,13 @@ import QtQuick.Controls.Styles 1.2 Item { id: datePicker property bool expanded: false - property var currentDate: new Date() + property date currentDate property bool showCurrentDate: true height: 37 width: 156 onExpandedChanged: if(expanded) appWindow.currentItem = datePicker + function hide() { datePicker.expanded = false } function containsPoint(px, py) { if(px < 0) @@ -121,12 +122,29 @@ Item { } Row { + id: dateInput anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: 10 + function setDate(date) { + var day = date.getDate() + var month = date.getMonth() + 1 + dayInput.text = day < 10 ? "0" + day : day + monthInput.text = month < 10 ? "0" + month : month + yearInput.text = date.getFullYear() + } + + Connections { + target: datePicker + onCurrentDateChanged: { + dateInput.setDate(datePicker.currentDate) + } + } + TextInput { id: dayInput + readOnly: true width: 22 font.family: "Arial" font.pixelSize: 18 @@ -135,6 +153,7 @@ Item { horizontalAlignment: TextInput.AlignHCenter validator: IntValidator{bottom: 01; top: 31;} KeyNavigation.tab: monthInput + text: { if(datePicker.showCurrentDate) { var day = datePicker.currentDate.getDate() @@ -158,6 +177,7 @@ Item { TextInput { id: monthInput + readOnly: true width: 22 font.family: "Arial" font.pixelSize: 18 @@ -277,12 +297,7 @@ Item { anchors.fill: parent onClicked: { if(styleData.visibleMonth) { - var date = styleData.date - var day = date.getDate() - var month = date.getMonth() + 1 - dayInput.text = day < 10 ? "0" + day : day - monthInput.text = month < 10 ? "0" + month : month - yearInput.text = date.getFullYear() + currentDate = styleData.date datePicker.expanded = false } else { var date = styleData.date diff --git a/components/HistoryTable.qml b/components/HistoryTable.qml index db92aa69..867fdedf 100644 --- a/components/HistoryTable.qml +++ b/components/HistoryTable.qml @@ -27,12 +27,14 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import QtQuick 2.0 -import moneroComponents 1.0 +import moneroComponents.Clipboard 1.0 + ListView { id: listView clip: true boundsBehavior: ListView.StopAtBounds + property var previousItem footer: Rectangle { height: 127 @@ -48,7 +50,7 @@ ListView { } } - property var previousItem + delegate: Rectangle { id: delegate height: 114 @@ -63,13 +65,13 @@ ListView { anchors.right: parent.right anchors.top: parent.top anchors.topMargin: 14 - + // -- direction indicator Rectangle { id: dot width: 14 height: width radius: width / 2 - color: out ? "#FF4F41" : "#36B05B" + color: isOut ? "#FF4F41" : "#36B05B" } Item { //separator @@ -77,6 +79,8 @@ ListView { height: 14 } + // -- description aka recepient name from address book (TODO) + /* Text { id: descriptionText width: text.length ? (descriptionArea.containsMouse ? parent.width - x - 12 : 120) : 0 @@ -94,13 +98,15 @@ ListView { hoverEnabled: true } } - + */ + /* Item { //separator width: descriptionText.width ? 12 : 0 height: 14 visible: !descriptionArea.containsMouse } - + */ + // -- address (in case outgoing transaction) - N/A in case of incoming Text { id: addressText anchors.verticalCenter: dot.verticalCenter @@ -109,11 +115,11 @@ ListView { font.family: "Arial" font.pixelSize: 14 color: "#545454" - text: address - visible: !descriptionArea.containsMouse + text: hash + // visible: !descriptionArea.containsMouse } } - + // -- "PaymentID" title Text { id: paymentLabel anchors.left: parent.left @@ -128,7 +134,7 @@ ListView { color: "#535353" text: paymentId !== "" ? qsTr("Payment ID:") + translationManager.emptyString : "" } - + // -- "PaymentID" value Text { anchors.bottom: paymentLabel.bottom anchors.left: paymentLabel.right @@ -143,7 +149,7 @@ ListView { color: "#545454" text: paymentId } - + // -- "Date", "Balance" and "Amound" section Row { anchors.left: parent.left anchors.bottom: parent.bottom @@ -155,6 +161,7 @@ ListView { height: 14 } + // -- "Date" column Column { anchors.top: parent.top width: 215 @@ -189,10 +196,13 @@ ListView { } } } - + // -- "Balance" column + // XXX: we don't have a balance + /* Column { anchors.top: parent.top width: 148 + visible: false Text { anchors.left: parent.left @@ -210,7 +220,9 @@ ListView { text: balance } } + */ + // -- "Amount column Column { anchors.top: parent.top width: 148 @@ -230,8 +242,8 @@ ListView { anchors.bottomMargin: 3 font.family: "Arial" font.pixelSize: 16 - color: out ? "#FF4F41" : "#36B05B" - text: out ? "↓" : "↑" + color: isOut ? "#FF4F41" : "#36B05B" + text: isOut ? "↓" : "↑" } Text { @@ -239,22 +251,44 @@ ListView { font.family: "Arial" font.pixelSize: 18 font.letterSpacing: -1 - color: out ? "#FF4F41" : "#36B05B" - text: amount + color: isOut ? "#FF4F41" : "#36B05B" + text: displayAmount + } + } + } + + // -- "Fee column + Column { + anchors.top: parent.top + width: 148 + visible: isOut + Text { + anchors.left: parent.left + font.family: "Arial" + font.pixelSize: 12 + color: "#545454" + text: qsTr("Fee") + translationManager.emptyString + } + + Row { + spacing: 2 + Text { + anchors.bottom: parent.bottom + font.family: "Arial" + font.pixelSize: 18 + font.letterSpacing: -1 + color: "#FF4F41" + text: fee } } } } - ListModel { - id: dropModel - ListElement { name: "Copy address to clipboard"; icon: "../images/dropdownCopy.png" } - ListElement { name: "Add to address book"; icon: "../images/dropdownAdd.png" } - ListElement { name: "Send to same destination"; icon: "../images/dropdownSend.png" } - ListElement { name: "Find similar transactions"; icon: "../images/dropdownSearch.png" } - } - Clipboard { id: clipboard } + + /* + // Transaction dropdown menu. + // Disable for now until AddressBook implemented TableDropdown { id: dropdown anchors.right: parent.right @@ -282,5 +316,16 @@ ListView { height: 1 color: "#DBDBDB" } + */ } + + ListModel { + id: dropModel + ListElement { name: "Copy address to clipboard"; icon: "../images/dropdownCopy.png" } + ListElement { name: "Add to address book"; icon: "../images/dropdownAdd.png" } + ListElement { name: "Send to same destination"; icon: "../images/dropdownSend.png" } + ListElement { name: "Find similar transactions"; icon: "../images/dropdownSearch.png" } + } + + Clipboard { id: clipboard } } diff --git a/components/LineEdit.qml b/components/LineEdit.qml index 81786881..2ebc6e0c 100644 --- a/components/LineEdit.qml +++ b/components/LineEdit.qml @@ -33,6 +33,7 @@ Item { property alias text: input.text property alias validator: input.validator property alias readOnly : input.readOnly + property alias cursorPosition: input.cursorPosition property int fontSize: 18 @@ -56,7 +57,7 @@ Item { id: input anchors.fill: parent anchors.leftMargin: 4 - anchors.rightMargin: 4 + anchors.rightMargin: 30 font.pixelSize: parent.fontSize } } diff --git a/components/PasswordDialog.qml b/components/PasswordDialog.qml index a8ff294a..9edf87fc 100644 --- a/components/PasswordDialog.qml +++ b/components/PasswordDialog.qml @@ -40,7 +40,6 @@ Dialog { standardButtons: StandardButton.Ok + StandardButton.Cancel ColumnLayout { id: column - height: 40 anchors.fill: parent Label { diff --git a/components/ProcessingSplash.qml b/components/ProcessingSplash.qml index 297631ec..84218f88 100644 --- a/components/ProcessingSplash.qml +++ b/components/ProcessingSplash.qml @@ -43,8 +43,13 @@ Window { opacity: 0.7 ColumnLayout { + id: rootLayout anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 30 + anchors.rightMargin: 30 BusyIndicator { running: parent.visible @@ -59,6 +64,7 @@ Window { } horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + Layout.fillWidth: true } @@ -69,6 +75,7 @@ Window { } horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + Layout.fillWidth: true } } } diff --git a/get_libwallet_api.sh b/get_libwallet_api.sh index 875ca2e9..fbba689a 100755 --- a/get_libwallet_api.sh +++ b/get_libwallet_api.sh @@ -5,6 +5,11 @@ MONERO_URL=https://github.com/monero-project/monero.git MONERO_BRANCH=master # MONERO_URL=https://github.com/mbg033/monero.git # MONERO_BRANCH=develop +# Buidling "debug" build optionally +BUILD_TYPE=$1 +if [ -z $BUILD_TYPE ]; then + BUILD_TYPE=Release +fi # thanks to SO: http://stackoverflow.com/a/20283965/4118915 CPU_CORE_COUNT=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || sysctl -n hw.ncpu) pushd $(pwd) @@ -39,19 +44,19 @@ platform=$(get_platform) if [ "$platform" == "darwin" ]; then # Do something under Mac OS X platform echo "Configuring build for MacOS.." - cmake -D CMAKE_BUILD_TYPE=Release -D STATIC=ON -D BUILD_GUI_DEPS=ON -D INSTALL_VENDORED_LIBUNBOUND=ON -D CMAKE_INSTALL_PREFIX="$MONERO_DIR" ../.. + cmake -D CMAKE_BUILD_TYPE=$BUILD_TYPE -D STATIC=ON -D BUILD_GUI_DEPS=ON -D INSTALL_VENDORED_LIBUNBOUND=ON -D CMAKE_INSTALL_PREFIX="$MONERO_DIR" ../.. elif [ "$platform" == "linux" ]; then # Do something under GNU/Linux platform echo "Configuring build for Linux.." - cmake -D CMAKE_BUILD_TYPE=Release -D STATIC=ON -D BUILD_GUI_DEPS=ON -D CMAKE_INSTALL_PREFIX="$MONERO_DIR" ../.. + cmake -D CMAKE_BUILD_TYPE=$BUILD_TYPE -D STATIC=ON -D BUILD_GUI_DEPS=ON -D CMAKE_INSTALL_PREFIX="$MONERO_DIR" ../.. elif [ "$platform" == "mingw64" ]; then # Do something under Windows NT platform echo "Configuring build for MINGW64.." - cmake -D CMAKE_BUILD_TYPE=Release -D STATIC=ON -D BUILD_GUI_DEPS=ON -D INSTALL_VENDORED_LIBUNBOUND=ON -D CMAKE_INSTALL_PREFIX="$MONERO_DIR" -G "MSYS Makefiles" ../.. + cmake -D CMAKE_BUILD_TYPE=$BUILD_TYPE -D STATIC=ON -D BUILD_GUI_DEPS=ON -D INSTALL_VENDORED_LIBUNBOUND=ON -D CMAKE_INSTALL_PREFIX="$MONERO_DIR" -G "MSYS Makefiles" ../.. elif [ "$platform" == "mingw32" ]; then # Do something under Windows NT platform echo "Configuring build for MINGW32.." - cmake -D CMAKE_BUILD_TYPE=Release -D STATIC=ON -D BUILD_GUI_DEPS=ON -D INSTALL_VENDORED_LIBUNBOUND=ON -D CMAKE_INSTALL_PREFIX="$MONERO_DIR" -G "MSYS Makefiles" ../.. + cmake -D CMAKE_BUILD_TYPE=$BUILD_TYPE -D STATIC=ON -D BUILD_GUI_DEPS=ON -D INSTALL_VENDORED_LIBUNBOUND=ON -D CMAKE_INSTALL_PREFIX="$MONERO_DIR" -G "MSYS Makefiles" ../.. else echo "Unsupported platform: $platform" popd diff --git a/main.cpp b/main.cpp index 129d38c2..0e0d58e3 100644 --- a/main.cpp +++ b/main.cpp @@ -39,8 +39,10 @@ #include "Wallet.h" #include "PendingTransaction.h" #include "TranslationManager.h" - - +#include "TransactionInfo.h" +#include "TransactionHistory.h" +#include "model/TransactionHistoryModel.h" +#include "model/TransactionHistorySortFilterModel.h" int main(int argc, char *argv[]) @@ -56,23 +58,38 @@ int main(int argc, char *argv[]) filter *eventFilter = new filter; app.installEventFilter(eventFilter); - qmlRegisterType("moneroComponents", 1, 0, "Clipboard"); + // registering types for QML + qmlRegisterType("moneroComponents.Clipboard", 1, 0, "Clipboard"); - qmlRegisterUncreatableType("Bitmonero.Wallet", 1, 0, "Wallet", "Wallet can't be instantiated directly"); + qmlRegisterUncreatableType("moneroComponents.Wallet", 1, 0, "Wallet", "Wallet can't be instantiated directly"); - qmlRegisterUncreatableType("Bitmonero.PendingTransaction", 1, 0, "PendingTransaction", + + qmlRegisterUncreatableType("moneroComponents.PendingTransaction", 1, 0, "PendingTransaction", "PendingTransaction can't be instantiated directly"); - qmlRegisterUncreatableType("Bitmonero.WalletManager", 1, 0, "WalletManager", + qmlRegisterUncreatableType("moneroComponents.WalletManager", 1, 0, "WalletManager", "WalletManager can't be instantiated directly"); - qmlRegisterUncreatableType("moneroComponents", 1, 0, "TranslationManager", + qmlRegisterUncreatableType("moneroComponents.TranslationManager", 1, 0, "TranslationManager", "TranslationManager can't be instantiated directly"); + + + qmlRegisterUncreatableType("moneroComponents.TransactionHistoryModel", 1, 0, "TransactionHistoryModel", + "TransactionHistoryModel can't be instantiated directly"); + + qmlRegisterUncreatableType("moneroComponents.TransactionHistorySortFilterModel", 1, 0, "TransactionHistorySortFilterModel", + "TransactionHistorySortFilterModel can't be instantiated directly"); + + qmlRegisterUncreatableType("moneroComponents.TransactionHistory", 1, 0, "TransactionHistory", + "TransactionHistory can't be instantiated directly"); + + qmlRegisterUncreatableType("moneroComponents.TransactionInfo", 1, 0, "TransactionInfo", + "TransactionHistory can't be instantiated directly"); + qRegisterMetaType(); - - - + qRegisterMetaType(); + qRegisterMetaType(); QQmlApplicationEngine engine; diff --git a/main.qml b/main.qml index d6706d5d..ac45ed20 100644 --- a/main.qml +++ b/main.qml @@ -32,8 +32,9 @@ import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Dialogs 1.2 import Qt.labs.settings 1.0 -import Bitmonero.Wallet 1.0 -import Bitmonero.PendingTransaction 1.0 + +import moneroComponents.Wallet 1.0 +import moneroComponents.PendingTransaction 1.0 import "components" @@ -54,6 +55,10 @@ ApplicationWindow { property alias password : passwordDialog.password property int splashCounter: 0 property bool isNewWallet: false + property int restoreHeight:0 + + // true if wallet ever synchronized + property bool walletInitialized : false function altKeyReleased() { ctrlPressed = false; } @@ -138,15 +143,31 @@ ApplicationWindow { middlePanel.paymentClicked.connect(handlePayment); // basicPanel.paymentClicked.connect(handlePayment); + // currentWallet is defined on daemon address change - close/reopen + if (currentWallet !== undefined) { + console.log("closing currentWallet") + walletManager.closeWallet(currentWallet); + } // wallet already opened with wizard, we just need to initialize it if (typeof wizard.settings['wallet'] !== 'undefined') { + console.log("using wizard wallet") + //Set restoreHeight + if(persistentSettings.restoreHeight > 0){ + restoreHeight = persistentSettings.restoreHeight + } + + console.log("using wizard wallet") + connectWallet(wizard.settings['wallet']) + isNewWallet = true + // We don't need the wizard wallet any more - delete to avoid conflict with daemon adress change + delete wizard.settings['wallet'] } else { var wallet_path = walletPath(); // console.log("opening wallet at: ", wallet_path, "with password: ", appWindow.password); - console.log("opening wallet at: ", wallet_path); + console.log("opening wallet at: ", wallet_path, ", testnet: ", persistentSettings.testnet); walletManager.openWalletAsync(wallet_path, appWindow.password, persistentSettings.testnet); } @@ -159,11 +180,10 @@ ApplicationWindow { currentWallet.refreshed.connect(onWalletRefresh) currentWallet.updated.connect(onWalletUpdate) currentWallet.newBlock.connect(onWalletNewBlock) - + currentWallet.moneySpent.connect(onWalletMoneySent) + currentWallet.moneyReceived.connect(onWalletMoneyReceived) console.log("initializing with daemon address: ", persistentSettings.daemon_address) - currentWallet.initAsync(persistentSettings.daemon_address, 0); - } function walletPath() { @@ -210,9 +230,8 @@ ApplicationWindow { function onWalletUpdate() { console.log(">>> wallet updated") - basicPanel.unlockedBalanceText = leftPanel.unlockedBalanceText = - walletManager.displayAmount(currentWallet.unlockedBalance); - basicPanel.balanceText = leftPanel.balanceText = walletManager.displayAmount(currentWallet.balance); + middlePanel.unlockedBalanceText = leftPanel.unlockedBalanceText = walletManager.displayAmount(currentWallet.unlockedBalance); + middlePanel.balanceText = leftPanel.balanceText = walletManager.displayAmount(currentWallet.balance); } function onWalletRefresh() { @@ -220,6 +239,9 @@ ApplicationWindow { if (splash.visible) { hideProcessingSplash() } + var dCurrentBlock = currentWallet.daemonBlockChainHeight(); + var dTargetBlock = currentWallet.daemonBlockChainTargetHeight(); + leftPanel.daemonProgress.updateProgress(dCurrentBlock,dTargetBlock); // Store wallet after first refresh. To prevent broken wallet after a crash // TODO: Move this to libwallet? @@ -229,22 +251,49 @@ ApplicationWindow { console.log("wallet stored after first successfull refresh") } + // initialize transaction history once wallet is initializef first time; + if (!walletInitialized) { + currentWallet.history.refresh() + walletInitialized = true + } + leftPanel.networkStatus.connected = currentWallet.connected + onWalletUpdate(); } function onWalletNewBlock(blockHeight) { if (splash.visible) { - var currHeight = blockHeight.toFixed(0) - if(currHeight > splashCounter + 1000){ + var currHeight = blockHeight + + //fast refresh until restoreHeight is reached + var increment = ((restoreHeight == 0) || currHeight < restoreHeight)? 1000 : 10 + + if(currHeight > splashCounter + increment){ splashCounter = currHeight - var progressText = qsTr("Synchronizing blocks %1/%2").arg(currHeight).arg(currentWallet.daemonBlockChainHeight().toFixed(0)); + var locale = Qt.locale() + var currHeightString = currHeight.toLocaleString(locale,"f",0) + var targetHeightString = currentWallet.daemonBlockChainHeight().toLocaleString(locale,"f",0) + var progressText = qsTr("Synchronizing blocks %1 / %2").arg(currHeightString).arg(targetHeightString); console.log("Progress text: " + progressText); splash.heightProgressText = progressText } } } + function onWalletMoneyReceived(txId, amount) { + // refresh transaction history here + currentWallet.refresh() + currentWallet.history.refresh() // this will refresh model + } + + function onWalletMoneySent(txId, amount) { + // refresh transaction history here + currentWallet.refresh() + currentWallet.history.refresh() // this will refresh model + } + + function walletsFound() { var wallets = walletManager.findWallets(moneroAccountsDir); @@ -338,7 +387,7 @@ ApplicationWindow { middlePanel.enabled = enable; leftPanel.enabled = enable; rightPanel.enabled = enable; - basicPanel.enabled = enable; + // basicPanel.enabled = enable; } function showProcessingSplash(message) { @@ -396,6 +445,7 @@ ApplicationWindow { property bool testnet: true property string daemon_address: "localhost:38081" property string payment_id + property int restoreHeight:0 } // TODO: replace with customized popups @@ -440,7 +490,7 @@ ApplicationWindow { ProcessingSplash { id: splash - width: appWindow.width / 2 + width: appWindow.width / 1.5 height: appWindow.height / 2 x: (appWindow.width - width) / 2 + appWindow.x y: (appWindow.height - height) / 2 + appWindow.y @@ -514,7 +564,7 @@ ApplicationWindow { MiddlePanel { id: middlePanel anchors.bottom: parent.bottom - anchors.left: leftPanel.right + anchors.left: leftPanel.visible ? leftPanel.right : parent.left anchors.right: rightPanel.left height: parent.height state: "Transfer" @@ -526,16 +576,6 @@ ApplicationWindow { visible: false } - BasicPanel { - id: basicPanel - x: 0 - anchors.bottom: parent.bottom - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - visible: false - } - MouseArea { id: frameArea property bool blocked: false @@ -591,25 +631,26 @@ ApplicationWindow { duration: 200 } PropertyAction { - targets: [leftPanel, middlePanel, rightPanel] + targets: [leftPanel, rightPanel] properties: "visible" value: false } PropertyAction { - target: basicPanel - properties: "visible" + target: middlePanel + properties: "basicMode" value: true } + NumberAnimation { target: appWindow properties: "height" - to: basicPanel.height + to: middlePanel.height easing.type: Easing.InCubic duration: 200 } onStopped: { - middlePanel.visible = false + // middlePanel.visible = false rightPanel.visible = false leftPanel.visible = false } @@ -625,8 +666,8 @@ ApplicationWindow { duration: 200 } PropertyAction { - target: basicPanel - properties: "visible" + target: middlePanel + properties: "basicMode" value: false } PropertyAction { @@ -719,8 +760,13 @@ ApplicationWindow { anchors.left: parent.left anchors.right: parent.right onGoToBasicVersion: { - if(yes) goToBasicAnimation.start() - else goToProAnimation.start() + if (yes) { + // basicPanel.currentView = middlePanel.currentView + goToBasicAnimation.start() + } else { + // middlePanel.currentView = basicPanel.currentView + goToProAnimation.start() + } } MouseArea { diff --git a/monero-core.pro b/monero-core.pro index 4c1a5158..daa1378d 100644 --- a/monero-core.pro +++ b/monero-core.pro @@ -10,7 +10,8 @@ CONFIG += c++11 QMAKE_DISTCLEAN += -r $$WALLET_ROOT INCLUDEPATH += $$WALLET_ROOT/include \ - $$PWD/src/libwalletqt + $$PWD/src/libwalletqt \ + $$PWD/src HEADERS += \ filter.h \ @@ -22,7 +23,9 @@ HEADERS += \ src/libwalletqt/TransactionHistory.h \ src/libwalletqt/TransactionInfo.h \ oshelper.h \ - TranslationManager.h + TranslationManager.h \ + src/model/TransactionHistoryModel.h \ + src/model/TransactionHistorySortFilterModel.h SOURCES += main.cpp \ @@ -35,7 +38,9 @@ SOURCES += main.cpp \ src/libwalletqt/TransactionHistory.cpp \ src/libwalletqt/TransactionInfo.cpp \ oshelper.cpp \ - TranslationManager.cpp + TranslationManager.cpp \ + src/model/TransactionHistoryModel.cpp \ + src/model/TransactionHistorySortFilterModel.cpp lupdate_only { SOURCES = *.qml \ diff --git a/pages/History.qml b/pages/History.qml index 15161a5d..4c2a0dec 100644 --- a/pages/History.qml +++ b/pages/History.qml @@ -27,10 +27,28 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import QtQuick 2.0 + +import moneroComponents.Wallet 1.0 +import moneroComponents.WalletManager 1.0 +import moneroComponents.TransactionHistory 1.0 +import moneroComponents.TransactionInfo 1.0 +import moneroComponents.TransactionHistoryModel 1.0 + import "../components" Rectangle { + id: root + property var model + color: "#F0EEEE" + onModelChanged: { + if (typeof model !== 'undefined') { + // setup date filter scope according to real transactions + fromDatePicker.currentDate = model.transactionHistory.firstDateTime + toDatePicker.currentDate = model.transactionHistory.lastDateTime + } + } + Text { id: filterHeaderText @@ -47,6 +65,8 @@ Rectangle { text: qsTr("Filter transactions history") + translationManager.emptyString } + // Filter by Address input (senseless, removing) + /* Label { id: addressLabel anchors.left: parent.left @@ -67,11 +87,14 @@ Rectangle { anchors.rightMargin: 17 anchors.topMargin: 5 } + */ + + // Filter by Payment ID input Label { id: paymentIdLabel anchors.left: parent.left - anchors.top: addressLine.bottom + anchors.top: filterHeaderText.bottom // addressLine.bottom anchors.leftMargin: 17 anchors.topMargin: 17 text: qsTr("Payment ID (Optional)") + translationManager.emptyString @@ -84,12 +107,16 @@ Rectangle { id: paymentIdLine anchors.left: parent.left anchors.right: parent.right - anchors.top: paymentIdLabel.bottom + anchors.top: paymentIdLabel.bottom // addressLabel.bottom anchors.leftMargin: 17 anchors.rightMargin: 17 anchors.topMargin: 5 + + } + // Filter by description input (not implemented yet) + /* Label { id: descriptionLabel anchors.left: parent.left @@ -110,11 +137,14 @@ Rectangle { anchors.rightMargin: 17 anchors.topMargin: 5 } + */ + + // DateFrom picker Label { id: dateFromText anchors.left: parent.left - anchors.top: descriptionLine.bottom + anchors.top: paymentIdLine.bottom // descriptionLine.bottom anchors.leftMargin: 17 anchors.topMargin: 17 width: 156 @@ -132,10 +162,11 @@ Rectangle { z: 2 } + // DateTo picker Label { id: dateToText anchors.left: dateFromText.right - anchors.top: descriptionLine.bottom + anchors.top: paymentIdLine.bottom //descriptionLine.bottom anchors.leftMargin: 17 anchors.topMargin: 17 text: qsTr("To") @@ -163,10 +194,30 @@ Rectangle { shadowPressedColor: "#2D002F" releasedColor: "#6B0072" pressedColor: "#4D0051" + onClicked: { + // Apply filter here; + model.paymentIdFilter = paymentIdLine.text + model.dateFromFilter = fromDatePicker.currentDate + model.dateToFilter = toDatePicker.currentDate + if (advancedFilteringCheckBox.checked) { + if (amountFromLine.text.length) { + model.amountFromFilter = parseFloat(amountFromLine.text) + } + if (amountToLine.text.length) { + model.amountToFilter = parseFloat(amountToLine.text) + } + + var directionFilter = transactionsModel.get(transactionTypeDropdown.currentIndex).value + console.log("Direction filter: " + directionFilter) + model.directionFilter = directionFilter + } + + + } } CheckBox { - id: checkBox + id: advancedFilteringCheckBox text: qsTr("Advance filtering") anchors.left: filterButton.right anchors.bottom: filterButton.bottom @@ -193,9 +244,10 @@ Rectangle { ListModel { id: transactionsModel - ListElement { column1: "SENT"; column2: "" } - ListElement { column1: "RECIVE"; column2: "" } - ListElement { column1: "ON HOLD"; column2: "" } + ListElement { column1: "ALL"; column2: ""; value: TransactionInfo.Direction_Both } + ListElement { column1: "SENT"; column2: ""; value: TransactionInfo.Direction_Out } + ListElement { column1: "RECEIVED"; column2: ""; value: TransactionInfo.Direction_In } + } StandardDropdown { @@ -274,8 +326,11 @@ Rectangle { anchors.fill: parent onClicked: { parent.expanded = !parent.expanded - if(checkBox.checked) tableRect.height = Qt.binding(function(){ return parent.expanded ? tableRect.expandedHeight : tableRect.collapsedHeight }) - else tableRect.height = Qt.binding(function(){ return parent.expanded ? tableRect.expandedHeight : tableRect.middleHeight }) + if (advancedFilteringCheckBox.checked) { + tableRect.height = Qt.binding(function() { return parent.expanded ? tableRect.expandedHeight : tableRect.collapsedHeight }) + } else { + tableRect.height = Qt.binding(function() { return parent.expanded ? tableRect.expandedHeight : tableRect.middleHeight }) + } } } } @@ -312,10 +367,11 @@ Rectangle { ListModel { id: columnsModel - ListElement { columnName: "Address"; columnWidth: 127 } + + ListElement { columnName: "Payment ID"; columnWidth: 127 } ListElement { columnName: "Date"; columnWidth: 100 } ListElement { columnName: "Amount"; columnWidth: 148 } - ListElement { columnName: "Description"; columnWidth: 148 } + // ListElement { columnName: "Description"; columnWidth: 148 } } TableHeader { @@ -328,21 +384,24 @@ Rectangle { anchors.rightMargin: 14 dataModel: columnsModel offset: 20 - onSortRequest: console.log("column: " + column + " desc: " + desc) - } - - ListModel { - id: testModel - ListElement { paymentId: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; address: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; date: "Jan 12, 2014"; time: "12:23 AM"; amount: "0.000709159241"; balance: "19301.870709159241"; description: "Client from Australia"; out: false } - ListElement { paymentId: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; address: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; date: "Jan 12, 2014"; time: "12:23 AM"; amount: "0.000709159241"; balance: "19301.870709159241"; description: ""; out: true } - ListElement { paymentId: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; address: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; date: "Jan 12, 2014"; time: "12:23 AM"; amount: "0.000709159241"; balance: "19301.870709159241"; description: ""; out: true } - ListElement { paymentId: ""; address: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; date: "Jan 12, 2014"; time: "12:23 AM"; amount: "0.000709159241"; balance: "19301.870709159241"; description: ""; out: false } - ListElement { paymentId: ""; address: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; date: "Jan 12, 2014"; time: "12:23 AM"; amount: "0.000709159241"; balance: "19301.870709159241"; description: "Client from Australia"; out: false } - ListElement { paymentId: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; address: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; date: "Jan 12, 2014"; time: "12:23 AM"; amount: "0.000709159241"; balance: "19301.870709159241"; description: ""; out: false } - ListElement { paymentId: ""; address: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; date: "Jan 12, 2014"; time: "12:23 AM"; amount: "0.000709159241"; balance: "19301.870709159241"; description: ""; out: false } - ListElement { paymentId: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; address: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; date: "Jan 12, 2014"; time: "12:23 AM"; amount: "0.000709159241"; balance: "19301.870709159241"; description: ""; out: false } - ListElement { paymentId: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; address: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; date: "Jan 12, 2014"; time: "12:23 AM"; amount: "0.000709159241"; balance: "19301.870709159241"; description: "Client from Australia"; out: false } - ListElement { paymentId: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; address: "faef56b9acf67a7dba75ec01f403497049d7cff111628edfe7b57278554dc798"; date: "Jan 12, 2014"; time: "12:23 AM"; amount: "0.000709159241"; balance: "19301.870709159241"; description: ""; out: false } + onSortRequest: { + console.log("column: " + column + " desc: " + desc) + switch (column) { + case 0: + // Payment ID + model.sortRole = TransactionHistoryModel.TransactionPaymentIdRole + break; + case 1: + // Date; + model.sortRole = TransactionHistoryModel.TransactionDateRole + break; + case 2: + // Amount; + model.sortRole = TransactionHistoryModel.TransactionAmountRole + break; + } + model.sort(0, desc ? Qt.DescendingOrder : Qt.AscendingOrder) + } } Scroll { @@ -363,7 +422,7 @@ Rectangle { anchors.leftMargin: 14 anchors.rightMargin: 14 onContentYChanged: flickableScroll.flickableContentYChanged() - model: testModel + model: root.model } } } diff --git a/pages/Receive.qml b/pages/Receive.qml index e2c1cb7f..4ea32a2f 100644 --- a/pages/Receive.qml +++ b/pages/Receive.qml @@ -1,21 +1,21 @@ // Copyright (c) 2014-2015, The Monero Project -// +// // All rights reserved. -// +// // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. -// +// // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL @@ -32,7 +32,7 @@ import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.1 import "../components" -import moneroComponents 1.0 +import moneroComponents.Clipboard 1.0 Rectangle { @@ -43,14 +43,14 @@ Rectangle { function updatePaymentId() { var payment_id = appWindow.persistentSettings.payment_id - if (payment_id.length === 0) { - payment_id = appWindow.wallet.generatePaymentId() + if (typeof appWindow.currentWallet !== 'undefined') { + payment_id = appWindow.currentWallet.generatePaymentId() appWindow.persistentSettings.payment_id = payment_id - appWindow.currentWallet.payment_id = payment_id + addressLine.text = appWindow.currentWallet.address + integratedAddressLine.text = appWindow.currentWallet.integratedAddress(payment_id) } + paymentIdLine.text = payment_id - addressLine.text = appWindow.currentWallet.address - integratedAddressLine.text = appWindow.currentWallet.integratedAddress(payment_id) } Clipboard { id: clipboard } @@ -87,6 +87,8 @@ Rectangle { readOnly: true width: mainLayout.editWidth Layout.fillWidth: true + onTextChanged: cursorPosition = 0 + IconButton { imageSource: "../images/copyToClipboard.png" onClicked: { @@ -116,6 +118,9 @@ Rectangle { readOnly: true width: mainLayout.editWidth Layout.fillWidth: true + + onTextChanged: cursorPosition = 0 + IconButton { imageSource: "../images/copyToClipboard.png" onClicked: { @@ -176,9 +181,13 @@ Rectangle { } - Component.onCompleted: { + function onPageCompleted() { console.log("Receive page loaded"); - updatePaymentId() + + if(addressLine.text.length == 0) { + updatePaymentId() + } + } } diff --git a/pages/Settings.qml b/pages/Settings.qml index e9a50476..d46186af 100644 --- a/pages/Settings.qml +++ b/pages/Settings.qml @@ -27,8 +27,264 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import QtQuick 2.0 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.2 + + +import "../components" +import moneroComponents.Clipboard 1.0 Rectangle { - width: 100 - height: 62 + + property var daemonAddress + + color: "#F0EEEE" + + Clipboard { id: clipboard } + + function initSettings() { + + + // Mnemonic seed settings + memoTextInput.text = qsTr("Click button to show seed") + translationManager.emptyString + showSeedButton.visible = true + + // Daemon settings + + daemonAddress = persistentSettings.daemon_address.split(":"); + console.log("address: " + persistentSettings.daemon_address) + // try connecting to daemon + var connectedToDaemon = currentWallet.connectToDaemon(); + + if(!connectedToDaemon){ + console.log("not connected"); + //TODO: Print error? + //daemonStatusText.text = qsTr("Unable to connect to Daemon.") + //daemonStatusText.visible = true + } + + } + + + PasswordDialog { + id: settingsPasswordDialog + standardButtons: StandardButton.Ok + StandardButton.Cancel + onAccepted: { + if(appWindow.password == settingsPasswordDialog.password){ + memoTextInput.text = currentWallet.seed + showSeedButton.visible = false + } + + } + onRejected: { + + } + onDiscard: { + + } + } + + + ColumnLayout { + id: mainLayout + anchors.margins: 40 + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + spacing: 10 + + + Label { + id: seedLabel + color: "#4A4949" + fontSize: 16 + text: qsTr("Mnemonic seed: ") + translationManager.emptyString + Layout.preferredWidth: 100 + Layout.alignment: Qt.AlignLeft + } + + TextArea { + id: memoTextInput + textMargin: 6 + font.family: "Arial" + font.pointSize: 14 + wrapMode: TextEdit.WordWrap + readOnly: true + selectByMouse: true + + Layout.fillWidth: true + Layout.preferredHeight: 100 + Layout.alignment: Qt.AlignHCenter + + text: qsTr("Click button to show seed") + translationManager.emptyString + + Image { + id : clipboardButton + anchors.right: memoTextInput.right + anchors.bottom: memoTextInput.bottom + source: "qrc:///images/greyTriangle.png" + + Image { + anchors.centerIn: parent + source: "qrc:///images/copyToClipboard.png" + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: clipboard.setText(memoTextInput.text) + } + } + } + + RowLayout { + Layout.fillWidth: true + Text { + id: wordsTipText + font.family: "Arial" + font.pointSize: 12 + color: "#4A4646" + Layout.fillWidth: true + wrapMode: Text.WordWrap + text: qsTr("It is very important to write it down as this is the only backup you will need for your wallet.") + + translationManager.emptyString + } + + StandardButton { + + id: showSeedButton + + fontSize: 14 + shadowReleasedColor: "#FF4304" + shadowPressedColor: "#B32D00" + releasedColor: "#FF6C3C" + pressedColor: "#FF4304" + text: qsTr("Show seed") + Layout.alignment: Qt.AlignRight + Layout.preferredWidth: 100 + onClicked: { + settingsPasswordDialog.open(); + } + } + } + + + + + Rectangle { + Layout.fillWidth: true + height: 1 + color: "#DEDEDE" + } + + RowLayout { + id: daemonAddrRow + Layout.fillWidth: true + Layout.preferredHeight: 40 + Layout.topMargin: 40 + spacing: 10 + + Label { + id: daemonAddrLabel + + Layout.fillWidth: true + color: "#4A4949" + text: qsTr("Daemon adress") + translationManager.emptyString + fontSize: 16 + } + + LineEdit { + id: daemonAddr + Layout.preferredWidth: 200 + Layout.fillWidth: true + text: (daemonAddress !== undefined) ? daemonAddress[0] : "" + placeholderText: qsTr("Hostname / IP") + } + + + LineEdit { + id: daemonPort + Layout.preferredWidth: 100 + Layout.fillWidth: true + text: (daemonAddress !== undefined) ? daemonAddress[1] : "" + placeholderText: qsTr("Port") + } + + + StandardButton { + id: daemonAddrSave + + Layout.fillWidth: false + + Layout.leftMargin: 30 + Layout.minimumWidth: 100 + width: 60 + text: qsTr("Save") + translationManager.emptyString + shadowReleasedColor: "#FF4304" + shadowPressedColor: "#B32D00" + releasedColor: "#FF6C3C" + pressedColor: "#FF4304" + visible: true + onClicked: { + console.log("saving daemon adress settings") + var newDaemon = daemonAddr.text + ":" + daemonPort.text + if(persistentSettings.daemon_address != newDaemon) { + persistentSettings.daemon_address = newDaemon + //reconnect wallet + appWindow.initialize(); + } + } + } + + } + + + RowLayout { + id: daemonStatusRow + + Layout.fillWidth: true + + Text { + id: daemonStatusText + font.family: "Arial" + font.pixelSize: 18 + wrapMode: Text.Wrap + textFormat: Text.RichText + horizontalAlignment: Text.AlignHCenter + color: "#FF0000" + visible: true //!currentWallet.connected + } + +// StandardButton { +// id: checkConnectionButton +// anchors.left: daemonStatusText.right +// anchors.leftMargin: 30 +// width: 90 +// text: qsTr("Check again") + translationManager.emptyString +// shadowReleasedColor: "#FF4304" +// shadowPressedColor: "#B32D00" +// releasedColor: "#FF6C3C" +// pressedColor: "#FF4304" +// visible: true +// onClicked: { +// checkDaemonConnection(); +// } +// } + } + + } + + + + function onPageCompleted() { + console.log("Settings page loaded"); + initSettings(); + } + + } + + + + diff --git a/pages/Transfer.qml b/pages/Transfer.qml index 2d011c18..3921b1ef 100644 --- a/pages/Transfer.qml +++ b/pages/Transfer.qml @@ -27,7 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import QtQuick 2.0 -import Bitmonero.PendingTransaction 1.0 +import moneroComponents.PendingTransaction 1.0 import "../components" diff --git a/qml.qrc b/qml.qrc index a44266b9..ad47ba44 100644 --- a/qml.qrc +++ b/qml.qrc @@ -116,5 +116,6 @@ lang/flags/italy.png components/PasswordDialog.qml components/ProcessingSplash.qml + components/DaemonProgress.qml diff --git a/src/libwalletqt/TransactionHistory.cpp b/src/libwalletqt/TransactionHistory.cpp index 2c2c9f73..7fd7c7bc 100644 --- a/src/libwalletqt/TransactionHistory.cpp +++ b/src/libwalletqt/TransactionHistory.cpp @@ -2,49 +2,89 @@ #include "TransactionInfo.h" #include +#include -int TransactionHistory::count() const -{ - return m_pimpl->count(); -} TransactionInfo *TransactionHistory::transaction(int index) { - // box up Bitmonero::TransactionInfo - Bitmonero::TransactionInfo * impl = m_pimpl->transaction(index); - TransactionInfo * result = new TransactionInfo(impl, this); - return result; + + if (index < 0 || index >= m_tinfo.size()) { + qCritical("%s: no transaction info for index %d", __FUNCTION__, index); + qCritical("%s: there's %d transactions in backend", __FUNCTION__, m_pimpl->count()); + return nullptr; + } + return m_tinfo.at(index); } -TransactionInfo *TransactionHistory::transaction(const QString &id) -{ - // box up Bitmonero::TransactionInfo - Bitmonero::TransactionInfo * impl = m_pimpl->transaction(id.toStdString()); - TransactionInfo * result = new TransactionInfo(impl, this); - return result; -} +//// XXX: not sure if this method really needed; +//TransactionInfo *TransactionHistory::transaction(const QString &id) +//{ +// return nullptr; +//} QList TransactionHistory::getAll() const { + // XXX this invalidates previously saved history that might be used by model + emit refreshStarted(); qDeleteAll(m_tinfo); m_tinfo.clear(); + + QDateTime firstDateTime = QDateTime::currentDateTime(); + QDateTime lastDateTime = QDateTime(QDate(1970, 1, 1)); + TransactionHistory * parent = const_cast(this); for (const auto i : m_pimpl->getAll()) { TransactionInfo * ti = new TransactionInfo(i, parent); m_tinfo.append(ti); + // looking for transactions timestamp scope + if (ti->timestamp() >= lastDateTime) { + lastDateTime = ti->timestamp(); + } + if (ti->timestamp() <= firstDateTime) { + firstDateTime = ti->timestamp(); + } } + emit refreshFinished(); + + if (m_firstDateTime != firstDateTime) { + m_firstDateTime = firstDateTime; + emit firstDateTimeChanged(); + } + if (m_lastDateTime != lastDateTime) { + m_lastDateTime = lastDateTime; + emit lastDateTimeChanged(); + } + return m_tinfo; } void TransactionHistory::refresh() { - // XXX this invalidates previously saved history that might be used by clients + // rebuilding transaction list in wallet_api; m_pimpl->refresh(); - emit invalidated(); + // copying list here and keep track on every item to avoid memleaks + getAll(); } +quint64 TransactionHistory::count() const +{ + return m_tinfo.count(); +} + +QDateTime TransactionHistory::firstDateTime() const +{ + return m_firstDateTime; +} + +QDateTime TransactionHistory::lastDateTime() const +{ + return m_lastDateTime; +} + + TransactionHistory::TransactionHistory(Bitmonero::TransactionHistory *pimpl, QObject *parent) : QObject(parent), m_pimpl(pimpl) { - + m_firstDateTime = QDateTime(QDate(1970, 1, 1)); + m_lastDateTime = QDateTime::currentDateTime(); } diff --git a/src/libwalletqt/TransactionHistory.h b/src/libwalletqt/TransactionHistory.h index a6287506..ee05787a 100644 --- a/src/libwalletqt/TransactionHistory.h +++ b/src/libwalletqt/TransactionHistory.h @@ -3,6 +3,7 @@ #include #include +#include namespace Bitmonero { class TransactionHistory; @@ -14,16 +15,23 @@ class TransactionHistory : public QObject { Q_OBJECT Q_PROPERTY(int count READ count) + Q_PROPERTY(QDateTime firstDateTime READ firstDateTime NOTIFY firstDateTimeChanged) + Q_PROPERTY(QDateTime lastDateTime READ lastDateTime NOTIFY lastDateTimeChanged) public: - int count() const; Q_INVOKABLE TransactionInfo *transaction(int index); - Q_INVOKABLE TransactionInfo * transaction(const QString &id); + // Q_INVOKABLE TransactionInfo * transaction(const QString &id); Q_INVOKABLE QList getAll() const; Q_INVOKABLE void refresh(); + quint64 count() const; + QDateTime firstDateTime() const; + QDateTime lastDateTime() const; signals: - void invalidated(); + void refreshStarted() const; + void refreshFinished() const; + void firstDateTimeChanged() const; + void lastDateTimeChanged() const; public slots: @@ -36,6 +44,8 @@ private: Bitmonero::TransactionHistory * m_pimpl; mutable QList m_tinfo; + mutable QDateTime m_firstDateTime; + mutable QDateTime m_lastDateTime; }; diff --git a/src/libwalletqt/TransactionInfo.cpp b/src/libwalletqt/TransactionInfo.cpp index 192e85e5..248fd73e 100644 --- a/src/libwalletqt/TransactionInfo.cpp +++ b/src/libwalletqt/TransactionInfo.cpp @@ -1,4 +1,6 @@ #include "TransactionInfo.h" +#include "WalletManager.h" + #include TransactionInfo::Direction TransactionInfo::direction() const @@ -16,15 +18,21 @@ bool TransactionInfo::isFailed() const return m_pimpl->isFailed(); } -quint64 TransactionInfo::amount() const + +double TransactionInfo::amount() const { - return m_pimpl->amount(); + // there's no unsigned uint64 for JS, so better use double + return WalletManager::instance()->displayAmount(m_pimpl->amount()).toDouble(); } -quint64 TransactionInfo::fee() const +QString TransactionInfo::displayAmount() const { - return m_pimpl->fee(); + return WalletManager::instance()->displayAmount(m_pimpl->amount()); +} +QString TransactionInfo::fee() const +{ + return WalletManager::instance()->displayAmount(m_pimpl->fee()); } quint64 TransactionInfo::blockHeight() const @@ -37,13 +45,23 @@ QString TransactionInfo::hash() const return QString::fromStdString(m_pimpl->hash()); } -QString TransactionInfo::timestamp() +QDateTime TransactionInfo::timestamp() const { - QString result = QDateTime::fromTime_t(m_pimpl->timestamp()).toString(Qt::ISODate); + QDateTime result = QDateTime::fromTime_t(m_pimpl->timestamp()); return result; } -QString TransactionInfo::paymentId() +QString TransactionInfo::date() const +{ + return timestamp().date().toString(Qt::ISODate); +} + +QString TransactionInfo::time() const +{ + return timestamp().time().toString(Qt::ISODate); +} + +QString TransactionInfo::paymentId() const { return QString::fromStdString(m_pimpl->paymentId()); } diff --git a/src/libwalletqt/TransactionInfo.h b/src/libwalletqt/TransactionInfo.h index 62c26e2f..f30f42fa 100644 --- a/src/libwalletqt/TransactionInfo.h +++ b/src/libwalletqt/TransactionInfo.h @@ -1,8 +1,9 @@ #ifndef TRANSACTIONINFO_H #define TRANSACTIONINFO_H -#include #include +#include +#include class TransactionInfo : public QObject { @@ -10,19 +11,25 @@ class TransactionInfo : public QObject Q_PROPERTY(Direction direction READ direction) Q_PROPERTY(bool isPending READ isPending) Q_PROPERTY(bool isFailed READ isFailed) - Q_PROPERTY(quint64 amount READ amount) - Q_PROPERTY(quint64 fee READ fee) + Q_PROPERTY(double amount READ amount) + Q_PROPERTY(QString displayAmount READ displayAmount) + Q_PROPERTY(QString fee READ fee) Q_PROPERTY(quint64 blockHeight READ blockHeight) Q_PROPERTY(QString hash READ hash) - Q_PROPERTY(QString timestamp READ timestamp) + Q_PROPERTY(QDateTime timestamp READ timestamp) + Q_PROPERTY(QString date READ date) + Q_PROPERTY(QString time READ time) Q_PROPERTY(QString paymentId READ paymentId) public: enum Direction { Direction_In = Bitmonero::TransactionInfo::Direction_In, - Direction_Out = Bitmonero::TransactionInfo::Direction_Out + Direction_Out = Bitmonero::TransactionInfo::Direction_Out, + Direction_Both // invalid direction value, used for filtering }; + Q_ENUM(Direction) + // TODO: implement as separate class; // struct Transfer { @@ -30,16 +37,21 @@ public: // const uint64_t amount; // const std::string address; // }; + Direction direction() const; bool isPending() const; bool isFailed() const; - quint64 amount() const; - quint64 fee() const; + double amount() const; + QString displayAmount() const; + QString fee() const; quint64 blockHeight() const; //! transaction_id QString hash() const; - QString timestamp(); - QString paymentId(); + QDateTime timestamp() const; + QString date() const; + QString time() const; + QString paymentId() const; + // TODO: implement it //! only applicable for output transactions @@ -51,4 +63,8 @@ private: Bitmonero::TransactionInfo * m_pimpl; }; +// in order to wrap it to QVariant +Q_DECLARE_METATYPE(TransactionInfo*) + + #endif // TRANSACTIONINFO_H diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 987f4640..b52963bc 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -1,6 +1,8 @@ #include "Wallet.h" #include "PendingTransaction.h" #include "TransactionHistory.h" +#include "model/TransactionHistoryModel.h" +#include "model/TransactionHistorySortFilterModel.h" #include "wallet/wallet2_api.h" #include @@ -31,7 +33,6 @@ public: virtual void moneyReceived(const std::string &txId, uint64_t amount) { - qDebug() << __FUNCTION__; emit m_wallet->moneyReceived(QString::fromStdString(txId), amount); } @@ -59,7 +60,10 @@ private: Wallet * m_wallet; }; - +Wallet::Wallet(QObject * parent) + : Wallet(nullptr, parent) +{ +} QString Wallet::getSeed() const { @@ -86,6 +90,11 @@ bool Wallet::connected() const return m_walletImpl->connected(); } +bool Wallet::synchronized() const +{ + return m_walletImpl->synchronized(); +} + QString Wallet::errorString() const { return QString::fromStdString(m_walletImpl->errorString()); @@ -153,9 +162,16 @@ quint64 Wallet::daemonBlockChainHeight() const return m_daemonBlockChainHeight; } +quint64 Wallet::daemonBlockChainTargetHeight() const +{ + m_daemonBlockChainTargetHeight = m_walletImpl->daemonBlockChainTargetHeight(); + return m_daemonBlockChainTargetHeight; +} + bool Wallet::refresh() { bool result = m_walletImpl->refresh(); + m_history->refresh(); if (result) emit updated(); return result; @@ -193,15 +209,24 @@ void Wallet::disposeTransaction(PendingTransaction *t) delete t; } -TransactionHistory *Wallet::history() +TransactionHistory *Wallet::history() const { - if (!m_history) { - Bitmonero::TransactionHistory * impl = m_walletImpl->history(); - m_history = new TransactionHistory(impl, this); - } return m_history; } +TransactionHistorySortFilterModel *Wallet::historyModel() const +{ + if (!m_historyModel) { + Wallet * w = const_cast(this); + m_historyModel = new TransactionHistoryModel(w); + m_historyModel->setTransactionHistory(this->history()); + m_historySortFilterModel = new TransactionHistorySortFilterModel(w); + m_historySortFilterModel->setSourceModel(m_historyModel); + } + + return m_historySortFilterModel; +} + QString Wallet::generatePaymentId() const { @@ -228,13 +253,16 @@ Wallet::Wallet(Bitmonero::Wallet *w, QObject *parent) : QObject(parent) , m_walletImpl(w) , m_history(nullptr) + , m_historyModel(nullptr) , m_daemonBlockChainHeight(0) , m_daemonBlockChainHeightTtl(DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS) { + m_history = new TransactionHistory(m_walletImpl->history(), this); m_walletImpl->setListener(new WalletListenerImpl(this)); } Wallet::~Wallet() { + Bitmonero::WalletManagerFactory::getWalletManager()->closeWallet(m_walletImpl); } diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index 703bd186..d794550a 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -13,6 +13,8 @@ namespace Bitmonero { class TransactionHistory; +class TransactionHistoryModel; +class TransactionHistorySortFilterModel; class Wallet : public QObject { @@ -21,14 +23,18 @@ class Wallet : public QObject Q_PROPERTY(QString seedLanguage READ getSeedLanguage) Q_PROPERTY(Status status READ status) Q_PROPERTY(bool connected READ connected) + Q_PROPERTY(bool synchronized READ synchronized) Q_PROPERTY(QString errorString READ errorString) Q_PROPERTY(QString address READ address) Q_PROPERTY(quint64 balance READ balance) Q_PROPERTY(quint64 unlockedBalance READ unlockedBalance) Q_PROPERTY(TransactionHistory * history READ history) Q_PROPERTY(QString paymentId READ paymentId WRITE setPaymentId) + Q_PROPERTY(TransactionHistorySortFilterModel * historyModel READ historyModel NOTIFY historyModelChanged) public: + + enum Status { Status_Ok = Bitmonero::Wallet::Status_Ok, Status_Error = Bitmonero::Wallet::Status_Error @@ -48,9 +54,13 @@ public: //! returns last operation's status Status status() const; - //! returns of wallet connected + //! returns true if wallet connected bool connected() const; + //! returns true if wallet was ever synchronized + bool synchronized() const; + + //! returns last operation's error message QString errorString() const; @@ -88,10 +98,12 @@ public: //! returns daemon's blockchain height Q_INVOKABLE quint64 daemonBlockChainHeight() const; + //! returns daemon's blockchain target height + Q_INVOKABLE quint64 daemonBlockChainTargetHeight() const; + //! refreshes the wallet Q_INVOKABLE bool refresh(); - //! refreshes the wallet asynchronously Q_INVOKABLE void refreshAsync(); @@ -109,7 +121,10 @@ public: Q_INVOKABLE void disposeTransaction(PendingTransaction * t); //! returns transaction history - TransactionHistory * history(); + TransactionHistory * history() const; + + //! returns transaction history model + TransactionHistorySortFilterModel *historyModel() const; //! generate payment id Q_INVOKABLE QString generatePaymentId() const; @@ -136,12 +151,13 @@ signals: void moneySpent(const QString &txId, quint64 amount); void moneyReceived(const QString &txId, quint64 amount); void newBlock(quint64 height); + void historyModelChanged() const; private: + Wallet(QObject * parent = nullptr); Wallet(Bitmonero::Wallet *w, QObject * parent = 0); ~Wallet(); - private: friend class WalletManager; friend class WalletListenerImpl; @@ -149,11 +165,16 @@ private: Bitmonero::Wallet * m_walletImpl; // history lifetime managed by wallet; TransactionHistory * m_history; + // Used for UI history view + mutable TransactionHistoryModel * m_historyModel; + mutable TransactionHistorySortFilterModel * m_historySortFilterModel; QString m_paymentId; mutable QTime m_daemonBlockChainHeightTime; mutable quint64 m_daemonBlockChainHeight; int m_daemonBlockChainHeightTtl; - + mutable quint64 m_daemonBlockChainTargetHeight; }; + + #endif // WALLET_H diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp index ea299cdc..2e1fe57b 100644 --- a/src/libwalletqt/WalletManager.cpp +++ b/src/libwalletqt/WalletManager.cpp @@ -61,9 +61,9 @@ void WalletManager::openWalletAsync(const QString &path, const QString &password } -Wallet *WalletManager::recoveryWallet(const QString &path, const QString &memo, bool testnet) +Wallet *WalletManager::recoveryWallet(const QString &path, const QString &memo, bool testnet, quint64 restoreHeight) { - Bitmonero::Wallet * w = m_pimpl->recoveryWallet(path.toStdString(), memo.toStdString(), testnet); + Bitmonero::Wallet * w = m_pimpl->recoveryWallet(path.toStdString(), memo.toStdString(), testnet, restoreHeight); Wallet * wallet = new Wallet(w); return wallet; } diff --git a/src/libwalletqt/WalletManager.h b/src/libwalletqt/WalletManager.h index 23c56925..24fb60fc 100644 --- a/src/libwalletqt/WalletManager.h +++ b/src/libwalletqt/WalletManager.h @@ -48,7 +48,7 @@ public: // wizard: recoveryWallet path; hint: internally it recorvers wallet and set password = "" Q_INVOKABLE Wallet * recoveryWallet(const QString &path, const QString &memo, - bool testnet = false); + bool testnet = false, quint64 restoreHeight = 0); /*! * \brief closeWallet - closes wallet and frees memory diff --git a/src/model/TransactionHistoryModel.cpp b/src/model/TransactionHistoryModel.cpp new file mode 100644 index 00000000..1ad17944 --- /dev/null +++ b/src/model/TransactionHistoryModel.cpp @@ -0,0 +1,126 @@ +#include "TransactionHistoryModel.h" +#include "TransactionHistory.h" +#include "TransactionInfo.h" + +#include + + +TransactionHistoryModel::TransactionHistoryModel(QObject *parent) + : QAbstractListModel(parent), m_transactionHistory(nullptr) +{ + +} + +void TransactionHistoryModel::setTransactionHistory(TransactionHistory *th) +{ + beginResetModel(); + m_transactionHistory = th; + endResetModel(); + + connect(m_transactionHistory, &TransactionHistory::refreshStarted, + this, &TransactionHistoryModel::beginResetModel); + connect(m_transactionHistory, &TransactionHistory::refreshFinished, + this, &TransactionHistoryModel::endResetModel); + + emit transactionHistoryChanged(); +} + +TransactionHistory *TransactionHistoryModel::transactionHistory() const +{ + return m_transactionHistory; +} + +QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const +{ + if (!m_transactionHistory) { + return QVariant(); + } + + if (index.row() < 0 || (unsigned)index.row() >= m_transactionHistory->count()) { + return QVariant(); + } + + TransactionInfo * tInfo = m_transactionHistory->transaction(index.row()); + + + Q_ASSERT(tInfo); + if (!tInfo) { + qCritical("%s: internal error: no transaction info for index %d", __FUNCTION__, index.row()); + return QVariant(); + } + QVariant result; + switch (role) { + case TransactionRole: + result = QVariant::fromValue(tInfo); + break; + case TransactionDirectionRole: + result = QVariant::fromValue(tInfo->direction()); + break; + case TransactionPendingRole: + result = tInfo->isPending(); + break; + case TransactionFailedRole: + result = tInfo->isFailed(); + break; + case TransactionAmountRole: + result = tInfo->amount(); + break; + case TransactionDisplayAmountRole: + result = tInfo->displayAmount(); + break; + case TransactionFeeRole: + result = tInfo->fee(); + break; + case TransactionBlockHeightRole: + result = tInfo->blockHeight(); + break; + case TransactionHashRole: + result = tInfo->hash(); + break; + case TransactionTimeStampRole: + result = tInfo->timestamp(); + break; + case TransactionPaymentIdRole: + result = tInfo->paymentId(); + break; + case TransactionIsOutRole: + result = tInfo->direction() == TransactionInfo::Direction_Out; + break; + case TransactionDateRole: + result = tInfo->date(); + break; + case TransactionTimeRole: + result = tInfo->time(); + break; + } + + return result; +} + +int TransactionHistoryModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_transactionHistory ? m_transactionHistory->count() : 0; +} + +QHash TransactionHistoryModel::roleNames() const +{ + QHash roleNames = QAbstractListModel::roleNames(); + roleNames.insert(TransactionRole, "transaction"); + roleNames.insert(TransactionDirectionRole, "direction"); + roleNames.insert(TransactionPendingRole, "isPending"); + roleNames.insert(TransactionFailedRole, "isFailed"); + roleNames.insert(TransactionAmountRole, "amount"); + roleNames.insert(TransactionDisplayAmountRole, "displayAmount"); + roleNames.insert(TransactionFeeRole, "fee"); + roleNames.insert(TransactionBlockHeightRole, "blockHeight"); + roleNames.insert(TransactionHashRole, "hash"); + roleNames.insert(TransactionTimeStampRole, "timeStamp"); + roleNames.insert(TransactionPaymentIdRole, "paymentId"); + roleNames.insert(TransactionIsOutRole, "isOut"); + roleNames.insert(TransactionDateRole, "date"); + roleNames.insert(TransactionTimeRole, "time"); + return roleNames; +} + + diff --git a/src/model/TransactionHistoryModel.h b/src/model/TransactionHistoryModel.h new file mode 100644 index 00000000..0960ceae --- /dev/null +++ b/src/model/TransactionHistoryModel.h @@ -0,0 +1,68 @@ +#ifndef TRANSACTIONHISTORYMODEL_H +#define TRANSACTIONHISTORYMODEL_H + +#include + +class TransactionHistory; +class TransactionInfo; + +/** + * @brief The TransactionHistoryModel class - read-only list model for Transaction History + */ + +class TransactionHistoryModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(TransactionHistory * transactionHistory READ transactionHistory WRITE setTransactionHistory NOTIFY transactionHistoryChanged) + +public: + enum TransactionInfoRole { + TransactionRole = Qt::UserRole + 1, // for the TransactionInfo object; + TransactionDirectionRole, + TransactionPendingRole, + TransactionFailedRole, + TransactionAmountRole, + TransactionDisplayAmountRole, + TransactionFeeRole, + TransactionBlockHeightRole, + TransactionHashRole, + TransactionTimeStampRole, + TransactionPaymentIdRole, + // extra role (alias) for TransactionDirectionRole (as UI currently wants just boolean "out") + TransactionIsOutRole, + // extra roles for date and time (as UI wants date and time separately) + TransactionDateRole, + TransactionTimeRole + }; + Q_ENUM(TransactionInfoRole) + + TransactionHistoryModel(QObject * parent = 0); + void setTransactionHistory(TransactionHistory * th); + TransactionHistory * transactionHistory() const; + /** + * @brief dateFrom - returns firstmost transaction datetime + * @return + */ + QDateTime firstDateTime() const; + + /** + * @brief dateTo - returns lastmost transaction datetime + * @return + */ + QDateTime lastDateTime() const; + + + + /// QAbstractListModel + virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; + virtual int rowCount(const QModelIndex & parent = QModelIndex()) const override; + virtual QHash roleNames() const override; + +signals: + void transactionHistoryChanged(); + +private: + TransactionHistory * m_transactionHistory; +}; + +#endif // TRANSACTIONHISTORYMODEL_H diff --git a/src/model/TransactionHistorySortFilterModel.cpp b/src/model/TransactionHistorySortFilterModel.cpp new file mode 100644 index 00000000..6d6a2b44 --- /dev/null +++ b/src/model/TransactionHistorySortFilterModel.cpp @@ -0,0 +1,209 @@ +#include "TransactionHistorySortFilterModel.h" +#include "TransactionHistoryModel.h" + +#include + +namespace { + /** + * helper to extract scope value from filter + */ + template + T scopeFilterValue(const QMap &filters, int role, int scopeIndex) + { + if (!filters.contains(role)) { + return T(); + } + return filters.value(role).toList().at(scopeIndex).value(); + } + + /** + * helper to setup scope value to filter + */ + template + void setScopeFilterValue(QMap &filters, int role, int scopeIndex, const T &value) + { + QVariantList scopeFilter; + + if (filters.contains(role)) { + scopeFilter = filters.value(role).toList(); + } + while (scopeFilter.size() < 2) { + scopeFilter.append(T()); + } + scopeFilter[scopeIndex] = QVariant::fromValue(value); + filters[role] = scopeFilter; + } +} + + +TransactionHistorySortFilterModel::TransactionHistorySortFilterModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ + setDynamicSortFilter(true); +} + +QString TransactionHistorySortFilterModel::paymentIdFilter() const +{ + return m_filterValues.value(TransactionHistoryModel::TransactionPaymentIdRole).toString(); +} + +void TransactionHistorySortFilterModel::setPaymentIdFilter(const QString &arg) +{ + if (paymentIdFilter() != arg) { + m_filterValues[TransactionHistoryModel::TransactionPaymentIdRole] = arg; + emit paymentIdFilterChanged(); + invalidateFilter(); + } +} + +QDate TransactionHistorySortFilterModel::dateFromFilter() const +{ + return scopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionTimeStampRole, ScopeIndex::From); +} + +void TransactionHistorySortFilterModel::setDateFromFilter(const QDate &date) +{ + if (date != dateFromFilter()) { + setScopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionTimeStampRole, ScopeIndex::From, date); + emit dateFromFilterChanged(); + invalidateFilter(); + } +} + +QDate TransactionHistorySortFilterModel::dateToFilter() const +{ + return scopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionTimeStampRole, ScopeIndex::To); +} + +void TransactionHistorySortFilterModel::setDateToFilter(const QDate &date) +{ + if (date != dateToFilter()) { + setScopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionTimeStampRole, ScopeIndex::To, date); + emit dateToFilterChanged(); + invalidateFilter(); + } +} + +double TransactionHistorySortFilterModel::amountFromFilter() const +{ + return scopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionAmountRole, ScopeIndex::From); +} + +void TransactionHistorySortFilterModel::setAmountFromFilter(double value) +{ + if (value != amountFromFilter()) { + setScopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionAmountRole, ScopeIndex::From, value); + emit amountFromFilterChanged(); + invalidateFilter(); + } +} + +double TransactionHistorySortFilterModel::amountToFilter() const +{ + return scopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionAmountRole, ScopeIndex::To); +} + +void TransactionHistorySortFilterModel::setAmountToFilter(double value) +{ + if (value != amountToFilter()) { + setScopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionAmountRole, ScopeIndex::To, value); + emit amountToFilterChanged(); + invalidateFilter(); + } +} + +int TransactionHistorySortFilterModel::directionFilter() const +{ + return m_filterValues.value(TransactionHistoryModel::TransactionDirectionRole).value(); +} + +void TransactionHistorySortFilterModel::setDirectionFilter(int value) +{ + if (value != directionFilter()) { + m_filterValues[TransactionHistoryModel::TransactionDirectionRole] = QVariant::fromValue(value); + emit directionFilterChanged(); + invalidateFilter(); + } +} + + +void TransactionHistorySortFilterModel::sort(int column, Qt::SortOrder order) +{ + QSortFilterProxyModel::sort(column, order); +} + +TransactionHistory *TransactionHistorySortFilterModel::transactionHistory() const +{ + const TransactionHistoryModel * model = static_cast (sourceModel()); + return model->transactionHistory(); +} + +bool TransactionHistorySortFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + + if (source_row < 0 || source_row >= sourceModel()->rowCount()) { + return false; + } + + QModelIndex index = sourceModel()->index(source_row, 0, source_parent); + if (!index.isValid()) { + return false; + } + + bool result = true; + + // iterating through filters + for (int role : m_filterValues.keys()) { + if (m_filterValues.contains(role)) { + QVariant data = sourceModel()->data(index, role); + switch (role) { + case TransactionHistoryModel::TransactionPaymentIdRole: + result = data.toString().contains(paymentIdFilter()); + break; + case TransactionHistoryModel::TransactionTimeStampRole: + { + QDateTime from = QDateTime(dateFromFilter()); + QDateTime to = QDateTime(dateToFilter()); + to = to.addDays(1); // including upperbound + QDateTime timestamp = data.toDateTime(); + bool matchFrom = from.isNull() || timestamp.isNull() || timestamp >= from; + bool matchTo = to.isNull() || timestamp.isNull() || timestamp <= to; + result = matchFrom && matchTo; + } + break; + case TransactionHistoryModel::TransactionAmountRole: + { + double from = amountFromFilter(); + double to = amountToFilter(); + double amount = data.toDouble(); + + bool matchFrom = from <= 0 || amount >= from; + bool matchTo = to <= 0 || amount <= to; + result = matchFrom && matchTo; + } + break; + case TransactionHistoryModel::TransactionDirectionRole: + result = directionFilter() == TransactionInfo::Direction_Both ? true + : data.toInt() == directionFilter(); + + + break; + + default: + break; + } + + + if (!result) { // stop the loop once filter doesn't match + break; + } + } + } + + return result; +} + +bool TransactionHistorySortFilterModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const +{ + return QSortFilterProxyModel::lessThan(source_left, source_right); +} diff --git a/src/model/TransactionHistorySortFilterModel.h b/src/model/TransactionHistorySortFilterModel.h new file mode 100644 index 00000000..1b5c2fce --- /dev/null +++ b/src/model/TransactionHistorySortFilterModel.h @@ -0,0 +1,79 @@ +#ifndef TRANSACTIONHISTORYSORTFILTERMODEL_H +#define TRANSACTIONHISTORYSORTFILTERMODEL_H + +#include "TransactionInfo.h" + +#include +#include +#include +#include + + +class TransactionHistory; + +class TransactionHistorySortFilterModel: public QSortFilterProxyModel +{ + Q_OBJECT + Q_PROPERTY(QString paymentIdFilter READ paymentIdFilter WRITE setPaymentIdFilter NOTIFY paymentIdFilterChanged) + Q_PROPERTY(QDate dateFromFilter READ dateFromFilter WRITE setDateFromFilter NOTIFY dateFromFilterChanged) + Q_PROPERTY(QDate dateToFilter READ dateToFilter WRITE setDateToFilter NOTIFY dateToFilterChanged) + Q_PROPERTY(double amountFromFilter READ amountFromFilter WRITE setAmountFromFilter NOTIFY amountFromFilterChanged) + Q_PROPERTY(double amountToFilter READ amountToFilter WRITE setAmountToFilter NOTIFY amountToFilterChanged) + Q_PROPERTY(int directionFilter READ directionFilter WRITE setDirectionFilter NOTIFY directionFilterChanged) + + Q_PROPERTY(TransactionHistory * transactionHistory READ transactionHistory) + +public: + TransactionHistorySortFilterModel(QObject * parent = nullptr); + //! filtering by payment id + QString paymentIdFilter() const; + void setPaymentIdFilter(const QString &arg); + + //! filtering by date (lower bound) + QDate dateFromFilter() const; + void setDateFromFilter(const QDate &date); + + //! filtering by to date (upper bound) + QDate dateToFilter() const; + void setDateToFilter(const QDate &date); + + //! filtering by amount (lower bound) + double amountFromFilter() const; + void setAmountFromFilter(double value); + + //! filtering by amount (upper bound) + double amountToFilter() const; + void setAmountToFilter(double value); + + //! filtering by direction + int directionFilter() const; + void setDirectionFilter(int value); + + Q_INVOKABLE void sort(int column, Qt::SortOrder order); + TransactionHistory * transactionHistory() const; + +signals: + void paymentIdFilterChanged(); + void dateFromFilterChanged(); + void dateToFilterChanged(); + void amountFromFilterChanged(); + void amountToFilterChanged(); + void directionFilterChanged(); + +protected: + // QSortFilterProxyModel overrides + virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; + virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; + + +private: + enum ScopeIndex { + From = 0, + To = 1 + }; + +private: + QMap m_filterValues; +}; + +#endif // TRANSACTIONHISTORYSORTFILTERMODEL_H diff --git a/wizard/WizardCreateWallet.qml b/wizard/WizardCreateWallet.qml index f0344a94..6d632cbe 100644 --- a/wizard/WizardCreateWallet.qml +++ b/wizard/WizardCreateWallet.qml @@ -27,7 +27,9 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import QtQuick 2.2 -import moneroComponents 1.0 +import moneroComponents.WalletManager 1.0 +import moneroComponents.Wallet 1.0 + import QtQuick.Dialogs 1.2 import 'utils.js' as Utils @@ -94,5 +96,6 @@ Item { wordsTextItem.clipboardButtonVisible: true wordsTextItem.tipTextVisible: true wordsTextItem.memoTextReadOnly: true + restoreHeightVisible:false } } diff --git a/wizard/WizardFinish.qml b/wizard/WizardFinish.qml index 48b4ae61..7c44b7bc 100644 --- a/wizard/WizardFinish.qml +++ b/wizard/WizardFinish.qml @@ -47,6 +47,7 @@ Item { + qsTr("Allow background mining: ") + wizard.settings['allow_background_mining'] + "
" + qsTr("Daemon address: ") + wizard.settings['daemon_address'] + "
" + qsTr("testnet: ") + wizard.settings['testnet'] + "
" + + (wizard.settings['restore_height'] === undefined ? "" : qsTr("Restore height: ") + wizard.settings['restore_height']) + "
" + translationManager.emptyString return str; } diff --git a/wizard/WizardMain.qml b/wizard/WizardMain.qml index 7466cdf8..3168733b 100644 --- a/wizard/WizardMain.qml +++ b/wizard/WizardMain.qml @@ -137,6 +137,7 @@ Rectangle { appWindow.persistentSettings.auto_donations_amount = settings.auto_donations_amount appWindow.persistentSettings.daemon_address = settings.daemon_address appWindow.persistentSettings.testnet = settings.testnet + appWindow.persistentSettings.restore_height = parseInt(settings.restore_height) } diff --git a/wizard/WizardManageWalletUI.qml b/wizard/WizardManageWalletUI.qml index cc8a5be3..7104dc9f 100644 --- a/wizard/WizardManageWalletUI.qml +++ b/wizard/WizardManageWalletUI.qml @@ -27,8 +27,10 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import QtQuick 2.2 -import moneroComponents 1.0 +import moneroComponents.TranslationManager 1.0 import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.1 +import "../components" // Reusable component for managing wallet (account name, path, private key) @@ -39,6 +41,8 @@ Item { property alias wordsTextTitle: frameHeader.text property alias walletPath: fileUrlInput.text property alias wordsTextItem : memoTextItem + property alias restoreHeight : restoreHeightItem.text + property alias restoreHeightVisible: restoreHeightItem.visible // TODO extend properties if needed @@ -112,7 +116,7 @@ Item { width: 300 height: 62 - TextInput { + TextEdit { id: accountName anchors.fill: parent horizontalAlignment: TextInput.AlignHCenter @@ -159,10 +163,22 @@ Item { anchors.topMargin: 16 } + // Restore Height + LineEdit { + id: restoreHeightItem + anchors.top: memoTextItem.bottom + width: 250 + anchors.topMargin: 20 + placeholderText: qsTr("Restore height") + Layout.alignment: Qt.AlignCenter + validator: IntValidator { + bottom:0 + } + } Row { anchors.left: parent.left anchors.right: parent.right - anchors.top: memoTextItem.bottom + anchors.top: (restoreHeightItem.visible)? restoreHeightItem.bottom : memoTextItem.bottom anchors.topMargin: 24 spacing: 16 diff --git a/wizard/WizardMemoTextInput.qml b/wizard/WizardMemoTextInput.qml index 9497d9e5..37dbcb39 100644 --- a/wizard/WizardMemoTextInput.qml +++ b/wizard/WizardMemoTextInput.qml @@ -1,5 +1,5 @@ import QtQuick 2.0 -import moneroComponents 1.0 +import moneroComponents.Clipboard 1.0 Column { diff --git a/wizard/WizardPassword.qml b/wizard/WizardPassword.qml index 8318a785..476c5742 100644 --- a/wizard/WizardPassword.qml +++ b/wizard/WizardPassword.qml @@ -52,6 +52,8 @@ Item { } else { passwordPage.titleText = qsTr("Now that your wallet has been restored, please set a password for the wallet") + translationManager.emptyString } + + passwordItem.focus = true; } function onPageClosed(settingsObject) { @@ -146,30 +148,34 @@ Item { anchors.topMargin: 24 width: 300 height: 62 + placeholderText : qsTr("Password") + translationManager.emptyString; + KeyNavigation.tab: retypePasswordItem onChanged: handlePassword() + } + WizardPasswordInput { + id: retypePasswordItem + anchors.top: passwordItem.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 24 + width: 300 + height: 62 + placeholderText : qsTr("Confirm password") + translationManager.emptyString; + KeyNavigation.tab: passwordItem + onChanged: handlePassword() + } PrivacyLevelSmall { id: privacyLevel anchors.left: parent.left anchors.right: parent.right - anchors.top: passwordItem.bottom - anchors.topMargin: 24 + anchors.top: retypePasswordItem.bottom + anchors.topMargin: 60 background: "#F0EEEE" interactive: false } - WizardPasswordInput { - id: retypePasswordItem - anchors.top: privacyLevel.bottom - anchors.horizontalCenter: parent.horizontalCenter - anchors.topMargin: 24 - width: 300 - height: 62 - onChanged: handlePassword() - } - Component.onCompleted: { console.log } diff --git a/wizard/WizardPasswordInput.qml b/wizard/WizardPasswordInput.qml index 82597421..00201ecf 100644 --- a/wizard/WizardPasswordInput.qml +++ b/wizard/WizardPasswordInput.qml @@ -27,24 +27,34 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import QtQuick 2.0 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 -Item { +FocusScope { property alias password: password.text + property alias placeholderText: password.placeholderText signal changed(string password) - TextInput { + TextField { id : password + focus:true anchors.fill: parent - horizontalAlignment: TextInput.AlignHCenter + horizontalAlignment: TextInput.AlignLeft verticalAlignment: TextInput.AlignVCenter font.family: "Arial" font.pixelSize: 32 - renderType: Text.NativeRendering - color: "#35B05A" - passwordCharacter: "•" echoMode: TextInput.Password - focus: true + style: TextFieldStyle { + renderType: Text.NativeRendering + textColor: "#35B05A" + passwordCharacter: "•" + background: Rectangle { + radius: 0 + border.width: 0 + } + } + Keys.onReleased: { changed(text) } diff --git a/wizard/WizardRecoveryWallet.qml b/wizard/WizardRecoveryWallet.qml index 505bc239..7f6d87f4 100644 --- a/wizard/WizardRecoveryWallet.qml +++ b/wizard/WizardRecoveryWallet.qml @@ -27,9 +27,8 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import QtQuick 2.2 -import moneroComponents 1.0 import QtQuick.Dialogs 1.2 -import Bitmonero.Wallet 1.0 +import moneroComponents.Wallet 1.0 import 'utils.js' as Utils Item { @@ -55,12 +54,14 @@ Item { settingsObject['account_name'] = uiItem.accountNameText settingsObject['words'] = Utils.lineBreaksToSpaces(uiItem.wordsTextItem.memoText) settingsObject['wallet_path'] = uiItem.walletPath + settingsObject['restoreHeight'] = parseInt(uiItem.restoreHeight) return recoveryWallet(settingsObject) } function recoveryWallet(settingsObject) { var testnet = appWindow.persistentSettings.testnet; - var wallet = walletManager.recoveryWallet(oshelper.temporaryFilename(), settingsObject.words, testnet); + var restoreHeight = settingsObject.restoreHeight; + var wallet = walletManager.recoveryWallet(oshelper.temporaryFilename(), settingsObject.words, testnet, restoreHeight); var success = wallet.status === Wallet.Status_Ok; if (success) { settingsObject['wallet'] = wallet; @@ -81,6 +82,7 @@ Item { wordsTextItem.tipTextVisible: false wordsTextItem.memoTextReadOnly: false wordsTextItem.memoText: "" + restoreHeightVisible: true wordsTextItem.onMemoTextChanged: { checkNextButton(); } diff --git a/wizard/WizardWelcome.qml b/wizard/WizardWelcome.qml index 46448e2c..ef97f984 100644 --- a/wizard/WizardWelcome.qml +++ b/wizard/WizardWelcome.qml @@ -163,6 +163,7 @@ Item { if (data !== null || data !== undefined) { var locale = data.locale translationManager.setLanguage(locale.split("_")[0]); + wizard.switchPage(true) } } }