diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
index d3de8f745b..04f106d0f9 100644
--- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
+++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
@@ -29,7 +29,6 @@ Rectangle {
property bool purchasesReceived: false;
property bool balanceReceived: false;
property bool securityImageResultReceived: false;
- property bool keyFilePathIfExistsResultReceived: false;
property string itemId: "";
property string itemHref: "";
property double balanceAfterPurchase: 0;
@@ -46,10 +45,15 @@ Rectangle {
root.activeView = "needsLogIn";
} else if (isLoggedIn) {
root.activeView = "initialize";
- commerce.getSecurityImage();
commerce.getKeyFilePathIfExists();
- commerce.balance();
- commerce.inventory();
+ }
+ }
+
+ onKeyFilePathIfExistsResult: {
+ if (path === "" && root.activeView !== "notSetUp") {
+ root.activeView = "notSetUp";
+ } else if (path !== "" && root.activeView === "initialize") {
+ commerce.getSecurityImage();
}
}
@@ -57,8 +61,8 @@ Rectangle {
securityImageResultReceived = true;
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp";
- } else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") {
- root.activeView = "checkoutMain";
+ } else if (exists && root.activeView === "initialize") {
+ commerce.getWalletAuthenticatedStatus();
} else if (exists) {
// just set the source again (to be sure the change was noticed)
securityImage.source = "";
@@ -66,12 +70,17 @@ Rectangle {
}
}
- onKeyFilePathIfExistsResult: {
- keyFilePathIfExistsResultReceived = true;
- if (path === "" && root.activeView !== "notSetUp") {
- root.activeView = "notSetUp";
- } else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") {
+ onWalletAuthenticatedStatusResult: {
+ if (!isAuthenticated && !passphraseModal.visible) {
+ passphraseModal.visible = true;
+ } else if (isAuthenticated) {
root.activeView = "checkoutMain";
+ if (!balanceReceived) {
+ commerce.balance();
+ }
+ if (!purchasesReceived) {
+ commerce.inventory();
+ }
}
}
@@ -110,10 +119,6 @@ Rectangle {
}
}
- HifiWallet.SecurityImageModel {
- id: securityImageModel;
- }
-
//
// TITLE BAR START
//
@@ -195,11 +200,10 @@ Rectangle {
securityImageResultReceived = false;
purchasesReceived = false;
balanceReceived = false;
- keyFilePathIfExistsResultReceived = false;
commerce.getLoginStatus();
}
}
-
+
HifiWallet.NeedsLogIn {
id: needsLogIn;
visible: root.activeView === "needsLogIn";
@@ -221,8 +225,21 @@ Rectangle {
}
}
+ HifiWallet.PassphraseModal {
+ id: passphraseModal;
+ visible: false;
+ anchors.top: titleBarContainer.bottom;
+ anchors.bottom: parent.bottom;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
-
+ Connections {
+ onSendSignalToParent: {
+ sendToScript(msg);
+ }
+ }
+ }
+
//
// "WALLET NOT SET UP" START
//
@@ -233,7 +250,7 @@ Rectangle {
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
-
+
RalewayRegular {
id: notSetUpText;
text: "Your wallet isn't set up.
Set up your Wallet (no credit card necessary) to claim your free HFC " +
@@ -264,7 +281,7 @@ Rectangle {
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 24;
-
+
// "Cancel" button
HifiControlsUit.Button {
id: cancelButton;
diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
index bc843a140d..383223a49c 100644
--- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
@@ -28,7 +28,6 @@ Rectangle {
property string activeView: "initialize";
property string referrerURL: "";
property bool securityImageResultReceived: false;
- property bool keyFilePathIfExistsResultReceived: false;
property bool purchasesReceived: false;
property bool punctuationMode: false;
// Style
@@ -41,9 +40,15 @@ Rectangle {
root.activeView = "needsLogIn";
} else if (isLoggedIn) {
root.activeView = "initialize";
- commerce.getSecurityImage();
commerce.getKeyFilePathIfExists();
- commerce.inventory();
+ }
+ }
+
+ onKeyFilePathIfExistsResult: {
+ if (path === "" && root.activeView !== "notSetUp") {
+ root.activeView = "notSetUp";
+ } else if (path !== "" && root.activeView === "initialize") {
+ commerce.getSecurityImage();
}
}
@@ -51,8 +56,8 @@ Rectangle {
securityImageResultReceived = true;
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp";
- } else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") {
- root.activeView = "purchasesMain";
+ } else if (exists && root.activeView === "initialize") {
+ commerce.getWalletAuthenticatedStatus();
} else if (exists) {
// just set the source again (to be sure the change was noticed)
securityImage.source = "";
@@ -60,12 +65,12 @@ Rectangle {
}
}
- onKeyFilePathIfExistsResult: {
- keyFilePathIfExistsResultReceived = true;
- if (path === "" && root.activeView !== "notSetUp") {
- root.activeView = "notSetUp";
- } else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") {
+ onWalletAuthenticatedStatusResult: {
+ if (!isAuthenticated && !passphraseModal.visible) {
+ passphraseModal.visible = true;
+ } else if (isAuthenticated) {
root.activeView = "purchasesMain";
+ commerce.inventory();
}
}
@@ -166,7 +171,6 @@ Rectangle {
Component.onCompleted: {
securityImageResultReceived = false;
purchasesReceived = false;
- keyFilePathIfExistsResultReceived = false;
commerce.getLoginStatus();
}
}
@@ -191,6 +195,21 @@ Rectangle {
commerce.getLoginStatus();
}
}
+
+ HifiWallet.PassphraseModal {
+ id: passphraseModal;
+ visible: false;
+ anchors.top: titleBarContainer.bottom;
+ anchors.bottom: parent.bottom;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+
+ Connections {
+ onSendSignalToParent: {
+ sendToScript(msg);
+ }
+ }
+ }
//
// "WALLET NOT SET UP" START
diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml
index 47b3f6daf6..402209f38d 100644
--- a/interface/resources/qml/hifi/commerce/wallet/Help.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml
@@ -47,11 +47,26 @@ Item {
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
- anchors.bottom: helpText.bottom;
+ anchors.bottom: resetButton.top;
+ anchors.bottomMargin: 15;
anchors.horizontalCenter: parent.horizontalCenter;
height: 50;
width: 250;
- text: "Testing: Reset Wallet!";
+ text: "DEBUG: Clear Cached Passphrase";
+ onClicked: {
+ commerce.setPassphrase("");
+ }
+ }
+ HifiControlsUit.Button {
+ id: resetButton;
+ color: hifi.buttons.red;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.bottom: helpText.bottom;
+ anchors.bottomMargin: 15;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ height: 50;
+ width: 250;
+ text: "DEBUG: Reset Wallet!";
onClicked: {
commerce.reset();
sendSignalToWallet({method: 'walletReset'});
diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml
new file mode 100644
index 0000000000..4157e4082f
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml
@@ -0,0 +1,304 @@
+//
+// PassphraseModal.qml
+// qml/hifi/commerce/wallet
+//
+// PassphraseModal
+//
+// Created by Zach Fox on 2017-08-31
+// Copyright 2017 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import Hifi 1.0 as Hifi
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import "../../../styles-uit"
+import "../../../controls-uit" as HifiControlsUit
+import "../../../controls" as HifiControls
+
+// references XXX from root context
+
+Item {
+ HifiConstants { id: hifi; }
+
+ id: root;
+ z: 998;
+ property bool keyboardRaised: false;
+
+ Hifi.QmlCommerce {
+ id: commerce;
+
+ onSecurityImageResult: {
+ passphraseModalSecurityImage.source = "";
+ passphraseModalSecurityImage.source = "image://security/securityImage";
+ }
+
+ onWalletAuthenticatedStatusResult: {
+ submitPassphraseInputButton.enabled = true;
+ if (!isAuthenticated) {
+ errorText.text = "Authentication failed - please try again.";
+ } else {
+ root.visible = false;
+ }
+ }
+ }
+
+ // This object is always used in a popup.
+ // This MouseArea is used to prevent a user from being
+ // able to click on a button/mouseArea underneath the popup.
+ MouseArea {
+ anchors.fill: parent;
+ propagateComposedEvents: false;
+ }
+
+ // This will cause a bug -- if you bring up passphrase selection in HUD mode while
+ // in HMD while having HMD preview enabled, then move, then finish passphrase selection,
+ // HMD preview will stay off.
+ // TODO: Fix this unlikely bug
+ onVisibleChanged: {
+ if (visible) {
+ passphraseField.focus = true;
+ sendSignalToParent({method: 'disableHmdPreview'});
+ } else {
+ sendSignalToParent({method: 'maybeEnableHmdPreview'});
+ }
+ }
+
+ // Background rectangle
+ Rectangle {
+ anchors.fill: parent;
+ color: "black";
+ opacity: 0.9;
+ }
+
+ Rectangle {
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ height: 250;
+ color: hifi.colors.baseGray;
+
+ RalewaySemiBold {
+ id: instructionsText;
+ text: "Enter Wallet Passphrase";
+ size: 16;
+ anchors.top: parent.top;
+ anchors.topMargin: 30;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ width: passphraseField.width;
+ height: paintedHeight;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignLeft;
+ }
+
+ HifiControlsUit.TextField {
+ id: passphraseField;
+ anchors.top: instructionsText.bottom;
+ anchors.topMargin: 4;
+ anchors.left: instructionsText.left;
+ width: 280;
+ height: 50;
+ echoMode: TextInput.Password;
+ placeholderText: "passphrase";
+
+ onFocusChanged: {
+ root.keyboardRaised = focus;
+ }
+
+ MouseArea {
+ anchors.fill: parent;
+
+ onClicked: {
+ parent.focus = true;
+ root.keyboardRaised = true;
+ }
+ }
+
+ onAccepted: {
+ submitPassphraseInputButton.enabled = false;
+ commerce.setPassphrase(passphraseField.text);
+ }
+ }
+
+ // Show passphrase text
+ HifiControlsUit.CheckBox {
+ id: showPassphrase;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.left: passphraseField.left;
+ anchors.top: passphraseField.bottom;
+ anchors.topMargin: 8;
+ height: 30;
+ text: "Show passphrase as plain text";
+ boxSize: 24;
+ onClicked: {
+ passphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password;
+ }
+ }
+
+ // Security Image
+ Item {
+ id: securityImageContainer;
+ // Anchors
+ anchors.top: instructionsText.top;
+ anchors.left: passphraseField.right;
+ anchors.leftMargin: 12;
+ anchors.right: parent.right;
+ Image {
+ id: passphraseModalSecurityImage;
+ anchors.top: parent.top;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ height: 75;
+ width: height;
+ fillMode: Image.PreserveAspectFit;
+ mipmap: true;
+ source: "image://security/securityImage";
+ cache: false;
+ onVisibleChanged: {
+ commerce.getSecurityImage();
+ }
+ }
+ Image {
+ id: passphraseModalSecurityImageOverlay;
+ source: "images/lockOverlay.png";
+ width: passphraseModalSecurityImage.width * 0.45;
+ height: passphraseModalSecurityImage.height * 0.45;
+ anchors.bottom: passphraseModalSecurityImage.bottom;
+ anchors.right: passphraseModalSecurityImage.right;
+ mipmap: true;
+ opacity: 0.9;
+ }
+ // "Security image" text below pic
+ RalewayRegular {
+ text: "security image";
+ // Text size
+ size: 12;
+ // Anchors
+ anchors.top: passphraseModalSecurityImage.bottom;
+ anchors.topMargin: 4;
+ anchors.left: securityImageContainer.left;
+ anchors.right: securityImageContainer.right;
+ height: paintedHeight;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ }
+
+ // Error text above buttons
+ RalewaySemiBold {
+ id: errorText;
+ text: "";
+ // Text size
+ size: 16;
+ // Anchors
+ anchors.bottom: passphrasePopupActionButtonsContainer.top;
+ anchors.bottomMargin: 4;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: 30;
+ // Style
+ color: hifi.colors.redHighlight;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ //
+ // ACTION BUTTONS START
+ //
+ Item {
+ id: passphrasePopupActionButtonsContainer;
+ // Size
+ width: root.width;
+ height: 50;
+ // Anchors
+ anchors.left: parent.left;
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 10;
+
+ // "Cancel" button
+ HifiControlsUit.Button {
+ id: cancelPassphraseInputButton;
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ height: 40;
+ anchors.left: parent.left;
+ anchors.leftMargin: 20;
+ width: parent.width/2 - anchors.leftMargin*2;
+ text: "Cancel"
+ onClicked: {
+ sendSignalToParent({method: 'passphrasePopup_cancelClicked'});
+ }
+ }
+
+ // "Submit" button
+ HifiControlsUit.Button {
+ id: submitPassphraseInputButton;
+ color: hifi.buttons.blue;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ height: 40;
+ anchors.right: parent.right;
+ anchors.rightMargin: 20;
+ width: parent.width/2 - anchors.rightMargin*2;
+ text: "Submit"
+ onClicked: {
+ submitPassphraseInputButton.enabled = false;
+ commerce.setPassphrase(passphraseField.text);
+ }
+ }
+ }
+ }
+
+ Item {
+ id: keyboardContainer;
+ z: 999;
+ visible: keyboard.raised;
+ property bool punctuationMode: false;
+ anchors {
+ bottom: parent.bottom;
+ left: parent.left;
+ right: parent.right;
+ }
+
+ Image {
+ id: lowerKeyboardButton;
+ source: "images/lowerKeyboard.png";
+ anchors.horizontalCenter: parent.horizontalCenter;
+ anchors.bottom: keyboard.top;
+ height: 30;
+ width: 120;
+
+ MouseArea {
+ anchors.fill: parent;
+
+ onClicked: {
+ root.keyboardRaised = false;
+ }
+ }
+ }
+
+ HifiControlsUit.Keyboard {
+ id: keyboard;
+ raised: HMD.mounted && root.keyboardRaised;
+ numeric: parent.punctuationMode;
+ anchors {
+ bottom: parent.bottom;
+ left: parent.left;
+ right: parent.right;
+ }
+ }
+ }
+
+ signal sendSignalToParent(var msg);
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml
index 39d07315d5..7dca5a75b8 100644
--- a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml
@@ -40,8 +40,8 @@ Item {
passphrasePageSecurityImage.source = "image://security/securityImage";
}
- onPassphraseSetupStatusResult: {
- sendMessageToLightbox({method: 'statusResult', status: passphraseIsSetup});
+ onWalletAuthenticatedStatusResult: {
+ sendMessageToLightbox({method: 'statusResult', status: isAuthenticated});
}
}
@@ -58,10 +58,6 @@ Item {
}
}
- SecurityImageModel {
- id: gridModel;
- }
-
HifiControlsUit.TextField {
id: passphraseField;
anchors.top: parent.top;
@@ -199,7 +195,7 @@ Item {
// Text below TextFields
RalewaySemiBold {
id: passwordReqs;
- text: "Passphrase must be at least 4 characters";
+ text: "Passphrase must be at least 3 characters";
// Text size
size: 16;
// Anchors
@@ -256,7 +252,7 @@ Item {
}
function validateAndSubmitPassphrase() {
- if (passphraseField.text.length < 4) {
+ if (passphraseField.text.length < 3) {
setErrorText("Passphrase too short.");
return false;
} else if (passphraseField.text !== passphraseFieldAgain.text) {
diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml
index 53838fa58c..5695d47a68 100644
--- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml
@@ -26,8 +26,6 @@ Rectangle {
id: root;
property string activeView: "initialize";
- property bool securityImageResultReceived: false;
- property bool keyFilePathIfExistsResultReceived: false;
property bool keyboardRaised: false;
// Style
@@ -40,25 +38,30 @@ Rectangle {
root.activeView = "needsLogIn";
} else if (isLoggedIn) {
root.activeView = "initialize";
- commerce.getSecurityImage();
commerce.getKeyFilePathIfExists();
}
}
- onSecurityImageResult: {
- securityImageResultReceived = true;
- if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
+ onKeyFilePathIfExistsResult: {
+ if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
- } else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") {
- root.activeView = "walletHome";
+ } else if (path !== "" && root.activeView === "initialize") {
+ commerce.getSecurityImage();
}
}
- onKeyFilePathIfExistsResult: {
- keyFilePathIfExistsResultReceived = true;
- if (path === "" && root.activeView !== "notSetUp") {
+ onSecurityImageResult: {
+ if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp";
- } else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") {
+ } else if (exists && root.activeView === "initialize") {
+ commerce.getWalletAuthenticatedStatus();
+ }
+ }
+
+ onWalletAuthenticatedStatusResult: {
+ if (!isAuthenticated && !passphraseModal.visible) {
+ passphraseModal.visible = true;
+ } else if (isAuthenticated) {
root.activeView = "walletHome";
}
}
@@ -89,7 +92,8 @@ Rectangle {
if (msg.method === 'walletSetup_cancelClicked') {
walletSetupLightbox.visible = false;
} else if (msg.method === 'walletSetup_finished') {
- root.activeView = "walletHome";
+ root.activeView = "initialize";
+ commerce.getLoginStatus();
} else if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true;
} else if (msg.method === 'walletSetup_lowerKeyboard') {
@@ -218,6 +222,21 @@ Rectangle {
}
}
+ PassphraseModal {
+ id: passphraseModal;
+ visible: false;
+ anchors.top: titleBarContainer.bottom;
+ anchors.bottom: parent.bottom;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+
+ Connections {
+ onSendSignalToParent: {
+ sendToScript(msg);
+ }
+ }
+ }
+
NotSetUp {
id: notSetUp;
visible: root.activeView === "notSetUp";
diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
index 413fd8b71c..a7050febfa 100644
--- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
@@ -88,13 +88,14 @@ Item {
height: 60;
Rectangle {
id: hfcBalanceField;
+ color: hifi.colors.darkGray;
anchors.right: parent.right;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
height: parent.height - 15;
// "HFC" balance label
- RalewayRegular {
+ FiraSansRegular {
id: balanceLabel;
text: "HFC";
// Text size
@@ -106,7 +107,7 @@ Item {
anchors.rightMargin: 4;
width: paintedWidth;
// Style
- color: hifi.colors.darkGray;
+ color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignVCenter;
@@ -121,7 +122,7 @@ Item {
}
// Balance Text
- FiraSansRegular {
+ FiraSansSemiBold {
id: balanceText;
text: "--";
// Text size
@@ -133,7 +134,7 @@ Item {
anchors.right: balanceLabel.left;
anchors.rightMargin: 4;
// Style
- color: hifi.colors.darkGray;
+ color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignVCenter;
@@ -258,7 +259,7 @@ Item {
delegate: Item {
width: parent.width;
height: transactionText.height + 30;
- RalewayRegular {
+ FiraSansRegular {
id: transactionText;
text: model.text;
// Style
@@ -288,7 +289,7 @@ Item {
}
// This should never be visible (since you immediately get 100 HFC)
- RalewayRegular {
+ FiraSansRegular {
id: emptyTransationHistory;
size: 24;
visible: !transactionHistory.visible && root.historyReceived;
diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml
index 4470ec7a75..2956dfb518 100644
--- a/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml
@@ -39,9 +39,9 @@ Rectangle {
}
}
- onPassphraseSetupStatusResult: {
+ onWalletAuthenticatedStatusResult: {
securityImageContainer.visible = false;
- if (passphraseIsSetup) {
+ if (isAuthenticated) {
privateKeysReadyContainer.visible = true;
} else {
choosePassphraseContainer.visible = true;
@@ -117,7 +117,7 @@ Rectangle {
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 280;
-
+
Connections {
onSendSignalToWallet: {
sendSignalToWallet(msg);
@@ -210,7 +210,7 @@ Rectangle {
onVisibleChanged: {
if (visible) {
- commerce.getPassphraseSetupStatus();
+ commerce.getWalletAuthenticatedStatus();
}
}
diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp
index 655f228672..96f2a02f31 100644
--- a/interface/src/commerce/QmlCommerce.cpp
+++ b/interface/src/commerce/QmlCommerce.cpp
@@ -29,6 +29,30 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) {
connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult);
}
+void QmlCommerce::getLoginStatus() {
+ emit loginStatusResult(DependencyManager::get()->isLoggedIn());
+}
+
+void QmlCommerce::getKeyFilePathIfExists() {
+ auto wallet = DependencyManager::get();
+ wallet->sendKeyFilePathIfExists();
+}
+
+void QmlCommerce::getWalletAuthenticatedStatus() {
+ auto wallet = DependencyManager::get();
+ emit walletAuthenticatedStatusResult(wallet->walletIsAuthenticatedWithPassphrase());
+}
+
+void QmlCommerce::getSecurityImage() {
+ auto wallet = DependencyManager::get();
+ wallet->getSecurityImage();
+}
+
+void QmlCommerce::chooseSecurityImage(const QString& imageFile) {
+ auto wallet = DependencyManager::get();
+ wallet->chooseSecurityImage(imageFile);
+}
+
void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) {
auto ledger = DependencyManager::get();
auto wallet = DependencyManager::get();
@@ -60,30 +84,14 @@ void QmlCommerce::history() {
ledger->history(wallet->listPublicKeys());
}
-void QmlCommerce::chooseSecurityImage(const QString& imageFile) {
- auto wallet = DependencyManager::get();
- wallet->chooseSecurityImage(imageFile);
-}
-
-void QmlCommerce::getSecurityImage() {
- auto wallet = DependencyManager::get();
- wallet->getSecurityImage();
-}
-
-void QmlCommerce::getLoginStatus() {
- emit loginStatusResult(DependencyManager::get()->isLoggedIn());
-}
-
void QmlCommerce::setPassphrase(const QString& passphrase) {
- emit passphraseSetupStatusResult(true);
-}
-
-void QmlCommerce::getPassphraseSetupStatus() {
- emit passphraseSetupStatusResult(false);
-}
-void QmlCommerce::getKeyFilePathIfExists() {
auto wallet = DependencyManager::get();
- wallet->sendKeyFilePathIfExists();
+ if (wallet->getPassphrase() && !wallet->getPassphrase()->isEmpty()) {
+ wallet->changePassphrase(passphrase);
+ } else {
+ wallet->setPassphrase(passphrase);
+ }
+ getWalletAuthenticatedStatus();
}
void QmlCommerce::reset() {
@@ -91,4 +99,4 @@ void QmlCommerce::reset() {
auto wallet = DependencyManager::get();
ledger->reset();
wallet->reset();
-}
\ No newline at end of file
+}
diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h
index deb11b7714..f66bf518f5 100644
--- a/interface/src/commerce/QmlCommerce.h
+++ b/interface/src/commerce/QmlCommerce.h
@@ -28,28 +28,32 @@ public:
QmlCommerce(QQuickItem* parent = nullptr);
signals:
+ void loginStatusResult(bool isLoggedIn);
+ void keyFilePathIfExistsResult(const QString& path);
+ void securityImageResult(bool exists);
+ void walletAuthenticatedStatusResult(bool isAuthenticated);
+
void buyResult(QJsonObject result);
// Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and
// because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain).
void balanceResult(QJsonObject result);
void inventoryResult(QJsonObject result);
- void securityImageResult(bool exists);
- void loginStatusResult(bool isLoggedIn);
- void passphraseSetupStatusResult(bool passphraseIsSetup);
void historyResult(QJsonObject result);
- void keyFilePathIfExistsResult(const QString& path);
protected:
+ Q_INVOKABLE void getLoginStatus();
+ Q_INVOKABLE void getKeyFilePathIfExists();
+ Q_INVOKABLE void getSecurityImage();
+ Q_INVOKABLE void getWalletAuthenticatedStatus();
+
+ Q_INVOKABLE void chooseSecurityImage(const QString& imageFile);
+ Q_INVOKABLE void setPassphrase(const QString& passphrase);
+
Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = "");
Q_INVOKABLE void balance();
Q_INVOKABLE void inventory();
Q_INVOKABLE void history();
- Q_INVOKABLE void chooseSecurityImage(const QString& imageFile);
- Q_INVOKABLE void getSecurityImage();
- Q_INVOKABLE void getLoginStatus();
- Q_INVOKABLE void setPassphrase(const QString& passphrase);
- Q_INVOKABLE void getPassphraseSetupStatus();
- Q_INVOKABLE void getKeyFilePathIfExists();
+
Q_INVOKABLE void reset();
};
diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp
index 32852602d7..1f56ae32c4 100644
--- a/interface/src/commerce/Wallet.cpp
+++ b/interface/src/commerce/Wallet.cpp
@@ -56,16 +56,72 @@ QString imageFilePath() {
int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
// just return a hardcoded pwd for now
auto passphrase = DependencyManager::get()->getPassphrase();
- if (passphrase) {
+ if (passphrase && !passphrase->isEmpty()) {
strcpy(password, passphrase->toLocal8Bit().constData());
return static_cast(passphrase->size());
} else {
- // ok gotta bring up modal dialog... But right now lets just
- // just keep it empty
+ // this shouldn't happen - so lets log it to tell us we have
+ // a problem with the flow...
+ qCCritical(commerce) << "no cached passphrase while decrypting!";
return 0;
}
}
+RSA* readKeys(const char* filename) {
+ FILE* fp;
+ RSA* key = NULL;
+ if ((fp = fopen(filename, "rt"))) {
+ // file opened successfully
+ qCDebug(commerce) << "opened key file" << filename;
+ if ((key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL))) {
+ // now read private key
+
+ qCDebug(commerce) << "read public key";
+
+ if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) {
+ qCDebug(commerce) << "read private key";
+ fclose(fp);
+ return key;
+ }
+ qCDebug(commerce) << "failed to read private key";
+ } else {
+ qCDebug(commerce) << "failed to read public key";
+ }
+ fclose(fp);
+ } else {
+ qCDebug(commerce) << "failed to open key file" << filename;
+ }
+ return key;
+}
+
+bool writeKeys(const char* filename, RSA* keys) {
+ FILE* fp;
+ bool retval = false;
+ if ((fp = fopen(filename, "wt"))) {
+ if (!PEM_write_RSAPublicKey(fp, keys)) {
+ fclose(fp);
+ qCDebug(commerce) << "failed to write public key";
+ QFile(QString(filename)).remove();
+ return retval;
+ }
+
+ if (!PEM_write_RSAPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
+ fclose(fp);
+ qCDebug(commerce) << "failed to write private key";
+ QFile(QString(filename)).remove();
+ return retval;
+ }
+
+ retval = true;
+ qCDebug(commerce) << "wrote keys successfully";
+ fclose(fp);
+ } else {
+ qCDebug(commerce) << "failed to open key file" << filename;
+ }
+ return retval;
+}
+
+
// BEGIN copied code - this will be removed/changed at some point soon
// copied (without emits for various signals) from libraries/networking/src/RSAKeypairGenerator.cpp.
// We will have a different implementation in practice, but this gives us a start for now
@@ -124,25 +180,9 @@ QPair generateRSAKeypair() {
}
-
- // now lets persist them to files
- // FIXME: for now I'm appending to the file if it exists. As long as we always put
- // the keys in the same order, this works fine. TODO: verify this will skip over
- // anything else (like an embedded image)
- FILE* fp;
- if ((fp = fopen(keyFilePath().toStdString().c_str(), "at"))) {
- if (!PEM_write_RSAPublicKey(fp, keyPair)) {
- fclose(fp);
- qCDebug(commerce) << "failed to write public key";
- return retval;
- }
-
- if (!PEM_write_RSAPrivateKey(fp, keyPair, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
- fclose(fp);
- qCDebug(commerce) << "failed to write private key";
- return retval;
- }
- fclose(fp);
+ if (!writeKeys(keyFilePath().toStdString().c_str(), keyPair)) {
+ qCDebug(commerce) << "couldn't save keys!";
+ return retval;
}
RSA_free(keyPair);
@@ -201,9 +241,6 @@ RSA* readPrivateKey(const char* filename) {
// file opened successfully
qCDebug(commerce) << "opened key file" << filename;
if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) {
- // cleanup
- fclose(fp);
-
qCDebug(commerce) << "parsed private key file successfully";
} else {
@@ -215,7 +252,6 @@ RSA* readPrivateKey(const char* filename) {
}
return key;
}
-
static const unsigned char IVEC[16] = "IAmAnIVecYay123";
void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) {
@@ -341,6 +377,24 @@ bool Wallet::decryptFile(const QString& inputFilePath, unsigned char** outputBuf
return true;
}
+bool Wallet::walletIsAuthenticatedWithPassphrase() {
+ // try to read existing keys if they exist...
+
+ // FIXME: initialize OpenSSL elsewhere soon
+ initialize();
+
+ auto publicKey = readPublicKey(keyFilePath().toStdString().c_str());
+
+ if (publicKey.size() > 0) {
+ if (auto key = readPrivateKey(keyFilePath().toStdString().c_str())) {
+ RSA_free(key);
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool Wallet::createIfNeeded() {
if (_publicKeys.count() > 0) return false;
@@ -512,3 +566,30 @@ void Wallet::reset() {
keyFile.remove();
imageFile.remove();
}
+
+bool Wallet::changePassphrase(const QString& newPassphrase) {
+ qCDebug(commerce) << "changing passphrase";
+ RSA* keys = readKeys(keyFilePath().toStdString().c_str());
+ if (keys) {
+ // we read successfully, so now write to a new temp file
+ // save old passphrase just in case
+ // TODO: force re-enter?
+ QString oldPassphrase = *_passphrase;
+ setPassphrase(newPassphrase);
+ QString tempFileName = QString("%1.%2").arg(keyFilePath(), QString("temp"));
+ if (writeKeys(tempFileName.toStdString().c_str(), keys)) {
+ // ok, now move the temp file to the correct spot
+ QFile(QString(keyFilePath())).remove();
+ QFile(tempFileName).rename(QString(keyFilePath()));
+ qCDebug(commerce) << "passphrase changed successfully";
+ return true;
+ } else {
+ qCDebug(commerce) << "couldn't write keys";
+ QFile(tempFileName).remove();
+ setPassphrase(oldPassphrase);
+ return false;
+ }
+ }
+ qCDebug(commerce) << "couldn't read keys";
+ return false;
+}
diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h
index 4acd913181..3b470210de 100644
--- a/interface/src/commerce/Wallet.h
+++ b/interface/src/commerce/Wallet.h
@@ -39,18 +39,21 @@ public:
void setPassphrase(const QString& passphrase);
QString* getPassphrase() { return _passphrase; }
+ bool getPassphraseIsCached() { return !(_passphrase->isEmpty()); }
+ bool walletIsAuthenticatedWithPassphrase();
+ bool changePassphrase(const QString& newPassphrase);
void reset();
signals:
- void securityImageResult(bool exists) ;
+ void securityImageResult(bool exists);
void keyFilePathIfExistsResult(const QString& path);
private:
QStringList _publicKeys{};
QPixmap* _securityImage { nullptr };
QByteArray _salt {"iamsalt!"};
- QString* _passphrase { new QString("pwd") };
+ QString* _passphrase { new QString("") };
void updateImageProvider();
bool encryptFile(const QString& inputFilePath, const QString& outputFilePath);
diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js
index a7b7b50379..5f07c4cbe7 100644
--- a/scripts/system/commerce/wallet.js
+++ b/scripts/system/commerce/wallet.js
@@ -56,6 +56,7 @@
var isHmdPreviewDisabled = true;
function fromQml(message) {
switch (message.method) {
+ case 'passphrasePopup_cancelClicked':
case 'walletSetup_cancelClicked':
case 'needsLogIn_cancelClicked':
tablet.gotoHomeScreen();
diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js
index 7d1aaee157..2eaefe7565 100644
--- a/scripts/system/marketplaces/marketplaces.js
+++ b/scripts/system/marketplaces/marketplaces.js
@@ -197,6 +197,7 @@
// Description:
// -Called when a message is received from Checkout.qml. The "message" argument is what is sent from the Checkout QML
// in the format "{method, params}", like json-rpc.
+ var isHmdPreviewDisabled = true;
function fromQml(message) {
switch (message.method) {
case 'checkout_setUpClicked':
@@ -231,12 +232,20 @@
case 'purchases_goToMarketplaceClicked':
tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL);
break;
+ case 'passphrasePopup_cancelClicked':
case 'needsLogIn_cancelClicked':
tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL);
break;
case 'needsLogIn_loginClicked':
openLoginWindow();
break;
+ case 'disableHmdPreview':
+ isHmdPreviewDisabled = Menu.isOptionChecked("Disable Preview");
+ Menu.setIsOptionChecked("Disable Preview", true);
+ break;
+ case 'maybeEnableHmdPreview':
+ Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled);
+ break;
default:
print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message));
}