forked from Public/monero-gui
view only wallets
wizard: fix dots on pw page wizard: fix focus on pw field viewOnly: added success message
This commit is contained in:
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$/, '')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user