Merge pull request #11235 from davidkelly/dk/encryptFile

Initial wallet app plus some encryption of security image
This commit is contained in:
David Kelly 2017-08-24 13:17:14 -07:00 committed by GitHub
commit eb9436ba96
33 changed files with 3750 additions and 399 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 52 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 56 KiB

View file

@ -17,6 +17,7 @@ import QtQuick.Controls 1.4
import "../../styles-uit"
import "../../controls-uit" as HifiControlsUit
import "../../controls" as HifiControls
import "./wallet" as HifiWallet
// references XXX from root context
@ -26,32 +27,33 @@ Rectangle {
id: checkoutRoot;
property bool inventoryReceived: false;
property bool balanceReceived: false;
property string itemId: "";
property string itemId: "";
property string itemHref: "";
property int balanceAfterPurchase: 0;
property bool alreadyOwned: false;
property int itemPriceFull: 0;
// Style
color: hifi.colors.baseGray;
Hifi.QmlCommerce {
id: commerce;
onBuyResult: {
if (result.status !== 'success') {
buyButton.text = result.message;
buyButton.enabled = false;
} else {
if (urlHandler.canHandleUrl(itemHref)) {
urlHandler.handleUrl(itemHref);
}
sendToScript({method: 'checkout_buySuccess', itemId: itemId});
if (result.status !== 'success') {
buyButton.text = result.message;
buyButton.enabled = false;
} else {
if (urlHandler.canHandleUrl(itemHref)) {
urlHandler.handleUrl(itemHref);
}
sendToScript({method: 'checkout_buySuccess', itemId: itemId});
}
}
onBalanceResult: {
if (result.status !== 'success') {
console.log("Failed to get balance", result.message);
} else {
balanceReceived = true;
hfcBalanceText.text = result.data.balance;
balanceAfterPurchase = result.data.balance - parseInt(itemPriceText.text, 10);
hfcBalanceText.text = parseFloat(result.data.balance/100).toFixed(2);
balanceAfterPurchase = parseFloat(result.data.balance/100) - parseFloat(checkoutRoot.itemPriceFull/100).toFixed(2);
}
}
onInventoryResult: {
@ -67,14 +69,6 @@ Rectangle {
}
}
}
onSecurityImageResult: {
securityImage.source = securityImageSelection.getImagePathFromImageID(imageID);
}
}
SecurityImageSelection {
id: securityImageSelection;
referrerURL: checkoutRoot.itemHref;
}
//
@ -89,20 +83,6 @@ Rectangle {
anchors.left: parent.left;
anchors.top: parent.top;
// Security Image
Image {
id: securityImage;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
height: parent.height - 5;
width: height;
anchors.verticalCenter: parent.verticalCenter;
fillMode: Image.PreserveAspectFit;
mipmap: true;
}
// Title Bar text
RalewaySemiBold {
id: titleBarText;
@ -111,7 +91,7 @@ Rectangle {
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: securityImage.right;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
@ -132,7 +112,7 @@ Rectangle {
//
// TITLE BAR END
//
//
// ITEM DESCRIPTION START
//
@ -147,7 +127,7 @@ Rectangle {
// Item Name text
Item {
id: itemNameContainer;
id: itemNameContainer;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 4;
@ -188,11 +168,11 @@ Rectangle {
verticalAlignment: Text.AlignVCenter;
}
}
// Item Author text
Item {
id: itemAuthorContainer;
id: itemAuthorContainer;
// Anchors
anchors.top: itemNameContainer.bottom;
anchors.topMargin: 4;
@ -233,10 +213,10 @@ Rectangle {
verticalAlignment: Text.AlignVCenter;
}
}
// HFC Balance text
Item {
id: hfcBalanceContainer;
id: hfcBalanceContainer;
// Anchors
anchors.top: itemAuthorContainer.bottom;
anchors.topMargin: 16;
@ -278,10 +258,10 @@ Rectangle {
verticalAlignment: Text.AlignVCenter;
}
}
// Item Price text
Item {
id: itemPriceContainer;
id: itemPriceContainer;
// Anchors
anchors.top: hfcBalanceContainer.bottom;
anchors.topMargin: 4;
@ -322,10 +302,10 @@ Rectangle {
verticalAlignment: Text.AlignVCenter;
}
}
// HFC "Balance After Purchase" text
Item {
id: hfcBalanceAfterPurchaseContainer;
id: hfcBalanceAfterPurchaseContainer;
// Anchors
anchors.top: itemPriceContainer.bottom;
anchors.topMargin: 4;
@ -372,7 +352,7 @@ Rectangle {
// ITEM DESCRIPTION END
//
//
// ACTION BUTTONS START
//
@ -420,7 +400,7 @@ Rectangle {
text: (inventoryReceived && balanceReceived) ? (alreadyOwned ? "Already Owned: Get Item" : "Buy") : "--";
onClicked: {
if (!alreadyOwned) {
commerce.buy(itemId, parseInt(itemPriceText.text));
commerce.buy(itemId, parseFloat(itemPriceText.text*100));
} else {
if (urlHandler.canHandleUrl(itemHref)) {
urlHandler.handleUrl(itemHref);
@ -456,11 +436,11 @@ Rectangle {
itemId = message.params.itemId;
itemNameText.text = message.params.itemName;
itemAuthorText.text = message.params.itemAuthor;
itemPriceText.text = message.params.itemPrice;
checkoutRoot.itemPriceFull = message.params.itemPrice;
itemPriceText.text = parseFloat(checkoutRoot.itemPriceFull/100).toFixed(2);
itemHref = message.params.itemHref;
commerce.balance();
commerce.inventory();
commerce.getSecurityImage();
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));

View file

@ -17,6 +17,7 @@ import QtQuick.Controls 1.4
import "../../styles-uit"
import "../../controls-uit" as HifiControlsUit
import "../../controls" as HifiControls
import "./wallet" as HifiWallet
// references XXX from root context
@ -33,7 +34,7 @@ Rectangle {
if (result.status !== 'success') {
console.log("Failed to get balance", result.message);
} else {
hfcBalanceText.text = result.data.balance;
hfcBalanceText.text = parseFloat(result.data.balance/100).toFixed(2);
}
}
onInventoryResult: {
@ -43,14 +44,6 @@ Rectangle {
inventoryContentsList.model = result.data.assets;
}
}
onSecurityImageResult: {
securityImage.source = securityImageSelection.getImagePathFromImageID(imageID);
}
}
SecurityImageSelection {
id: securityImageSelection;
referrerURL: inventoryRoot.referrerURL;
}
//
@ -65,20 +58,6 @@ Rectangle {
anchors.left: parent.left;
anchors.top: parent.top;
// Security Image
Image {
id: securityImage;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
height: parent.height - 5;
width: height;
anchors.verticalCenter: parent.verticalCenter;
fillMode: Image.PreserveAspectFit;
mipmap: true;
}
// Title Bar text
RalewaySemiBold {
id: titleBarText;
@ -87,7 +66,7 @@ Rectangle {
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: securityImage.right;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
@ -98,25 +77,6 @@ Rectangle {
verticalAlignment: Text.AlignVCenter;
}
// "Change Security Image" button
HifiControlsUit.Button {
id: changeSecurityImageButton;
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: 200;
text: "Change Security Image"
onClicked: {
securityImageSelection.isManuallyChangingSecurityImage = true;
securityImageSelection.visible = true;
}
}
// Separator
HifiControlsUit.Separator {
anchors.left: parent.left;
@ -132,7 +92,7 @@ Rectangle {
// HFC BALANCE START
//
Item {
id: hfcBalanceContainer;
id: hfcBalanceContainer;
// Size
width: inventoryRoot.width;
height: childrenRect.height + 20;
@ -177,7 +137,7 @@ Rectangle {
//
// HFC BALANCE END
//
//
// INVENTORY CONTENTS START
//
@ -192,7 +152,7 @@ Rectangle {
anchors.topMargin: 8;
anchors.bottom: actionButtonsContainer.top;
anchors.bottomMargin: 8;
RalewaySemiBold {
id: inventoryContentsLabel;
text: "Inventory:";
@ -249,7 +209,7 @@ Rectangle {
//
// INVENTORY CONTENTS END
//
//
// ACTION BUTTONS START
//
@ -307,7 +267,6 @@ Rectangle {
referrerURL = message.referrerURL;
commerce.balance();
commerce.inventory();
commerce.getSecurityImage();
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));

View file

@ -1,271 +0,0 @@
//
// SecurityImageSelection.qml
// qml/hifi/commerce
//
// SecurityImageSelection
//
// Created by Zach Fox on 2017-08-15
// 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: securityImageSelectionRoot;
property string referrerURL: "";
property bool isManuallyChangingSecurityImage: false;
anchors.fill: parent;
// Style
color: hifi.colors.baseGray;
z:999; // On top of everything else
visible: false;
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
if (!isManuallyChangingSecurityImage) {
securityImageSelectionRoot.visible = (imageID == 0);
}
if (imageID > 0) {
for (var itr = 0; itr < gridModel.count; itr++) {
var thisValue = gridModel.get(itr).securityImageEnumValue;
if (thisValue === imageID) {
securityImageGrid.currentIndex = itr;
break;
}
}
}
}
}
Component.onCompleted: {
commerce.getSecurityImage();
}
//
// TITLE BAR START
//
Item {
id: titleBarContainer;
// Size
width: securityImageSelectionRoot.width;
height: 30;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
id: titleBarText;
text: "Select a Security Image";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.fill: parent;
anchors.leftMargin: 16;
// Style
color: hifi.colors.lightGrayText;
// 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
//
//
// EXPLANATION START
//
Item {
id: explanationContainer;
// Size
width: securityImageSelectionRoot.width;
height: 85;
// Anchors
anchors.top: titleBarContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
RalewayRegular {
id: explanationText;
text: "This image will be displayed on secure Inventory and Marketplace Checkout dialogs.<b><br>If you don't see your selected image on these dialogs, do not use them!</b>";
// Text size
size: 16;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 4;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
// Style
color: hifi.colors.lightGrayText;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Separator
HifiControlsUit.Separator {
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
}
}
//
// EXPLANATION END
//
//
// SECURITY IMAGE GRID START
//
Item {
id: securityImageGridContainer;
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
anchors.top: explanationContainer.bottom;
anchors.topMargin: 8;
anchors.bottom: actionButtonsContainer.top;
anchors.bottomMargin: 8;
SecurityImageModel {
id: gridModel;
}
GridView {
id: securityImageGrid;
clip: true;
// Anchors
anchors.fill: parent;
currentIndex: -1;
cellWidth: width / 2;
cellHeight: height / 3;
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;
onClicked: {
securityImageGrid.currentIndex = index;
}
}
}
highlight: Rectangle {
width: securityImageGrid.cellWidth;
height: securityImageGrid.cellHeight;
color: hifi.colors.blueHighlight;
}
}
}
//
// SECURITY IMAGE GRID END
//
//
// ACTION BUTTONS START
//
Item {
id: actionButtonsContainer;
// Size
width: securityImageSelectionRoot.width;
height: 40;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 8;
// "Cancel" button
HifiControlsUit.Button {
id: cancelButton;
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: parent.width/2 - anchors.leftMargin*2;
text: "Cancel"
onClicked: {
if (!securityImageSelectionRoot.isManuallyChangingSecurityImage) {
sendToScript({method: 'securityImageSelection_cancelClicked', referrerURL: securityImageSelectionRoot.referrerURL});
} else {
securityImageSelectionRoot.visible = false;
}
}
}
// "Confirm" button
HifiControlsUit.Button {
id: confirmButton;
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: parent.width/2 - anchors.rightMargin*2;
text: "Confirm";
onClicked: {
securityImageSelectionRoot.isManuallyChangingSecurityImage = false;
commerce.chooseSecurityImage(gridModel.get(securityImageGrid.currentIndex).securityImageEnumValue);
}
}
}
//
// ACTION BUTTONS END
//
//
// FUNCTION DEFINITIONS START
//
signal sendToScript(var message);
function getImagePathFromImageID(imageID) {
return (imageID ? gridModel.get(imageID - 1).sourcePath : "");
}
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -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
//
}

View file

@ -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.<br><b>You do not need to submit a credit card or personal information to set up your wallet.</b>";
// 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
//
}

View file

@ -0,0 +1,232 @@
//
// 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 = "";
passphrasePageSecurityImage.source = "image://security/securityImage";
}
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;
source: "image://security/securityImage";
cache: false;
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. <b>Please write it down.</b> 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);
}

View file

@ -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";
}
}

View file

@ -0,0 +1,322 @@
//
// 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 (exists) { // "If security image is set up"
var path = "image://security/securityImage";
topSecurityImage.source = "";
topSecurityImage.source = path;
changeSecurityImageImage.source = "";
changeSecurityImageImage.source = path;
changePassphraseImage.source = "";
changePassphraseImage.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;
source: "image://security/securityImage";
cache: false;
}
// "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;
source: "image://security/securityImage";
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
}
// "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;
cache: false;
source: "image://security/securityImage";
}
// "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. <b>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:</b>";
// 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
//
}

View file

@ -4,7 +4,7 @@
//
// SecurityImageModel
//
// Created by Zach Fox on 2017-08-15
// Created by Zach Fox on 2017-08-17
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
@ -39,4 +39,8 @@ ListModel {
sourcePath: "images/06gingerbread.jpg"
securityImageEnumValue: 6;
}
function getImagePathFromImageID(imageID) {
return (imageID ? root.get(imageID - 1).sourcePath : "");
}
}

View file

@ -0,0 +1,98 @@
//
// 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: {
}
}
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
//
}

View file

@ -0,0 +1,200 @@
//
// 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 (exists) { // 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: "<b>Your security picture shows you that the service asking for your passphrase is authorized.</b> 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;
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
commerce.chooseSecurityImage(securityImagePath);
}
}
}
}
//
// SECURITY IMAGE SELECTION END
//
function resetSubmitButton() {
securityImageSubmitButton.enabled = true;
securityImageSubmitButton.text = "Submit";
}
}

