diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
index d88ded6a15..372fb3c774 100644
--- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
+++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
@@ -59,17 +59,17 @@ Rectangle {
if (root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
}
- } else if (walletStatus === 1) {
+ } else if ((walletStatus === 1) || (walletStatus === 2) || (walletStatus === 3)) {
if (root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
notSetUpTimer.start();
}
- } else if (walletStatus === 2) {
+ } else if (walletStatus === 4) {
if (root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
UserActivityLogger.commercePassphraseEntry("marketplace checkout");
}
- } else if (walletStatus === 3) {
+ } else if (walletStatus === 5) {
authSuccessStep();
} else {
console.log("ERROR in Checkout.qml: Unknown wallet status: " + walletStatus);
diff --git a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml
index b24af716ad..eeda3afc71 100644
--- a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml
+++ b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml
@@ -25,10 +25,13 @@ Rectangle {
property string titleText;
property string bodyImageSource;
property string bodyText;
+ property string button1color: hifi.buttons.noneBorderlessGray;
property string button1text;
property string button1method;
+ property string button2color: hifi.buttons.noneBorderless;
property string button2text;
property string button2method;
+ property string buttonLayout: "leftright";
readonly property string securityPicBodyText: "When you see your Security Pic, your actions and data are securely making use of your " +
"Wallet's private keys.
You can change your Security Pic in your Wallet.";
@@ -39,6 +42,12 @@ Rectangle {
color: Qt.rgba(0, 0, 0, 0.5);
z: 999;
+ onVisibleChanged: {
+ if (!visible) {
+ resetLightbox();
+ }
+ }
+
// 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.
@@ -112,18 +121,21 @@ Rectangle {
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.right: parent.right;
- height: 70;
+ height: root.buttonLayout === "leftright" ? 70 : 150;
// Button 1
HifiControlsUit.Button {
- color: hifi.buttons.noneBorderlessGray;
+ id: button1;
+ color: root.button1color;
colorScheme: hifi.colorSchemes.light;
- anchors.top: parent.top;
- anchors.bottom: parent.bottom;
- anchors.bottomMargin: 20;
+ anchors.top: root.buttonLayout === "leftright" ? parent.top : parent.top;
anchors.left: parent.left;
anchors.leftMargin: 10;
- width: root.button2text ? parent.width/2 - anchors.leftMargin*2 : parent.width - anchors.leftMargin * 2;
+ anchors.right: root.buttonLayout === "leftright" ? undefined : parent.right;
+ anchors.rightMargin: root.buttonLayout === "leftright" ? undefined : 10;
+ width: root.buttonLayout === "leftright" ? (root.button2text ? parent.width/2 - anchors.leftMargin*2 : parent.width - anchors.leftMargin * 2) :
+ (undefined);
+ height: 50;
text: root.button1text;
onClicked: {
eval(button1method);
@@ -132,15 +144,18 @@ Rectangle {
// Button 2
HifiControlsUit.Button {
+ id: button2;
visible: root.button2text;
- color: hifi.buttons.noneBorderless;
+ color: root.button2color;
colorScheme: hifi.colorSchemes.light;
- anchors.top: parent.top;
- anchors.bottom: parent.bottom;
- anchors.bottomMargin: 20;
+ anchors.top: root.buttonLayout === "leftright" ? parent.top : button1.bottom;
+ anchors.topMargin: root.buttonLayout === "leftright" ? undefined : 20;
+ anchors.left: root.buttonLayout === "leftright" ? undefined : parent.left;
+ anchors.leftMargin: root.buttonLayout === "leftright" ? undefined : 10;
anchors.right: parent.right;
anchors.rightMargin: 10;
- width: parent.width/2 - anchors.rightMargin*2;
+ width: root.buttonLayout === "leftright" ? parent.width/2 - anchors.rightMargin*2 : undefined;
+ height: 50;
text: root.button2text;
onClicked: {
eval(button2method);
@@ -153,6 +168,19 @@ Rectangle {
// FUNCTION DEFINITIONS START
//
signal sendToParent(var msg);
+
+ function resetLightbox() {
+ root.titleText = "";
+ root.bodyImageSource = "";
+ root.bodyText = "";
+ root.button1color = hifi.buttons.noneBorderlessGray;
+ root.button1text = "";
+ root.button1method = "";
+ root.button2color = hifi.buttons.noneBorderless;
+ root.button2text = "";
+ root.button2method = "";
+ root.buttonLayout = "leftright";
+ }
//
// FUNCTION DEFINITIONS END
//
diff --git a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml
index a4b0f57e8f..eb8159a4e7 100644
--- a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml
+++ b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml
@@ -37,9 +37,9 @@ Item {
onWalletStatusResult: {
if (walletStatus === 0) {
sendToParent({method: "needsLogIn"});
- } else if (walletStatus === 3) {
+ } else if (walletStatus === 5) {
Commerce.getSecurityImage();
- } else if (walletStatus > 3) {
+ } else if (walletStatus > 5) {
console.log("ERROR in EmulatedMarketplaceHeader.qml: Unknown wallet status: " + walletStatus);
}
}
diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
index 3612de7323..c505baebf4 100644
--- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
@@ -47,17 +47,17 @@ Rectangle {
if (root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
}
- } else if (walletStatus === 1) {
+ } else if ((walletStatus === 1) || (walletStatus === 2) || (walletStatus === 3)) {
if (root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
notSetUpTimer.start();
}
- } else if (walletStatus === 2) {
+ } else if (walletStatus === 4) {
if (root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
UserActivityLogger.commercePassphraseEntry("marketplace purchases");
}
- } else if (walletStatus === 3) {
+ } else if (walletStatus === 5) {
if ((Settings.getValue("isFirstUseOfPurchases", true) || root.isDebuggingFirstUseTutorial) && root.activeView !== "firstUseTutorial") {
root.activeView = "firstUseTutorial";
} else if (!Settings.getValue("isFirstUseOfPurchases", true) && root.activeView === "initialize") {
diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml
index ae42b8e3e1..952390a7a4 100644
--- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml
@@ -47,20 +47,22 @@ Rectangle {
}
} else if (walletStatus === 1) {
if (root.activeView !== "walletSetup") {
- root.activeView = "walletSetup";
- Commerce.resetLocalWalletOnly();
- var timestamp = new Date();
- walletSetup.startingTimestamp = timestamp;
- walletSetup.setupAttemptID = generateUUID();
- UserActivityLogger.commerceWalletSetupStarted(timestamp, setupAttemptID, walletSetup.setupFlowVersion, walletSetup.referrer ? walletSetup.referrer : "wallet app",
- (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : ''));
+ walletResetSetup();
}
} else if (walletStatus === 2) {
+ if (root.activeView != "preexisting") {
+ root.activeView = "preexisting";
+ }
+ } else if (walletStatus === 3) {
+ if (root.activeView != "conflicting") {
+ root.activeView = "conflicting";
+ }
+ } else if (walletStatus === 4) {
if (root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
UserActivityLogger.commercePassphraseEntry("wallet app");
}
- } else if (walletStatus === 3) {
+ } else if (walletStatus === 5) {
if (root.activeView !== "walletSetup") {
root.activeView = "walletHome";
Commerce.getSecurityImage();
@@ -169,6 +171,25 @@ Rectangle {
// TITLE BAR END
//
+ WalletChoice {
+ id: walletChoice;
+ proceedFunction: function (isReset) {
+ console.log(isReset ? "Reset wallet." : "Trying again with new wallet.");
+ Commerce.setSoftReset();
+ if (isReset) {
+ walletResetSetup();
+ } else {
+ var msg = { referrer: walletChoice.referrer }
+ followReferrer(msg);
+ }
+ }
+ copyFunction: Commerce.copyKeyFileFrom;
+ z: 997;
+ visible: (root.activeView === "preexisting") || (root.activeView === "conflicting");
+ activeView: root.activeView;
+ anchors.fill: parent;
+ }
+
WalletSetup {
id: walletSetup;
visible: root.activeView === "walletSetup";
@@ -178,14 +199,7 @@ Rectangle {
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSetup_finished') {
- if (msg.referrer === '' || msg.referrer === 'marketplace cta') {
- root.activeView = "initialize";
- Commerce.getWalletStatus();
- } else if (msg.referrer === 'purchases') {
- sendToScript({method: 'goToPurchases'});
- } else {
- sendToScript({method: 'goToMarketplaceItemPage', itemId: msg.referrer});
- }
+ followReferrer(msg);
} else if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true;
root.isPassword = msg.isPasswordField;
@@ -738,6 +752,7 @@ Rectangle {
switch (message.method) {
case 'updateWalletReferrer':
walletSetup.referrer = message.referrer;
+ walletChoice.referrer = message.referrer;
break;
case 'inspectionCertificate_resetCert':
// NOP
@@ -768,6 +783,28 @@ Rectangle {
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
+
+ function walletResetSetup() {
+ root.activeView = "walletSetup";
+ var timestamp = new Date();
+ walletSetup.startingTimestamp = timestamp;
+ walletSetup.setupAttemptID = generateUUID();
+ UserActivityLogger.commerceWalletSetupStarted(timestamp, walletSetup.setupAttemptID, walletSetup.setupFlowVersion, walletSetup.referrer ? walletSetup.referrer : "wallet app",
+ (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : ''));
+ }
+
+ function followReferrer(msg) {
+ if (msg.referrer === '' || msg.referrer === 'marketplace cta') {
+ root.activeView = "initialize";
+ Commerce.getWalletStatus();
+ } else if (msg.referrer === 'purchases') {
+ sendToScript({method: 'goToPurchases'});
+ } else if (msg.referrer === 'marketplace cta' || msg.referrer === 'mainPage') {
+ sendToScript({method: 'goToMarketplaceMainPage', itemId: msg.referrer});
+ } else {
+ sendToScript({method: 'goToMarketplaceItemPage', itemId: msg.referrer});
+ }
+ }
//
// FUNCTION DEFINITIONS END
//
diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletChoice.qml b/interface/resources/qml/hifi/commerce/wallet/WalletChoice.qml
new file mode 100644
index 0000000000..457db55fd1
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/WalletChoice.qml
@@ -0,0 +1,297 @@
+//
+// WalletChoice.qml
+// qml/hifi/commerce/wallet
+//
+// WalletChoice
+//
+// Created by Howard Stearns
+// Copyright 2018 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 "../common" as HifiCommerceCommon
+import "../../../styles-uit"
+import "../../../controls-uit" as HifiControlsUit
+
+
+Item {
+ HifiConstants { id: hifi; }
+
+ id: root;
+ property string activeView: "conflict";
+ property var proceedFunction: nil;
+ property var copyFunction: nil;
+ property string referrer: "";
+
+ Image {
+ anchors.fill: parent;
+ source: "images/wallet-bg.jpg";
+ }
+
+ HifiCommerceCommon.CommerceLightbox {
+ id: lightboxPopup;
+ visible: false;
+ anchors.fill: parent;
+ }
+
+ // 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;
+ hoverEnabled: true;
+ }
+
+ //
+ // TITLE BAR START
+ //
+ Item {
+ id: titleBarContainer;
+ // Size
+ height: 50;
+ // Anchors
+ anchors.left: parent.left;
+ anchors.top: parent.top;
+ anchors.right: parent.right;
+
+ // Wallet icon
+ HiFiGlyphs {
+ id: walletIcon;
+ text: hifi.glyphs.wallet;
+ // Size
+ size: parent.height * 0.8;
+ // Anchors
+ anchors.left: parent.left;
+ anchors.leftMargin: 8;
+ anchors.verticalCenter: parent.verticalCenter;
+ // Style
+ color: hifi.colors.blueHighlight;
+ }
+
+ // Title Bar text
+ RalewayRegular {
+ id: titleBarText;
+ text: "Wallet Setup";
+ // Text size
+ size: hifi.fontSizes.overlayTitle;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: walletIcon.right;
+ anchors.leftMargin: 8;
+ anchors.bottom: parent.bottom;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.white;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ }
+ //
+ // TITLE BAR END
+ //
+
+ //
+ // MAIN PAGE START
+ //
+ Item {
+ id: preexistingContainer;
+ // Anchors
+ anchors.top: titleBarContainer.bottom;
+ anchors.bottom: parent.bottom;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+
+ HiFiGlyphs {
+ id: bigKeyIcon;
+ text: hifi.glyphs.walletKey;
+ // Size
+ size: 180;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.topMargin: 40;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ // Style
+ color: hifi.colors.white;
+ }
+
+ RalewayRegular {
+ id: text01;
+ text: root.activeView === "preexisting" ?
+ "Where are your private keys?" :
+ "Hmm, your keys are different"
+ // Text size
+ size: 26;
+ // Anchors
+ anchors.top: bigKeyIcon.bottom;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: paintedHeight;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.white;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ RalewayRegular {
+ id: text02;
+ text: root.activeView === "preexisting" ?
+ "Our records indicate that you created a wallet, but the private keys are not in the folder where we checked." :
+ "Our records indicate that you created a wallet with different keys than the keys you're providing."
+ // Text size
+ size: 18;
+ // Anchors
+ anchors.top: text01.bottom;
+ anchors.topMargin: 40;
+ anchors.left: parent.left;
+ anchors.leftMargin: 65;
+ anchors.right: parent.right;
+ anchors.rightMargin: 65;
+ height: paintedHeight;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.white;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ // "Locate" button
+ HifiControlsUit.Button {
+ id: locateButton;
+ color: hifi.buttons.blue;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: text02.bottom;
+ anchors.topMargin: 40;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ width: parent.width/2;
+ height: 50;
+ text: root.activeView === "preexisting" ?
+ "LOCATE MY KEYS" :
+ "LOCATE OTHER KEYS"
+ onClicked: {
+ walletChooser();
+ }
+ }
+
+ // "Create New" OR "Continue" button
+ HifiControlsUit.Button {
+ id: button02;
+ color: hifi.buttons.none;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: locateButton.bottom;
+ anchors.topMargin: 20;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ width: parent.width/2;
+ height: 50;
+ text: root.activeView === "preexisting" ?
+ "CREATE NEW WALLET" :
+ "CONTINUE WITH THESE KEYS"
+ onClicked: {
+ lightboxPopup.titleText = "Are you sure?";
+ lightboxPopup.bodyText = "Taking this step will abandon your old wallet and you will no " +
+ "longer be able to access your money and your past purchases.
" +
+ "This step should only be used if you cannot find your keys.
" +
+ "This step cannot be undone.";
+ lightboxPopup.button1color = hifi.buttons.red;
+ lightboxPopup.button1text = "YES, CREATE NEW WALLET";
+ lightboxPopup.button1method = "root.visible = false;proceed(true);";
+ lightboxPopup.button2text = "CANCEL";
+ lightboxPopup.button2method = "root.visible = false;"
+ lightboxPopup.buttonLayout = "topbottom";
+ lightboxPopup.visible = true;
+ }
+ }
+
+ // "What's This?" link
+ RalewayRegular {
+ id: whatsThisLink;
+ text: 'What\'s this?';
+ // Anchors
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 48;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ width: paintedWidth;
+ height: paintedHeight;
+ // Text size
+ size: 18;
+ // Style
+ color: hifi.colors.white;
+
+ MouseArea {
+ anchors.fill: parent;
+
+ onClicked: {
+ if (root.activeView === "preexisting") {
+ lightboxPopup.titleText = "Your wallet's private keys are not in the folder we expected";
+ lightboxPopup.bodyText = "We see that you have created a wallet but the private keys " +
+ "for it seem to have been moved to a different folder.
" +
+ "To tell us where the keys are, click 'Locate My Keys'.
" +
+ "If you'd prefer to create a new wallet (not recommended - you will lose your money and past " +
+ "purchases), click 'Create New Wallet'.";
+ lightboxPopup.button1text = "CLOSE";
+ lightboxPopup.button1method = "root.visible = false;"
+ lightboxPopup.visible = true;
+ } else {
+ lightboxPopup.titleText = "You may have set up more than one wallet";
+ lightboxPopup.bodyText = "We see that the private keys stored on your computer are different " +
+ "from the ones you used last time. This may mean that you set up more than one wallet. " +
+ "If you would like to use these keys, click 'Continue With These Keys'.
" +
+ "If you would prefer to use another wallet, click 'Locate Other Keys' to show us where " +
+ "you've stored the private keys for that wallet.";
+ lightboxPopup.button1text = "CLOSE";
+ lightboxPopup.button1method = "root.visible = false;"
+ lightboxPopup.visible = true;
+ }
+ }
+ }
+ }
+ }
+ //
+ // MAIN PAGE END
+ //
+
+ //
+ // FUNCTION DEFINITIONS START
+ //
+ function onFileOpenChanged(filename) {
+ // disconnect the event, otherwise the requests will stack up
+ try { // Not all calls to onFileOpenChanged() connect an event.
+ Window.browseChanged.disconnect(onFileOpenChanged);
+ } catch (e) {
+ console.log('WalletChoice.qml ignoring', e);
+ }
+ if (filename) {
+ if (copyFunction && copyFunction(filename)) {
+ proceed(false);
+ } else {
+ console.log("WalletChoice.qml copyFunction", copyFunction, "failed.");
+ }
+ } // Else we're still at WalletChoice
+ }
+ function walletChooser() {
+ Window.browseChanged.connect(onFileOpenChanged);
+ Window.browseAsync("Locate your .hifikey file", "", "*.hifikey");
+ }
+ function proceed(isReset) {
+ if (!proceedFunction) {
+ console.log("Provide a function of no arguments to WalletChoice.qml.");
+ } else {
+ proceedFunction(isReset);
+ }
+ }
+ //
+ // FUNCTION DEFINITIONS END
+ //
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
index 83c1a2035d..27660b5e9e 100644
--- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
@@ -310,7 +310,7 @@ Item {
height: parent.height;
HifiControlsUit.Separator {
- colorScheme: 1;
+ colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
@@ -318,20 +318,42 @@ Item {
RalewayRegular {
id: noActivityText;
- text: "The Wallet app is in closed Beta.
To request entry and receive free HFC, please contact " +
- "info@highfidelity.com with your High Fidelity account username and the email address registered to that account.";
- // Text size
- size: 24;
- // Style
- color: hifi.colors.blueAccent;
- anchors.left: parent.left;
- anchors.leftMargin: 12;
- anchors.right: parent.right;
- anchors.rightMargin: 12;
- anchors.verticalCenter: parent.verticalCenter;
- height: paintedHeight;
- wrapMode: Text.WordWrap;
- horizontalAlignment: Text.AlignHCenter;
+ text: "Congrats! Your wallet is all set!
" +
+ "Where's my HFC?
" +
+ "High Fidelity commerce is in open beta right now. Want more HFC? Get it by meeting with a banker at " +
+ "BankOfHighFidelity!"
+ // Text size
+ size: 22;
+ // Style
+ color: hifi.colors.blueAccent;
+ anchors.top: parent.top;
+ anchors.topMargin: 36;
+ anchors.left: parent.left;
+ anchors.leftMargin: 12;
+ anchors.right: parent.right;
+ anchors.rightMargin: 12;
+ height: paintedHeight;
+ wrapMode: Text.WordWrap;
+ horizontalAlignment: Text.AlignHCenter;
+
+ onLinkActivated: {
+ sendSignalToWallet({ method: "transactionHistory_goToBank" });
+ }
+ }
+
+ HifiControlsUit.Button {
+ id: bankButton;
+ color: hifi.buttons.blue;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: noActivityText.bottom;
+ anchors.topMargin: 30;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ width: parent.width/2;
+ height: 50;
+ text: "VISIT BANK OF HIGH FIDELITY";
+ onClicked: {
+ sendSignalToWallet({ method: "transactionHistory_goToBank" });
+ }
}
}
diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp
index dff441f840..712c505e8a 100644
--- a/interface/src/commerce/Ledger.cpp
+++ b/interface/src/commerce/Ledger.cpp
@@ -22,6 +22,8 @@
// inventory answers {status: 'success', data: {assets: [{id: "guid", title: "name", preview: "url"}....]}}
// balance answers {status: 'success', data: {balance: integer}}
// buy and receive_at answer {status: 'success'}
+// account synthesizes a result {status: 'success', data: {keyStatus: "preexisting"|"conflicting"|"ok"}}
+
QJsonObject Ledger::apiResponse(const QString& label, QNetworkReply& reply) {
QByteArray response = reply.readAll();
@@ -99,7 +101,7 @@ void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, cons
signedSend("transaction", transactionString, hfc_key, "buy", "buySuccess", "buyFailure", controlled_failure);
}
-bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key) {
+bool Ledger::receiveAt(const QString& hfc_key, const QString& signing_key) {
auto accountManager = DependencyManager::get();
if (!accountManager->isLoggedIn()) {
qCWarning(commerce) << "Cannot set receiveAt when not logged in.";
@@ -108,7 +110,7 @@ bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key) {
return false; // We know right away that we will fail, so tell the caller.
}
- signedSend("public_key", hfc_key.toUtf8(), old_key, "receive_at", "receiveAtSuccess", "receiveAtFailure");
+ signedSend("public_key", hfc_key.toUtf8(), signing_key, "receive_at", "receiveAtSuccess", "receiveAtFailure");
return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in.
}
@@ -179,7 +181,7 @@ QString transactionString(const QJsonObject& valueObject) {
} else {
result += valueObject["message"].toString();
}
-
+
// no matter what we append a smaller date to the bottom of this...
result += QString("
%1").arg(createdAt.toLocalTime().toString(Qt::DefaultLocaleShortDate));
return result;
@@ -246,18 +248,33 @@ void Ledger::accountSuccess(QNetworkReply& reply) {
auto iv = QByteArray::fromBase64(data["iv"].toString().toUtf8());
auto ckey = QByteArray::fromBase64(data["ckey"].toString().toUtf8());
QString remotePublicKey = data["public_key"].toString();
+ bool isOverride = wallet->wasSoftReset();
wallet->setSalt(salt);
wallet->setIv(iv);
wallet->setCKey(ckey);
+ QString keyStatus = "ok";
QStringList localPublicKeys = wallet->listPublicKeys();
- if (remotePublicKey.isEmpty() && !localPublicKeys.isEmpty()) {
- receiveAt(localPublicKeys.first(), "");
+ if (remotePublicKey.isEmpty() || isOverride) {
+ if (!localPublicKeys.isEmpty()) {
+ QString key = localPublicKeys.first();
+ receiveAt(key, key);
+ }
+ } else {
+ if (localPublicKeys.isEmpty()) {
+ keyStatus = "preexisting";
+ } else if (localPublicKeys.first() != remotePublicKey) {
+ keyStatus = "conflicting";
+ }
}
// none of the hfc account info should be emitted
- emit accountResult(QJsonObject{ {"status", "success"} });
+ QJsonObject json;
+ QJsonObject responseData{ { "status", "success"} };
+ json["keyStatus"] = keyStatus;
+ responseData["data"] = json;
+ emit accountResult(responseData);
}
void Ledger::accountFailure(QNetworkReply& reply) {
diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h
index ac9fe950d9..703ebda2dc 100644
--- a/interface/src/commerce/Ledger.h
+++ b/interface/src/commerce/Ledger.h
@@ -26,7 +26,7 @@ class Ledger : public QObject, public Dependency {
public:
void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure = false);
- bool receiveAt(const QString& hfc_key, const QString& old_key);
+ bool receiveAt(const QString& hfc_key, const QString& signing_key);
void balance(const QStringList& keys);
void inventory(const QStringList& keys);
void history(const QStringList& keys, const int& pageNumber);
diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp
index e7d62930cf..557193c074 100644
--- a/interface/src/commerce/QmlCommerce.cpp
+++ b/interface/src/commerce/QmlCommerce.cpp
@@ -62,6 +62,11 @@ void QmlCommerce::getKeyFilePathIfExists() {
emit keyFilePathIfExistsResult(wallet->getKeyFilePath());
}
+bool QmlCommerce::copyKeyFileFrom(const QString& pathname) {
+ auto wallet = DependencyManager::get();
+ return wallet->copyKeyFileFrom(pathname);
+}
+
void QmlCommerce::getWalletAuthenticatedStatus() {
auto wallet = DependencyManager::get();
emit walletAuthenticatedStatusResult(wallet->walletIsAuthenticatedWithPassphrase());
@@ -128,6 +133,11 @@ void QmlCommerce::changePassphrase(const QString& oldPassphrase, const QString&
}
}
+void QmlCommerce::setSoftReset() {
+ auto wallet = DependencyManager::get();
+ wallet->setSoftReset();
+}
+
void QmlCommerce::setPassphrase(const QString& passphrase) {
auto wallet = DependencyManager::get();
wallet->setPassphrase(passphrase);
diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h
index 09eb7137af..6a4eaa2be2 100644
--- a/interface/src/commerce/QmlCommerce.h
+++ b/interface/src/commerce/QmlCommerce.h
@@ -61,10 +61,12 @@ protected:
Q_INVOKABLE void getKeyFilePathIfExists();
Q_INVOKABLE void getSecurityImage();
Q_INVOKABLE void getWalletAuthenticatedStatus();
+ Q_INVOKABLE bool copyKeyFileFrom(const QString& pathname);
Q_INVOKABLE void chooseSecurityImage(const QString& imageFile);
Q_INVOKABLE void setPassphrase(const QString& passphrase);
Q_INVOKABLE void changePassphrase(const QString& oldPassphrase, const QString& newPassphrase);
+ Q_INVOKABLE void setSoftReset();
Q_INVOKABLE void buy(const QString& assetId, int cost, const bool controlledFailure = false);
Q_INVOKABLE void balance();
diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp
index 9599af827f..fad82115d6 100644
--- a/interface/src/commerce/Wallet.cpp
+++ b/interface/src/commerce/Wallet.cpp
@@ -59,6 +59,23 @@ QString keyFilePath() {
auto accountManager = DependencyManager::get();
return PathUtils::getAppDataFilePath(QString("%1.%2").arg(accountManager->getAccountInfo().getUsername(), KEY_FILE));
}
+bool Wallet::copyKeyFileFrom(const QString& pathname) {
+ QString existing = getKeyFilePath();
+ qCDebug(commerce) << "Old keyfile" << existing;
+ if (!existing.isEmpty()) {
+ QString backup = QString(existing).insert(existing.indexOf(KEY_FILE) - 1,
+ QDateTime::currentDateTime().toString(Qt::ISODate).replace(":", ""));
+ qCDebug(commerce) << "Renaming old keyfile to" << backup;
+ if (!QFile::rename(existing, backup)) {
+ qCCritical(commerce) << "Unable to backup" << existing << "to" << backup;
+ return false;
+ }
+ }
+ QString destination = keyFilePath();
+ bool result = QFile::copy(pathname, destination);
+ qCDebug(commerce) << "copy" << pathname << "to" << destination << "=>" << result;
+ return result;
+}
// use the cached _passphrase if it exists, otherwise we need to prompt
int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
@@ -300,17 +317,24 @@ Wallet::Wallet() {
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket");
packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest, this, "handleChallengeOwnershipPacket");
- connect(ledger.data(), &Ledger::accountResult, this, [&]() {
+ connect(ledger.data(), &Ledger::accountResult, this, [&](QJsonObject result) {
auto wallet = DependencyManager::get();
auto walletScriptingInterface = DependencyManager::get();
uint status;
+ QString keyStatus = result.contains("data") ? result["data"].toObject()["keyStatus"].toString() : "";
if (wallet->getKeyFilePath() == "" || !wallet->getSecurityImage()) {
- status = (uint)WalletStatus::WALLET_STATUS_NOT_SET_UP;
+ if (keyStatus == "preexisting") {
+ status = (uint) WalletStatus::WALLET_STATUS_PREEXISTING;
+ } else{
+ status = (uint) WalletStatus::WALLET_STATUS_NOT_SET_UP;
+ }
} else if (!wallet->walletIsAuthenticatedWithPassphrase()) {
- status = (uint)WalletStatus::WALLET_STATUS_NOT_AUTHENTICATED;
+ status = (uint) WalletStatus::WALLET_STATUS_NOT_AUTHENTICATED;
+ } else if (keyStatus == "conflicting") {
+ status = (uint) WalletStatus::WALLET_STATUS_CONFLICTING;
} else {
- status = (uint)WalletStatus::WALLET_STATUS_READY;
+ status = (uint) WalletStatus::WALLET_STATUS_READY;
}
walletScriptingInterface->setWalletStatus(status);
@@ -524,17 +548,17 @@ bool Wallet::generateKeyPair() {
// TODO: redo this soon -- need error checking and so on
writeSecurityImage(_securityImage, keyFilePath());
- QString oldKey = _publicKeys.count() == 0 ? "" : _publicKeys.last();
QString key = keyPair.first->toBase64();
_publicKeys.push_back(key);
qCDebug(commerce) << "public key:" << key;
+ _isOverridingServer = false;
// It's arguable whether we want to change the receiveAt every time, but:
// 1. It's certainly needed the first time, when createIfNeeded answers true.
// 2. It is maximally private, and we can step back from that later if desired.
// 3. It maximally exercises all the machinery, so we are most likely to surface issues now.
auto ledger = DependencyManager::get();
- return ledger->receiveAt(key, oldKey);
+ return ledger->receiveAt(key, key);
}
QStringList Wallet::listPublicKeys() {
diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h
index fe3a9f1d5f..d771f404e5 100644
--- a/interface/src/commerce/Wallet.h
+++ b/interface/src/commerce/Wallet.h
@@ -35,6 +35,7 @@ public:
void chooseSecurityImage(const QString& imageFile);
bool getSecurityImage();
QString getKeyFilePath();
+ bool copyKeyFileFrom(const QString& pathname);
void setSalt(const QByteArray& salt) { _salt = salt; }
QByteArray getSalt() { return _salt; }
@@ -48,11 +49,15 @@ public:
bool getPassphraseIsCached() { return !(_passphrase->isEmpty()); }
bool walletIsAuthenticatedWithPassphrase();
bool changePassphrase(const QString& newPassphrase);
+ void setSoftReset() { _isOverridingServer = true; }
+ bool wasSoftReset() { bool was = _isOverridingServer; _isOverridingServer = false; return was; }
void getWalletStatus();
enum WalletStatus {
WALLET_STATUS_NOT_LOGGED_IN = 0,
WALLET_STATUS_NOT_SET_UP,
+ WALLET_STATUS_PREEXISTING,
+ WALLET_STATUS_CONFLICTING,
WALLET_STATUS_NOT_AUTHENTICATED,
WALLET_STATUS_READY
};
@@ -73,6 +78,7 @@ private:
QByteArray _iv;
QByteArray _ckey;
QString* _passphrase { new QString("") };
+ bool _isOverridingServer { false };
bool writeWallet(const QString& newPassphrase = QString(""));
void updateImageProvider();
diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp
index d1a2ff3943..b324bf39c4 100644
--- a/interface/src/ui/overlays/Web3DOverlay.cpp
+++ b/interface/src/ui/overlays/Web3DOverlay.cpp
@@ -26,6 +26,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -233,6 +234,7 @@ void Web3DOverlay::setupQmlSurface() {
_webSurface->getSurfaceContext()->setContextProperty("Controller", DependencyManager::get().data());
_webSurface->getSurfaceContext()->setContextProperty("Pointers", DependencyManager::get().data());
_webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this);
+ _webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get().data());
_webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../");
diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js
index 8cf5b72b9a..26ffb08796 100644
--- a/scripts/system/commerce/wallet.js
+++ b/scripts/system/commerce/wallet.js
@@ -655,6 +655,9 @@
case 'goToPurchases':
tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH);
break;
+ case 'goToMarketplaceMainPage':
+ tablet.gotoWebScreen(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL);
+ break;
case 'goToMarketplaceItemPage':
tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL);
break;
@@ -688,6 +691,13 @@
updateSendMoneyParticleEffect();
sendMoneyParticleEffectUpdateTimer = Script.setInterval(updateSendMoneyParticleEffect, SEND_MONEY_PARTICLE_TIMER_UPDATE);
break;
+ case 'transactionHistory_goToBank':
+ if (Account.metaverseServerURL.indexOf("staging") >= 0) {
+ Window.location = "hifi://hifiqa-master-metaverse-staging"; // So that we can test in staging.
+ } else {
+ Window.location = "hifi://BankOfHighFidelity";
+ }
+ break;
default:
print('Unrecognized message from QML:', JSON.stringify(message));
}