Merge pull request #11235 from davidkelly/dk/encryptFile
Initial wallet app plus some encryption of security image
180
interface/resources/icons/tablet-icons/wallet-a.svg
Normal file
After Width: | Height: | Size: 52 KiB |
276
interface/resources/icons/tablet-icons/wallet-i.svg
Normal file
After Width: | Height: | Size: 56 KiB |
|
@ -17,6 +17,7 @@ import QtQuick.Controls 1.4
|
||||||
import "../../styles-uit"
|
import "../../styles-uit"
|
||||||
import "../../controls-uit" as HifiControlsUit
|
import "../../controls-uit" as HifiControlsUit
|
||||||
import "../../controls" as HifiControls
|
import "../../controls" as HifiControls
|
||||||
|
import "./wallet" as HifiWallet
|
||||||
|
|
||||||
// references XXX from root context
|
// references XXX from root context
|
||||||
|
|
||||||
|
@ -30,28 +31,29 @@ Rectangle {
|
||||||
property string itemHref: "";
|
property string itemHref: "";
|
||||||
property int balanceAfterPurchase: 0;
|
property int balanceAfterPurchase: 0;
|
||||||
property bool alreadyOwned: false;
|
property bool alreadyOwned: false;
|
||||||
|
property int itemPriceFull: 0;
|
||||||
// Style
|
// Style
|
||||||
color: hifi.colors.baseGray;
|
color: hifi.colors.baseGray;
|
||||||
Hifi.QmlCommerce {
|
Hifi.QmlCommerce {
|
||||||
id: commerce;
|
id: commerce;
|
||||||
onBuyResult: {
|
onBuyResult: {
|
||||||
if (result.status !== 'success') {
|
if (result.status !== 'success') {
|
||||||
buyButton.text = result.message;
|
buyButton.text = result.message;
|
||||||
buyButton.enabled = false;
|
buyButton.enabled = false;
|
||||||
} else {
|
} else {
|
||||||
if (urlHandler.canHandleUrl(itemHref)) {
|
if (urlHandler.canHandleUrl(itemHref)) {
|
||||||
urlHandler.handleUrl(itemHref);
|
urlHandler.handleUrl(itemHref);
|
||||||
}
|
|
||||||
sendToScript({method: 'checkout_buySuccess', itemId: itemId});
|
|
||||||
}
|
}
|
||||||
|
sendToScript({method: 'checkout_buySuccess', itemId: itemId});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onBalanceResult: {
|
onBalanceResult: {
|
||||||
if (result.status !== 'success') {
|
if (result.status !== 'success') {
|
||||||
console.log("Failed to get balance", result.message);
|
console.log("Failed to get balance", result.message);
|
||||||
} else {
|
} else {
|
||||||
balanceReceived = true;
|
balanceReceived = true;
|
||||||
hfcBalanceText.text = result.data.balance;
|
hfcBalanceText.text = parseFloat(result.data.balance/100).toFixed(2);
|
||||||
balanceAfterPurchase = result.data.balance - parseInt(itemPriceText.text, 10);
|
balanceAfterPurchase = parseFloat(result.data.balance/100) - parseFloat(checkoutRoot.itemPriceFull/100).toFixed(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onInventoryResult: {
|
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.left: parent.left;
|
||||||
anchors.top: parent.top;
|
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
|
// Title Bar text
|
||||||
RalewaySemiBold {
|
RalewaySemiBold {
|
||||||
id: titleBarText;
|
id: titleBarText;
|
||||||
|
@ -111,7 +91,7 @@ Rectangle {
|
||||||
size: hifi.fontSizes.overlayTitle;
|
size: hifi.fontSizes.overlayTitle;
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.top: parent.top;
|
anchors.top: parent.top;
|
||||||
anchors.left: securityImage.right;
|
anchors.left: parent.left;
|
||||||
anchors.leftMargin: 16;
|
anchors.leftMargin: 16;
|
||||||
anchors.bottom: parent.bottom;
|
anchors.bottom: parent.bottom;
|
||||||
width: paintedWidth;
|
width: paintedWidth;
|
||||||
|
@ -420,7 +400,7 @@ Rectangle {
|
||||||
text: (inventoryReceived && balanceReceived) ? (alreadyOwned ? "Already Owned: Get Item" : "Buy") : "--";
|
text: (inventoryReceived && balanceReceived) ? (alreadyOwned ? "Already Owned: Get Item" : "Buy") : "--";
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!alreadyOwned) {
|
if (!alreadyOwned) {
|
||||||
commerce.buy(itemId, parseInt(itemPriceText.text));
|
commerce.buy(itemId, parseFloat(itemPriceText.text*100));
|
||||||
} else {
|
} else {
|
||||||
if (urlHandler.canHandleUrl(itemHref)) {
|
if (urlHandler.canHandleUrl(itemHref)) {
|
||||||
urlHandler.handleUrl(itemHref);
|
urlHandler.handleUrl(itemHref);
|
||||||
|
@ -456,11 +436,11 @@ Rectangle {
|
||||||
itemId = message.params.itemId;
|
itemId = message.params.itemId;
|
||||||
itemNameText.text = message.params.itemName;
|
itemNameText.text = message.params.itemName;
|
||||||
itemAuthorText.text = message.params.itemAuthor;
|
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;
|
itemHref = message.params.itemHref;
|
||||||
commerce.balance();
|
commerce.balance();
|
||||||
commerce.inventory();
|
commerce.inventory();
|
||||||
commerce.getSecurityImage();
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
||||||
|
|
|
@ -17,6 +17,7 @@ import QtQuick.Controls 1.4
|
||||||
import "../../styles-uit"
|
import "../../styles-uit"
|
||||||
import "../../controls-uit" as HifiControlsUit
|
import "../../controls-uit" as HifiControlsUit
|
||||||
import "../../controls" as HifiControls
|
import "../../controls" as HifiControls
|
||||||
|
import "./wallet" as HifiWallet
|
||||||
|
|
||||||
// references XXX from root context
|
// references XXX from root context
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ Rectangle {
|
||||||
if (result.status !== 'success') {
|
if (result.status !== 'success') {
|
||||||
console.log("Failed to get balance", result.message);
|
console.log("Failed to get balance", result.message);
|
||||||
} else {
|
} else {
|
||||||
hfcBalanceText.text = result.data.balance;
|
hfcBalanceText.text = parseFloat(result.data.balance/100).toFixed(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onInventoryResult: {
|
onInventoryResult: {
|
||||||
|
@ -43,14 +44,6 @@ Rectangle {
|
||||||
inventoryContentsList.model = result.data.assets;
|
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.left: parent.left;
|
||||||
anchors.top: parent.top;
|
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
|
// Title Bar text
|
||||||
RalewaySemiBold {
|
RalewaySemiBold {
|
||||||
id: titleBarText;
|
id: titleBarText;
|
||||||
|
@ -87,7 +66,7 @@ Rectangle {
|
||||||
size: hifi.fontSizes.overlayTitle;
|
size: hifi.fontSizes.overlayTitle;
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.top: parent.top;
|
anchors.top: parent.top;
|
||||||
anchors.left: securityImage.right;
|
anchors.left: parent.left;
|
||||||
anchors.leftMargin: 16;
|
anchors.leftMargin: 16;
|
||||||
anchors.bottom: parent.bottom;
|
anchors.bottom: parent.bottom;
|
||||||
width: paintedWidth;
|
width: paintedWidth;
|
||||||
|
@ -98,25 +77,6 @@ Rectangle {
|
||||||
verticalAlignment: Text.AlignVCenter;
|
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
|
// Separator
|
||||||
HifiControlsUit.Separator {
|
HifiControlsUit.Separator {
|
||||||
anchors.left: parent.left;
|
anchors.left: parent.left;
|
||||||
|
@ -307,7 +267,6 @@ Rectangle {
|
||||||
referrerURL = message.referrerURL;
|
referrerURL = message.referrerURL;
|
||||||
commerce.balance();
|
commerce.balance();
|
||||||
commerce.inventory();
|
commerce.inventory();
|
||||||
commerce.getSecurityImage();
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
||||||
|
|
|
@ -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
|
|
||||||
//
|
|
||||||
}
|
|
73
interface/resources/qml/hifi/commerce/wallet/Help.qml
Normal 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
|
||||||
|
//
|
||||||
|
}
|
127
interface/resources/qml/hifi/commerce/wallet/NotSetUp.qml
Normal 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
|
||||||
|
//
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
322
interface/resources/qml/hifi/commerce/wallet/Security.qml
Normal 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
|
||||||
|
//
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
//
|
//
|
||||||
// SecurityImageModel
|
// SecurityImageModel
|
||||||
//
|
//
|
||||||
// Created by Zach Fox on 2017-08-15
|
// Created by Zach Fox on 2017-08-17
|
||||||
// Copyright 2017 High Fidelity, Inc.
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
//
|
//
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
@ -39,4 +39,8 @@ ListModel {
|
||||||
sourcePath: "images/06gingerbread.jpg"
|
sourcePath: "images/06gingerbread.jpg"
|
||||||
securityImageEnumValue: 6;
|
securityImageEnumValue: 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getImagePathFromImageID(imageID) {
|
||||||
|
return (imageID ? root.get(imageID - 1).sourcePath : "");
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
//
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
73
interface/resources/qml/hifi/commerce/wallet/SendMoney.qml
Normal 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
|
||||||
|
//
|
||||||
|
}
|
479
interface/resources/qml/hifi/commerce/wallet/Wallet.qml
Normal 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
|
||||||
|
//
|
||||||
|
}
|
357
interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
Normal 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
|
||||||
|
//
|
||||||
|
}
|
|
@ -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
|
||||||
|
//
|
||||||
|
}
|
BIN
interface/resources/qml/hifi/commerce/wallet/images/01cat.jpg
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/02car.jpg
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/03dog.jpg
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/04stars.jpg
Normal file
After Width: | Height: | Size: 86 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/05plane.jpg
Normal file
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 74 KiB |
|
@ -183,6 +183,7 @@
|
||||||
#include "ui/UpdateDialog.h"
|
#include "ui/UpdateDialog.h"
|
||||||
#include "ui/overlays/Overlays.h"
|
#include "ui/overlays/Overlays.h"
|
||||||
#include "ui/DomainConnectionModel.h"
|
#include "ui/DomainConnectionModel.h"
|
||||||
|
#include "ui/ImageProvider.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "InterfaceParentFinder.h"
|
#include "InterfaceParentFinder.h"
|
||||||
#include "ui/OctreeStatsProvider.h"
|
#include "ui/OctreeStatsProvider.h"
|
||||||
|
@ -2165,6 +2166,9 @@ void Application::initializeUi() {
|
||||||
qApp->quit();
|
qApp->quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// register the pixmap image provider (used only for security image, for now)
|
||||||
|
engine->addImageProvider(ImageProvider::PROVIDER_NAME, new ImageProvider());
|
||||||
|
|
||||||
setupPreferences();
|
setupPreferences();
|
||||||
|
|
||||||
// For some reason there is already an "Application" object in the QML context,
|
// For some reason there is already an "Application" object in the QML context,
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "DependencyManager.h"
|
#include "DependencyManager.h"
|
||||||
#include "Ledger.h"
|
#include "Ledger.h"
|
||||||
#include "Wallet.h"
|
#include "Wallet.h"
|
||||||
|
#include <AccountManager.h>
|
||||||
|
|
||||||
HIFI_QML_DEF(QmlCommerce)
|
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::balanceResult, this, &QmlCommerce::balanceResult);
|
||||||
connect(ledger.data(), &Ledger::inventoryResult, this, &QmlCommerce::inventoryResult);
|
connect(ledger.data(), &Ledger::inventoryResult, this, &QmlCommerce::inventoryResult);
|
||||||
connect(wallet.data(), &Wallet::securityImageResult, this, &QmlCommerce::securityImageResult);
|
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) {
|
void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) {
|
||||||
|
@ -50,11 +52,24 @@ void QmlCommerce::inventory() {
|
||||||
ledger->inventory(wallet->listPublicKeys());
|
ledger->inventory(wallet->listPublicKeys());
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlCommerce::chooseSecurityImage(uint imageID) {
|
void QmlCommerce::chooseSecurityImage(const QString& imageFile) {
|
||||||
auto wallet = DependencyManager::get<Wallet>();
|
auto wallet = DependencyManager::get<Wallet>();
|
||||||
wallet->chooseSecurityImage(imageID);
|
wallet->chooseSecurityImage(imageFile);
|
||||||
}
|
}
|
||||||
void QmlCommerce::getSecurityImage() {
|
void QmlCommerce::getSecurityImage() {
|
||||||
auto wallet = DependencyManager::get<Wallet>();
|
auto wallet = DependencyManager::get<Wallet>();
|
||||||
wallet->getSecurityImage();
|
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();
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <OffscreenQmlDialog.h>
|
#include <OffscreenQmlDialog.h>
|
||||||
|
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
class QmlCommerce : public OffscreenQmlDialog {
|
class QmlCommerce : public OffscreenQmlDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
HIFI_QML_DECL
|
HIFI_QML_DECL
|
||||||
|
@ -31,14 +33,21 @@ signals:
|
||||||
// because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain).
|
// 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 balanceResult(QJsonObject result);
|
||||||
void inventoryResult(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:
|
protected:
|
||||||
Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = "");
|
Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = "");
|
||||||
Q_INVOKABLE void balance();
|
Q_INVOKABLE void balance();
|
||||||
Q_INVOKABLE void inventory();
|
Q_INVOKABLE void inventory();
|
||||||
Q_INVOKABLE void chooseSecurityImage(uint imageID);
|
Q_INVOKABLE void chooseSecurityImage(const QString& imageFile);
|
||||||
Q_INVOKABLE void getSecurityImage();
|
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
|
#endif // hifi_QmlCommerce_h
|
||||||
|
|
|
@ -12,10 +12,15 @@
|
||||||
#include "CommerceLogging.h"
|
#include "CommerceLogging.h"
|
||||||
#include "Ledger.h"
|
#include "Ledger.h"
|
||||||
#include "Wallet.h"
|
#include "Wallet.h"
|
||||||
|
#include "Application.h"
|
||||||
|
#include "ui/ImageProvider.h"
|
||||||
|
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
|
#include <OffscreenUi.h>
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
|
#include <QQmlContext>
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
@ -23,8 +28,10 @@
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/aes.h>
|
||||||
|
|
||||||
static const char* KEY_FILE = "hifikey";
|
static const char* KEY_FILE = "hifikey";
|
||||||
|
static const char* IMAGE_FILE = "hifi_image"; // eventually this will live in keyfile
|
||||||
|
|
||||||
void initialize() {
|
void initialize() {
|
||||||
static bool initialized = false;
|
static bool initialized = false;
|
||||||
|
@ -40,18 +47,30 @@ QString keyFilePath() {
|
||||||
return PathUtils::getAppDataFilePath(KEY_FILE);
|
return PathUtils::getAppDataFilePath(KEY_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// for now the callback function just returns the same string. Later we can hook
|
QString imageFilePath() {
|
||||||
// this to the gui (some thought required)
|
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) {
|
int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
|
||||||
// just return a hardcoded pwd for now
|
// just return a hardcoded pwd for now
|
||||||
static const char* pwd = "pwd";
|
auto passphrase = DependencyManager::get<Wallet>()->getPassphrase();
|
||||||
strcpy(password, pwd);
|
if (passphrase) {
|
||||||
return static_cast<int>(strlen(pwd));
|
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
|
// BEGIN copied code - this will be removed/changed at some point soon
|
||||||
// copied (without emits for various signals) from libraries/networking/src/RSAKeypairGenerator.cpp.
|
// 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
|
// 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() {
|
QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
|
||||||
|
|
||||||
RSA* keyPair = RSA_new();
|
RSA* keyPair = RSA_new();
|
||||||
|
@ -106,9 +125,11 @@ QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
|
||||||
|
|
||||||
|
|
||||||
// now lets persist them to files
|
// 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;
|
FILE* fp;
|
||||||
if ((fp = fopen(keyFilePath().toStdString().c_str(), "wt"))) {
|
if ((fp = fopen(keyFilePath().toStdString().c_str(), "at"))) {
|
||||||
if (!PEM_write_RSAPublicKey(fp, keyPair)) {
|
if (!PEM_write_RSAPublicKey(fp, keyPair)) {
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
qCDebug(commerce) << "failed to write public key";
|
qCDebug(commerce) << "failed to write public key";
|
||||||
|
@ -125,7 +146,9 @@ QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
|
||||||
|
|
||||||
RSA_free(keyPair);
|
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.first = new QByteArray(reinterpret_cast<char*>(publicKeyDER), publicKeyLength ),
|
||||||
retval.second = new QByteArray(reinterpret_cast<char*>(privateKeyDER), privateKeyLength );
|
retval.second = new QByteArray(reinterpret_cast<char*>(privateKeyDER), privateKeyLength );
|
||||||
|
|
||||||
|
@ -192,6 +215,126 @@ RSA* readPrivateKey(const char* filename) {
|
||||||
return key;
|
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() {
|
bool Wallet::createIfNeeded() {
|
||||||
if (_publicKeys.count() > 0) return false;
|
if (_publicKeys.count() > 0) return false;
|
||||||
|
|
||||||
|
@ -205,7 +348,7 @@ bool Wallet::createIfNeeded() {
|
||||||
qCDebug(commerce) << "read private key";
|
qCDebug(commerce) << "read private key";
|
||||||
RSA_free(key);
|
RSA_free(key);
|
||||||
// K -- add the public key since we have a legit private key associated with it
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,6 +371,7 @@ bool Wallet::generateKeyPair() {
|
||||||
auto ledger = DependencyManager::get<Ledger>();
|
auto ledger = DependencyManager::get<Ledger>();
|
||||||
return ledger->receiveAt(key, oldKey);
|
return ledger->receiveAt(key, oldKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Wallet::listPublicKeys() {
|
QStringList Wallet::listPublicKeys() {
|
||||||
qCInfo(commerce) << "Enumerating public keys.";
|
qCInfo(commerce) << "Enumerating public keys.";
|
||||||
createIfNeeded();
|
createIfNeeded();
|
||||||
|
@ -268,10 +412,65 @@ QString Wallet::signWithKey(const QByteArray& text, const QString& key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Wallet::chooseSecurityImage(uint imageID) {
|
void Wallet::chooseSecurityImage(const QString& filename) {
|
||||||
_chosenSecurityImage = (SecurityImage)imageID;
|
|
||||||
emit securityImageResult(imageID);
|
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() {
|
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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
|
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
class Wallet : public QObject, public Dependency {
|
class Wallet : public QObject, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
@ -26,16 +28,21 @@ public:
|
||||||
bool generateKeyPair();
|
bool generateKeyPair();
|
||||||
QStringList listPublicKeys();
|
QStringList listPublicKeys();
|
||||||
QString signWithKey(const QByteArray& text, const QString& key);
|
QString signWithKey(const QByteArray& text, const QString& key);
|
||||||
void chooseSecurityImage(uint imageID);
|
void chooseSecurityImage(const QString& imageFile);
|
||||||
void getSecurityImage();
|
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:
|
signals:
|
||||||
void securityImageResult(uint imageID);
|
void securityImageResult(bool exists) ;
|
||||||
|
void keyFilePathResult(const QString& path);
|
||||||
|
|
||||||
protected:
|
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 {
|
enum SecurityImage {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
Cat,
|
Cat,
|
||||||
|
@ -48,7 +55,12 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QStringList _publicKeys{};
|
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
|
#endif // hifi_Wallet_h
|
||||||
|
|
29
interface/src/ui/ImageProvider.cpp
Normal 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();
|
||||||
|
}
|
33
interface/src/ui/ImageProvider.h
Normal 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
|
||||||
|
|
|
@ -28,7 +28,8 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
||||||
"system/notifications.js",
|
"system/notifications.js",
|
||||||
"system/dialTone.js",
|
"system/dialTone.js",
|
||||||
"system/firstPersonHMD.js",
|
"system/firstPersonHMD.js",
|
||||||
"system/tablet-ui/tabletUI.js"
|
"system/tablet-ui/tabletUI.js",
|
||||||
|
"system/commerce/wallet.js"
|
||||||
];
|
];
|
||||||
var DEFAULT_SCRIPTS_SEPARATE = [
|
var DEFAULT_SCRIPTS_SEPARATE = [
|
||||||
"system/controllers/controllerScripts.js",
|
"system/controllers/controllerScripts.js",
|
||||||
|
|
152
scripts/system/commerce/wallet.js
Normal 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
|
|
@ -115,7 +115,7 @@
|
||||||
itemId: id,
|
itemId: id,
|
||||||
itemName: name,
|
itemName: name,
|
||||||
itemAuthor: author,
|
itemAuthor: author,
|
||||||
itemPrice: price ? parseInt(price, 10) : Math.round(Math.random() * 50),
|
itemPrice: price ? parseInt(price, 10) : 0,
|
||||||
itemHref: href
|
itemHref: href
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|