diff --git a/interface/resources/icons/tablet-icons/wallet-a.svg b/interface/resources/icons/tablet-icons/wallet-a.svg
new file mode 100644
index 0000000000..4a0bab4b33
--- /dev/null
+++ b/interface/resources/icons/tablet-icons/wallet-a.svg
@@ -0,0 +1,180 @@
+
+
diff --git a/interface/resources/icons/tablet-icons/wallet-i.svg b/interface/resources/icons/tablet-icons/wallet-i.svg
new file mode 100644
index 0000000000..2a16ecf973
--- /dev/null
+++ b/interface/resources/icons/tablet-icons/wallet-i.svg
@@ -0,0 +1,276 @@
+
+
+
+
diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml
new file mode 100644
index 0000000000..2252cbfb59
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml
@@ -0,0 +1,73 @@
+//
+// SendMoney.qml
+// qml/hifi/commerce/wallet
+//
+// SendMoney
+//
+// Created by Zach Fox on 2017-08-18
+// 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;
+
+ Hifi.QmlCommerce {
+ id: commerce;
+ }
+
+ // "Unavailable"
+ RalewayRegular {
+ text: "Help me!";
+ // Anchors
+ anchors.fill: parent;
+ // Text size
+ size: 24;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ //
+ // FUNCTION DEFINITIONS START
+ //
+ //
+ // Function Name: fromScript()
+ //
+ // Relevant Variables:
+ // None
+ //
+ // Arguments:
+ // message: The message sent from the JavaScript.
+ // Messages are in format "{method, params}", like json-rpc.
+ //
+ // Description:
+ // Called when a message is received from a script.
+ //
+ function fromScript(message) {
+ switch (message.method) {
+ default:
+ console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
+ }
+ }
+ signal sendSignalToWallet(var msg);
+ //
+ // FUNCTION DEFINITIONS END
+ //
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/NotSetUp.qml b/interface/resources/qml/hifi/commerce/wallet/NotSetUp.qml
new file mode 100644
index 0000000000..3efb592ba1
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/NotSetUp.qml
@@ -0,0 +1,127 @@
+//
+// NotSetUp.qml
+// qml/hifi/commerce/wallet
+//
+// NotSetUp
+//
+// Created by Zach Fox on 2017-08-18
+// 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;
+ Hifi.QmlCommerce {
+ id: commerce;
+ }
+
+ //
+ // TAB CONTENTS START
+ //
+
+ // Text below title bar
+ RalewaySemiBold {
+ id: notSetUpText;
+ text: "Your Wallet Account Has Not Been Set Up";
+ // Text size
+ size: 22;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.topMargin: 100;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: 50;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ // Explanitory text
+ RalewayRegular {
+ text: "To buy and sell items in High Fidelity Coin (HFC), you first need " +
+ "to set up your wallet.
You do not need to submit a credit card or personal information to set up your wallet.";
+ // Text size
+ size: 18;
+ // Anchors
+ anchors.top: notSetUpText.bottom;
+ anchors.topMargin: 16;
+ anchors.left: parent.left;
+ anchors.leftMargin: 30;
+ anchors.right: parent.right;
+ anchors.rightMargin: 30;
+ height: 100;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ // "Set Up" button
+ HifiControlsUit.Button {
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 150;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ width: parent.width/2;
+ height: 50;
+ text: "Set Up My Wallet";
+ onClicked: {
+ sendSignalToWallet({method: 'setUpClicked'});
+ }
+ }
+
+
+ //
+ // TAB CONTENTS END
+ //
+
+ //
+ // FUNCTION DEFINITIONS START
+ //
+ //
+ // Function Name: fromScript()
+ //
+ // Relevant Variables:
+ // None
+ //
+ // Arguments:
+ // message: The message sent from the JavaScript.
+ // Messages are in format "{method, params}", like json-rpc.
+ //
+ // Description:
+ // Called when a message is received from a script.
+ //
+ function fromScript(message) {
+ switch (message.method) {
+ default:
+ console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
+ }
+ }
+ signal sendSignalToWallet(var msg);
+ //
+ // FUNCTION DEFINITIONS END
+ //
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml
new file mode 100644
index 0000000000..ff01aa65da
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml
@@ -0,0 +1,230 @@
+//
+// PassphraseSelection.qml
+// qml/hifi/commerce/wallet
+//
+// PassphraseSelection
+//
+// Created by Zach Fox on 2017-08-18
+// 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;
+
+ // 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;
+ }
+
+ Hifi.QmlCommerce {
+ id: commerce;
+ onSecurityImageResult: {
+ passphrasePageSecurityImage.source = gridModel.getImagePathFromImageID(imageID);
+ }
+
+ onPassphraseSetupStatusResult: {
+ sendMessageToLightbox({method: 'statusResult', status: passphraseIsSetup});
+ }
+ }
+
+ onVisibleChanged: {
+ if (visible) {
+ passphraseField.focus = true;
+ }
+ }
+
+ SecurityImageModel {
+ id: gridModel;
+ }
+
+ HifiControlsUit.TextField {
+ id: passphraseField;
+ anchors.top: parent.top;
+ anchors.topMargin: 30;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ width: 280;
+ height: 50;
+ echoMode: TextInput.Password;
+ placeholderText: "passphrase";
+
+ onVisibleChanged: {
+ if (visible) {
+ text = "";
+ }
+ }
+ }
+ HifiControlsUit.TextField {
+ id: passphraseFieldAgain;
+ anchors.top: passphraseField.bottom;
+ anchors.topMargin: 10;
+ anchors.left: passphraseField.left;
+ anchors.right: passphraseField.right;
+ height: 50;
+ echoMode: TextInput.Password;
+ placeholderText: "re-enter passphrase";
+
+ onVisibleChanged: {
+ if (visible) {
+ text = "";
+ }
+ }
+ }
+
+ // Security Image
+ Item {
+ id: securityImageContainer;
+ // Anchors
+ anchors.top: passphraseField.top;
+ anchors.left: passphraseField.right;
+ anchors.leftMargin: 12;
+ anchors.right: parent.right;
+ Image {
+ id: passphrasePageSecurityImage;
+ anchors.top: parent.top;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ height: 75;
+ width: height;
+ fillMode: Image.PreserveAspectFit;
+ mipmap: true;
+
+ onVisibleChanged: {
+ commerce.getSecurityImage();
+ }
+ }
+ // "Security picture" text below pic
+ RalewayRegular {
+ text: "security picture";
+ // Text size
+ size: 12;
+ // Anchors
+ anchors.top: passphrasePageSecurityImage.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 below TextFields
+ RalewaySemiBold {
+ id: errorText;
+ text: "";
+ // Text size
+ size: 16;
+ // Anchors
+ anchors.top: passphraseFieldAgain.bottom;
+ anchors.topMargin: 0;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: 30;
+ // Style
+ color: hifi.colors.redHighlight;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ // Text below TextFields
+ RalewaySemiBold {
+ id: passwordReqs;
+ text: "Passphrase must be at least 4 characters";
+ // Text size
+ size: 16;
+ // Anchors
+ anchors.top: passphraseFieldAgain.bottom;
+ anchors.topMargin: 16;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: 30;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ // Show passphrase text
+ HifiControlsUit.CheckBox {
+ id: showPassphrase;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.top: passwordReqs.bottom;
+ anchors.topMargin: 16;
+ height: 30;
+ text: "Show passphrase as plain text";
+ boxSize: 24;
+ onClicked: {
+ passphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password;
+ passphraseFieldAgain.echoMode = checked ? TextInput.Normal : TextInput.Password;
+ }
+ }
+
+ // Text below checkbox
+ RalewayRegular {
+ text: "Your passphrase is used to encrypt your private keys. Please write it down. If it is lost, you will not be able to recover it.";
+ // Text size
+ size: 16;
+ // Anchors
+ anchors.top: showPassphrase.bottom;
+ anchors.topMargin: 16;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: paintedHeight;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ function validateAndSubmitPassphrase() {
+ if (passphraseField.text.length < 4) {
+ setErrorText("Passphrase too short.");
+ return false;
+ } else if (passphraseField.text !== passphraseFieldAgain.text) {
+ setErrorText("Passphrases don't match.");
+ return false;
+ } else {
+ setErrorText("");
+ commerce.setPassphrase(passphraseField.text);
+ return true;
+ }
+ }
+
+ function setErrorText(text) {
+ errorText.text = text;
+ }
+
+ signal sendMessageToLightbox(var msg);
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelectionLightbox.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelectionLightbox.qml
new file mode 100644
index 0000000000..862d1894db
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelectionLightbox.qml
@@ -0,0 +1,175 @@
+//
+// PassphraseSelectionLightbox.qml
+// qml/hifi/commerce/wallet
+//
+// PassphraseSelectionLightbox
+//
+// Created by Zach Fox on 2017-08-18
+// 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
+
+Rectangle {
+ HifiConstants { id: hifi; }
+
+ id: root;
+ // Style
+ color: hifi.colors.baseGray;
+
+ onVisibleChanged: {
+ if (visible) {
+ root.resetSubmitButton();
+ }
+ }
+
+ Connections {
+ target: passphraseSelection;
+ onSendMessageToLightbox: {
+ if (msg.method === 'statusResult') {
+ if (msg.status) {
+ // Success submitting new passphrase
+ root.resetSubmitButton();
+ root.visible = false;
+ } else {
+ // Error submitting new passphrase
+ root.resetSubmitButton();
+ passphraseSelection.setErrorText("Backend error");
+ }
+ }
+ }
+ }
+
+ //
+ // SECURE PASSPHRASE SELECTION START
+ //
+ Item {
+ id: choosePassphraseContainer;
+ // Anchors
+ anchors.fill: parent;
+
+ Item {
+ id: passphraseTitle;
+ // Size
+ width: parent.width;
+ height: 50;
+ // Anchors
+ anchors.left: parent.left;
+ anchors.top: parent.top;
+
+ // Title Bar text
+ RalewaySemiBold {
+ text: "CHANGE PASSPHRASE";
+ // Text size
+ size: hifi.fontSizes.overlayTitle;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.bottom: parent.bottom;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ }
+
+ // Text below title bar
+ RalewaySemiBold {
+ id: passphraseTitleHelper;
+ text: "Choose a Secure Passphrase";
+ // Text size
+ size: 24;
+ // Anchors
+ anchors.top: passphraseTitle.bottom;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: 50;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ PassphraseSelection {
+ id: passphraseSelection;
+ anchors.top: passphraseTitleHelper.bottom;
+ anchors.topMargin: 30;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ anchors.bottom: passphraseNavBar.top;
+ }
+
+ // Navigation Bar
+ Item {
+ id: passphraseNavBar;
+ // Size
+ width: parent.width;
+ height: 100;
+ // Anchors:
+ anchors.left: parent.left;
+ anchors.bottom: parent.bottom;
+
+ // "Cancel" button
+ HifiControlsUit.Button {
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ anchors.topMargin: 3;
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 3;
+ anchors.left: parent.left;
+ anchors.leftMargin: 20;
+ width: 100;
+ text: "Cancel"
+ onClicked: {
+ root.visible = false;
+ }
+ }
+
+ // "Submit" button
+ HifiControlsUit.Button {
+ id: passphraseSubmitButton;
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ anchors.topMargin: 3;
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 3;
+ anchors.right: parent.right;
+ anchors.rightMargin: 20;
+ width: 100;
+ text: "Submit";
+ onClicked: {
+ if (passphraseSelection.validateAndSubmitPassphrase()) {
+ passphraseSubmitButton.text = "Submitting...";
+ passphraseSubmitButton.enabled = false;
+ }
+ }
+ }
+ }
+ }
+ //
+ // SECURE PASSPHRASE SELECTION END
+ //
+
+ function resetSubmitButton() {
+ passphraseSubmitButton.enabled = true;
+ passphraseSubmitButton.text = "Submit";
+ }
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/Security.qml b/interface/resources/qml/hifi/commerce/wallet/Security.qml
new file mode 100644
index 0000000000..7fc3d9324f
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/Security.qml
@@ -0,0 +1,312 @@
+//
+// Security.qml
+// qml/hifi/commerce/wallet
+//
+// Security
+//
+// Created by Zach Fox on 2017-08-18
+// 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;
+
+ Hifi.QmlCommerce {
+ id: commerce;
+
+ onSecurityImageResult: {
+ if (imageID !== 0) { // "If security image is set up"
+ var path = securityImageModel.getImagePathFromImageID(imageID);
+ topSecurityImage.source = path;
+ changeSecurityImageImage.source = path;
+ }
+ }
+
+ onKeyFilePathResult: {
+ if (path !== "") {
+ keyFilePath.text = path;
+ }
+ }
+ }
+
+ SecurityImageModel {
+ id: securityImageModel;
+ }
+
+ // Username Text
+ RalewayRegular {
+ id: usernameText;
+ text: Account.username;
+ // Text size
+ size: 24;
+ // Style
+ color: hifi.colors.faintGray;
+ elide: Text.ElideRight;
+ // Anchors
+ anchors.top: securityImageContainer.top;
+ anchors.bottom: securityImageContainer.bottom;
+ anchors.left: parent.left;
+ anchors.right: securityImageContainer.left;
+ }
+
+ // Security Image
+ Item {
+ id: securityImageContainer;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.right: parent.right;
+ width: 75;
+ height: childrenRect.height;
+
+ onVisibleChanged: {
+ if (visible) {
+ commerce.getSecurityImage();
+ }
+ }
+
+ Image {
+ id: topSecurityImage;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ height: parent.width - 10;
+ width: height;
+ fillMode: Image.PreserveAspectFit;
+ mipmap: true;
+ }
+ // "Security picture" text below pic
+ RalewayRegular {
+ text: "security picture";
+ // Text size
+ size: 12;
+ // Anchors
+ anchors.top: topSecurityImage.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;
+ }
+ }
+
+ Item {
+ id: securityContainer;
+ anchors.top: securityImageContainer.bottom;
+ anchors.topMargin: 20;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ height: childrenRect.height;
+
+ RalewayRegular {
+ id: securityText;
+ text: "Security";
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ height: 30;
+ // Text size
+ size: 22;
+ // Style
+ color: hifi.colors.faintGray;
+ }
+
+ Item {
+ id: changePassphraseContainer;
+ anchors.top: securityText.bottom;
+ anchors.topMargin: 16;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ height: 75;
+
+ Image {
+ id: changePassphraseImage;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ height: parent.height;
+ width: height;
+ fillMode: Image.PreserveAspectFit;
+ mipmap: true;
+ }
+ // "Change Passphrase" button
+ HifiControlsUit.Button {
+ id: changePassphraseButton;
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.verticalCenter: parent.verticalCenter;
+ anchors.left: changePassphraseImage.right;
+ anchors.leftMargin: 16;
+ width: 250;
+ height: 50;
+ text: "Change My Passphrase";
+ onClicked: {
+ sendSignalToWallet({method: 'walletSecurity_changePassphrase'});
+ }
+ }
+ }
+
+ Item {
+ id: changeSecurityImageContainer;
+ anchors.top: changePassphraseContainer.bottom;
+ anchors.topMargin: 8;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ height: 75;
+
+ Image {
+ id: changeSecurityImageImage;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ height: parent.height;
+ width: height;
+ fillMode: Image.PreserveAspectFit;
+ mipmap: true;
+ }
+ // "Change Security Image" button
+ HifiControlsUit.Button {
+ id: changeSecurityImageButton;
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.verticalCenter: parent.verticalCenter;
+ anchors.left: changeSecurityImageImage.right;
+ anchors.leftMargin: 16;
+ width: 250;
+ height: 50;
+ text: "Change My Security Image";
+ onClicked: {
+ sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'});
+ }
+ }
+ }
+ }
+
+ Item {
+ id: yourPrivateKeysContainer;
+ anchors.top: securityContainer.bottom;
+ anchors.topMargin: 20;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ height: childrenRect.height;
+
+ RalewaySemiBold {
+ id: yourPrivateKeysText;
+ text: "Your Private Keys";
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ height: 30;
+ // Text size
+ size: 22;
+ // Style
+ color: hifi.colors.faintGray;
+ }
+
+ // Text below "your private keys"
+ RalewayRegular {
+ id: explanitoryText;
+ text: "Your money and purchases are secured with private keys that only you " +
+ "have access to. If they are lost, you will not be able to access your money or purchases. " +
+ "To safeguard your private keys, back up this file regularly:";
+ // Text size
+ size: 18;
+ // Anchors
+ anchors.top: yourPrivateKeysText.bottom;
+ anchors.topMargin: 10;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ height: paintedHeight;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ HifiControlsUit.TextField {
+ id: keyFilePath;
+ anchors.top: explanitoryText.bottom;
+ anchors.topMargin: 10;
+ anchors.left: parent.left;
+ anchors.right: clipboardButton.left;
+ height: 40;
+ readOnly: true;
+
+ onVisibleChanged: {
+ if (visible) {
+ commerce.getKeyFilePath();
+ }
+ }
+ }
+ HifiControlsUit.Button {
+ id: clipboardButton;
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.right: parent.right;
+ anchors.top: keyFilePath.top;
+ anchors.bottom: keyFilePath.bottom;
+ width: height;
+ HiFiGlyphs {
+ text: hifi.glyphs.question;
+ // Size
+ size: parent.height*1.3;
+ // Anchors
+ anchors.fill: parent;
+ // Style
+ horizontalAlignment: Text.AlignHCenter;
+ color: enabled ? hifi.colors.white : hifi.colors.faintGray;
+ }
+
+ onClicked: {
+ Window.copyToClipboard(keyFilePath.text);
+ }
+ }
+ }
+
+ //
+ // FUNCTION DEFINITIONS START
+ //
+ //
+ // Function Name: fromScript()
+ //
+ // Relevant Variables:
+ // None
+ //
+ // Arguments:
+ // message: The message sent from the JavaScript.
+ // Messages are in format "{method, params}", like json-rpc.
+ //
+ // Description:
+ // Called when a message is received from a script.
+ //
+ function fromScript(message) {
+ switch (message.method) {
+ default:
+ console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
+ }
+ }
+ signal sendSignalToWallet(var msg);
+ //
+ // FUNCTION DEFINITIONS END
+ //
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageModel.qml b/interface/resources/qml/hifi/commerce/wallet/SecurityImageModel.qml
new file mode 100644
index 0000000000..b49f16857b
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/SecurityImageModel.qml
@@ -0,0 +1,46 @@
+//
+// SecurityImageModel.qml
+// qml/hifi/commerce
+//
+// SecurityImageModel
+//
+// Created by Zach Fox on 2017-08-17
+// 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 QtQuick 2.5
+
+ListModel {
+ id: root;
+ ListElement{
+ sourcePath: "images/01cat.jpg"
+ securityImageEnumValue: 1;
+ }
+ ListElement{
+ sourcePath: "images/02car.jpg"
+ securityImageEnumValue: 2;
+ }
+ ListElement{
+ sourcePath: "images/03dog.jpg"
+ securityImageEnumValue: 3;
+ }
+ ListElement{
+ sourcePath: "images/04stars.jpg"
+ securityImageEnumValue: 4;
+ }
+ ListElement{
+ sourcePath: "images/05plane.jpg"
+ securityImageEnumValue: 5;
+ }
+ ListElement{
+ sourcePath: "images/06gingerbread.jpg"
+ securityImageEnumValue: 6;
+ }
+
+ function getImagePathFromImageID(imageID) {
+ return (imageID ? root.get(imageID - 1).sourcePath : "");
+ }
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml b/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml
new file mode 100644
index 0000000000..0d64217717
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml
@@ -0,0 +1,107 @@
+//
+// SecurityImageSelection.qml
+// qml/hifi/commerce/wallet
+//
+// SecurityImageSelection
+//
+// Created by Zach Fox on 2017-08-17
+// 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;
+
+ Hifi.QmlCommerce {
+ id: commerce;
+ onSecurityImageResult: {
+ if (imageID > 0) {
+ for (var itr = 0; itr < gridModel.count; itr++) {
+ var thisValue = gridModel.get(itr).securityImageEnumValue;
+ if (thisValue === imageID) {
+ securityImageGrid.currentIndex = itr;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ onVisibleChanged: {
+ if (visible) {
+ commerce.getSecurityImage();
+ }
+ }
+
+ SecurityImageModel {
+ id: gridModel;
+ }
+
+ GridView {
+ id: securityImageGrid;
+ clip: true;
+ // Anchors
+ anchors.fill: parent;
+ currentIndex: -1;
+ cellWidth: width / 3;
+ cellHeight: height / 2;
+ model: gridModel;
+ delegate: Item {
+ width: securityImageGrid.cellWidth;
+ height: securityImageGrid.cellHeight;
+ Item {
+ anchors.fill: parent;
+ Image {
+ width: parent.width - 8;
+ height: parent.height - 8;
+ source: sourcePath;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ anchors.verticalCenter: parent.verticalCenter;
+ fillMode: Image.PreserveAspectFit;
+ mipmap: true;
+ }
+ }
+ MouseArea {
+ anchors.fill: parent;
+ propagateComposedEvents: false;
+ onClicked: {
+ securityImageGrid.currentIndex = index;
+ }
+ }
+ }
+ highlight: Rectangle {
+ width: securityImageGrid.cellWidth;
+ height: securityImageGrid.cellHeight;
+ color: hifi.colors.blueHighlight;
+ }
+ }
+
+ //
+ // FUNCTION DEFINITIONS START
+ //
+ signal sendToScript(var message);
+
+ function getImagePathFromImageID(imageID) {
+ return (imageID ? gridModel.getImagePathFromImageID(imageID) : "");
+ }
+
+ function getSelectedImageIndex() {
+ return gridModel.get(securityImageGrid.currentIndex).securityImageEnumValue;
+ }
+ //
+ // FUNCTION DEFINITIONS END
+ //
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelectionLightbox.qml b/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelectionLightbox.qml
new file mode 100644
index 0000000000..12511d6e6c
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelectionLightbox.qml
@@ -0,0 +1,199 @@
+//
+// SecurityImageSelectionLightbox.qml
+// qml/hifi/commerce/wallet
+//
+// SecurityImageSelectionLightbox
+//
+// Created by Zach Fox on 2017-08-18
+// 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
+
+Rectangle {
+ HifiConstants { id: hifi; }
+
+ id: root;
+ property bool justSubmitted: false;
+ // Style
+ color: hifi.colors.baseGray;
+
+ onVisibleChanged: {
+ if (visible) {
+ root.resetSubmitButton();
+ }
+ }
+
+ Hifi.QmlCommerce {
+ id: commerce;
+
+ onSecurityImageResult: {
+ if (imageID !== 0) { // Success submitting new security image
+ if (root.justSubmitted) {
+ root.resetSubmitButton();
+ root.visible = false;
+ root.justSubmitted = false;
+ }
+ } else if (root.justSubmitted) {
+ // Error submitting new security image.
+ root.resetSubmitButton();
+ root.justSubmitted = false;
+ }
+ }
+ }
+
+ //
+ // SECURITY IMAGE SELECTION START
+ //
+ Item {
+ id: securityImageContainer;
+ // Anchors
+ anchors.fill: parent;
+
+ Item {
+ id: securityImageTitle;
+ // Size
+ width: parent.width;
+ height: 50;
+ // Anchors
+ anchors.left: parent.left;
+ anchors.top: parent.top;
+
+ // Title Bar text
+ RalewaySemiBold {
+ text: "CHANGE SECURITY IMAGE";
+ // Text size
+ size: hifi.fontSizes.overlayTitle;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.bottom: parent.bottom;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ }
+
+ // Text below title bar
+ RalewaySemiBold {
+ id: securityImageTitleHelper;
+ text: "Choose a Security Picture:";
+ // Text size
+ size: 24;
+ // Anchors
+ anchors.top: securityImageTitle.bottom;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ height: 50;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ SecurityImageSelection {
+ id: securityImageSelection;
+ // Anchors
+ anchors.top: securityImageTitleHelper.bottom;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: 280;
+ }
+
+ // Text below security images
+ RalewayRegular {
+ text: "Your security picture shows you that the service asking for your passphrase is authorized. You can change your secure picture at any time.";
+ // Text size
+ size: 18;
+ // Anchors
+ anchors.top: securityImageSelection.bottom;
+ anchors.topMargin: 40;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: paintedHeight;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ // Navigation Bar
+ Item {
+ id: securityImageNavBar;
+ // Size
+ width: parent.width;
+ height: 100;
+ // Anchors:
+ anchors.left: parent.left;
+ anchors.bottom: parent.bottom;
+
+ // "Cancel" button
+ HifiControlsUit.Button {
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ anchors.topMargin: 3;
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 3;
+ anchors.left: parent.left;
+ anchors.leftMargin: 20;
+ width: 100;
+ text: "Cancel"
+ onClicked: {
+ root.visible = false;
+ }
+ }
+
+ // "Submit" button
+ HifiControlsUit.Button {
+ id: securityImageSubmitButton;
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ anchors.topMargin: 3;
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 3;
+ anchors.right: parent.right;
+ anchors.rightMargin: 20;
+ width: 100;
+ text: "Submit";
+ onClicked: {
+ root.justSubmitted = true;
+ securityImageSubmitButton.text = "Submitting...";
+ securityImageSubmitButton.enabled = false;
+ commerce.chooseSecurityImage(securityImageSelection.getSelectedImageIndex());
+ }
+ }
+ }
+ }
+ //
+ // SECURITY IMAGE SELECTION END
+ //
+
+ function resetSubmitButton() {
+ securityImageSubmitButton.enabled = true;
+ securityImageSubmitButton.text = "Submit";
+ }
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/SendMoney.qml b/interface/resources/qml/hifi/commerce/wallet/SendMoney.qml
new file mode 100644
index 0000000000..75334b1686
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/SendMoney.qml
@@ -0,0 +1,73 @@
+//
+// SendMoney.qml
+// qml/hifi/commerce/wallet
+//
+// SendMoney
+//
+// Created by Zach Fox on 2017-08-18
+// 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;
+
+ Hifi.QmlCommerce {
+ id: commerce;
+ }
+
+ // "Unavailable"
+ RalewayRegular {
+ text: "You currently cannot send money to other High Fidelity users.";
+ // Anchors
+ anchors.fill: parent;
+ // Text size
+ size: 24;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ //
+ // FUNCTION DEFINITIONS START
+ //
+ //
+ // Function Name: fromScript()
+ //
+ // Relevant Variables:
+ // None
+ //
+ // Arguments:
+ // message: The message sent from the JavaScript.
+ // Messages are in format "{method, params}", like json-rpc.
+ //
+ // Description:
+ // Called when a message is received from a script.
+ //
+ function fromScript(message) {
+ switch (message.method) {
+ default:
+ console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
+ }
+ }
+ signal sendSignalToWallet(var msg);
+ //
+ // FUNCTION DEFINITIONS END
+ //
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml
new file mode 100644
index 0000000000..23989f0db5
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml
@@ -0,0 +1,479 @@
+//
+// Wallet.qml
+// qml/hifi/commerce/wallet
+//
+// Wallet
+//
+// Created by Zach Fox on 2017-08-17
+// 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
+
+Rectangle {
+ HifiConstants { id: hifi; }
+
+ id: root;
+
+ property string activeView: "walletHome";
+
+ // Style
+ color: hifi.colors.baseGray;
+ Hifi.QmlCommerce {
+ id: commerce;
+
+ onSecurityImageResult: {
+ if (imageID === 0) { // "If security image is not set up"
+ if (root.activeView !== "notSetUp") {
+ root.activeView = "notSetUp";
+ }
+ }
+ }
+
+ onKeyFilePathResult: {
+ if (path === "" && root.activeView !== "notSetUp") {
+ root.activeView = "notSetUp";
+ }
+ }
+ }
+
+ SecurityImageModel {
+ id: securityImageModel;
+ }
+
+ Connections {
+ target: walletSetupLightbox;
+ onSendSignalToWallet: {
+ if (msg.method === 'walletSetup_cancelClicked') {
+ walletSetupLightbox.visible = false;
+ } else if (msg.method === 'walletSetup_finished') {
+ root.activeView = "walletHome";
+ } else {
+ sendToScript(msg);
+ }
+ }
+ }
+ Connections {
+ target: notSetUp;
+ onSendSignalToWallet: {
+ if (msg.method === 'setUpClicked') {
+ walletSetupLightbox.visible = true;
+ }
+ }
+ }
+
+ Rectangle {
+ id: walletSetupLightboxContainer;
+ visible: walletSetupLightbox.visible || passphraseSelectionLightbox.visible || securityImageSelectionLightbox.visible;
+ z: 998;
+ anchors.fill: parent;
+ color: "black";
+ opacity: 0.5;
+ }
+ WalletSetupLightbox {
+ id: walletSetupLightbox;
+ visible: false;
+ z: 999;
+ anchors.centerIn: walletSetupLightboxContainer;
+ width: walletSetupLightboxContainer.width - 50;
+ height: walletSetupLightboxContainer.height - 50;
+ }
+ PassphraseSelectionLightbox {
+ id: passphraseSelectionLightbox;
+ visible: false;
+ z: 999;
+ anchors.centerIn: walletSetupLightboxContainer;
+ width: walletSetupLightboxContainer.width - 50;
+ height: walletSetupLightboxContainer.height - 50;
+ }
+ SecurityImageSelectionLightbox {
+ id: securityImageSelectionLightbox;
+ visible: false;
+ z: 999;
+ anchors.centerIn: walletSetupLightboxContainer;
+ width: walletSetupLightboxContainer.width - 50;
+ height: walletSetupLightboxContainer.height - 50;
+ }
+
+
+ //
+ // TITLE BAR START
+ //
+ Item {
+ id: titleBarContainer;
+ // Size
+ width: parent.width;
+ height: 50;
+ // Anchors
+ anchors.left: parent.left;
+ anchors.top: parent.top;
+
+ // Title Bar text
+ RalewaySemiBold {
+ id: titleBarText;
+ text: "WALLET";
+ // Text size
+ size: hifi.fontSizes.overlayTitle;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.bottom: parent.bottom;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ // Separator
+ HifiControlsUit.Separator {
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ anchors.bottom: parent.bottom;
+ }
+ }
+ //
+ // TITLE BAR END
+ //
+
+ //
+ // TAB CONTENTS START
+ //
+ NotSetUp {
+ id: notSetUp;
+ visible: root.activeView === "notSetUp";
+ anchors.top: titleBarContainer.bottom;
+ anchors.bottom: tabButtonsContainer.top;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ }
+
+ WalletHome {
+ id: walletHome;
+ visible: root.activeView === "walletHome";
+ anchors.top: titleBarContainer.bottom;
+ anchors.topMargin: 16;
+ anchors.bottom: tabButtonsContainer.top;
+ anchors.bottomMargin: 16;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ }
+
+ SendMoney {
+ id: sendMoney;
+ visible: root.activeView === "sendMoney";
+ anchors.top: titleBarContainer.bottom;
+ anchors.topMargin: 16;
+ anchors.bottom: tabButtonsContainer.top;
+ anchors.bottomMargin: 16;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ }
+
+ Security {
+ id: security;
+ visible: root.activeView === "security";
+ anchors.top: titleBarContainer.bottom;
+ anchors.topMargin: 16;
+ anchors.bottom: tabButtonsContainer.top;
+ anchors.bottomMargin: 16;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ }
+ Connections {
+ target: security;
+ onSendSignalToWallet: {
+ if (msg.method === 'walletSecurity_changePassphrase') {
+ passphraseSelectionLightbox.visible = true;
+ } else if (msg.method === 'walletSecurity_changeSecurityImage') {
+ securityImageSelectionLightbox.visible = true;
+ }
+ }
+ }
+
+ Help {
+ id: help;
+ visible: root.activeView === "help";
+ anchors.top: titleBarContainer.bottom;
+ anchors.topMargin: 16;
+ anchors.bottom: tabButtonsContainer.top;
+ anchors.bottomMargin: 16;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ }
+
+
+ //
+ // TAB CONTENTS END
+ //
+
+ //
+ // TAB BUTTONS START
+ //
+ Item {
+ id: tabButtonsContainer;
+ property int numTabs: 5;
+ // Size
+ width: root.width;
+ height: 80;
+ // Anchors
+ anchors.left: parent.left;
+ anchors.bottom: parent.bottom;
+
+ // Separator
+ HifiControlsUit.Separator {
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ anchors.top: parent.top;
+ }
+
+ // "WALLET HOME" tab button
+ Rectangle {
+ id: walletHomeButtonContainer;
+ visible: !notSetUp.visible;
+ color: root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black;
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ anchors.bottom: parent.bottom;
+ width: parent.width / tabButtonsContainer.numTabs;
+
+ RalewaySemiBold {
+ text: "WALLET HOME";
+ // Text size
+ size: hifi.fontSizes.overlayTitle;
+ // Anchors
+ anchors.fill: parent;
+ anchors.leftMargin: 4;
+ anchors.rightMargin: 4;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ MouseArea {
+ enabled: !walletSetupLightboxContainer.visible;
+ anchors.fill: parent;
+ hoverEnabled: enabled;
+ onClicked: {
+ root.activeView = "walletHome";
+ tabButtonsContainer.resetTabButtonColors();
+ }
+ onEntered: parent.color = hifi.colors.blueHighlight;
+ onExited: parent.color = root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black;
+ }
+
+ onVisibleChanged: {
+ if (visible) {
+ commerce.getSecurityImage();
+ commerce.balance();
+ }
+ }
+ }
+
+ // "SEND MONEY" tab button
+ Rectangle {
+ id: sendMoneyButtonContainer;
+ visible: !notSetUp.visible;
+ color: hifi.colors.black;
+ anchors.top: parent.top;
+ anchors.left: walletHomeButtonContainer.right;
+ anchors.bottom: parent.bottom;
+ width: parent.width / tabButtonsContainer.numTabs;
+
+ RalewaySemiBold {
+ text: "SEND MONEY";
+ // Text size
+ size: 14;
+ // Anchors
+ anchors.fill: parent;
+ anchors.leftMargin: 4;
+ anchors.rightMargin: 4;
+ // Style
+ color: hifi.colors.lightGray50;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ }
+
+ // "EXCHANGE MONEY" tab button
+ Rectangle {
+ id: exchangeMoneyButtonContainer;
+ visible: !notSetUp.visible;
+ color: hifi.colors.black;
+ anchors.top: parent.top;
+ anchors.left: sendMoneyButtonContainer.right;
+ anchors.bottom: parent.bottom;
+ width: parent.width / tabButtonsContainer.numTabs;
+
+ RalewaySemiBold {
+ text: "EXCHANGE MONEY";
+ // Text size
+ size: 14;
+ // Anchors
+ anchors.fill: parent;
+ anchors.leftMargin: 4;
+ anchors.rightMargin: 4;
+ // Style
+ color: hifi.colors.lightGray50;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ }
+
+ // "SECURITY" tab button
+ Rectangle {
+ id: securityButtonContainer;
+ visible: !notSetUp.visible;
+ color: root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black;
+ anchors.top: parent.top;
+ anchors.left: exchangeMoneyButtonContainer.right;
+ anchors.bottom: parent.bottom;
+ width: parent.width / tabButtonsContainer.numTabs;
+
+ RalewaySemiBold {
+ text: "SECURITY";
+ // Text size
+ size: hifi.fontSizes.overlayTitle;
+ // Anchors
+ anchors.fill: parent;
+ anchors.leftMargin: 4;
+ anchors.rightMargin: 4;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ MouseArea {
+ enabled: !walletSetupLightboxContainer.visible;
+ anchors.fill: parent;
+ hoverEnabled: enabled;
+ onClicked: {
+ root.activeView = "security";
+ tabButtonsContainer.resetTabButtonColors();
+ }
+ onEntered: parent.color = hifi.colors.blueHighlight;
+ onExited: parent.color = root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black;
+ }
+
+ onVisibleChanged: {
+ if (visible) {
+ commerce.getSecurityImage();
+ commerce.getKeyFilePath();
+ }
+ }
+ }
+
+ // "HELP" tab button
+ Rectangle {
+ id: helpButtonContainer;
+ visible: !notSetUp.visible;
+ color: root.activeView === "help" ? hifi.colors.blueAccent : hifi.colors.black;
+ anchors.top: parent.top;
+ anchors.left: securityButtonContainer.right;
+ anchors.bottom: parent.bottom;
+ width: parent.width / tabButtonsContainer.numTabs;
+
+ RalewaySemiBold {
+ text: "HELP";
+ // Text size
+ size: hifi.fontSizes.overlayTitle;
+ // Anchors
+ anchors.fill: parent;
+ anchors.leftMargin: 4;
+ anchors.rightMargin: 4;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ MouseArea {
+ enabled: !walletSetupLightboxContainer.visible;
+ anchors.fill: parent;
+ hoverEnabled: enabled;
+ onClicked: {
+ root.activeView = "help";
+ tabButtonsContainer.resetTabButtonColors();
+ }
+ onEntered: parent.color = hifi.colors.blueHighlight;
+ onExited: parent.color = root.activeView === "help" ? hifi.colors.blueAccent : hifi.colors.black;
+ }
+ }
+
+ function resetTabButtonColors() {
+ walletHomeButtonContainer.color = hifi.colors.black;
+ sendMoneyButtonContainer.color = hifi.colors.black;
+ securityButtonContainer.color = hifi.colors.black;
+ helpButtonContainer.color = hifi.colors.black;
+ if (root.activeView === "walletHome") {
+ walletHomeButtonContainer.color = hifi.colors.blueAccent;
+ } else if (root.activeView === "sendMoney") {
+ sendMoneyButtonContainer.color = hifi.colors.blueAccent;
+ } else if (root.activeView === "security") {
+ securityButtonContainer.color = hifi.colors.blueAccent;
+ } else if (root.activeView === "help") {
+ helpButtonContainer.color = hifi.colors.blueAccent;
+ }
+ }
+ }
+ //
+ // TAB BUTTONS END
+ //
+
+ //
+ // FUNCTION DEFINITIONS START
+ //
+ //
+ // Function Name: fromScript()
+ //
+ // Relevant Variables:
+ // None
+ //
+ // Arguments:
+ // message: The message sent from the JavaScript.
+ // Messages are in format "{method, params}", like json-rpc.
+ //
+ // Description:
+ // Called when a message is received from a script.
+ //
+ function fromScript(message) {
+ switch (message.method) {
+ default:
+ console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
+ }
+ }
+ signal sendToScript(var message);
+
+ //
+ // FUNCTION DEFINITIONS END
+ //
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
new file mode 100644
index 0000000000..281d110f21
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
@@ -0,0 +1,354 @@
+//
+// WalletHome.qml
+// qml/hifi/commerce/wallet
+//
+// WalletHome
+//
+// Created by Zach Fox on 2017-08-18
+// 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;
+
+ Hifi.QmlCommerce {
+ id: commerce;
+
+ onSecurityImageResult: {
+ if (imageID !== 0) { // "If security image is set up"
+ var path = securityImageModel.getImagePathFromImageID(imageID);
+ securityImage.source = path;
+ }
+ }
+
+ onBalanceResult : {
+ balanceText.text = parseFloat(result.data.balance/100).toFixed(2);
+ }
+ }
+
+ SecurityImageModel {
+ id: securityImageModel;
+ }
+
+ Connections {
+ target: GlobalServices
+ onMyUsernameChanged: {
+ usernameText.text = Account.username;
+ }
+ }
+
+ // Username Text
+ RalewayRegular {
+ id: usernameText;
+ text: Account.username;
+ // Text size
+ size: 24;
+ // Style
+ color: hifi.colors.faintGray;
+ elide: Text.ElideRight;
+ // Anchors
+ anchors.top: securityImageContainer.top;
+ anchors.bottom: securityImageContainer.bottom;
+ anchors.left: parent.left;
+ anchors.right: hfcBalanceContainer.left;
+ }
+
+ // HFC Balance Container
+ Item {
+ id: hfcBalanceContainer;
+ // Anchors
+ anchors.top: securityImageContainer.top;
+ anchors.right: securityImageContainer.left;
+ anchors.rightMargin: 16;
+ width: 175;
+ height: 60;
+ Rectangle {
+ id: hfcBalanceField;
+ anchors.right: parent.right;
+ anchors.left: parent.left;
+ anchors.bottom: parent.bottom;
+ height: parent.height - 15;
+
+ // "HFC" balance label
+ RalewayRegular {
+ id: balanceLabel;
+ text: "HFC";
+ // Text size
+ size: 20;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.bottom: parent.bottom;
+ anchors.right: hfcBalanceField.right;
+ anchors.rightMargin: 4;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.darkGray;
+ // Alignment
+ horizontalAlignment: Text.AlignRight;
+ verticalAlignment: Text.AlignVCenter;
+
+ onVisibleChanged: {
+ if (visible) {
+ commerce.balance();
+ }
+ }
+ }
+
+ // Balance Text
+ FiraSansRegular {
+ id: balanceText;
+ text: "--";
+ // Text size
+ size: 28;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.bottom: parent.bottom;
+ anchors.left: parent.left;
+ anchors.right: balanceLabel.left;
+ anchors.rightMargin: 4;
+ // Style
+ color: hifi.colors.darkGray;
+ // Alignment
+ horizontalAlignment: Text.AlignRight;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ }
+ // "balance" text above field
+ RalewayRegular {
+ text: "balance";
+ // Text size
+ size: 12;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.bottom: hfcBalanceField.top;
+ anchors.bottomMargin: 4;
+ anchors.left: hfcBalanceField.left;
+ anchors.right: hfcBalanceField.right;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ }
+
+ // Security Image
+ Item {
+ id: securityImageContainer;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.right: parent.right;
+ width: 75;
+ height: childrenRect.height;
+
+ onVisibleChanged: {
+ if (visible) {
+ commerce.getSecurityImage();
+ }
+ }
+
+ Image {
+ id: securityImage;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ height: parent.width - 10;
+ width: height;
+ fillMode: Image.PreserveAspectFit;
+ mipmap: true;
+ }
+ // "Security picture" text below pic
+ RalewayRegular {
+ text: "security picture";
+ // Text size
+ size: 12;
+ // Anchors
+ anchors.top: securityImage.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;
+ }
+ }
+
+ // Recent Activity
+ Item {
+ id: recentActivityContainer;
+ anchors.top: securityImageContainer.bottom;
+ anchors.topMargin: 8;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ anchors.bottom: homeMessage.visible ? homeMessage.top : root.bottom;
+ anchors.bottomMargin: 10;
+
+ RalewayRegular {
+ id: recentActivityText;
+ text: "Recent Activity";
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ height: 30;
+ // Text size
+ size: 22;
+ // Style
+ color: hifi.colors.faintGray;
+ }
+
+ Rectangle {
+ id: transactionHistory;
+ anchors.top: recentActivityText.bottom;
+ anchors.topMargin: 4;
+ anchors.bottom: toggleFullHistoryButton.top;
+ anchors.bottomMargin: 8;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+
+ // some placeholder stuff
+ RalewayRegular {
+ text: homeMessage.visible ? "you CANNOT scroll through this." : "you CAN scroll through this";
+ // Text size
+ size: 16;
+ // Anchors
+ anchors.fill: parent;
+ // Style
+ color: hifi.colors.darkGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ }
+
+ HifiControlsUit.Button {
+ id: toggleFullHistoryButton;
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.bottom: parent.bottom;
+ anchors.right: parent.right;
+ width: 250;
+ height: 40;
+ text: homeMessage.visible ? "See Full Transaction History" : "Collapse Transaction History";
+ onClicked: {
+ if (homeMessage.visible) {
+ homeMessage.visible = false;
+ } else {
+ homeMessage.visible = true;
+ }
+ }
+ }
+ }
+
+ // Item for "messages" - like "Welcome"
+ Item {
+ id: homeMessage;
+ anchors.bottom: parent.bottom;
+ anchors.left: parent.left;
+ anchors.leftMargin: 20;
+ anchors.right: parent.right;
+ anchors.rightMargin: 20;
+ height: childrenRect.height;
+
+ RalewayRegular {
+ id: messageText;
+ text: "Welcome! Let's get you some spending money.
" +
+ "Now that your account is all set up, click the button below to request your starter money. " +
+ "A robot will promptly review your request and put money into your account.";
+ // Text size
+ size: 16;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ height: 130;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ Item {
+ id: homeMessageButtons;
+ anchors.top: messageText.bottom;
+ anchors.topMargin: 4;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ height: 40;
+ HifiControlsUit.Button {
+ id: noThanksButton;
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ anchors.bottom: parent.bottom;
+ anchors.left: parent.left;
+ width: 100;
+ text: "No Thanks"
+ onClicked: {
+ messageText.text = "Okay...weird. Who doesn't like free money? If you change your mind, too bad. Sorry."
+ homeMessageButtons.visible = false;
+ }
+ }
+ HifiControlsUit.Button {
+ id: freeMoneyButton;
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ anchors.bottom: parent.bottom;
+ anchors.right: parent.right;
+ width: 210;
+ text: "Free Money Please"
+ onClicked: {
+ messageText.text = "Go, MoneyRobots, Go!"
+ homeMessageButtons.visible = false;
+ }
+ }
+ }
+ }
+
+ //
+ // FUNCTION DEFINITIONS START
+ //
+ //
+ // Function Name: fromScript()
+ //
+ // Relevant Variables:
+ // None
+ //
+ // Arguments:
+ // message: The message sent from the JavaScript.
+ // Messages are in format "{method, params}", like json-rpc.
+ //
+ // Description:
+ // Called when a message is received from a script.
+ //
+ function fromScript(message) {
+ switch (message.method) {
+ default:
+ console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
+ }
+ }
+ signal sendSignalToWallet(var msg);
+ //
+ // FUNCTION DEFINITIONS END
+ //
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml
new file mode 100644
index 0000000000..ecb277f800
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml
@@ -0,0 +1,632 @@
+//
+// WalletSetupLightbox.qml
+// qml/hifi/commerce/wallet
+//
+// WalletSetupLightbox
+//
+// Created by Zach Fox on 2017-08-17
+// 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
+
+Rectangle {
+ HifiConstants { id: hifi; }
+
+ id: root;
+ property string lastPage: "login";
+ // Style
+ color: hifi.colors.baseGray;
+
+ Hifi.QmlCommerce {
+ id: commerce;
+
+ onLoginStatusResult: {
+ if (isLoggedIn) {
+ securityImageContainer.visible = true;
+ } else {
+ loginPageContainer.visible = true;
+ }
+ }
+
+ onSecurityImageResult: {
+ if (imageID === 0 && root.lastPage === "securityImage") {
+ // ERROR! Invalid security image.
+ securityImageContainer.visible = true;
+ choosePassphraseContainer.visible = false;
+ }
+ }
+
+ onPassphraseSetupStatusResult: {
+ securityImageContainer.visible = false;
+ if (passphraseIsSetup) {
+ privateKeysReadyContainer.visible = true;
+ } else {
+ choosePassphraseContainer.visible = true;
+ }
+ }
+
+ onKeyFilePathResult: {
+ if (path !== "") {
+ keyFilePath.text = path;
+ }
+ }
+ }
+
+ //
+ // LOGIN PAGE START
+ //
+ Item {
+ id: loginPageContainer;
+ visible: false;
+ // Anchors
+ anchors.fill: parent;
+
+ Component.onCompleted: {
+ commerce.getLoginStatus();
+ }
+
+ Item {
+ id: loginTitle;
+ // Size
+ width: parent.width;
+ height: 50;
+ // Anchors
+ anchors.left: parent.left;
+ anchors.top: parent.top;
+
+ // Title Bar text
+ RalewaySemiBold {
+ text: "WALLET SETUP - LOGIN";
+ // Text size
+ size: hifi.fontSizes.overlayTitle;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.bottom: parent.bottom;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ }
+
+ // Text below title bar
+ RalewaySemiBold {
+ id: loginTitleHelper;
+ text: "Please Log In to High Fidelity";
+ // Text size
+ size: 24;
+ // Anchors
+ anchors.top: loginTitle.bottom;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: 50;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ // Text below helper text
+ RalewaySemiBold {
+ id: loginDetailText;
+ text: "To set up your wallet, you must first log in to High Fidelity.";
+ // Text size
+ size: 18;
+ // Anchors
+ anchors.top: loginTitleHelper.bottom;
+ anchors.topMargin: 25;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: 50;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ // "Cancel" button
+ HifiControlsUit.Button {
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: loginDetailText.bottom;
+ anchors.topMargin: 25;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ width: 150;
+ height: 50;
+ text: "Log In"
+ onClicked: {
+ sendSignalToWallet({method: 'walletSetup_loginClicked'});
+ }
+ }
+
+ // Navigation Bar
+ Item {
+ // Size
+ width: parent.width;
+ height: 100;
+ // Anchors:
+ anchors.left: parent.left;
+ anchors.bottom: parent.bottom;
+
+ // "Cancel" button
+ HifiControlsUit.Button {
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ anchors.topMargin: 3;
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 3;
+ anchors.left: parent.left;
+ anchors.leftMargin: 20;
+ width: 100;
+ text: "Cancel"
+ onClicked: {
+ sendSignalToWallet({method: 'walletSetup_cancelClicked'});
+ }
+ }
+ }
+ }
+ //
+ // LOGIN PAGE END
+ //
+
+ //
+ // SECURITY IMAGE SELECTION START
+ //
+ Item {
+ id: securityImageContainer;
+ visible: false;
+ // Anchors
+ anchors.fill: parent;
+
+ onVisibleChanged: {
+ if (visible) {
+ commerce.getSecurityImage();
+ }
+ }
+
+ Item {
+ id: securityImageTitle;
+ // Size
+ width: parent.width;
+ height: 50;
+ // Anchors
+ anchors.left: parent.left;
+ anchors.top: parent.top;
+
+ // Title Bar text
+ RalewaySemiBold {
+ text: "WALLET SETUP - STEP 1 OF 3";
+ // Text size
+ size: hifi.fontSizes.overlayTitle;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.bottom: parent.bottom;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ }
+
+ // Text below title bar
+ RalewaySemiBold {
+ id: securityImageTitleHelper;
+ text: "Choose a Security Picture:";
+ // Text size
+ size: 24;
+ // Anchors
+ anchors.top: securityImageTitle.bottom;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ height: 50;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ SecurityImageSelection {
+ id: securityImageSelection;
+ // Anchors
+ anchors.top: securityImageTitleHelper.bottom;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: 280;
+ }
+
+ // Text below security images
+ RalewayRegular {
+ text: "Your security picture shows you that the service asking for your passphrase is authorized. You can change your secure picture at any time.";
+ // Text size
+ size: 18;
+ // Anchors
+ anchors.top: securityImageSelection.bottom;
+ anchors.topMargin: 40;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: paintedHeight;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ // Navigation Bar
+ Item {
+ // Size
+ width: parent.width;
+ height: 100;
+ // Anchors:
+ anchors.left: parent.left;
+ anchors.bottom: parent.bottom;
+
+ // "Cancel" button
+ HifiControlsUit.Button {
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ anchors.topMargin: 3;
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 3;
+ anchors.left: parent.left;
+ anchors.leftMargin: 20;
+ width: 100;
+ text: "Cancel"
+ onClicked: {
+ sendSignalToWallet({method: 'walletSetup_cancelClicked'});
+ }
+ }
+
+ // "Next" button
+ HifiControlsUit.Button {
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ anchors.topMargin: 3;
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 3;
+ anchors.right: parent.right;
+ anchors.rightMargin: 20;
+ width: 100;
+ text: "Next";
+ onClicked: {
+ root.lastPage = "securityImage";
+ commerce.chooseSecurityImage(securityImageSelection.getSelectedImageIndex());
+ securityImageContainer.visible = false;
+ choosePassphraseContainer.visible = true;
+ }
+ }
+ }
+ }
+ //
+ // SECURITY IMAGE SELECTION END
+ //
+
+ //
+ // SECURE PASSPHRASE SELECTION START
+ //
+ Item {
+ id: choosePassphraseContainer;
+ visible: false;
+ // Anchors
+ anchors.fill: parent;
+
+ onVisibleChanged: {
+ if (visible) {
+ commerce.getPassphraseSetupStatus();
+ }
+ }
+
+ Item {
+ id: passphraseTitle;
+ // Size
+ width: parent.width;
+ height: 50;
+ // Anchors
+ anchors.left: parent.left;
+ anchors.top: parent.top;
+
+ // Title Bar text
+ RalewaySemiBold {
+ text: "WALLET SETUP - STEP 2 OF 3";
+ // Text size
+ size: hifi.fontSizes.overlayTitle;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.bottom: parent.bottom;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ }
+
+ // Text below title bar
+ RalewaySemiBold {
+ id: passphraseTitleHelper;
+ text: "Choose a Secure Passphrase";
+ // Text size
+ size: 24;
+ // Anchors
+ anchors.top: passphraseTitle.bottom;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: 50;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ PassphraseSelection {
+ id: passphraseSelection;
+ anchors.top: passphraseTitleHelper.bottom;
+ anchors.topMargin: 30;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ anchors.bottom: passphraseNavBar.top;
+ }
+
+ // Navigation Bar
+ Item {
+ id: passphraseNavBar;
+ // Size
+ width: parent.width;
+ height: 100;
+ // Anchors:
+ anchors.left: parent.left;
+ anchors.bottom: parent.bottom;
+
+ // "Back" button
+ HifiControlsUit.Button {
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ anchors.topMargin: 3;
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 3;
+ anchors.left: parent.left;
+ anchors.leftMargin: 20;
+ width: 100;
+ text: "Back"
+ onClicked: {
+ root.lastPage = "choosePassphrase";
+ choosePassphraseContainer.visible = false;
+ securityImageContainer.visible = true;
+ }
+ }
+
+ // "Next" button
+ HifiControlsUit.Button {
+ id: passphrasePageNextButton;
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ anchors.topMargin: 3;
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 3;
+ anchors.right: parent.right;
+ anchors.rightMargin: 20;
+ width: 100;
+ text: "Next";
+ onClicked: {
+ if (passphraseSelection.validateAndSubmitPassphrase()) {
+ root.lastPage = "passphrase";
+ choosePassphraseContainer.visible = false;
+ privateKeysReadyContainer.visible = true;
+ commerce.balance(); // Do this here so that keys are generated. Order might change as backend changes?
+ }
+ }
+ }
+ }
+ }
+ //
+ // SECURE PASSPHRASE SELECTION END
+ //
+
+ //
+ // PRIVATE KEYS READY START
+ //
+ Item {
+ id: privateKeysReadyContainer;
+ visible: false;
+ // Anchors
+ anchors.fill: parent;
+
+ Item {
+ id: keysReadyTitle;
+ // Size
+ width: parent.width;
+ height: 50;
+ // Anchors
+ anchors.left: parent.left;
+ anchors.top: parent.top;
+
+ // Title Bar text
+ RalewaySemiBold {
+ text: "WALLET SETUP - STEP 3 OF 3";
+ // Text size
+ size: hifi.fontSizes.overlayTitle;
+ // Anchors
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.bottom: parent.bottom;
+ width: paintedWidth;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+ }
+
+ // Text below title bar
+ RalewaySemiBold {
+ id: keysReadyTitleHelper;
+ text: "Your Private Keys are Ready";
+ // Text size
+ size: 24;
+ // Anchors
+ anchors.top: keysReadyTitle.bottom;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: 50;
+ // Style
+ color: hifi.colors.faintGray;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ // Text below checkbox
+ RalewayRegular {
+ id: explanationText;
+ text: "Your money and purchases are secured with private keys that only you have access to. " +
+ "If they are lost, you will not be able to access your money or purchases.
" +
+ "To protect your privacy, High Fidelity has no access to your private keys and cannot " +
+ "recover them for any reason.
To safeguard your private keys, backup this file on a regular basis:";
+ // Text size
+ size: 16;
+ // Anchors
+ anchors.top: keysReadyTitleHelper.bottom;
+ anchors.topMargin: 16;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ height: paintedHeight;
+ // Style
+ color: hifi.colors.faintGray;
+ wrapMode: Text.WordWrap;
+ // Alignment
+ horizontalAlignment: Text.AlignHLeft;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ HifiControlsUit.TextField {
+ id: keyFilePath;
+ anchors.top: explanationText.bottom;
+ anchors.topMargin: 10;
+ anchors.left: parent.left;
+ anchors.leftMargin: 16;
+ anchors.right: clipboardButton.left;
+ height: 40;
+ readOnly: true;
+
+ onVisibleChanged: {
+ if (visible) {
+ commerce.getKeyFilePath();
+ }
+ }
+ }
+ HifiControlsUit.Button {
+ id: clipboardButton;
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.right: parent.right;
+ anchors.rightMargin: 16;
+ anchors.top: keyFilePath.top;
+ anchors.bottom: keyFilePath.bottom;
+ width: height;
+ HiFiGlyphs {
+ text: hifi.glyphs.question;
+ // Size
+ size: parent.height*1.3;
+ // Anchors
+ anchors.fill: parent;
+ // Style
+ horizontalAlignment: Text.AlignHCenter;
+ color: enabled ? hifi.colors.white : hifi.colors.faintGray;
+ }
+
+ onClicked: {
+ Window.copyToClipboard(keyFilePath.text);
+ }
+ }
+
+ // Navigation Bar
+ Item {
+ // Size
+ width: parent.width;
+ height: 100;
+ // Anchors:
+ anchors.left: parent.left;
+ anchors.bottom: parent.bottom;
+ // "Next" button
+ HifiControlsUit.Button {
+ id: keysReadyPageNextButton;
+ color: hifi.buttons.black;
+ colorScheme: hifi.colorSchemes.dark;
+ anchors.top: parent.top;
+ anchors.topMargin: 3;
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 3;
+ anchors.right: parent.right;
+ anchors.rightMargin: 20;
+ width: 100;
+ text: "Finish";
+ onClicked: {
+ root.visible = false;
+ sendSignalToWallet({method: 'walletSetup_finished'});
+ }
+ }
+ }
+ }
+ //
+ // PRIVATE KEYS READY END
+ //
+
+ //
+ // FUNCTION DEFINITIONS START
+ //
+ signal sendSignalToWallet(var msg);
+ //
+ // FUNCTION DEFINITIONS END
+ //
+}
diff --git a/interface/resources/qml/hifi/commerce/wallet/images/01cat.jpg b/interface/resources/qml/hifi/commerce/wallet/images/01cat.jpg
new file mode 100644
index 0000000000..6e7897cb82
Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/01cat.jpg differ
diff --git a/interface/resources/qml/hifi/commerce/wallet/images/02car.jpg b/interface/resources/qml/hifi/commerce/wallet/images/02car.jpg
new file mode 100644
index 0000000000..5dd8091e57
Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/02car.jpg differ
diff --git a/interface/resources/qml/hifi/commerce/wallet/images/03dog.jpg b/interface/resources/qml/hifi/commerce/wallet/images/03dog.jpg
new file mode 100644
index 0000000000..4a85b80c0c
Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/03dog.jpg differ
diff --git a/interface/resources/qml/hifi/commerce/wallet/images/04stars.jpg b/interface/resources/qml/hifi/commerce/wallet/images/04stars.jpg
new file mode 100644
index 0000000000..8f2bf62f83
Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/04stars.jpg differ
diff --git a/interface/resources/qml/hifi/commerce/wallet/images/05plane.jpg b/interface/resources/qml/hifi/commerce/wallet/images/05plane.jpg
new file mode 100644
index 0000000000..6504459d8b
Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/05plane.jpg differ
diff --git a/interface/resources/qml/hifi/commerce/wallet/images/06gingerbread.jpg b/interface/resources/qml/hifi/commerce/wallet/images/06gingerbread.jpg
new file mode 100644
index 0000000000..54c37faa2f
Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/06gingerbread.jpg differ
diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp
index c50137fe9d..f1e9fa139e 100644
--- a/interface/src/commerce/QmlCommerce.cpp
+++ b/interface/src/commerce/QmlCommerce.cpp
@@ -14,6 +14,7 @@
#include "DependencyManager.h"
#include "Ledger.h"
#include "Wallet.h"
+#include
HIFI_QML_DEF(QmlCommerce)
@@ -24,6 +25,7 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) {
connect(ledger.data(), &Ledger::balanceResult, this, &QmlCommerce::balanceResult);
connect(ledger.data(), &Ledger::inventoryResult, this, &QmlCommerce::inventoryResult);
connect(wallet.data(), &Wallet::securityImageResult, this, &QmlCommerce::securityImageResult);
+ connect(wallet.data(), &Wallet::keyFilePathResult, this, &QmlCommerce::keyFilePathResult);
}
void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) {
@@ -58,3 +60,16 @@ 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::getKeyFilePath() {
+ auto wallet = DependencyManager::get();
+ wallet->getKeyFilePath();
+}
diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h
index decc727928..ae606f80de 100644
--- a/interface/src/commerce/QmlCommerce.h
+++ b/interface/src/commerce/QmlCommerce.h
@@ -34,6 +34,9 @@ signals:
void balanceResult(QJsonObject result);
void inventoryResult(QJsonObject result);
void securityImageResult(QPixmap* image);
+ void loginStatusResult(bool isLoggedIn);
+ void passphraseSetupStatusResult(bool passphraseIsSetup);
+ void keyFilePathResult(const QString& path);
protected:
Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = "");
@@ -41,6 +44,10 @@ protected:
Q_INVOKABLE void inventory();
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 getKeyFilePath();
};
#endif // hifi_QmlCommerce_h
diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp
index 9e7bc58024..68fdecca4a 100644
--- a/interface/src/commerce/Wallet.cpp
+++ b/interface/src/commerce/Wallet.cpp
@@ -430,3 +430,6 @@ void Wallet::getSecurityImage() {
emit securityImageResult(nullptr);
}
}
+void Wallet::getKeyFilePath() {
+ emit keyFilePathResult(keyFilePath());
+}
diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h
index 2c558e358f..8f962a1bc9 100644
--- a/interface/src/commerce/Wallet.h
+++ b/interface/src/commerce/Wallet.h
@@ -30,12 +30,25 @@ public:
QString signWithKey(const QByteArray& text, const QString& key);
void chooseSecurityImage(const QString& imageFile);
void getSecurityImage();
+ void getKeyFilePath();
void setSalt(const QByteArray& salt) { _salt = salt; }
QByteArray getSalt() { return _salt; }
signals:
void securityImageResult(QPixmap* image);
+ void keyFilePathResult(const QString& path);
+
+protected:
+ enum SecurityImage {
+ NONE = 0,
+ Cat,
+ Car,
+ Dog,
+ Stars,
+ Plane,
+ Gingerbread
+ };
private:
QStringList _publicKeys{};
diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js
index 2270118861..2285337f41 100644
--- a/scripts/defaultScripts.js
+++ b/scripts/defaultScripts.js
@@ -28,7 +28,8 @@ var DEFAULT_SCRIPTS_COMBINED = [
"system/notifications.js",
"system/dialTone.js",
"system/firstPersonHMD.js",
- "system/tablet-ui/tabletUI.js"
+ "system/tablet-ui/tabletUI.js",
+ "system/commerce/wallet.js"
];
var DEFAULT_SCRIPTS_SEPARATE = [
"system/controllers/controllerScripts.js",
diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js
new file mode 100644
index 0000000000..6045bef8ef
--- /dev/null
+++ b/scripts/system/commerce/wallet.js
@@ -0,0 +1,149 @@
+"use strict";
+/*jslint vars:true, plusplus:true, forin:true*/
+/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
+//
+// wallet.js
+//
+// Created by Zach Fox on 2017-08-17
+// 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
+//
+
+/*global XXX */
+
+(function () { // BEGIN LOCAL_SCOPE
+
+
+ // Function Name: onButtonClicked()
+ //
+ // Description:
+ // -Fired when the app button is pressed.
+ //
+ // Relevant Variables:
+ // -WALLET_QML_SOURCE: The path to the Wallet QML
+ // -onWalletScreen: true/false depending on whether we're looking at the app.
+ var WALLET_QML_SOURCE = Script.resourcesPath() + "qml/hifi/commerce/wallet/Wallet.qml";
+ var onWalletScreen = false;
+ function onButtonClicked() {
+ if (!tablet) {
+ print("Warning in buttonClicked(): 'tablet' undefined!");
+ return;
+ }
+ if (onWalletScreen) {
+ // for toolbar-mode: go back to home screen, this will close the window.
+ tablet.gotoHomeScreen();
+ } else {
+ tablet.loadQMLSource(WALLET_QML_SOURCE);
+ }
+ }
+
+ // Function Name: sendToQml()
+ //
+ // Description:
+ // -Use this function to send a message to the QML (i.e. to change appearances). The "message" argument is what is sent to
+ // the QML in the format "{method, params}", like json-rpc. See also fromQml().
+ function sendToQml(message) {
+ tablet.sendToQml(message);
+ }
+
+ // Function Name: fromQml()
+ //
+ // Description:
+ // -Called when a message is received from SpectatorCamera.qml. The "message" argument is what is sent from the QML
+ // in the format "{method, params}", like json-rpc. See also sendToQml().
+ function fromQml(message) {
+ switch (message.method) {
+ case 'walletSetup_cancelClicked':
+ tablet.gotoHomeScreen();
+ break;
+ case 'walletSetup_loginClicked':
+ if ((HMD.active && Settings.getValue("hmdTabletBecomesToolbar", false))
+ || (!HMD.active && Settings.getValue("desktopTabletBecomesToolbar", true))) {
+ Menu.triggerOption("Login / Sign Up");
+ tablet.gotoHomeScreen();
+ } else {
+ tablet.loadQMLOnTop("../../../dialogs/TabletLoginDialog.qml");
+ }
+ break;
+ default:
+ print('Unrecognized message from QML:', JSON.stringify(message));
+ }
+ }
+
+ // Function Name: wireEventBridge()
+ //
+ // Description:
+ // -Used to connect/disconnect the script's response to the tablet's "fromQml" signal. Set the "on" argument to enable or
+ // disable to event bridge.
+ //
+ // Relevant Variables:
+ // -hasEventBridge: true/false depending on whether we've already connected the event bridge.
+ var hasEventBridge = false;
+ function wireEventBridge(on) {
+ if (!tablet) {
+ print("Warning in wireEventBridge(): 'tablet' undefined!");
+ return;
+ }
+ if (on) {
+ if (!hasEventBridge) {
+ tablet.fromQml.connect(fromQml);
+ hasEventBridge = true;
+ }
+ } else {
+ if (hasEventBridge) {
+ tablet.fromQml.disconnect(fromQml);
+ hasEventBridge = false;
+ }
+ }
+ }
+
+ // Function Name: onTabletScreenChanged()
+ //
+ // Description:
+ // -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string
+ // value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML.
+ function onTabletScreenChanged(type, url) {
+ onWalletScreen = (type === "QML" && url === WALLET_QML_SOURCE);
+ wireEventBridge(onWalletScreen);
+ // Change button to active when window is first openend, false otherwise.
+ if (button) {
+ button.editProperties({ isActive: onWalletScreen });
+ }
+ }
+
+ //
+ // Manage the connection between the button and the window.
+ //
+ var button;
+ var buttonName = "WALLET";
+ var tablet = null;
+ function startup() {
+ tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
+ button = tablet.addButton({
+ text: buttonName,
+ icon: "icons/tablet-icons/wallet-i.svg",
+ activeIcon: "icons/tablet-icons/wallet-a.svg"
+ });
+ button.clicked.connect(onButtonClicked);
+ tablet.screenChanged.connect(onTabletScreenChanged);
+ }
+ function shutdown() {
+ button.clicked.disconnect(onButtonClicked);
+ tablet.removeButton(button);
+ if (tablet) {
+ tablet.screenChanged.disconnect(onTabletScreenChanged);
+ if (onWalletScreen) {
+ tablet.gotoHomeScreen();
+ }
+ }
+ }
+
+ //
+ // Run the functions.
+ //
+ startup();
+ Script.scriptEnding.connect(shutdown);
+
+}()); // END LOCAL_SCOPE
diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js
index 77be746bf4..2889a1514a 100644
--- a/scripts/system/html/js/marketplacesInject.js
+++ b/scripts/system/html/js/marketplacesInject.js
@@ -115,7 +115,7 @@
itemId: id,
itemName: name,
itemAuthor: author,
- itemPrice: price ? parseInt(price, 10) : Math.round(Math.random() * 50),
+ itemPrice: price ? parseInt(price, 10) : 0,
itemHref: href
}));
}
diff --git a/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js b/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js
index f0b943ad92..fd08cd5655 100644
--- a/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js
+++ b/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js
@@ -153,7 +153,7 @@
// -showSpectatorInDesktop: Set to "true" to show the "SPECTATOR" app in desktop mode.
var button = false;
var buttonName = "SPECTATOR";
- var showSpectatorInDesktop = false;
+ var showSpectatorInDesktop = true;
function addOrRemoveButton(isShuttingDown, isHMDMode) {
if (!tablet) {
print("Warning in addOrRemoveButton(): 'tablet' undefined!");