View file

@ -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
//
}

View file

@ -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 (!exists) { // "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
//
}

View file

@ -0,0 +1,357 @@
//
// 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 (exists) {
// just set the source again (to be sure the change was noticed)
securityImage.source = "";
securityImage.source = "image://security/securityImage";
}
}
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;
cache: false;
source: "image://security/securityImage";
}
// "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 <b>CANNOT</b> scroll through this." : "you <b>CAN</b> 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: "<b>Welcome! Let's get you some spending money.</b><br><br>" +
"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
//
}

View file

@ -0,0 +1,633 @@
//
// 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 (!exists && 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: "<b>Your security picture shows you that the service asking for your passphrase is authorized.</b> 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";
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
commerce.chooseSecurityImage(securityImagePath);
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. " +
"<b>If they are lost, you will not be able to access your money or purchases.</b><br><br>" +
"<b>To protect your privacy, High Fidelity has no access to your private keys and cannot " +
"recover them for any reason.<br><br>To safeguard your private keys, backup this file on a regular basis:</b>";
// 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
//
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View file

@ -183,6 +183,7 @@
#include "ui/UpdateDialog.h"
#include "ui/overlays/Overlays.h"
#include "ui/DomainConnectionModel.h"
#include "ui/ImageProvider.h"
#include "Util.h"
#include "InterfaceParentFinder.h"
#include "ui/OctreeStatsProvider.h"
@ -2165,6 +2166,9 @@ void Application::initializeUi() {
qApp->quit();
});
// register the pixmap image provider (used only for security image, for now)
engine->addImageProvider(ImageProvider::PROVIDER_NAME, new ImageProvider());
setupPreferences();
// For some reason there is already an "Application" object in the QML context,
@ -3757,7 +3761,7 @@ bool Application::shouldPaint() {
(float)paintDelaySamples / paintDelayUsecs << "us";
}
#endif
// Throttle if requested
if (displayPlugin->isThrottled() && (_lastTimeUpdated.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) {
return false;
@ -6297,7 +6301,7 @@ bool Application::askToReplaceDomainContent(const QString& url) {
OffscreenUi::warning("Unable to replace content", "You do not have permissions to replace domain content",
QMessageBox::Ok, QMessageBox::Ok);
}
QJsonObject messageProperties = {
QJsonObject messageProperties = {
{ "status", methodDetails },
{ "content_set_url", url }
};
@ -6380,7 +6384,7 @@ void Application::showAssetServerWidget(QString filePath) {
void Application::addAssetToWorldFromURL(QString url) {
qInfo(interfaceapp) << "Download model and add to world from" << url;
QString filename;
if (url.contains("filename")) {
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.

View file

@ -14,6 +14,7 @@
#include "DependencyManager.h"
#include "Ledger.h"
#include "Wallet.h"
#include <AccountManager.h>
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) {
@ -50,11 +52,24 @@ void QmlCommerce::inventory() {
ledger->inventory(wallet->listPublicKeys());
}
void QmlCommerce::chooseSecurityImage(uint imageID) {
void QmlCommerce::chooseSecurityImage(const QString& imageFile) {
auto wallet = DependencyManager::get<Wallet>();
wallet->chooseSecurityImage(imageID);
wallet->chooseSecurityImage(imageFile);
}
void QmlCommerce::getSecurityImage() {
auto wallet = DependencyManager::get<Wallet>();
wallet->getSecurityImage();
}
}
void QmlCommerce::getLoginStatus() {
emit loginStatusResult(DependencyManager::get<AccountManager>()->isLoggedIn());
}
void QmlCommerce::setPassphrase(const QString& passphrase) {
emit passphraseSetupStatusResult(true);
}
void QmlCommerce::getPassphraseSetupStatus() {
emit passphraseSetupStatusResult(false);
}
void QmlCommerce::getKeyFilePath() {
auto wallet = DependencyManager::get<Wallet>();
wallet->getKeyFilePath();
}

View file

@ -18,6 +18,8 @@
#include <QJsonObject>
#include <OffscreenQmlDialog.h>
#include <QPixmap>
class QmlCommerce : public OffscreenQmlDialog {
Q_OBJECT
HIFI_QML_DECL
@ -27,18 +29,25 @@ public:
signals:
void buyResult(QJsonObject result);
// Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and
// Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and
// because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain).
void balanceResult(QJsonObject result);
void inventoryResult(QJsonObject result);
void securityImageResult(uint imageID);
void securityImageResult(bool exists);
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 = "");
Q_INVOKABLE void balance();
Q_INVOKABLE void inventory();
Q_INVOKABLE void chooseSecurityImage(uint imageID);
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

View file

@ -12,10 +12,15 @@
#include "CommerceLogging.h"
#include "Ledger.h"
#include "Wallet.h"
#include "Application.h"
#include "ui/ImageProvider.h"
#include <PathUtils.h>
#include <OffscreenUi.h>
#include <QFile>
#include <QCryptographicHash>
#include <QQmlContext>
#include <openssl/ssl.h>
#include <openssl/err.h>
@ -23,8 +28,10 @@
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
static const char* KEY_FILE = "hifikey";
static const char* IMAGE_FILE = "hifi_image"; // eventually this will live in keyfile
void initialize() {
static bool initialized = false;
@ -40,18 +47,30 @@ QString keyFilePath() {
return PathUtils::getAppDataFilePath(KEY_FILE);
}
// for now the callback function just returns the same string. Later we can hook
// this to the gui (some thought required)
QString imageFilePath() {
return PathUtils::getAppDataFilePath(IMAGE_FILE);
}
// use the cached _passphrase if it exists, otherwise we need to prompt
int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
// just return a hardcoded pwd for now
static const char* pwd = "pwd";
strcpy(password, pwd);
return static_cast<int>(strlen(pwd));
auto passphrase = DependencyManager::get<Wallet>()->getPassphrase();
if (passphrase) {
strcpy(password, passphrase->toLocal8Bit().constData());
return static_cast<int>(passphrase->size());
} else {
// ok gotta bring up modal dialog... But right now lets just
// just keep it empty
return 0;
}
}
// BEGIN copied code - this will be removed/changed at some point soon
// copied (without emits for various signals) from libraries/networking/src/RSAKeypairGenerator.cpp.
// We will have a different implementation in practice, but this gives us a start for now
//
// NOTE: we don't really use the private keys returned - we can see how this evolves, but probably
// we should just return a list of public keys?
QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
RSA* keyPair = RSA_new();
@ -106,9 +125,11 @@ QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
// now lets persist them to files
// TODO: figure out a scheme for multiple keys, etc...
// FIXME: for now I'm appending to the file if it exists. As long as we always put
// the keys in the same order, this works fine. TODO: verify this will skip over
// anything else (like an embedded image)
FILE* fp;
if ((fp = fopen(keyFilePath().toStdString().c_str(), "wt"))) {
if ((fp = fopen(keyFilePath().toStdString().c_str(), "at"))) {
if (!PEM_write_RSAPublicKey(fp, keyPair)) {
fclose(fp);
qCDebug(commerce) << "failed to write public key";
@ -125,7 +146,9 @@ QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
RSA_free(keyPair);
// prepare the return values
// prepare the return values. TODO: Fix this - we probably don't really even want the
// private key at all (better to read it when we need it?). Or maybe we do, when we have
// multiple keys?
retval.first = new QByteArray(reinterpret_cast<char*>(publicKeyDER), publicKeyLength ),
retval.second = new QByteArray(reinterpret_cast<char*>(privateKeyDER), privateKeyLength );
@ -192,6 +215,126 @@ RSA* readPrivateKey(const char* filename) {
return key;
}
static const unsigned char IVEC[16] = "IAmAnIVecYay123";
void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) {
// first ivec
memcpy(ivec, IVEC, 16);
auto hash = QCryptographicHash::hash(salt, QCryptographicHash::Md5);
memcpy(ckey, hash.data(), 16);
}
void Wallet::setPassphrase(const QString& passphrase) {
if (_passphrase) {
delete _passphrase;
}
_passphrase = new QString(passphrase);
}
// encrypt some stuff
bool Wallet::encryptFile(const QString& inputFilePath, const QString& outputFilePath) {
// aes requires a couple 128-bit keys (ckey and ivec). For now, I'll just
// use the md5 of the salt as the ckey (md5 is 128-bit), and ivec will be
// a constant. We can review this later - there are ways to generate keys
// from a password that may be better.
unsigned char ivec[16];
unsigned char ckey[16];
initializeAESKeys(ivec, ckey, _salt);
int tempSize, outSize;
// read entire unencrypted file into memory
QFile inputFile(inputFilePath);
if (!inputFile.exists()) {
qCDebug(commerce) << "cannot encrypt" << inputFilePath << "file doesn't exist";
return false;
}
inputFile.open(QIODevice::ReadOnly);
QByteArray inputFileBuffer = inputFile.readAll();
inputFile.close();
// reserve enough capacity for encrypted bytes
unsigned char* outputFileBuffer = new unsigned char[inputFileBuffer.size() + AES_BLOCK_SIZE];
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
// TODO: add error handling!!!
if (!EVP_EncryptInit_ex(ctx, EVP_des_ede3_cbc(), NULL, ckey, ivec)) {
qCDebug(commerce) << "encrypt init failure";
delete[] outputFileBuffer;
return false;
}
if (!EVP_EncryptUpdate(ctx, outputFileBuffer, &tempSize, (unsigned char*)inputFileBuffer.data(), inputFileBuffer.size())) {
qCDebug(commerce) << "encrypt update failure";
delete[] outputFileBuffer;
return false;
}
outSize = tempSize;
if (!EVP_EncryptFinal_ex(ctx, outputFileBuffer + outSize, &tempSize)) {
qCDebug(commerce) << "encrypt final failure";
delete[] outputFileBuffer;
return false;
}
outSize += tempSize;
EVP_CIPHER_CTX_free(ctx);
qCDebug(commerce) << "encrypted buffer size" << outSize;
QByteArray output((const char*)outputFileBuffer, outSize);
QFile outputFile(outputFilePath);
outputFile.open(QIODevice::WriteOnly);
outputFile.write(output);
outputFile.close();
delete[] outputFileBuffer;
return true;
}
bool Wallet::decryptFile(const QString& inputFilePath, unsigned char** outputBufferPtr, int* outputBufferSize) {
unsigned char ivec[16];
unsigned char ckey[16];
initializeAESKeys(ivec, ckey, _salt);
// read encrypted file
QFile inputFile(inputFilePath);
if (!inputFile.exists()) {
qCDebug(commerce) << "cannot decrypt file" << inputFilePath << "it doesn't exist";
return false;
}
inputFile.open(QIODevice::ReadOnly);
QByteArray encryptedBuffer = inputFile.readAll();
inputFile.close();
// setup decrypted buffer
unsigned char* outputBuffer = new unsigned char[encryptedBuffer.size()];
int tempSize;
// TODO: add error handling
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (!EVP_DecryptInit_ex(ctx, EVP_des_ede3_cbc(), NULL, ckey, ivec)) {
qCDebug(commerce) << "decrypt init failure";
delete[] outputBuffer;
return false;
}
if (!EVP_DecryptUpdate(ctx, outputBuffer, &tempSize, (unsigned char*)encryptedBuffer.data(), encryptedBuffer.size())) {
qCDebug(commerce) << "decrypt update failure";
delete[] outputBuffer;
return false;
}
*outputBufferSize = tempSize;
if (!EVP_DecryptFinal_ex(ctx, outputBuffer + tempSize, &tempSize)) {
qCDebug(commerce) << "decrypt final failure";
delete[] outputBuffer;
return false;
}
EVP_CIPHER_CTX_free(ctx);
*outputBufferSize += tempSize;
*outputBufferPtr = outputBuffer;
qCDebug(commerce) << "decrypted buffer size" << *outputBufferSize;
delete[] outputBuffer;
return true;
}
bool Wallet::createIfNeeded() {
if (_publicKeys.count() > 0) return false;
@ -205,7 +348,7 @@ bool Wallet::createIfNeeded() {
qCDebug(commerce) << "read private key";
RSA_free(key);
// K -- add the public key since we have a legit private key associated with it
_publicKeys.push_back(publicKey.toBase64());
_publicKeys.push_back(QUrl::toPercentEncoding(publicKey.toBase64()));
return false;
}
}
@ -228,6 +371,7 @@ bool Wallet::generateKeyPair() {
auto ledger = DependencyManager::get<Ledger>();
return ledger->receiveAt(key, oldKey);
}
QStringList Wallet::listPublicKeys() {
qCInfo(commerce) << "Enumerating public keys.";
createIfNeeded();
@ -268,10 +412,65 @@ QString Wallet::signWithKey(const QByteArray& text, const QString& key) {
}
void Wallet::chooseSecurityImage(uint imageID) {
_chosenSecurityImage = (SecurityImage)imageID;
emit securityImageResult(imageID);
void Wallet::chooseSecurityImage(const QString& filename) {
if (_securityImage) {
delete _securityImage;
}
// temporary...
QString path = qApp->applicationDirPath();
path.append("/resources/qml/hifi/commerce/");
path.append(filename);
// now create a new security image pixmap
_securityImage = new QPixmap();
qCDebug(commerce) << "loading data for pixmap from" << path;
_securityImage->load(path);
// encrypt it and save.
if (encryptFile(path, imageFilePath())) {
qCDebug(commerce) << "emitting pixmap";
// inform the image provider
auto engine = DependencyManager::get<OffscreenUi>()->getSurfaceContext()->engine();
auto imageProvider = reinterpret_cast<ImageProvider*>(engine->imageProvider(ImageProvider::PROVIDER_NAME));
imageProvider->setSecurityImage(_securityImage);
emit securityImageResult(true);
} else {
qCDebug(commerce) << "failed to encrypt security image";
emit securityImageResult(false);
}
}
void Wallet::getSecurityImage() {
emit securityImageResult(_chosenSecurityImage);
unsigned char* data;
int dataLen;
// if already decrypted, don't do it again
if (_securityImage) {
emit securityImageResult(true);
return;
}
// decrypt and return
if (decryptFile(imageFilePath(), &data, &dataLen)) {
// create the pixmap
_securityImage = new QPixmap();
_securityImage->loadFromData(data, dataLen, "jpg");
qCDebug(commerce) << "created pixmap from encrypted file";
// inform the image provider
auto engine = DependencyManager::get<OffscreenUi>()->getSurfaceContext()->engine();
auto imageProvider = reinterpret_cast<ImageProvider*>(engine->imageProvider(ImageProvider::PROVIDER_NAME));
imageProvider->setSecurityImage(_securityImage);
emit securityImageResult(true);
} else {
qCDebug(commerce) << "failed to decrypt security image (maybe none saved yet?)";
emit securityImageResult(false);
}
}
void Wallet::getKeyFilePath() {
emit keyFilePathResult(keyFilePath());
}

View file

@ -16,6 +16,8 @@
#include <DependencyManager.h>
#include <QPixmap>
class Wallet : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
@ -26,16 +28,21 @@ public:
bool generateKeyPair();
QStringList listPublicKeys();
QString signWithKey(const QByteArray& text, const QString& key);
void chooseSecurityImage(uint imageID);
void chooseSecurityImage(const QString& imageFile);
void getSecurityImage();
void getKeyFilePath();
void setSalt(const QByteArray& salt) { _salt = salt; }
QByteArray getSalt() { return _salt; }
void setPassphrase(const QString& passphrase);
QString* getPassphrase() { return _passphrase; }
signals:
void securityImageResult(uint imageID);
void securityImageResult(bool exists) ;
void keyFilePathResult(const QString& path);
protected:
// ALWAYS add SecurityImage enum values to the END of the enum.
// They must be in the same order as the images are listed in
// SecurityImageSelection.qml
enum SecurityImage {
NONE = 0,
Cat,
@ -48,7 +55,12 @@ protected:
private:
QStringList _publicKeys{};
SecurityImage _chosenSecurityImage = SecurityImage::NONE;
QPixmap* _securityImage { nullptr };
QByteArray _salt {"iamsalt!"};
QString* _passphrase { new QString("pwd") };
bool encryptFile(const QString& inputFilePath, const QString& outputFilePath);
bool decryptFile(const QString& inputFilePath, unsigned char** outputBufferPtr, int* outputBufferLen);
};
#endif // hifi_Wallet_h

View file

@ -0,0 +1,29 @@
//
// ImageProvider.cpp
// interface/src/ui
//
// Created by David Kelly on 8/23/2017.
// 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
//
#include "ImageProvider.h"
#include <QDebug>
const QString ImageProvider::PROVIDER_NAME = "security";
QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) {
// adjust the internal pixmap to have the requested size
if (id == "securityImage" && _securityImage) {
*size = _securityImage->size();
if (requestedSize.width() > 0 && requestedSize.height() > 0) {
return _securityImage->scaled(requestedSize.width(), requestedSize.height(), Qt::KeepAspectRatio);
} else {
return _securityImage->copy();
}
}
return QPixmap();
}

View file

@ -0,0 +1,33 @@
//
// ImageProvider.h
//
// Created by David Kelly on 2017/08/23
// 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
//
#pragma once
#ifndef hifi_ImageProvider_h
#define hifi_ImageProvider_h
#include <QQuickImageProvider>
class ImageProvider: public QQuickImageProvider {
public:
static const QString PROVIDER_NAME;
ImageProvider() : QQuickImageProvider(QQuickImageProvider::Pixmap) {}
QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) override;
void setSecurityImage(QPixmap* pixmap) { _securityImage = pixmap; }
protected:
QPixmap* _securityImage { nullptr };
};
#endif //hifi_ImageProvider_h

View file

@ -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",

View file

@ -0,0 +1,152 @@
"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;
var walletEnabled = Settings.getValue("inspectionMode", false);
function startup() {
if (walletEnabled) {
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

View file

@ -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
}));
}