mirror of
https://github.com/monero-project/monero-gui.git
synced 2025-01-19 08:03:48 +02:00
Merge pull request #398
ae5c21e
export/import key images when cold signing (Jaquee)24ccd27
Add rescan spent button (Jaquee)15c79df
GUI cold signing (Jaquee)fd98395
view only wallets (Jaquee)8f56e98
save wallet name in appwindow (Jaquee)842e4df
adjust button size dynamically (Jaquee)
This commit is contained in:
commit
98ca4f1a70
@ -52,13 +52,6 @@ Window {
|
||||
show()
|
||||
}
|
||||
|
||||
function usefulName(path) {
|
||||
// arbitrary "short enough" limit
|
||||
if (path.length < 32)
|
||||
return path
|
||||
return path.replace(/.*[\/\\]/, '').replace(/\.keys$/, '')
|
||||
}
|
||||
|
||||
// TODO: implement without hardcoding sizes
|
||||
width: 480
|
||||
height: walletName ? 240 : 200
|
||||
@ -74,7 +67,7 @@ Window {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
Label {
|
||||
text: root.walletName.length > 0 ? qsTr("Please enter wallet password for:<br>") + usefulName(root.walletName) : qsTr("Please enter wallet password")
|
||||
text: root.walletName.length > 0 ? qsTr("Please enter wallet password for:<br>") + root.walletName : qsTr("Please enter wallet password")
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.columnSpan: 2
|
||||
Layout.fillWidth: true
|
||||
|
@ -27,6 +27,7 @@
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
Item {
|
||||
id: button
|
||||
@ -41,6 +42,10 @@ Item {
|
||||
property alias text: label.text
|
||||
signal clicked()
|
||||
|
||||
// Dynamic label width
|
||||
width: label.contentWidth + 20
|
||||
Layout.minimumWidth: 100
|
||||
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
@ -78,13 +83,13 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
elide: Text.ElideRight
|
||||
font.family: "Arial"
|
||||
font.bold: true
|
||||
font.letterSpacing: -1
|
||||
font.pixelSize: button.fontSize
|
||||
color: parent.textColor
|
||||
visible: parent.icon === ""
|
||||
font.capitalization : Font.AllUppercase
|
||||
}
|
||||
|
||||
Image {
|
||||
|
4
main.cpp
4
main.cpp
@ -40,6 +40,7 @@
|
||||
#include "Wallet.h"
|
||||
#include "QRCodeImageProvider.h"
|
||||
#include "PendingTransaction.h"
|
||||
#include "UnsignedTransaction.h"
|
||||
#include "TranslationManager.h"
|
||||
#include "TransactionInfo.h"
|
||||
#include "TransactionHistory.h"
|
||||
@ -72,6 +73,9 @@ int main(int argc, char *argv[])
|
||||
qmlRegisterUncreatableType<PendingTransaction>("moneroComponents.PendingTransaction", 1, 0, "PendingTransaction",
|
||||
"PendingTransaction can't be instantiated directly");
|
||||
|
||||
qmlRegisterUncreatableType<UnsignedTransaction>("moneroComponents.UnsignedTransaction", 1, 0, "UnsignedTransaction",
|
||||
"UnsignedTransaction can't be instantiated directly");
|
||||
|
||||
qmlRegisterUncreatableType<WalletManager>("moneroComponents.WalletManager", 1, 0, "WalletManager",
|
||||
"WalletManager can't be instantiated directly");
|
||||
|
||||
|
77
main.qml
77
main.qml
@ -228,6 +228,8 @@ ApplicationWindow {
|
||||
currentWallet = wallet
|
||||
updateSyncing(false)
|
||||
|
||||
viewOnly = currentWallet.viewOnly;
|
||||
|
||||
// connect handlers
|
||||
currentWallet.refreshed.connect(onWalletRefresh)
|
||||
currentWallet.updated.connect(onWalletUpdate)
|
||||
@ -252,12 +254,20 @@ ApplicationWindow {
|
||||
return wallet_path;
|
||||
}
|
||||
|
||||
function usefulName(path) {
|
||||
// arbitrary "short enough" limit
|
||||
if (path.length < 32)
|
||||
return path
|
||||
return path.replace(/.*[\/\\]/, '').replace(/\.keys$/, '')
|
||||
}
|
||||
|
||||
function onWalletConnectionStatusChanged(){
|
||||
console.log("Wallet connection status changed")
|
||||
middlePanel.updateStatus();
|
||||
}
|
||||
|
||||
function onWalletOpened(wallet) {
|
||||
walletName = usefulName(wallet.path)
|
||||
console.log(">>> wallet opened: " + wallet)
|
||||
if (wallet.status !== Wallet.Status_Ok) {
|
||||
if (appWindow.password === '') {
|
||||
@ -265,7 +275,7 @@ ApplicationWindow {
|
||||
console.log("closing wallet async : " + wallet.address)
|
||||
closeWallet();
|
||||
// try to open wallet with password;
|
||||
passwordDialog.open(wallet.path);
|
||||
passwordDialog.open(walletName);
|
||||
} else {
|
||||
// opening with password but password doesn't match
|
||||
console.error("Error opening wallet with password: ", wallet.errorString);
|
||||
@ -277,7 +287,7 @@ ApplicationWindow {
|
||||
closeWallet();
|
||||
informationPopup.open()
|
||||
informationPopup.onCloseCallback = function() {
|
||||
passwordDialog.open(wallet.path)
|
||||
passwordDialog.open(walletName)
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -285,7 +295,6 @@ ApplicationWindow {
|
||||
|
||||
// wallet opened successfully, subscribing for wallet updates
|
||||
connectWallet(wallet)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -466,7 +475,7 @@ ApplicationWindow {
|
||||
|
||||
|
||||
// called on "transfer"
|
||||
function handlePayment(address, paymentId, amount, mixinCount, priority, description) {
|
||||
function handlePayment(address, paymentId, amount, mixinCount, priority, description, createFile) {
|
||||
console.log("Creating transaction: ")
|
||||
console.log("\taddress: ", address,
|
||||
", payment_id: ", paymentId,
|
||||
@ -514,6 +523,24 @@ ApplicationWindow {
|
||||
currentWallet.createTransactionAsync(address, paymentId, amountxmr, mixinCount, priority);
|
||||
}
|
||||
|
||||
//Choose where to save transaction
|
||||
FileDialog {
|
||||
id: saveTxDialog
|
||||
title: "Please choose a location"
|
||||
folder: "file://" +moneroAccountsDir
|
||||
selectExisting: false;
|
||||
|
||||
onAccepted: {
|
||||
handleTransactionConfirmed()
|
||||
}
|
||||
onRejected: {
|
||||
// do nothing
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function handleSweepUnmixable() {
|
||||
console.log("Creating transaction: ")
|
||||
|
||||
@ -554,7 +581,7 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
// called after user confirms transaction
|
||||
function handleTransactionConfirmed() {
|
||||
function handleTransactionConfirmed(fileName) {
|
||||
// grab transaction.txid before commit, since it clears it.
|
||||
// we actually need to copy it, because QML will incredibly
|
||||
// call the function multiple times when the variable is used
|
||||
@ -565,6 +592,20 @@ ApplicationWindow {
|
||||
for (var i = 0; i < txid_org.length; ++i)
|
||||
txid[i] = txid_org[i]
|
||||
|
||||
// View only wallet - we save the tx
|
||||
if(viewOnly && saveTxDialog.fileUrl){
|
||||
// No file specified - abort
|
||||
if(!saveTxDialog.fileUrl) {
|
||||
currentWallet.disposeTransaction(transaction)
|
||||
return;
|
||||
}
|
||||
|
||||
var path = walletManager.urlToLocalPath(saveTxDialog.fileUrl)
|
||||
|
||||
// Store to file
|
||||
transaction.setFilename(path);
|
||||
}
|
||||
|
||||
if (!transaction.commit()) {
|
||||
console.log("Error committing transaction: " + transaction.errorString);
|
||||
informationPopup.title = qsTr("Error") + translationManager.emptyString
|
||||
@ -577,7 +618,7 @@ ApplicationWindow {
|
||||
txid_text += ", "
|
||||
txid_text += txid[i]
|
||||
}
|
||||
informationPopup.text = qsTr("Money sent successfully: %1 transaction(s) ").arg(txid.length) + txid_text + translationManager.emptyString
|
||||
informationPopup.text = (viewOnly)? qsTr("Transaction saved to file: %1").arg(path) : qsTr("Money sent successfully: %1 transaction(s) ").arg(txid.length) + txid_text + translationManager.emptyString
|
||||
informationPopup.icon = StandardIcon.Information
|
||||
if (transactionDescription.length > 0) {
|
||||
for (var i = 0; i < txid.length; ++i)
|
||||
@ -674,7 +715,6 @@ ApplicationWindow {
|
||||
rootItem.state = "wizard"
|
||||
}
|
||||
|
||||
|
||||
objectName: "appWindow"
|
||||
visible: true
|
||||
width: rightPanelExpanded ? 1269 : 1269 - 300
|
||||
@ -764,10 +804,31 @@ ApplicationWindow {
|
||||
id: transactionConfirmationPopup
|
||||
onAccepted: {
|
||||
close();
|
||||
handleTransactionConfirmed()
|
||||
|
||||
// Save transaction to file if view only wallet
|
||||
if(viewOnly) {
|
||||
saveTxDialog.open();
|
||||
return;
|
||||
} else
|
||||
handleTransactionConfirmed()
|
||||
}
|
||||
}
|
||||
|
||||
StandardDialog {
|
||||
id: confirmationDialog
|
||||
property var onAcceptedCallback
|
||||
property var onRejectedCallback
|
||||
onAccepted: {
|
||||
if (onAcceptedCallback)
|
||||
onAcceptedCallback()
|
||||
}
|
||||
onRejected: {
|
||||
if (onRejectedCallback)
|
||||
onRejectedCallback();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Open Wallet from file
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
|
@ -9,7 +9,7 @@ CONFIG += c++11
|
||||
# cleaning "auto-generated" bitmonero directory on "make distclean"
|
||||
QMAKE_DISTCLEAN += -r $$WALLET_ROOT
|
||||
|
||||
INCLUDEPATH += $$WALLET_ROOT/include \
|
||||
INCLUDEPATH += $$WALLET_ROOT/include \
|
||||
$$PWD/src/libwalletqt \
|
||||
$$PWD/src/QR-Code-generator \
|
||||
$$PWD/src \
|
||||
@ -36,7 +36,8 @@ HEADERS += \
|
||||
src/daemon/DaemonManager.h \
|
||||
src/model/AddressBookModel.h \
|
||||
src/libwalletqt/AddressBook.h \
|
||||
src/zxcvbn-c/zxcvbn.h
|
||||
src/zxcvbn-c/zxcvbn.h \
|
||||
src/libwalletqt/UnsignedTransaction.h
|
||||
|
||||
|
||||
SOURCES += main.cpp \
|
||||
@ -59,7 +60,8 @@ SOURCES += main.cpp \
|
||||
src/daemon/DaemonManager.cpp \
|
||||
src/model/AddressBookModel.cpp \
|
||||
src/libwalletqt/AddressBook.cpp \
|
||||
src/zxcvbn-c/zxcvbn.c
|
||||
src/zxcvbn-c/zxcvbn.c \
|
||||
src/libwalletqt/UnsignedTransaction.cpp
|
||||
|
||||
lupdate_only {
|
||||
SOURCES = *.qml \
|
||||
@ -289,7 +291,8 @@ OTHER_FILES += \
|
||||
$$TRANSLATIONS
|
||||
|
||||
DISTFILES += \
|
||||
notes.txt
|
||||
notes.txt \
|
||||
monero/src/wallet/CMakeLists.txt
|
||||
|
||||
|
||||
# windows application icon
|
||||
|
@ -39,51 +39,26 @@ import moneroComponents.Clipboard 1.0
|
||||
|
||||
Rectangle {
|
||||
property var daemonAddress
|
||||
property bool viewOnly: false
|
||||
|
||||
color: "#F0EEEE"
|
||||
|
||||
Clipboard { id: clipboard }
|
||||
|
||||
function initSettings() {
|
||||
//runs on every page load
|
||||
|
||||
|
||||
// Mnemonic seed settings
|
||||
memoTextInput.text = qsTr("Click button to show seed") + translationManager.emptyString
|
||||
showSeedButton.visible = true
|
||||
// Mnemonic seed setting
|
||||
memoTextInput.text = (viewOnly)? qsTr("View only wallets doesn't have a mnemonic seed") : qsTr("Click button to show seed") + translationManager.emptyString
|
||||
showSeedButton.enabled = !viewOnly
|
||||
|
||||
// Daemon settings
|
||||
|
||||
daemonAddress = persistentSettings.daemon_address.split(":");
|
||||
console.log("address: " + persistentSettings.daemon_address)
|
||||
// try connecting to daemon
|
||||
}
|
||||
|
||||
|
||||
PasswordDialog {
|
||||
id: settingsPasswordDialog
|
||||
|
||||
onAccepted: {
|
||||
if(appWindow.password === settingsPasswordDialog.password){
|
||||
memoTextInput.text = currentWallet.seed
|
||||
showSeedButton.visible = false
|
||||
} else {
|
||||
informationPopup.title = qsTr("Error") + translationManager.emptyString;
|
||||
informationPopup.text = qsTr("Wrong password");
|
||||
informationPopup.open()
|
||||
informationPopup.onCloseCallback = function() {
|
||||
settingsPasswordDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
settingsPasswordDialog.password = ""
|
||||
}
|
||||
onRejected: {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
anchors.margins: 40
|
||||
@ -92,17 +67,59 @@ Rectangle {
|
||||
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
|
||||
//! Manage wallet
|
||||
RowLayout {
|
||||
Label {
|
||||
id: manageWalletLabel
|
||||
Layout.fillWidth: true
|
||||
color: "#4A4949"
|
||||
text: qsTr("Manage wallet") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
Layout.topMargin: 10
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: "#DEDEDE"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
StandardButton {
|
||||
id: closeWalletButton
|
||||
width: 100
|
||||
text: qsTr("Close wallet") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
visible: true
|
||||
onClicked: {
|
||||
console.log("closing wallet button clicked")
|
||||
appWindow.showWizard();
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
enabled: !viewOnly
|
||||
id: createViewOnlyWalletButton
|
||||
text: qsTr("Create view only wallet") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
visible: true
|
||||
onClicked: {
|
||||
wizard.openCreateViewOnlyWalletPage();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//! show seed
|
||||
TextArea {
|
||||
enabled: !viewOnly
|
||||
id: memoTextInput
|
||||
textMargin: 6
|
||||
wrapMode: TextEdit.WordWrap
|
||||
@ -113,7 +130,7 @@ Rectangle {
|
||||
Layout.preferredHeight: 100
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
text: qsTr("Click button to show seed") + translationManager.emptyString
|
||||
text: (viewOnly)? qsTr("View only wallets doesn't have a mnemonic seed") : qsTr("Click button to show seed") + translationManager.emptyString
|
||||
|
||||
style: TextAreaStyle {
|
||||
backgroundColor: "#FFFFFF"
|
||||
@ -137,7 +154,9 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RowLayout {
|
||||
enabled: !viewOnly
|
||||
Layout.fillWidth: true
|
||||
Text {
|
||||
id: wordsTipText
|
||||
@ -151,37 +170,99 @@ Rectangle {
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//! Manage daemon
|
||||
RowLayout {
|
||||
Label {
|
||||
id: manageDaemonLabel
|
||||
color: "#4A4949"
|
||||
text: qsTr("Manage daemon") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
anchors.topMargin: 30
|
||||
Layout.topMargin: 30
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: "#DEDEDE"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
StandardButton {
|
||||
visible: true
|
||||
enabled: !appWindow.daemonRunning
|
||||
id: startDaemonButton
|
||||
text: qsTr("Start daemon") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
onClicked: {
|
||||
appWindow.startDaemon(daemonFlags.text)
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
visible: true
|
||||
enabled: appWindow.daemonRunning
|
||||
id: stopDaemonButton
|
||||
text: qsTr("Stop daemon") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
onClicked: {
|
||||
appWindow.stopDaemon()
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
visible: true
|
||||
id: daemonConsolePopupButton
|
||||
text: qsTr("Show log") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
onClicked: {
|
||||
daemonConsolePopup.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: daemonFlagsRow
|
||||
Label {
|
||||
id: daemonFlagsLabel
|
||||
color: "#4A4949"
|
||||
text: qsTr("Daemon startup flags") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
}
|
||||
LineEdit {
|
||||
id: daemonFlags
|
||||
Layout.preferredWidth: 200
|
||||
Layout.fillWidth: true
|
||||
text: appWindow.persistentSettings.daemonFlags;
|
||||
placeholderText: qsTr("(optional)") + translationManager.emptyString
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: daemonAddrRow
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
Layout.topMargin: 40
|
||||
spacing: 10
|
||||
|
||||
Label {
|
||||
@ -213,12 +294,8 @@ Rectangle {
|
||||
|
||||
StandardButton {
|
||||
id: daemonAddrSave
|
||||
|
||||
Layout.fillWidth: false
|
||||
|
||||
Layout.leftMargin: 30
|
||||
Layout.minimumWidth: 100
|
||||
width: 60
|
||||
text: qsTr("Save") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
@ -238,120 +315,19 @@ Rectangle {
|
||||
|
||||
}
|
||||
|
||||
|
||||
RowLayout {
|
||||
Label {
|
||||
id: closeWalletLabel
|
||||
|
||||
Layout.fillWidth: true
|
||||
color: "#4A4949"
|
||||
text: qsTr("Manage wallet") + translationManager.emptyString
|
||||
text: qsTr("Layout settings") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
anchors.topMargin: 30
|
||||
Layout.topMargin: 30
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
|
||||
Text {
|
||||
id: closeWalletTip
|
||||
font.family: "Arial"
|
||||
font.pointSize: 12
|
||||
color: "#4A4646"
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Close current wallet and open wizard")
|
||||
+ translationManager.emptyString
|
||||
}
|
||||
|
||||
|
||||
StandardButton {
|
||||
id: closeWalletButton
|
||||
|
||||
// Layout.leftMargin: 30
|
||||
// Layout.minimumWidth: 100
|
||||
width: 100
|
||||
text: qsTr("Close wallet") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
visible: true
|
||||
onClicked: {
|
||||
console.log("closing wallet button clicked")
|
||||
appWindow.showWizard();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Label {
|
||||
id: manageDaemonLabel
|
||||
color: "#4A4949"
|
||||
text: qsTr("Manage daemon") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
visible: true
|
||||
enabled: !appWindow.daemonRunning
|
||||
id: startDaemonButton
|
||||
width: 110
|
||||
text: qsTr("Start daemon") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
onClicked: {
|
||||
appWindow.startDaemon(daemonFlags.text)
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
visible: true
|
||||
enabled: appWindow.daemonRunning
|
||||
id: stopDaemonButton
|
||||
width: 110
|
||||
text: qsTr("Stop daemon") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
onClicked: {
|
||||
appWindow.stopDaemon()
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
visible: true
|
||||
// enabled: appWindow.daemonRunning
|
||||
id: daemonConsolePopupButton
|
||||
width: 110
|
||||
text: qsTr("Show log") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
onClicked: {
|
||||
daemonConsolePopup.open();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: daemonFlagsRow
|
||||
Label {
|
||||
id: daemonFlagsLabel
|
||||
color: "#4A4949"
|
||||
text: qsTr("Daemon startup flags") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
}
|
||||
LineEdit {
|
||||
id: daemonFlags
|
||||
Layout.preferredWidth: 200
|
||||
Layout.fillWidth: true
|
||||
text: appWindow.persistentSettings.daemonFlags;
|
||||
placeholderText: qsTr("(optional)") + translationManager.emptyString
|
||||
}
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: "#DEDEDE"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@ -386,6 +362,22 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
// Version
|
||||
RowLayout {
|
||||
Label {
|
||||
color: "#4A4949"
|
||||
text: qsTr("Version") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
anchors.topMargin: 30
|
||||
Layout.topMargin: 30
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: "#DEDEDE"
|
||||
}
|
||||
|
||||
Label {
|
||||
id: guiVersion
|
||||
Layout.topMargin: 8
|
||||
@ -414,11 +406,35 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
PasswordDialog {
|
||||
id: settingsPasswordDialog
|
||||
|
||||
onAccepted: {
|
||||
if(appWindow.password === settingsPasswordDialog.password){
|
||||
memoTextInput.text = currentWallet.seed
|
||||
showSeedButton.enabled = false
|
||||
} else {
|
||||
informationPopup.title = qsTr("Error") + translationManager.emptyString;
|
||||
informationPopup.text = qsTr("Wrong password");
|
||||
informationPopup.open()
|
||||
informationPopup.onCloseCallback = function() {
|
||||
settingsPasswordDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
settingsPasswordDialog.password = ""
|
||||
}
|
||||
onRejected: {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// fires on every page load
|
||||
function onPageCompleted() {
|
||||
console.log("Settings page loaded");
|
||||
initSettings();
|
||||
viewOnly = currentWallet.viewOnly;
|
||||
}
|
||||
|
||||
// fires only once
|
||||
|
@ -92,7 +92,10 @@ Rectangle {
|
||||
|
||||
Item {
|
||||
id: pageRoot
|
||||
anchors.fill: parent
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height:550
|
||||
Label {
|
||||
id: amountLabel
|
||||
anchors.left: parent.left
|
||||
@ -381,7 +384,7 @@ Rectangle {
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
enabled : pageRoot.checkInformation(amountLine.text, addressLine.text, paymentIdLine.text, appWindow.persistentSettings.testnet)
|
||||
enabled : !appWindow.viewOnly && pageRoot.checkInformation(amountLine.text, addressLine.text, paymentIdLine.text, appWindow.persistentSettings.testnet)
|
||||
onClicked: {
|
||||
console.log("Transfer: paymentClicked")
|
||||
var priority = priorityModel.get(priorityDropdown.currentIndex).priority
|
||||
@ -395,25 +398,7 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: sweepUnmixableButton
|
||||
anchors.right: parent.right
|
||||
anchors.top: descriptionLine.bottom
|
||||
anchors.rightMargin: 17
|
||||
anchors.topMargin: 17
|
||||
width: 60*2
|
||||
text: qsTr("SWEEP UNMIXABLE") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
enabled : true
|
||||
onClicked: {
|
||||
console.log("Transfer: sweepUnmixableClicked")
|
||||
root.sweepUnmixableClicked()
|
||||
|
||||
}
|
||||
}
|
||||
} // pageRoot
|
||||
|
||||
Rectangle {
|
||||
id:desaturate
|
||||
@ -422,7 +407,218 @@ Rectangle {
|
||||
opacity: 0.1
|
||||
visible: (pageRoot.enabled)? 0 : 1;
|
||||
}
|
||||
} // Rectangle
|
||||
|
||||
ColumnLayout {
|
||||
anchors.top: pageRoot.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 17
|
||||
spacing:10
|
||||
enabled: !viewOnly || pageRoot.enabled
|
||||
|
||||
RowLayout {
|
||||
Label {
|
||||
id: manageWalletLabel
|
||||
Layout.fillWidth: true
|
||||
color: "#4A4949"
|
||||
text: qsTr("Advanced") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
Layout.topMargin: 20
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: "#DEDEDE"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
StandardButton {
|
||||
id: sweepUnmixableButton
|
||||
text: qsTr("SWEEP UNMIXABLE") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
enabled : pageRoot.enabled
|
||||
onClicked: {
|
||||
console.log("Transfer: sweepUnmixableClicked")
|
||||
root.sweepUnmixableClicked()
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: saveTxButton
|
||||
text: qsTr("create tx file") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
visible: appWindow.viewOnly
|
||||
enabled: pageRoot.checkInformation(amountLine.text, addressLine.text, paymentIdLine.text, appWindow.persistentSettings.testnet)
|
||||
onClicked: {
|
||||
console.log("Transfer: saveTx Clicked")
|
||||
var priority = priorityModel.get(priorityDropdown.currentIndex).priority
|
||||
console.log("priority: " + priority)
|
||||
console.log("amount: " + amountLine.text)
|
||||
addressLine.text = addressLine.text.trim()
|
||||
paymentIdLine.text = paymentIdLine.text.trim()
|
||||
root.paymentClicked(addressLine.text, paymentIdLine.text, amountLine.text, scaleValueToMixinCount(privacyLevelItem.fillLevel),
|
||||
priority, descriptionLine.text)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: signTxButton
|
||||
text: qsTr("sign tx file") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
visible: !appWindow.viewOnly
|
||||
onClicked: {
|
||||
console.log("Transfer: sign tx clicked")
|
||||
signTxDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: submitTxButton
|
||||
text: qsTr("submit tx file") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
visible: appWindow.viewOnly
|
||||
enabled: pageRoot.enabled
|
||||
onClicked: {
|
||||
console.log("Transfer: submit tx clicked")
|
||||
submitTxDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: rescanSpentButton
|
||||
text: qsTr("Rescan spent") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
enabled: pageRoot.enabled
|
||||
onClicked: {
|
||||
if (!currentWallet.rescanSpent()) {
|
||||
console.error("Error: ", currentWallet.errorString);
|
||||
informationPopup.title = qsTr("Error") + translationManager.emptyString;
|
||||
informationPopup.text = qsTr("Error: ") + currentWallet.errorString
|
||||
informationPopup.icon = StandardIcon.Critical
|
||||
informationPopup.onCloseCallback = null
|
||||
informationPopup.open();
|
||||
} else {
|
||||
informationPopup.title = qsTr("Information") + translationManager.emptyString
|
||||
informationPopup.text = qsTr("Sucessfully rescanned spent outputs") + translationManager.emptyString
|
||||
informationPopup.icon = StandardIcon.Information
|
||||
informationPopup.onCloseCallback = null
|
||||
informationPopup.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//SignTxDialog
|
||||
FileDialog {
|
||||
id: signTxDialog
|
||||
title: "Please choose a file"
|
||||
folder: "file://" +moneroAccountsDir
|
||||
nameFilters: [ "Unsigned transfers (*)"]
|
||||
|
||||
onAccepted: {
|
||||
var path = walletManager.urlToLocalPath(fileUrl);
|
||||
// Load the unsigned tx from file
|
||||
var transaction = currentWallet.loadTxFile(path);
|
||||
|
||||
if (transaction.status !== PendingTransaction.Status_Ok) {
|
||||
console.error("Can't load unsigned transaction: ", transaction.errorString);
|
||||
informationPopup.title = qsTr("Error") + translationManager.emptyString;
|
||||
informationPopup.text = qsTr("Can't load unsigned transaction: ") + transaction.errorString
|
||||
informationPopup.icon = StandardIcon.Critical
|
||||
informationPopup.onCloseCallback = null
|
||||
informationPopup.open();
|
||||
// deleting transaction object, we don't want memleaks
|
||||
transaction.destroy();
|
||||
} else {
|
||||
confirmationDialog.text = qsTr("\nNumber of transactions: ") + transaction.txCount
|
||||
for (var i = 0; i < transaction.txCount; ++i) {
|
||||
confirmationDialog.text += qsTr("\nTransaction #%1").arg(i+1)
|
||||
+qsTr("\nRecipient: ") + transaction.recipientAddress[i]
|
||||
+ (transaction.paymentId[i] == "" ? "" : qsTr("\n\payment ID: ") + transaction.paymentId[i])
|
||||
+ qsTr("\nAmount: ") + walletManager.displayAmount(transaction.amount(i))
|
||||
+ qsTr("\nFee: ") + walletManager.displayAmount(transaction.fee(i))
|
||||
+ qsTr("\nMixin: ") + transaction.mixin(i)
|
||||
|
||||
// TODO: add descriptions to unsigned_tx_set?
|
||||
// + (transactionDescription === "" ? "" : (qsTr("\n\nDescription: ") + transactionDescription))
|
||||
+ translationManager.emptyString
|
||||
if (i > 0) {
|
||||
confirmationDialog.text += "\n\n"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
console.log(transaction.confirmationMessage);
|
||||
|
||||
// Show confirmation dialog
|
||||
confirmationDialog.title = qsTr("Confirmation") + translationManager.emptyString
|
||||
confirmationDialog.icon = StandardIcon.Question
|
||||
confirmationDialog.onAcceptedCallback = function() {
|
||||
transaction.sign(path+"_signed");
|
||||
transaction.destroy();
|
||||
};
|
||||
confirmationDialog.onRejectedCallback = transaction.destroy;
|
||||
|
||||
confirmationDialog.open()
|
||||
}
|
||||
|
||||
}
|
||||
onRejected: {
|
||||
// File dialog closed
|
||||
console.log("Canceled")
|
||||
}
|
||||
}
|
||||
|
||||
//SignTxDialog
|
||||
FileDialog {
|
||||
id: submitTxDialog
|
||||
title: "Please choose a file"
|
||||
folder: "file://" +moneroAccountsDir
|
||||
nameFilters: [ "signed transfers (*)"]
|
||||
|
||||
onAccepted: {
|
||||
if(!currentWallet.submitTxFile(walletManager.urlToLocalPath(fileUrl))){
|
||||
informationPopup.title = qsTr("Error") + translationManager.emptyString;
|
||||
informationPopup.text = qsTr("Can't submit transaction: ") + currentWallet.errorString
|
||||
informationPopup.icon = StandardIcon.Critical
|
||||
informationPopup.onCloseCallback = null
|
||||
informationPopup.open();
|
||||
} else {
|
||||
informationPopup.title = qsTr("Information") + translationManager.emptyString
|
||||
informationPopup.text = qsTr("Money sent successfully") + translationManager.emptyString
|
||||
informationPopup.icon = StandardIcon.Information
|
||||
informationPopup.onCloseCallback = null
|
||||
informationPopup.open();
|
||||
}
|
||||
}
|
||||
onRejected: {
|
||||
console.log("Canceled")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
x: root.width/2 - width/2
|
||||
@ -464,6 +660,12 @@ Rectangle {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentWallet.viewOnly) {
|
||||
// statusText.text = qsTr("Wallet is view only.")
|
||||
//return;
|
||||
}
|
||||
pageRoot.enabled = false;
|
||||
|
||||
switch (currentWallet.connected) {
|
||||
case Wallet.ConnectionStatus_Disconnected:
|
||||
statusText.text = qsTr("Wallet is not connected to daemon.") + "<br>" + root.startLinkText
|
||||
|
2
qml.qrc
2
qml.qrc
@ -122,5 +122,7 @@
|
||||
<file>pages/Sign.qml</file>
|
||||
<file>components/DaemonManagerDialog.qml</file>
|
||||
<file>version.js</file>
|
||||
<file>wizard/WizardPasswordUI.qml</file>
|
||||
<file>wizard/WizardCreateViewOnlyWallet.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -13,7 +13,10 @@ QString PendingTransaction::errorString() const
|
||||
|
||||
bool PendingTransaction::commit()
|
||||
{
|
||||
return m_pimpl->commit();
|
||||
// Save transaction to file if fileName is set.
|
||||
if(!m_fileName.isEmpty())
|
||||
return m_pimpl->commit(m_fileName.toStdString());
|
||||
return m_pimpl->commit(m_fileName.toStdString());
|
||||
}
|
||||
|
||||
quint64 PendingTransaction::amount() const
|
||||
@ -47,6 +50,11 @@ quint64 PendingTransaction::txCount() const
|
||||
return m_pimpl->txCount();
|
||||
}
|
||||
|
||||
void PendingTransaction::setFilename(const QString &fileName)
|
||||
{
|
||||
m_fileName = fileName;
|
||||
}
|
||||
|
||||
PendingTransaction::PendingTransaction(Monero::PendingTransaction *pt, QObject *parent)
|
||||
: QObject(parent), m_pimpl(pt)
|
||||
{
|
||||
|
@ -44,6 +44,7 @@ public:
|
||||
quint64 fee() const;
|
||||
QStringList txid() const;
|
||||
quint64 txCount() const;
|
||||
Q_INVOKABLE void setFilename(const QString &fileName);
|
||||
|
||||
private:
|
||||
explicit PendingTransaction(Monero::PendingTransaction * pt, QObject *parent = 0);
|
||||
@ -51,6 +52,7 @@ private:
|
||||
private:
|
||||
friend class Wallet;
|
||||
Monero::PendingTransaction * m_pimpl;
|
||||
QString m_fileName;
|
||||
};
|
||||
|
||||
#endif // PENDINGTRANSACTION_H
|
||||
|
92
src/libwalletqt/UnsignedTransaction.cpp
Normal file
92
src/libwalletqt/UnsignedTransaction.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
#include "UnsignedTransaction.h"
|
||||
#include <QVector>
|
||||
#include <QDebug>
|
||||
|
||||
UnsignedTransaction::Status UnsignedTransaction::status() const
|
||||
{
|
||||
return static_cast<Status>(m_pimpl->status());
|
||||
}
|
||||
|
||||
QString UnsignedTransaction::errorString() const
|
||||
{
|
||||
return QString::fromStdString(m_pimpl->errorString());
|
||||
}
|
||||
|
||||
quint64 UnsignedTransaction::amount(int index) const
|
||||
{
|
||||
std::vector<uint64_t> arr = m_pimpl->amount();
|
||||
if(index > arr.size() - 1)
|
||||
return 0;
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
quint64 UnsignedTransaction::fee(int index) const
|
||||
{
|
||||
std::vector<uint64_t> arr = m_pimpl->fee();
|
||||
if(index > arr.size() - 1)
|
||||
return 0;
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
quint64 UnsignedTransaction::mixin(int index) const
|
||||
{
|
||||
std::vector<uint64_t> arr = m_pimpl->mixin();
|
||||
if(index > arr.size() - 1)
|
||||
return 0;
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
quint64 UnsignedTransaction::txCount() const
|
||||
{
|
||||
return m_pimpl->txCount();
|
||||
}
|
||||
|
||||
quint64 UnsignedTransaction::minMixinCount() const
|
||||
{
|
||||
return m_pimpl->minMixinCount();
|
||||
}
|
||||
|
||||
QString UnsignedTransaction::confirmationMessage() const
|
||||
{
|
||||
return QString::fromStdString(m_pimpl->confirmationMessage());
|
||||
}
|
||||
|
||||
QStringList UnsignedTransaction::paymentId() const
|
||||
{
|
||||
QList<QString> list;
|
||||
for (const auto &t: m_pimpl->paymentId())
|
||||
list.append(QString::fromStdString(t));
|
||||
return list;
|
||||
}
|
||||
|
||||
QStringList UnsignedTransaction::recipientAddress() const
|
||||
{
|
||||
QList<QString> list;
|
||||
for (const auto &t: m_pimpl->recipientAddress())
|
||||
list.append(QString::fromStdString(t));
|
||||
return list;
|
||||
}
|
||||
|
||||
bool UnsignedTransaction::sign(const QString &fileName) const
|
||||
{
|
||||
if(!m_pimpl->sign(fileName.toStdString()))
|
||||
return false;
|
||||
// export key images
|
||||
return m_walletImpl->exportKeyImages(fileName.toStdString() + "_keyImages");
|
||||
}
|
||||
|
||||
void UnsignedTransaction::setFilename(const QString &fileName)
|
||||
{
|
||||
m_fileName = fileName;
|
||||
}
|
||||
|
||||
UnsignedTransaction::UnsignedTransaction(Monero::UnsignedTransaction *pt, Monero::Wallet *walletImpl, QObject *parent)
|
||||
: QObject(parent), m_pimpl(pt), m_walletImpl(walletImpl)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
UnsignedTransaction::~UnsignedTransaction()
|
||||
{
|
||||
delete m_pimpl;
|
||||
}
|
59
src/libwalletqt/UnsignedTransaction.h
Normal file
59
src/libwalletqt/UnsignedTransaction.h
Normal file
@ -0,0 +1,59 @@
|
||||
#ifndef UNSIGNEDTRANSACTION_H
|
||||
#define UNSIGNEDTRANSACTION_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <wallet/wallet2_api.h>
|
||||
|
||||
class UnsignedTransaction : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(Status status READ status)
|
||||
Q_PROPERTY(QString errorString READ errorString)
|
||||
// Q_PROPERTY(QList<qulonglong> amount READ amount)
|
||||
// Q_PROPERTY(QList<qulonglong> fee READ fee)
|
||||
Q_PROPERTY(quint64 txCount READ txCount)
|
||||
Q_PROPERTY(QString confirmationMessage READ confirmationMessage)
|
||||
Q_PROPERTY(QStringList recipientAddress READ recipientAddress)
|
||||
Q_PROPERTY(QStringList paymentId READ paymentId)
|
||||
Q_PROPERTY(quint64 minMixinCount READ minMixinCount)
|
||||
|
||||
public:
|
||||
enum Status {
|
||||
Status_Ok = Monero::UnsignedTransaction::Status_Ok,
|
||||
Status_Error = Monero::UnsignedTransaction::Status_Error,
|
||||
Status_Critical = Monero::UnsignedTransaction::Status_Critical
|
||||
};
|
||||
Q_ENUM(Status)
|
||||
|
||||
enum Priority {
|
||||
Priority_Low = Monero::UnsignedTransaction::Priority_Low,
|
||||
Priority_Medium = Monero::UnsignedTransaction::Priority_Medium,
|
||||
Priority_High = Monero::UnsignedTransaction::Priority_High
|
||||
};
|
||||
Q_ENUM(Priority)
|
||||
|
||||
Status status() const;
|
||||
QString errorString() const;
|
||||
Q_INVOKABLE quint64 amount(int index) const;
|
||||
Q_INVOKABLE quint64 fee(int index) const;
|
||||
Q_INVOKABLE quint64 mixin(int index) const;
|
||||
QStringList recipientAddress() const;
|
||||
QStringList paymentId() const;
|
||||
quint64 txCount() const;
|
||||
QString confirmationMessage() const;
|
||||
quint64 minMixinCount() const;
|
||||
Q_INVOKABLE bool sign(const QString &fileName) const;
|
||||
Q_INVOKABLE void setFilename(const QString &fileName);
|
||||
|
||||
private:
|
||||
explicit UnsignedTransaction(Monero::UnsignedTransaction * pt, Monero::Wallet *walletImpl, QObject *parent = 0);
|
||||
~UnsignedTransaction();
|
||||
private:
|
||||
friend class Wallet;
|
||||
Monero::UnsignedTransaction * m_pimpl;
|
||||
QString m_fileName;
|
||||
Monero::Wallet * m_walletImpl;
|
||||
};
|
||||
|
||||
#endif // UNSIGNEDTRANSACTION_H
|
@ -1,5 +1,6 @@
|
||||
#include "Wallet.h"
|
||||
#include "PendingTransaction.h"
|
||||
#include "UnsignedTransaction.h"
|
||||
#include "TransactionHistory.h"
|
||||
#include "AddressBook.h"
|
||||
#include "model/TransactionHistoryModel.h"
|
||||
@ -158,6 +159,15 @@ void Wallet::initAsync(const QString &daemonAddress, quint64 upperTransactionLim
|
||||
m_walletImpl->initAsync(daemonAddress.toStdString(), upperTransactionLimit);
|
||||
}
|
||||
|
||||
//! create a view only wallet
|
||||
bool Wallet::createViewOnly(const QString &path, const QString &password) const
|
||||
{
|
||||
// Create path
|
||||
QDir d = QFileInfo(path).absoluteDir();
|
||||
d.mkpath(d.absolutePath());
|
||||
return m_walletImpl->createWatchOnly(path.toStdString(),password.toStdString(),m_walletImpl->getSeedLanguage());
|
||||
}
|
||||
|
||||
bool Wallet::connectToDaemon()
|
||||
{
|
||||
return m_walletImpl->connectToDaemon();
|
||||
@ -168,6 +178,11 @@ void Wallet::setTrustedDaemon(bool arg)
|
||||
m_walletImpl->setTrustedDaemon(arg);
|
||||
}
|
||||
|
||||
bool Wallet::viewOnly() const
|
||||
{
|
||||
return m_walletImpl->watchOnly();
|
||||
}
|
||||
|
||||
quint64 Wallet::balance() const
|
||||
{
|
||||
return m_walletImpl->balance();
|
||||
@ -197,7 +212,6 @@ quint64 Wallet::daemonBlockChainHeight() const
|
||||
|
||||
quint64 Wallet::daemonBlockChainTargetHeight() const
|
||||
{
|
||||
|
||||
if (m_daemonBlockChainTargetHeight == 0
|
||||
|| m_daemonBlockChainTargetHeightTime.elapsed() / 1000 > m_daemonBlockChainTargetHeightTtl) {
|
||||
m_daemonBlockChainTargetHeight = m_walletImpl->daemonBlockChainTargetHeight();
|
||||
@ -309,12 +323,34 @@ void Wallet::createSweepUnmixableTransactionAsync()
|
||||
});
|
||||
}
|
||||
|
||||
UnsignedTransaction * Wallet::loadTxFile(const QString &fileName)
|
||||
{
|
||||
qDebug() << "Trying to sign " << fileName;
|
||||
Monero::UnsignedTransaction * ptImpl = m_walletImpl->loadUnsignedTx(fileName.toStdString());
|
||||
UnsignedTransaction * result = new UnsignedTransaction(ptImpl, m_walletImpl, this);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Wallet::submitTxFile(const QString &fileName) const
|
||||
{
|
||||
qDebug() << "Trying to submit " << fileName;
|
||||
if (!m_walletImpl->submitTransaction(fileName.toStdString()))
|
||||
return false;
|
||||
// import key images
|
||||
return m_walletImpl->importKeyImages(fileName.toStdString() + "_keyImages");
|
||||
}
|
||||
|
||||
void Wallet::disposeTransaction(PendingTransaction *t)
|
||||
{
|
||||
m_walletImpl->disposeTransaction(t->m_pimpl);
|
||||
delete t;
|
||||
}
|
||||
|
||||
void Wallet::disposeTransaction(UnsignedTransaction *t)
|
||||
{
|
||||
delete t;
|
||||
}
|
||||
|
||||
TransactionHistory *Wallet::history() const
|
||||
{
|
||||
return m_history;
|
||||
@ -474,6 +510,11 @@ bool Wallet::parse_uri(const QString &uri, QString &address, QString &payment_id
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Wallet::rescanSpent()
|
||||
{
|
||||
return m_walletImpl->rescanSpent();
|
||||
}
|
||||
|
||||
Wallet::Wallet(Monero::Wallet *w, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_walletImpl(w)
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "wallet/wallet2_api.h" // we need to have an access to the Monero::Wallet::Status enum here;
|
||||
#include "PendingTransaction.h" // we need to have an access to the PendingTransaction::Priority enum here;
|
||||
#include "UnsignedTransaction.h"
|
||||
|
||||
namespace Monero {
|
||||
class Wallet; // forward declaration
|
||||
@ -36,7 +37,7 @@ class Wallet : public QObject
|
||||
Q_PROPERTY(QString path READ path)
|
||||
Q_PROPERTY(AddressBookModel * addressBookModel READ addressBookModel)
|
||||
Q_PROPERTY(AddressBook * addressBook READ addressBook)
|
||||
|
||||
Q_PROPERTY(bool viewOnly READ viewOnly)
|
||||
|
||||
public:
|
||||
|
||||
@ -98,6 +99,9 @@ public:
|
||||
//! initializes wallet asynchronously
|
||||
Q_INVOKABLE void initAsync(const QString &daemonAddress, quint64 upperTransactionLimit, bool isRecovering = false, quint64 restoreHeight = 0);
|
||||
|
||||
//! create a view only wallet
|
||||
Q_INVOKABLE bool createViewOnly(const QString &path, const QString &password) const;
|
||||
|
||||
//! connects to daemon
|
||||
Q_INVOKABLE bool connectToDaemon();
|
||||
|
||||
@ -110,6 +114,9 @@ public:
|
||||
//! returns unlocked balance
|
||||
Q_INVOKABLE quint64 unlockedBalance() const;
|
||||
|
||||
//! returns if view only wallet
|
||||
Q_INVOKABLE bool viewOnly() const;
|
||||
|
||||
//! returns current wallet's block height
|
||||
//! (can be less than daemon's blockchain height when wallet sync in progress)
|
||||
Q_INVOKABLE quint64 blockChainHeight() const;
|
||||
@ -156,9 +163,19 @@ public:
|
||||
//! creates async sweep unmixable transaction
|
||||
Q_INVOKABLE void createSweepUnmixableTransactionAsync();
|
||||
|
||||
//! Sign a transfer from file
|
||||
Q_INVOKABLE UnsignedTransaction * loadTxFile(const QString &fileName);
|
||||
|
||||
//! Submit a transfer from file
|
||||
Q_INVOKABLE bool submitTxFile(const QString &fileName) const;
|
||||
|
||||
|
||||
//! deletes transaction and frees memory
|
||||
Q_INVOKABLE void disposeTransaction(PendingTransaction * t);
|
||||
|
||||
//! deletes unsigned transaction and frees memory
|
||||
Q_INVOKABLE void disposeTransaction(UnsignedTransaction * t);
|
||||
|
||||
//! returns transaction history
|
||||
TransactionHistory * history() const;
|
||||
|
||||
@ -193,8 +210,9 @@ public:
|
||||
|
||||
Q_INVOKABLE bool setUserNote(const QString &txid, const QString ¬e);
|
||||
Q_INVOKABLE QString getUserNote(const QString &txid) const;
|
||||
|
||||
Q_INVOKABLE QString getTxKey(const QString &txid) const;
|
||||
// Rescan spent outputs
|
||||
Q_INVOKABLE bool rescanSpent();
|
||||
|
||||
// TODO: setListenter() when it implemented in API
|
||||
signals:
|
||||
|
94
wizard/WizardCreateViewOnlyWallet.qml
Normal file
94
wizard/WizardCreateViewOnlyWallet.qml
Normal file
@ -0,0 +1,94 @@
|
||||
// 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 moneroComponents.WalletManager 1.0
|
||||
import QtQuick 2.2
|
||||
import "../components"
|
||||
import "utils.js" as Utils
|
||||
|
||||
Item {
|
||||
|
||||
id: passwordPage
|
||||
opacity: 0
|
||||
visible: false
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 100; easing.type: Easing.InQuad }
|
||||
}
|
||||
|
||||
onOpacityChanged: visible = opacity !== 0
|
||||
|
||||
|
||||
function onPageOpened(settingsObject) {
|
||||
wizard.nextButton.enabled = true
|
||||
}
|
||||
|
||||
function onPageClosed(settingsObject) {
|
||||
var walletFullPath = wizard.createWalletPath(uiItem.walletPath,uiItem.accountNameText);
|
||||
settingsObject['view_only_wallet_path'] = walletFullPath
|
||||
console.log("wallet path", walletFullPath)
|
||||
return wizard.walletPathValid(walletFullPath);
|
||||
}
|
||||
|
||||
Row {
|
||||
id: dotsRow
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 85
|
||||
spacing: 6
|
||||
|
||||
ListModel {
|
||||
id: dotsModel
|
||||
ListElement { dotColor: "#FFE00A" }
|
||||
ListElement { dotColor: "#DBDBDB" }
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: dotsModel
|
||||
delegate: Rectangle {
|
||||
width: 12; height: 12
|
||||
radius: 6
|
||||
color: dotColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WizardManageWalletUI {
|
||||
id: uiItem
|
||||
titleText: qsTr("Give your view only wallet a name") + translationManager.emptyString
|
||||
wordsTextItem.visible: false
|
||||
restoreHeightVisible:false
|
||||
walletName: appWindow.walletName + "-viewonly"
|
||||
progressDotsModel: dotsModel
|
||||
}
|
||||
|
||||
|
||||
Component.onCompleted: {
|
||||
//parent.wizardRestarted.connect(onWizardRestarted)
|
||||
}
|
||||
}
|
@ -44,6 +44,7 @@ Rectangle {
|
||||
// disable donation page
|
||||
"create_wallet" : [welcomePage, optionsPage, createWalletPage, passwordPage, finishPage ],
|
||||
"recovery_wallet" : [welcomePage, optionsPage, recoveryWalletPage, passwordPage, finishPage ],
|
||||
"create_view_only_wallet" : [ createViewOnlyWalletPage, passwordPage ],
|
||||
|
||||
}
|
||||
property string currentPath: "create_wallet"
|
||||
@ -89,15 +90,12 @@ Rectangle {
|
||||
currentPage += step_value
|
||||
pages[currentPage].opacity = 1;
|
||||
|
||||
var nextButtonVisible = pages[currentPage] !== optionsPage;
|
||||
var nextButtonVisible = pages[currentPage] !== optionsPage && currentPage < pages.length - 1;
|
||||
nextButton.visible = nextButtonVisible;
|
||||
|
||||
if (typeof pages[currentPage].onPageOpened !== 'undefined') {
|
||||
pages[currentPage].onPageOpened(settings,next)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,6 +128,16 @@ Rectangle {
|
||||
wizard.openWalletFromFileClicked();
|
||||
}
|
||||
|
||||
function openCreateViewOnlyWalletPage(){
|
||||
pages[currentPage].opacity = 0
|
||||
currentPath = "create_view_only_wallet"
|
||||
pages = paths[currentPath]
|
||||
currentPage = pages.indexOf(createViewOnlyWalletPage)
|
||||
createViewOnlyWalletPage.opacity = 1
|
||||
nextButton.visible = true
|
||||
rootItem.state = "wizard";
|
||||
}
|
||||
|
||||
function createWalletPath(folder_path,account_name){
|
||||
|
||||
// Remove trailing slash - (default on windows and mac)
|
||||
@ -274,6 +282,16 @@ Rectangle {
|
||||
anchors.rightMargin: 50
|
||||
}
|
||||
|
||||
WizardCreateViewOnlyWallet {
|
||||
id: createViewOnlyWalletPage
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: nextButton.left
|
||||
anchors.left: prevButton.right
|
||||
anchors.leftMargin: 50
|
||||
anchors.rightMargin: 50
|
||||
}
|
||||
|
||||
WizardRecoveryWallet {
|
||||
id: recoveryWalletPage
|
||||
anchors.top: parent.top
|
||||
@ -356,4 +374,59 @@ Rectangle {
|
||||
wizard.useMoneroClicked();
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: createViewOnlyWalletButton
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: 50
|
||||
width: 110
|
||||
text: qsTr("Create wallet") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
visible: currentPath === "create_view_only_wallet" && parent.paths[currentPath][currentPage] === passwordPage
|
||||
enabled: passwordPage.passwordsMatch
|
||||
onClicked: {
|
||||
if (currentWallet.createViewOnly(settings['view_only_wallet_path'],passwordPage.password)) {
|
||||
console.log("view only wallet created in ",settings['view_only_wallet_path']);
|
||||
informationPopup.title = qsTr("Success") + translationManager.emptyString;
|
||||
informationPopup.text = qsTr('The view only wallet has been created. You can open it by closing this current wallet, clicking the "Open wallet from file" option, and selecting the view wallet in: \n%1')
|
||||
.arg(settings['view_only_wallet_path']);
|
||||
informationPopup.open()
|
||||
informationPopup.onCloseCallback = null
|
||||
rootItem.state = "normal"
|
||||
wizard.restart();
|
||||
|
||||
} else {
|
||||
informationPopup.title = qsTr("Error") + translationManager.emptyString;
|
||||
informationPopup.text = currentWallet.errorString;
|
||||
informationPopup.open()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: abortViewOnlyButton
|
||||
anchors.right: createViewOnlyWalletButton.left
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: 50
|
||||
width: 110
|
||||
text: qsTr("Abort") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
visible: currentPath === "create_view_only_wallet" && parent.paths[currentPath][currentPage] === passwordPage
|
||||
onClicked: {
|
||||
wizard.restart();
|
||||
rootItem.state = "normal"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -43,7 +43,8 @@ Item {
|
||||
property alias wordsTextItem : memoTextItem
|
||||
property alias restoreHeight : restoreHeightItem.text
|
||||
property alias restoreHeightVisible: restoreHeightItem.visible
|
||||
|
||||
property alias walletName : accountName.text
|
||||
property alias progressDotsModel : progressDots.model
|
||||
|
||||
// TODO extend properties if needed
|
||||
|
||||
@ -64,6 +65,7 @@ Item {
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: progressDots
|
||||
model: dotsModel
|
||||
delegate: Rectangle {
|
||||
width: 12; height: 12
|
||||
@ -184,7 +186,7 @@ Item {
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: (restoreHeightItem.visible)? restoreHeightItem.bottom : memoTextItem.bottom
|
||||
anchors.top: (restoreHeightItem.visible)? restoreHeightItem.bottom : (memoTextItem.visible)? memoTextItem.bottom : frameHeader.bottom
|
||||
anchors.topMargin: 24
|
||||
spacing: 16
|
||||
|
||||
|
@ -36,8 +36,9 @@ Item {
|
||||
id: passwordPage
|
||||
opacity: 0
|
||||
visible: false
|
||||
|
||||
property alias titleText: titleText.text
|
||||
property alias passwordsMatch: passwordUI.passwordsMatch
|
||||
property alias password: passwordUI.password
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 100; easing.type: Easing.InQuad }
|
||||
}
|
||||
@ -47,7 +48,7 @@ Item {
|
||||
|
||||
function onPageOpened(settingsObject) {
|
||||
wizard.nextButton.enabled = true
|
||||
handlePassword();
|
||||
passwordUI.handlePassword();
|
||||
|
||||
if (wizard.currentPath === "create_wallet") {
|
||||
passwordPage.titleText = qsTr("Give your wallet a password") + translationManager.emptyString
|
||||
@ -55,44 +56,22 @@ Item {
|
||||
passwordPage.titleText = qsTr("Give your wallet a password") + translationManager.emptyString
|
||||
}
|
||||
|
||||
passwordItem.focus = true;
|
||||
passwordUI.focus = true;
|
||||
}
|
||||
|
||||
function onPageClosed(settingsObject) {
|
||||
// TODO: set password on the final page
|
||||
// settingsObject.wallet.setPassword(passwordItem.password)
|
||||
settingsObject['wallet_password'] = passwordItem.password
|
||||
settingsObject['wallet_password'] = passwordUI.password
|
||||
return true
|
||||
}
|
||||
|
||||
function onWizardRestarted(){
|
||||
// Reset password fields
|
||||
passwordItem.password = "";
|
||||
retypePasswordItem.password = "";
|
||||
passwordUI.password = "";
|
||||
passwordUI.confirmPassword = "";
|
||||
}
|
||||
|
||||
function handlePassword() {
|
||||
// allow to forward step only if passwords match
|
||||
|
||||
wizard.nextButton.enabled = passwordItem.password === retypePasswordItem.password
|
||||
|
||||
// scorePassword returns value from 0 to... lots
|
||||
var strength = walletManager.getPasswordStrength(passwordItem.password);
|
||||
// consider anything below 10 bits as dire
|
||||
strength -= 10
|
||||
if (strength < 0)
|
||||
strength = 0
|
||||
// use a slight parabola to discourage short passwords
|
||||
strength = strength ^ 1.2 / 3
|
||||
// mapScope does not clamp
|
||||
if (strength > 100)
|
||||
strength = 100
|
||||
// privacyLevel component uses 1..13 scale
|
||||
privacyLevel.fillLevel = Utils.mapScope(1, 100, 1, 13, strength)
|
||||
}
|
||||
|
||||
|
||||
|
||||
Row {
|
||||
id: dotsRow
|
||||
anchors.top: parent.top
|
||||
@ -111,6 +90,9 @@ Item {
|
||||
Repeater {
|
||||
model: dotsModel
|
||||
delegate: Rectangle {
|
||||
// Password page is last page when creating view only wallet
|
||||
// TODO: make this dynamic for all pages in wizard
|
||||
visible: (wizard.currentPath != "create_view_only_wallet" || index < 2)
|
||||
width: 12; height: 12
|
||||
radius: 6
|
||||
color: dotColor
|
||||
@ -157,39 +139,12 @@ Item {
|
||||
}
|
||||
|
||||
|
||||
WizardPasswordInput {
|
||||
id: passwordItem
|
||||
anchors.top: headerColumn.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
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
|
||||
WizardPasswordUI {
|
||||
id: passwordUI
|
||||
anchors.right: parent.right
|
||||
anchors.top: retypePasswordItem.bottom
|
||||
anchors.topMargin: 60
|
||||
background: "#F0EEEE"
|
||||
interactive: false
|
||||
anchors.left: parent.left
|
||||
anchors.top: headerColumn.bottom
|
||||
anchors.topMargin: 30
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
|
96
wizard/WizardPasswordUI.qml
Normal file
96
wizard/WizardPasswordUI.qml
Normal file
@ -0,0 +1,96 @@
|
||||
// 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 moneroComponents.WalletManager 1.0
|
||||
import QtQuick 2.2
|
||||
import "../components"
|
||||
import "utils.js" as Utils
|
||||
|
||||
FocusScope {
|
||||
property alias password: passwordItem.password
|
||||
property alias confirmPassword: retypePasswordItem.password
|
||||
property bool passwordsMatch: passwordItem.password === retypePasswordItem.password
|
||||
|
||||
function handlePassword() {
|
||||
// allow to forward step only if passwords match
|
||||
|
||||
wizard.nextButton.enabled = passwordItem.password === retypePasswordItem.password
|
||||
|
||||
// scorePassword returns value from 0 to... lots
|
||||
var strength = walletManager.getPasswordStrength(passwordItem.password);
|
||||
// consider anything below 10 bits as dire
|
||||
strength -= 10
|
||||
if (strength < 0)
|
||||
strength = 0
|
||||
// use a slight parabola to discourage short passwords
|
||||
strength = strength ^ 1.2 / 3
|
||||
// mapScope does not clamp
|
||||
if (strength > 100)
|
||||
strength = 100
|
||||
// privacyLevel component uses 1..13 scale
|
||||
privacyLevel.fillLevel = Utils.mapScope(1, 100, 1, 13, strength)
|
||||
}
|
||||
|
||||
WizardPasswordInput {
|
||||
id: passwordItem
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 24
|
||||
width: 300
|
||||
height: 62
|
||||
placeholderText : qsTr("Password") + translationManager.emptyString;
|
||||
KeyNavigation.tab: retypePasswordItem
|
||||
onChanged: handlePassword()
|
||||
focus: true
|
||||
}
|
||||
|
||||
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: retypePasswordItem.bottom
|
||||
anchors.topMargin: 60
|
||||
background: "#F0EEEE"
|
||||
interactive: false
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
//parent.wizardRestarted.connect(onWizardRestarted)
|
||||
}
|
||||
}
|
@ -15,3 +15,10 @@ function tr(text) {
|
||||
function lineBreaksToSpaces(text) {
|
||||
return text.trim().replace(/(\r\n|\n|\r)/gm, " ");
|
||||
}
|
||||
|
||||
function usefulName(path) {
|
||||
// arbitrary "short enough" limit
|
||||
if (path.length < 32)
|
||||
return path
|
||||
return path.replace(/.*[\/\\]/, '').replace(/\.keys$/, '')
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user