Merge branch 'master' into vr-edit-a
|
@ -3,7 +3,7 @@ This is a stand-alone guide for creating your first High Fidelity build for Wind
|
|||
## Building High Fidelity
|
||||
Note: We are now using Visual Studio 2017 and Qt 5.9.1. If you are upgrading from Visual Studio 2013 and Qt 5.6.2, do a clean uninstall of those versions before going through this guide.
|
||||
|
||||
Note: The prerequisites will require about 10 GB of space on your drive.
|
||||
Note: The prerequisites will require about 10 GB of space on your drive. You will also need a system with at least 8GB of main memory.
|
||||
|
||||
### Step 1. Visual Studio 2017
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
|||
if (message->getSize() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
QDataStream packetStream(message->getMessage());
|
||||
|
||||
// read a NodeConnectionData object from the packet so we can pass around this data while we're inspecting it
|
||||
|
|
|
@ -458,7 +458,7 @@ const QString DISABLED_AUTOMATIC_NETWORKING_VALUE = "disabled";
|
|||
|
||||
|
||||
|
||||
bool DomainServer::packetVersionMatch(const udt::Packet& packet) {
|
||||
bool DomainServer::isPacketVerified(const udt::Packet& packet) {
|
||||
PacketType headerType = NLPacket::typeInHeader(packet);
|
||||
PacketVersion headerVersion = NLPacket::versionInHeader(packet);
|
||||
|
||||
|
@ -471,7 +471,48 @@ bool DomainServer::packetVersionMatch(const udt::Packet& packet) {
|
|||
DomainGatekeeper::sendProtocolMismatchConnectionDenial(packet.getSenderSockAddr());
|
||||
}
|
||||
|
||||
// let the normal nodeList implementation handle all other packets.
|
||||
if (!PacketTypeEnum::getNonSourcedPackets().contains(headerType)) {
|
||||
// this is a sourced packet - first check if we have a node that matches
|
||||
QUuid sourceID = NLPacket::sourceIDInHeader(packet);
|
||||
SharedNodePointer sourceNode = nodeList->nodeWithUUID(sourceID);
|
||||
|
||||
if (sourceNode) {
|
||||
// unverified DS packets (due to a lack of connection secret between DS + node)
|
||||
// must come either from the same public IP address or a local IP address (set by RFC 1918)
|
||||
|
||||
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(sourceNode->getLinkedData());
|
||||
|
||||
bool exactAddressMatch = nodeData->getSendingSockAddr() == packet.getSenderSockAddr();
|
||||
bool bothPrivateAddresses = nodeData->getSendingSockAddr().hasPrivateAddress()
|
||||
&& packet.getSenderSockAddr().hasPrivateAddress();
|
||||
|
||||
if (nodeData && (exactAddressMatch || bothPrivateAddresses)) {
|
||||
// to the best of our ability we've verified that this packet comes from the right place
|
||||
// let the NodeList do its checks now (but pass it the sourceNode so it doesn't need to look it up again)
|
||||
return nodeList->isPacketVerifiedWithSource(packet, sourceNode.data());
|
||||
} else {
|
||||
static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unmatched IP for UUID";
|
||||
static QString repeatedMessage
|
||||
= LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX);
|
||||
|
||||
qDebug() << "Packet of type" << headerType
|
||||
<< "received from unmatched IP for UUID" << uuidStringWithoutCurlyBraces(sourceID);
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unknown node with UUID";
|
||||
static QString repeatedMessage
|
||||
= LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX);
|
||||
|
||||
qDebug() << "Packet of type" << headerType
|
||||
<< "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceID);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to allow the normal NodeList implementation to verify packets
|
||||
return nodeList->isPacketVerified(packet);
|
||||
}
|
||||
|
||||
|
@ -570,7 +611,7 @@ void DomainServer::setupNodeListAndAssignments() {
|
|||
addStaticAssignmentsToQueue();
|
||||
|
||||
// set a custom packetVersionMatch as the verify packet operator for the udt::Socket
|
||||
nodeList->setPacketFilterOperator(&DomainServer::packetVersionMatch);
|
||||
nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified);
|
||||
}
|
||||
|
||||
const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token";
|
||||
|
@ -853,7 +894,6 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
|||
}
|
||||
|
||||
void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
|
||||
QDataStream packetStream(message->getMessage());
|
||||
NodeConnectionData nodeRequestData = NodeConnectionData::fromDataStream(packetStream, message->getSenderSockAddr(), false);
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ public slots:
|
|||
|
||||
void restart();
|
||||
|
||||
private slots:
|
||||
void processRequestAssignmentPacket(QSharedPointer<ReceivedMessage> packet);
|
||||
void processListRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void processNodeJSONStatsPacket(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer sendingNode);
|
||||
|
@ -79,7 +80,6 @@ public slots:
|
|||
void processICEServerHeartbeatDenialPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void processICEServerHeartbeatACK(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
private slots:
|
||||
void setupPendingAssignmentCredits();
|
||||
void sendPendingTransactionsToServer();
|
||||
|
||||
|
@ -129,7 +129,7 @@ private:
|
|||
|
||||
void getTemporaryName(bool force = false);
|
||||
|
||||
static bool packetVersionMatch(const udt::Packet& packet);
|
||||
static bool isPacketVerified(const udt::Packet& packet);
|
||||
|
||||
bool resetAccountManagerAccessToken();
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ find_package(Qt5LinguistTools REQUIRED)
|
|||
find_package(Qt5LinguistToolsMacros)
|
||||
|
||||
if (WIN32)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -bigobj")
|
||||
add_definitions(-D_USE_MATH_DEFINES) # apparently needed to get M_PI and other defines from cmath/math.h
|
||||
add_definitions(-DWINDOWS_LEAN_AND_MEAN) # needed to make sure windows doesn't go to crazy with its defines
|
||||
endif()
|
||||
|
|
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 |
|
@ -35,7 +35,6 @@ Column {
|
|||
property string metaverseServerUrl: '';
|
||||
property string actions: 'snapshot';
|
||||
// sendToScript doesn't get wired until after everything gets created. So we have to queue fillDestinations on nextTick.
|
||||
Component.onCompleted: delay.start();
|
||||
property string labelText: actions;
|
||||
property string filter: '';
|
||||
onFilterChanged: filterChoicesByText();
|
||||
|
@ -125,11 +124,9 @@ Column {
|
|||
cb();
|
||||
});
|
||||
}
|
||||
property var delay: Timer { // No setTimeout or nextTick in QML.
|
||||
interval: 0;
|
||||
onTriggered: fillDestinations();
|
||||
}
|
||||
function fillDestinations() { // Public
|
||||
console.debug('Feed::fillDestinations()')
|
||||
|
||||
function report(label, error) {
|
||||
console.log(label, actions, error || 'ok', allStories.length, 'filtered to', suggestions.count);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import QtQuick.Controls 1.4
|
|||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
import "./wallet" as HifiWallet
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
|
@ -26,32 +27,33 @@ Rectangle {
|
|||
id: checkoutRoot;
|
||||
property bool inventoryReceived: false;
|
||||
property bool balanceReceived: false;
|
||||
property string itemId: "";
|
||||
property string itemId: "";
|
||||
property string itemHref: "";
|
||||
property int balanceAfterPurchase: 0;
|
||||
property bool alreadyOwned: false;
|
||||
property int itemPriceFull: 0;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
onBuyResult: {
|
||||
if (result.status !== 'success') {
|
||||
buyButton.text = result.message;
|
||||
buyButton.enabled = false;
|
||||
} else {
|
||||
if (urlHandler.canHandleUrl(itemHref)) {
|
||||
urlHandler.handleUrl(itemHref);
|
||||
}
|
||||
sendToScript({method: 'checkout_buySuccess', itemId: itemId});
|
||||
if (result.status !== 'success') {
|
||||
buyButton.text = result.message;
|
||||
buyButton.enabled = false;
|
||||
} else {
|
||||
if (urlHandler.canHandleUrl(itemHref)) {
|
||||
urlHandler.handleUrl(itemHref);
|
||||
}
|
||||
sendToScript({method: 'checkout_buySuccess', itemId: itemId});
|
||||
}
|
||||
}
|
||||
onBalanceResult: {
|
||||
if (result.status !== 'success') {
|
||||
console.log("Failed to get balance", result.message);
|
||||
} else {
|
||||
balanceReceived = true;
|
||||
hfcBalanceText.text = result.data.balance;
|
||||
balanceAfterPurchase = result.data.balance - parseInt(itemPriceText.text, 10);
|
||||
hfcBalanceText.text = parseFloat(result.data.balance/100).toFixed(2);
|
||||
balanceAfterPurchase = parseFloat(result.data.balance/100) - parseFloat(checkoutRoot.itemPriceFull/100).toFixed(2);
|
||||
}
|
||||
}
|
||||
onInventoryResult: {
|
||||
|
@ -67,14 +69,6 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
onSecurityImageResult: {
|
||||
securityImage.source = securityImageSelection.getImagePathFromImageID(imageID);
|
||||
}
|
||||
}
|
||||
|
||||
SecurityImageSelection {
|
||||
id: securityImageSelection;
|
||||
referrerURL: checkoutRoot.itemHref;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -89,20 +83,6 @@ Rectangle {
|
|||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Security Image
|
||||
Image {
|
||||
id: securityImage;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
height: parent.height - 5;
|
||||
width: height;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
}
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
id: titleBarText;
|
||||
|
@ -111,7 +91,7 @@ Rectangle {
|
|||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: securityImage.right;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
|
@ -132,7 +112,7 @@ Rectangle {
|
|||
//
|
||||
// TITLE BAR END
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// ITEM DESCRIPTION START
|
||||
//
|
||||
|
@ -147,7 +127,7 @@ Rectangle {
|
|||
|
||||
// Item Name text
|
||||
Item {
|
||||
id: itemNameContainer;
|
||||
id: itemNameContainer;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 4;
|
||||
|
@ -188,11 +168,11 @@ Rectangle {
|
|||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Item Author text
|
||||
Item {
|
||||
id: itemAuthorContainer;
|
||||
id: itemAuthorContainer;
|
||||
// Anchors
|
||||
anchors.top: itemNameContainer.bottom;
|
||||
anchors.topMargin: 4;
|
||||
|
@ -233,10 +213,10 @@ Rectangle {
|
|||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// HFC Balance text
|
||||
Item {
|
||||
id: hfcBalanceContainer;
|
||||
id: hfcBalanceContainer;
|
||||
// Anchors
|
||||
anchors.top: itemAuthorContainer.bottom;
|
||||
anchors.topMargin: 16;
|
||||
|
@ -278,10 +258,10 @@ Rectangle {
|
|||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Item Price text
|
||||
Item {
|
||||
id: itemPriceContainer;
|
||||
id: itemPriceContainer;
|
||||
// Anchors
|
||||
anchors.top: hfcBalanceContainer.bottom;
|
||||
anchors.topMargin: 4;
|
||||
|
@ -322,10 +302,10 @@ Rectangle {
|
|||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// HFC "Balance After Purchase" text
|
||||
Item {
|
||||
id: hfcBalanceAfterPurchaseContainer;
|
||||
id: hfcBalanceAfterPurchaseContainer;
|
||||
// Anchors
|
||||
anchors.top: itemPriceContainer.bottom;
|
||||
anchors.topMargin: 4;
|
||||
|
@ -372,7 +352,7 @@ Rectangle {
|
|||
// ITEM DESCRIPTION END
|
||||
//
|
||||
|
||||
|
||||
|
||||
//
|
||||
// ACTION BUTTONS START
|
||||
//
|
||||
|
@ -420,7 +400,7 @@ Rectangle {
|
|||
text: (inventoryReceived && balanceReceived) ? (alreadyOwned ? "Already Owned: Get Item" : "Buy") : "--";
|
||||
onClicked: {
|
||||
if (!alreadyOwned) {
|
||||
commerce.buy(itemId, parseInt(itemPriceText.text));
|
||||
commerce.buy(itemId, parseFloat(itemPriceText.text*100));
|
||||
} else {
|
||||
if (urlHandler.canHandleUrl(itemHref)) {
|
||||
urlHandler.handleUrl(itemHref);
|
||||
|
@ -456,11 +436,11 @@ Rectangle {
|
|||
itemId = message.params.itemId;
|
||||
itemNameText.text = message.params.itemName;
|
||||
itemAuthorText.text = message.params.itemAuthor;
|
||||
itemPriceText.text = message.params.itemPrice;
|
||||
checkoutRoot.itemPriceFull = message.params.itemPrice;
|
||||
itemPriceText.text = parseFloat(checkoutRoot.itemPriceFull/100).toFixed(2);
|
||||
itemHref = message.params.itemHref;
|
||||
commerce.balance();
|
||||
commerce.inventory();
|
||||
commerce.getSecurityImage();
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
||||
|
|
|
@ -17,6 +17,7 @@ import QtQuick.Controls 1.4
|
|||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
import "./wallet" as HifiWallet
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
|
@ -33,7 +34,7 @@ Rectangle {
|
|||
if (result.status !== 'success') {
|
||||
console.log("Failed to get balance", result.message);
|
||||
} else {
|
||||
hfcBalanceText.text = result.data.balance;
|
||||
hfcBalanceText.text = parseFloat(result.data.balance/100).toFixed(2);
|
||||
}
|
||||
}
|
||||
onInventoryResult: {
|
||||
|
@ -43,14 +44,6 @@ Rectangle {
|
|||
inventoryContentsList.model = result.data.assets;
|
||||
}
|
||||
}
|
||||
onSecurityImageResult: {
|
||||
securityImage.source = securityImageSelection.getImagePathFromImageID(imageID);
|
||||
}
|
||||
}
|
||||
|
||||
SecurityImageSelection {
|
||||
id: securityImageSelection;
|
||||
referrerURL: inventoryRoot.referrerURL;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -65,20 +58,6 @@ Rectangle {
|
|||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Security Image
|
||||
Image {
|
||||
id: securityImage;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
height: parent.height - 5;
|
||||
width: height;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
}
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
id: titleBarText;
|
||||
|
@ -87,7 +66,7 @@ Rectangle {
|
|||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: securityImage.right;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
|
@ -98,25 +77,6 @@ Rectangle {
|
|||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// "Change Security Image" button
|
||||
HifiControlsUit.Button {
|
||||
id: changeSecurityImageButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
width: 200;
|
||||
text: "Change Security Image"
|
||||
onClicked: {
|
||||
securityImageSelection.isManuallyChangingSecurityImage = true;
|
||||
securityImageSelection.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Separator
|
||||
HifiControlsUit.Separator {
|
||||
anchors.left: parent.left;
|
||||
|
@ -132,7 +92,7 @@ Rectangle {
|
|||
// HFC BALANCE START
|
||||
//
|
||||
Item {
|
||||
id: hfcBalanceContainer;
|
||||
id: hfcBalanceContainer;
|
||||
// Size
|
||||
width: inventoryRoot.width;
|
||||
height: childrenRect.height + 20;
|
||||
|
@ -177,7 +137,7 @@ Rectangle {
|
|||
//
|
||||
// HFC BALANCE END
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// INVENTORY CONTENTS START
|
||||
//
|
||||
|
@ -192,7 +152,7 @@ Rectangle {
|
|||
anchors.topMargin: 8;
|
||||
anchors.bottom: actionButtonsContainer.top;
|
||||
anchors.bottomMargin: 8;
|
||||
|
||||
|
||||
RalewaySemiBold {
|
||||
id: inventoryContentsLabel;
|
||||
text: "Inventory:";
|
||||
|
@ -249,7 +209,7 @@ Rectangle {
|
|||
//
|
||||
// INVENTORY CONTENTS END
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// ACTION BUTTONS START
|
||||
//
|
||||
|
@ -307,7 +267,6 @@ Rectangle {
|
|||
referrerURL = message.referrerURL;
|
||||
commerce.balance();
|
||||
commerce.inventory();
|
||||
commerce.getSecurityImage();
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
||||
|
|
|
@ -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
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-15
|
||||
// Created by Zach Fox on 2017-08-17
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
|
@ -39,4 +39,8 @@ ListModel {
|
|||
sourcePath: "images/06gingerbread.jpg"
|
||||
securityImageEnumValue: 6;
|
||||
}
|
||||
|
||||
function getImagePathFromImageID(imageID) {
|
||||
return (imageID ? root.get(imageID - 1).sourcePath : "");
|
||||
}
|
||||
}
|
|
@ -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 |
|
@ -39,11 +39,24 @@ StackView {
|
|||
property var rpcCounter: 0;
|
||||
signal sendToScript(var message);
|
||||
function rpc(method, parameters, callback) {
|
||||
console.debug('TabletAddressDialog: rpc: method = ', method, 'parameters = ', parameters, 'callback = ', callback)
|
||||
|
||||
rpcCalls[rpcCounter] = callback;
|
||||
var message = {method: method, params: parameters, id: rpcCounter++, jsonrpc: "2.0"};
|
||||
sendToScript(message);
|
||||
}
|
||||
function fromScript(message) {
|
||||
if (message.method === 'refreshFeeds') {
|
||||
var feeds = [happeningNow, places, snapshots];
|
||||
console.debug('TabletAddressDialog::fromScript: refreshFeeds', 'feeds = ', feeds);
|
||||
|
||||
feeds.forEach(function(feed) {
|
||||
Qt.callLater(feed.fillDestinations);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var callback = rpcCalls[message.id];
|
||||
if (!callback) {
|
||||
console.log('No callback for message fromScript', JSON.stringify(message));
|
||||
|
|
|
@ -183,6 +183,7 @@
|
|||
#include "ui/UpdateDialog.h"
|
||||
#include "ui/overlays/Overlays.h"
|
||||
#include "ui/DomainConnectionModel.h"
|
||||
#include "ui/ImageProvider.h"
|
||||
#include "Util.h"
|
||||
#include "InterfaceParentFinder.h"
|
||||
#include "ui/OctreeStatsProvider.h"
|
||||
|
@ -1735,6 +1736,27 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged);
|
||||
|
||||
// Setup the mouse ray pick and related operators
|
||||
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickID(_rayPickManager.createRayPick(
|
||||
RayPickFilter(DependencyManager::get<RayPickScriptingInterface>()->PICK_ENTITIES() | DependencyManager::get<RayPickScriptingInterface>()->PICK_INCLUDE_NONCOLLIDABLE()),
|
||||
0.0f, true));
|
||||
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickResultOperator([&](QUuid rayPickID) {
|
||||
RayToEntityIntersectionResult entityResult;
|
||||
RayPickResult result = _rayPickManager.getPrevRayPickResult(rayPickID);
|
||||
entityResult.intersects = result.type != DependencyManager::get<RayPickScriptingInterface>()->INTERSECTED_NONE();
|
||||
if (entityResult.intersects) {
|
||||
entityResult.intersection = result.intersection;
|
||||
entityResult.distance = result.distance;
|
||||
entityResult.surfaceNormal = result.surfaceNormal;
|
||||
entityResult.entityID = result.objectID;
|
||||
entityResult.entity = DependencyManager::get<EntityTreeRenderer>()->getTree()->findEntityByID(entityResult.entityID);
|
||||
}
|
||||
return entityResult;
|
||||
});
|
||||
DependencyManager::get<EntityTreeRenderer>()->setSetPrecisionPickingOperator([&](QUuid rayPickID, bool value) {
|
||||
_rayPickManager.setPrecisionPicking(rayPickID, value);
|
||||
});
|
||||
|
||||
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
|
||||
}
|
||||
|
||||
|
@ -2144,6 +2166,9 @@ void Application::initializeUi() {
|
|||
qApp->quit();
|
||||
});
|
||||
|
||||
// register the pixmap image provider (used only for security image, for now)
|
||||
engine->addImageProvider(ImageProvider::PROVIDER_NAME, new ImageProvider());
|
||||
|
||||
setupPreferences();
|
||||
|
||||
// For some reason there is already an "Application" object in the QML context,
|
||||
|
@ -3736,7 +3761,7 @@ bool Application::shouldPaint() {
|
|||
(float)paintDelaySamples / paintDelayUsecs << "us";
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Throttle if requested
|
||||
if (displayPlugin->isThrottled() && (_lastTimeUpdated.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) {
|
||||
return false;
|
||||
|
@ -6276,7 +6301,7 @@ bool Application::askToReplaceDomainContent(const QString& url) {
|
|||
OffscreenUi::warning("Unable to replace content", "You do not have permissions to replace domain content",
|
||||
QMessageBox::Ok, QMessageBox::Ok);
|
||||
}
|
||||
QJsonObject messageProperties = {
|
||||
QJsonObject messageProperties = {
|
||||
{ "status", methodDetails },
|
||||
{ "content_set_url", url }
|
||||
};
|
||||
|
@ -6359,7 +6384,7 @@ void Application::showAssetServerWidget(QString filePath) {
|
|||
|
||||
void Application::addAssetToWorldFromURL(QString url) {
|
||||
qInfo(interfaceapp) << "Download model and add to world from" << url;
|
||||
|
||||
|
||||
QString filename;
|
||||
if (url.contains("filename")) {
|
||||
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "DependencyManager.h"
|
||||
#include "Ledger.h"
|
||||
#include "Wallet.h"
|
||||
#include <AccountManager.h>
|
||||
|
||||
HIFI_QML_DEF(QmlCommerce)
|
||||
|
||||
|
@ -24,6 +25,7 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) {
|
|||
connect(ledger.data(), &Ledger::balanceResult, this, &QmlCommerce::balanceResult);
|
||||
connect(ledger.data(), &Ledger::inventoryResult, this, &QmlCommerce::inventoryResult);
|
||||
connect(wallet.data(), &Wallet::securityImageResult, this, &QmlCommerce::securityImageResult);
|
||||
connect(wallet.data(), &Wallet::keyFilePathResult, this, &QmlCommerce::keyFilePathResult);
|
||||
}
|
||||
|
||||
void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) {
|
||||
|
@ -50,11 +52,24 @@ void QmlCommerce::inventory() {
|
|||
ledger->inventory(wallet->listPublicKeys());
|
||||
}
|
||||
|
||||
void QmlCommerce::chooseSecurityImage(uint imageID) {
|
||||
void QmlCommerce::chooseSecurityImage(const QString& imageFile) {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->chooseSecurityImage(imageID);
|
||||
wallet->chooseSecurityImage(imageFile);
|
||||
}
|
||||
void QmlCommerce::getSecurityImage() {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->getSecurityImage();
|
||||
}
|
||||
}
|
||||
void QmlCommerce::getLoginStatus() {
|
||||
emit loginStatusResult(DependencyManager::get<AccountManager>()->isLoggedIn());
|
||||
}
|
||||
void QmlCommerce::setPassphrase(const QString& passphrase) {
|
||||
emit passphraseSetupStatusResult(true);
|
||||
}
|
||||
void QmlCommerce::getPassphraseSetupStatus() {
|
||||
emit passphraseSetupStatusResult(false);
|
||||
}
|
||||
void QmlCommerce::getKeyFilePath() {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->getKeyFilePath();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <QJsonObject>
|
||||
#include <OffscreenQmlDialog.h>
|
||||
|
||||
#include <QPixmap>
|
||||
|
||||
class QmlCommerce : public OffscreenQmlDialog {
|
||||
Q_OBJECT
|
||||
HIFI_QML_DECL
|
||||
|
@ -27,18 +29,25 @@ public:
|
|||
|
||||
signals:
|
||||
void buyResult(QJsonObject result);
|
||||
// Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and
|
||||
// Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and
|
||||
// because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain).
|
||||
void balanceResult(QJsonObject result);
|
||||
void inventoryResult(QJsonObject result);
|
||||
void securityImageResult(uint imageID);
|
||||
void securityImageResult(bool exists);
|
||||
void loginStatusResult(bool isLoggedIn);
|
||||
void passphraseSetupStatusResult(bool passphraseIsSetup);
|
||||
void keyFilePathResult(const QString& path);
|
||||
|
||||
protected:
|
||||
Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = "");
|
||||
Q_INVOKABLE void balance();
|
||||
Q_INVOKABLE void inventory();
|
||||
Q_INVOKABLE void chooseSecurityImage(uint imageID);
|
||||
Q_INVOKABLE void chooseSecurityImage(const QString& imageFile);
|
||||
Q_INVOKABLE void getSecurityImage();
|
||||
Q_INVOKABLE void getLoginStatus();
|
||||
Q_INVOKABLE void setPassphrase(const QString& passphrase);
|
||||
Q_INVOKABLE void getPassphraseSetupStatus();
|
||||
Q_INVOKABLE void getKeyFilePath();
|
||||
};
|
||||
|
||||
#endif // hifi_QmlCommerce_h
|
||||
|
|
|
@ -12,10 +12,15 @@
|
|||
#include "CommerceLogging.h"
|
||||
#include "Ledger.h"
|
||||
#include "Wallet.h"
|
||||
#include "Application.h"
|
||||
#include "ui/ImageProvider.h"
|
||||
|
||||
#include <PathUtils.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QCryptographicHash>
|
||||
#include <QQmlContext>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
@ -23,8 +28,10 @@
|
|||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/aes.h>
|
||||
|
||||
static const char* KEY_FILE = "hifikey";
|
||||
static const char* IMAGE_FILE = "hifi_image"; // eventually this will live in keyfile
|
||||
|
||||
void initialize() {
|
||||
static bool initialized = false;
|
||||
|
@ -40,18 +47,30 @@ QString keyFilePath() {
|
|||
return PathUtils::getAppDataFilePath(KEY_FILE);
|
||||
}
|
||||
|
||||
// for now the callback function just returns the same string. Later we can hook
|
||||
// this to the gui (some thought required)
|
||||
QString imageFilePath() {
|
||||
return PathUtils::getAppDataFilePath(IMAGE_FILE);
|
||||
}
|
||||
|
||||
// use the cached _passphrase if it exists, otherwise we need to prompt
|
||||
int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
|
||||
// just return a hardcoded pwd for now
|
||||
static const char* pwd = "pwd";
|
||||
strcpy(password, pwd);
|
||||
return static_cast<int>(strlen(pwd));
|
||||
auto passphrase = DependencyManager::get<Wallet>()->getPassphrase();
|
||||
if (passphrase) {
|
||||
strcpy(password, passphrase->toLocal8Bit().constData());
|
||||
return static_cast<int>(passphrase->size());
|
||||
} else {
|
||||
// ok gotta bring up modal dialog... But right now lets just
|
||||
// just keep it empty
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// BEGIN copied code - this will be removed/changed at some point soon
|
||||
// copied (without emits for various signals) from libraries/networking/src/RSAKeypairGenerator.cpp.
|
||||
// We will have a different implementation in practice, but this gives us a start for now
|
||||
//
|
||||
// NOTE: we don't really use the private keys returned - we can see how this evolves, but probably
|
||||
// we should just return a list of public keys?
|
||||
QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
|
||||
|
||||
RSA* keyPair = RSA_new();
|
||||
|
@ -106,9 +125,11 @@ QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
|
|||
|
||||
|
||||
// now lets persist them to files
|
||||
// TODO: figure out a scheme for multiple keys, etc...
|
||||
// FIXME: for now I'm appending to the file if it exists. As long as we always put
|
||||
// the keys in the same order, this works fine. TODO: verify this will skip over
|
||||
// anything else (like an embedded image)
|
||||
FILE* fp;
|
||||
if ((fp = fopen(keyFilePath().toStdString().c_str(), "wt"))) {
|
||||
if ((fp = fopen(keyFilePath().toStdString().c_str(), "at"))) {
|
||||
if (!PEM_write_RSAPublicKey(fp, keyPair)) {
|
||||
fclose(fp);
|
||||
qCDebug(commerce) << "failed to write public key";
|
||||
|
@ -125,7 +146,9 @@ QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
|
|||
|
||||
RSA_free(keyPair);
|
||||
|
||||
// prepare the return values
|
||||
// prepare the return values. TODO: Fix this - we probably don't really even want the
|
||||
// private key at all (better to read it when we need it?). Or maybe we do, when we have
|
||||
// multiple keys?
|
||||
retval.first = new QByteArray(reinterpret_cast<char*>(publicKeyDER), publicKeyLength ),
|
||||
retval.second = new QByteArray(reinterpret_cast<char*>(privateKeyDER), privateKeyLength );
|
||||
|
||||
|
@ -192,6 +215,126 @@ RSA* readPrivateKey(const char* filename) {
|
|||
return key;
|
||||
}
|
||||
|
||||
static const unsigned char IVEC[16] = "IAmAnIVecYay123";
|
||||
|
||||
void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) {
|
||||
// first ivec
|
||||
memcpy(ivec, IVEC, 16);
|
||||
auto hash = QCryptographicHash::hash(salt, QCryptographicHash::Md5);
|
||||
memcpy(ckey, hash.data(), 16);
|
||||
}
|
||||
|
||||
void Wallet::setPassphrase(const QString& passphrase) {
|
||||
if (_passphrase) {
|
||||
delete _passphrase;
|
||||
}
|
||||
_passphrase = new QString(passphrase);
|
||||
}
|
||||
|
||||
// encrypt some stuff
|
||||
bool Wallet::encryptFile(const QString& inputFilePath, const QString& outputFilePath) {
|
||||
// aes requires a couple 128-bit keys (ckey and ivec). For now, I'll just
|
||||
// use the md5 of the salt as the ckey (md5 is 128-bit), and ivec will be
|
||||
// a constant. We can review this later - there are ways to generate keys
|
||||
// from a password that may be better.
|
||||
unsigned char ivec[16];
|
||||
unsigned char ckey[16];
|
||||
|
||||
initializeAESKeys(ivec, ckey, _salt);
|
||||
|
||||
int tempSize, outSize;
|
||||
|
||||
// read entire unencrypted file into memory
|
||||
QFile inputFile(inputFilePath);
|
||||
if (!inputFile.exists()) {
|
||||
qCDebug(commerce) << "cannot encrypt" << inputFilePath << "file doesn't exist";
|
||||
return false;
|
||||
}
|
||||
inputFile.open(QIODevice::ReadOnly);
|
||||
QByteArray inputFileBuffer = inputFile.readAll();
|
||||
inputFile.close();
|
||||
|
||||
// reserve enough capacity for encrypted bytes
|
||||
unsigned char* outputFileBuffer = new unsigned char[inputFileBuffer.size() + AES_BLOCK_SIZE];
|
||||
|
||||
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
|
||||
|
||||
// TODO: add error handling!!!
|
||||
if (!EVP_EncryptInit_ex(ctx, EVP_des_ede3_cbc(), NULL, ckey, ivec)) {
|
||||
qCDebug(commerce) << "encrypt init failure";
|
||||
delete[] outputFileBuffer;
|
||||
return false;
|
||||
}
|
||||
if (!EVP_EncryptUpdate(ctx, outputFileBuffer, &tempSize, (unsigned char*)inputFileBuffer.data(), inputFileBuffer.size())) {
|
||||
qCDebug(commerce) << "encrypt update failure";
|
||||
delete[] outputFileBuffer;
|
||||
return false;
|
||||
}
|
||||
outSize = tempSize;
|
||||
if (!EVP_EncryptFinal_ex(ctx, outputFileBuffer + outSize, &tempSize)) {
|
||||
qCDebug(commerce) << "encrypt final failure";
|
||||
delete[] outputFileBuffer;
|
||||
return false;
|
||||
}
|
||||
|
||||
outSize += tempSize;
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
qCDebug(commerce) << "encrypted buffer size" << outSize;
|
||||
QByteArray output((const char*)outputFileBuffer, outSize);
|
||||
QFile outputFile(outputFilePath);
|
||||
outputFile.open(QIODevice::WriteOnly);
|
||||
outputFile.write(output);
|
||||
outputFile.close();
|
||||
|
||||
delete[] outputFileBuffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Wallet::decryptFile(const QString& inputFilePath, unsigned char** outputBufferPtr, int* outputBufferSize) {
|
||||
unsigned char ivec[16];
|
||||
unsigned char ckey[16];
|
||||
initializeAESKeys(ivec, ckey, _salt);
|
||||
|
||||
// read encrypted file
|
||||
QFile inputFile(inputFilePath);
|
||||
if (!inputFile.exists()) {
|
||||
qCDebug(commerce) << "cannot decrypt file" << inputFilePath << "it doesn't exist";
|
||||
return false;
|
||||
}
|
||||
inputFile.open(QIODevice::ReadOnly);
|
||||
QByteArray encryptedBuffer = inputFile.readAll();
|
||||
inputFile.close();
|
||||
|
||||
// setup decrypted buffer
|
||||
unsigned char* outputBuffer = new unsigned char[encryptedBuffer.size()];
|
||||
int tempSize;
|
||||
|
||||
// TODO: add error handling
|
||||
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
|
||||
if (!EVP_DecryptInit_ex(ctx, EVP_des_ede3_cbc(), NULL, ckey, ivec)) {
|
||||
qCDebug(commerce) << "decrypt init failure";
|
||||
delete[] outputBuffer;
|
||||
return false;
|
||||
}
|
||||
if (!EVP_DecryptUpdate(ctx, outputBuffer, &tempSize, (unsigned char*)encryptedBuffer.data(), encryptedBuffer.size())) {
|
||||
qCDebug(commerce) << "decrypt update failure";
|
||||
delete[] outputBuffer;
|
||||
return false;
|
||||
}
|
||||
*outputBufferSize = tempSize;
|
||||
if (!EVP_DecryptFinal_ex(ctx, outputBuffer + tempSize, &tempSize)) {
|
||||
qCDebug(commerce) << "decrypt final failure";
|
||||
delete[] outputBuffer;
|
||||
return false;
|
||||
}
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
*outputBufferSize += tempSize;
|
||||
*outputBufferPtr = outputBuffer;
|
||||
qCDebug(commerce) << "decrypted buffer size" << *outputBufferSize;
|
||||
delete[] outputBuffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Wallet::createIfNeeded() {
|
||||
if (_publicKeys.count() > 0) return false;
|
||||
|
||||
|
@ -205,7 +348,7 @@ bool Wallet::createIfNeeded() {
|
|||
qCDebug(commerce) << "read private key";
|
||||
RSA_free(key);
|
||||
// K -- add the public key since we have a legit private key associated with it
|
||||
_publicKeys.push_back(publicKey.toBase64());
|
||||
_publicKeys.push_back(QUrl::toPercentEncoding(publicKey.toBase64()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -228,6 +371,7 @@ bool Wallet::generateKeyPair() {
|
|||
auto ledger = DependencyManager::get<Ledger>();
|
||||
return ledger->receiveAt(key, oldKey);
|
||||
}
|
||||
|
||||
QStringList Wallet::listPublicKeys() {
|
||||
qCInfo(commerce) << "Enumerating public keys.";
|
||||
createIfNeeded();
|
||||
|
@ -268,10 +412,65 @@ QString Wallet::signWithKey(const QByteArray& text, const QString& key) {
|
|||
}
|
||||
|
||||
|
||||
void Wallet::chooseSecurityImage(uint imageID) {
|
||||
_chosenSecurityImage = (SecurityImage)imageID;
|
||||
emit securityImageResult(imageID);
|
||||
void Wallet::chooseSecurityImage(const QString& filename) {
|
||||
|
||||
if (_securityImage) {
|
||||
delete _securityImage;
|
||||
}
|
||||
// temporary...
|
||||
QString path = qApp->applicationDirPath();
|
||||
path.append("/resources/qml/hifi/commerce/");
|
||||
path.append(filename);
|
||||
// now create a new security image pixmap
|
||||
_securityImage = new QPixmap();
|
||||
|
||||
qCDebug(commerce) << "loading data for pixmap from" << path;
|
||||
_securityImage->load(path);
|
||||
|
||||
// encrypt it and save.
|
||||
if (encryptFile(path, imageFilePath())) {
|
||||
qCDebug(commerce) << "emitting pixmap";
|
||||
|
||||
// inform the image provider
|
||||
auto engine = DependencyManager::get<OffscreenUi>()->getSurfaceContext()->engine();
|
||||
auto imageProvider = reinterpret_cast<ImageProvider*>(engine->imageProvider(ImageProvider::PROVIDER_NAME));
|
||||
imageProvider->setSecurityImage(_securityImage);
|
||||
|
||||
emit securityImageResult(true);
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to encrypt security image";
|
||||
emit securityImageResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Wallet::getSecurityImage() {
|
||||
emit securityImageResult(_chosenSecurityImage);
|
||||
unsigned char* data;
|
||||
int dataLen;
|
||||
|
||||
// if already decrypted, don't do it again
|
||||
if (_securityImage) {
|
||||
emit securityImageResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// decrypt and return
|
||||
if (decryptFile(imageFilePath(), &data, &dataLen)) {
|
||||
// create the pixmap
|
||||
_securityImage = new QPixmap();
|
||||
_securityImage->loadFromData(data, dataLen, "jpg");
|
||||
qCDebug(commerce) << "created pixmap from encrypted file";
|
||||
|
||||
// inform the image provider
|
||||
auto engine = DependencyManager::get<OffscreenUi>()->getSurfaceContext()->engine();
|
||||
auto imageProvider = reinterpret_cast<ImageProvider*>(engine->imageProvider(ImageProvider::PROVIDER_NAME));
|
||||
imageProvider->setSecurityImage(_securityImage);
|
||||
|
||||
emit securityImageResult(true);
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to decrypt security image (maybe none saved yet?)";
|
||||
emit securityImageResult(false);
|
||||
}
|
||||
}
|
||||
void Wallet::getKeyFilePath() {
|
||||
emit keyFilePathResult(keyFilePath());
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include <QPixmap>
|
||||
|
||||
class Wallet : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
@ -26,16 +28,21 @@ public:
|
|||
bool generateKeyPair();
|
||||
QStringList listPublicKeys();
|
||||
QString signWithKey(const QByteArray& text, const QString& key);
|
||||
void chooseSecurityImage(uint imageID);
|
||||
void chooseSecurityImage(const QString& imageFile);
|
||||
void getSecurityImage();
|
||||
void getKeyFilePath();
|
||||
|
||||
void setSalt(const QByteArray& salt) { _salt = salt; }
|
||||
QByteArray getSalt() { return _salt; }
|
||||
|
||||
void setPassphrase(const QString& passphrase);
|
||||
QString* getPassphrase() { return _passphrase; }
|
||||
|
||||
signals:
|
||||
void securityImageResult(uint imageID);
|
||||
void securityImageResult(bool exists) ;
|
||||
void keyFilePathResult(const QString& path);
|
||||
|
||||
protected:
|
||||
// ALWAYS add SecurityImage enum values to the END of the enum.
|
||||
// They must be in the same order as the images are listed in
|
||||
// SecurityImageSelection.qml
|
||||
enum SecurityImage {
|
||||
NONE = 0,
|
||||
Cat,
|
||||
|
@ -48,7 +55,12 @@ protected:
|
|||
|
||||
private:
|
||||
QStringList _publicKeys{};
|
||||
SecurityImage _chosenSecurityImage = SecurityImage::NONE;
|
||||
QPixmap* _securityImage { nullptr };
|
||||
QByteArray _salt {"iamsalt!"};
|
||||
QString* _passphrase { new QString("pwd") };
|
||||
|
||||
bool encryptFile(const QString& inputFilePath, const QString& outputFilePath);
|
||||
bool decryptFile(const QString& inputFilePath, unsigned char** outputBufferPtr, int* outputBufferLen);
|
||||
};
|
||||
|
||||
#endif // hifi_Wallet_h
|
||||
|
|
|
@ -64,6 +64,7 @@ public:
|
|||
// You cannot use editRenderState to change the overlay type of any part of the laser pointer. You can only edit the properties of the existing overlays.
|
||||
void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps);
|
||||
|
||||
void setPrecisionPicking(const bool precisionPicking) { DependencyManager::get<RayPickScriptingInterface>()->setPrecisionPicking(_rayPickUID, precisionPicking); }
|
||||
void setIgnoreEntities(const QScriptValue& ignoreEntities) { DependencyManager::get<RayPickScriptingInterface>()->setIgnoreEntities(_rayPickUID, ignoreEntities); }
|
||||
void setIncludeEntities(const QScriptValue& includeEntities) { DependencyManager::get<RayPickScriptingInterface>()->setIncludeEntities(_rayPickUID, includeEntities); }
|
||||
void setIgnoreOverlays(const QScriptValue& ignoreOverlays) { DependencyManager::get<RayPickScriptingInterface>()->setIgnoreOverlays(_rayPickUID, ignoreOverlays); }
|
||||
|
|
|
@ -97,6 +97,14 @@ void LaserPointerManager::update() {
|
|||
}
|
||||
}
|
||||
|
||||
void LaserPointerManager::setPrecisionPicking(QUuid uid, const bool precisionPicking) {
|
||||
QReadLocker lock(&_containsLock);
|
||||
if (_laserPointers.contains(uid)) {
|
||||
QWriteLocker laserLock(_laserPointerLocks[uid].get());
|
||||
_laserPointers[uid]->setPrecisionPicking(precisionPicking);
|
||||
}
|
||||
}
|
||||
|
||||
void LaserPointerManager::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) {
|
||||
QReadLocker lock(&_containsLock);
|
||||
if (_laserPointers.contains(uid)) {
|
||||
|
|
|
@ -31,6 +31,7 @@ public:
|
|||
void editRenderState(QUuid uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps);
|
||||
const RayPickResult getPrevRayPickResult(const QUuid uid);
|
||||
|
||||
void setPrecisionPicking(QUuid uid, const bool precisionPicking);
|
||||
void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities);
|
||||
void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities);
|
||||
void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays);
|
||||
|
|
|
@ -30,6 +30,7 @@ public slots:
|
|||
Q_INVOKABLE void setRenderState(QUuid uid, const QString& renderState) { qApp->getLaserPointerManager().setRenderState(uid, renderState.toStdString()); }
|
||||
Q_INVOKABLE RayPickResult getPrevRayPickResult(QUuid uid) { return qApp->getLaserPointerManager().getPrevRayPickResult(uid); }
|
||||
|
||||
Q_INVOKABLE void setPrecisionPicking(QUuid uid, const bool precisionPicking) { qApp->getLaserPointerManager().setPrecisionPicking(uid, precisionPicking); }
|
||||
Q_INVOKABLE void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) { qApp->getLaserPointerManager().setIgnoreEntities(uid, ignoreEntities); }
|
||||
Q_INVOKABLE void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) { qApp->getLaserPointerManager().setIncludeEntities(uid, includeEntities); }
|
||||
Q_INVOKABLE void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) { qApp->getLaserPointerManager().setIgnoreOverlays(uid, ignoreOverlays); }
|
||||
|
|
|
@ -41,12 +41,14 @@ public:
|
|||
// The key is the Flags
|
||||
Flags _flags;
|
||||
|
||||
RayPickFilter() : _flags(PICK_NOTHING) {}
|
||||
RayPickFilter() : _flags(getBitMask(PICK_NOTHING)) {}
|
||||
RayPickFilter(const Flags& flags) : _flags(flags) {}
|
||||
|
||||
bool operator== (const RayPickFilter& rhs) const { return _flags == rhs._flags; }
|
||||
bool operator!= (const RayPickFilter& rhs) const { return _flags != rhs._flags; }
|
||||
|
||||
void setFlag(FlagBit flag, bool value) { _flags[flag] = value; }
|
||||
|
||||
bool doesPickNothing() const { return _flags[PICK_NOTHING]; }
|
||||
bool doesPickEntities() const { return _flags[PICK_ENTITIES]; }
|
||||
bool doesPickOverlays() const { return _flags[PICK_OVERLAYS]; }
|
||||
|
@ -61,27 +63,27 @@ public:
|
|||
|
||||
// Helpers for RayPickManager
|
||||
Flags getEntityFlags() const {
|
||||
Flags toReturn(PICK_ENTITIES);
|
||||
unsigned int toReturn = getBitMask(PICK_ENTITIES);
|
||||
if (doesPickInvisible()) {
|
||||
toReturn |= Flags(PICK_INCLUDE_INVISIBLE);
|
||||
toReturn |= getBitMask(PICK_INCLUDE_INVISIBLE);
|
||||
}
|
||||
if (doesPickNonCollidable()) {
|
||||
toReturn |= Flags(PICK_INCLUDE_NONCOLLIDABLE);
|
||||
toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE);
|
||||
}
|
||||
return toReturn;
|
||||
return Flags(toReturn);
|
||||
}
|
||||
Flags getOverlayFlags() const {
|
||||
Flags toReturn(PICK_OVERLAYS);
|
||||
unsigned int toReturn = getBitMask(PICK_OVERLAYS);
|
||||
if (doesPickInvisible()) {
|
||||
toReturn |= Flags(PICK_INCLUDE_INVISIBLE);
|
||||
toReturn |= getBitMask(PICK_INCLUDE_INVISIBLE);
|
||||
}
|
||||
if (doesPickNonCollidable()) {
|
||||
toReturn |= Flags(PICK_INCLUDE_NONCOLLIDABLE);
|
||||
toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE);
|
||||
}
|
||||
return toReturn;
|
||||
return Flags(toReturn);
|
||||
}
|
||||
Flags getAvatarFlags() const { return Flags(PICK_AVATARS); }
|
||||
Flags getHUDFlags() const { return Flags(PICK_HUD); }
|
||||
Flags getAvatarFlags() const { return Flags(getBitMask(PICK_AVATARS)); }
|
||||
Flags getHUDFlags() const { return Flags(getBitMask(PICK_HUD)); }
|
||||
|
||||
static unsigned int getBitMask(FlagBit bit) { return 1 << bit; }
|
||||
|
||||
|
@ -98,10 +100,12 @@ public:
|
|||
void disable() { _enabled = false; }
|
||||
|
||||
const RayPickFilter& getFilter() { return _filter; }
|
||||
const float& getMaxDistance() { return _maxDistance; }
|
||||
const bool& isEnabled() { return _enabled; }
|
||||
float getMaxDistance() { return _maxDistance; }
|
||||
bool isEnabled() { return _enabled; }
|
||||
const RayPickResult& getPrevRayPickResult() { return _prevResult; }
|
||||
|
||||
void setPrecisionPicking(bool precisionPicking) { _filter.setFlag(RayPickFilter::PICK_COURSE, !precisionPicking); }
|
||||
|
||||
void setRayPickResult(const RayPickResult& rayPickResult) { _prevResult = rayPickResult; }
|
||||
|
||||
const QVector<EntityItemID>& getIgnoreEntites() { return _ignoreEntities; }
|
||||
|
|
|
@ -64,10 +64,11 @@ void RayPickManager::update() {
|
|||
RayToEntityIntersectionResult entityRes;
|
||||
bool fromCache = true;
|
||||
bool invisible = rayPick->getFilter().doesPickInvisible();
|
||||
bool noncollidable = rayPick->getFilter().doesPickNonCollidable();
|
||||
bool nonCollidable = rayPick->getFilter().doesPickNonCollidable();
|
||||
RayPickFilter::Flags entityMask = rayPick->getFilter().getEntityFlags();
|
||||
if (!checkAndCompareCachedResults(rayKey, results, res, entityMask)) {
|
||||
entityRes = DependencyManager::get<EntityScriptingInterface>()->findRayIntersectionVector(ray, true, rayPick->getIncludeEntites(), rayPick->getIgnoreEntites(), !invisible, !noncollidable);
|
||||
entityRes = DependencyManager::get<EntityScriptingInterface>()->findRayIntersectionVector(ray, !rayPick->getFilter().doesPickCourse(),
|
||||
rayPick->getIncludeEntites(), rayPick->getIgnoreEntites(), !invisible, !nonCollidable);
|
||||
fromCache = false;
|
||||
}
|
||||
|
||||
|
@ -81,10 +82,11 @@ void RayPickManager::update() {
|
|||
RayToOverlayIntersectionResult overlayRes;
|
||||
bool fromCache = true;
|
||||
bool invisible = rayPick->getFilter().doesPickInvisible();
|
||||
bool noncollidable = rayPick->getFilter().doesPickNonCollidable();
|
||||
bool nonCollidable = rayPick->getFilter().doesPickNonCollidable();
|
||||
RayPickFilter::Flags overlayMask = rayPick->getFilter().getOverlayFlags();
|
||||
if (!checkAndCompareCachedResults(rayKey, results, res, overlayMask)) {
|
||||
overlayRes = qApp->getOverlays().findRayIntersectionVector(ray, true, rayPick->getIncludeOverlays(), rayPick->getIgnoreOverlays(), !invisible, !noncollidable);
|
||||
overlayRes = qApp->getOverlays().findRayIntersectionVector(ray, !rayPick->getFilter().doesPickCourse(),
|
||||
rayPick->getIncludeOverlays(), rayPick->getIgnoreOverlays(), !invisible, !nonCollidable);
|
||||
fromCache = false;
|
||||
}
|
||||
|
||||
|
@ -204,6 +206,14 @@ const RayPickResult RayPickManager::getPrevRayPickResult(const QUuid uid) {
|
|||
return RayPickResult();
|
||||
}
|
||||
|
||||
void RayPickManager::setPrecisionPicking(QUuid uid, const bool precisionPicking) {
|
||||
QReadLocker containsLock(&_containsLock);
|
||||
if (_rayPicks.contains(uid)) {
|
||||
QWriteLocker lock(_rayPickLocks[uid].get());
|
||||
_rayPicks[uid]->setPrecisionPicking(precisionPicking);
|
||||
}
|
||||
}
|
||||
|
||||
void RayPickManager::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) {
|
||||
QReadLocker containsLock(&_containsLock);
|
||||
if (_rayPicks.contains(uid)) {
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
void disableRayPick(const QUuid uid);
|
||||
const RayPickResult getPrevRayPickResult(const QUuid uid);
|
||||
|
||||
void setPrecisionPicking(QUuid uid, const bool precisionPicking);
|
||||
void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities);
|
||||
void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities);
|
||||
void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays);
|
||||
|
|
|
@ -82,6 +82,10 @@ RayPickResult RayPickScriptingInterface::getPrevRayPickResult(QUuid uid) {
|
|||
return qApp->getRayPickManager().getPrevRayPickResult(uid);
|
||||
}
|
||||
|
||||
void RayPickScriptingInterface::setPrecisionPicking(QUuid uid, const bool precisionPicking) {
|
||||
qApp->getRayPickManager().setPrecisionPicking(uid, precisionPicking);
|
||||
}
|
||||
|
||||
void RayPickScriptingInterface::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) {
|
||||
qApp->getRayPickManager().setIgnoreEntities(uid, ignoreEntities);
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ public slots:
|
|||
Q_INVOKABLE void removeRayPick(QUuid uid);
|
||||
Q_INVOKABLE RayPickResult getPrevRayPickResult(QUuid uid);
|
||||
|
||||
Q_INVOKABLE void setPrecisionPicking(QUuid uid, const bool precisionPicking);
|
||||
Q_INVOKABLE void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities);
|
||||
Q_INVOKABLE void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities);
|
||||
Q_INVOKABLE void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays);
|
||||
|
@ -50,7 +51,6 @@ public slots:
|
|||
Q_INVOKABLE void setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars);
|
||||
Q_INVOKABLE void setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars);
|
||||
|
||||
private:
|
||||
unsigned int PICK_NOTHING() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_NOTHING); }
|
||||
unsigned int PICK_ENTITIES() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_ENTITIES); }
|
||||
unsigned int PICK_OVERLAYS() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_OVERLAYS); }
|
||||
|
|
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
|
||||
|
|
@ -264,7 +264,7 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
const render::ShapeKey Circle3DOverlay::getShapeKey() {
|
||||
auto builder = render::ShapeKey::Builder().withoutCullFace().withUnlit();
|
||||
if (getAlpha() != 1.0f) {
|
||||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
if (!getIsSolid() || shouldDrawHUDLayer()) {
|
||||
|
|
|
@ -117,7 +117,7 @@ void Cube3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
const render::ShapeKey Cube3DOverlay::getShapeKey() {
|
||||
auto builder = render::ShapeKey::Builder();
|
||||
if (getAlpha() != 1.0f) {
|
||||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
if (!getIsSolid() || shouldDrawHUDLayer()) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "GeometryUtil.h"
|
||||
|
||||
#include "AbstractViewStateInterface.h"
|
||||
|
||||
QString const Image3DOverlay::TYPE = "image3d";
|
||||
|
||||
|
@ -58,12 +59,29 @@ void Image3DOverlay::render(RenderArgs* args) {
|
|||
if (!_isLoaded) {
|
||||
_isLoaded = true;
|
||||
_texture = DependencyManager::get<TextureCache>()->getTexture(_url);
|
||||
_textureIsLoaded = false;
|
||||
}
|
||||
|
||||
if (!_visible || !getParentVisible() || !_texture || !_texture->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Once the texture has loaded, check if we need to update the render item because of transparency
|
||||
if (!_textureIsLoaded && _texture && _texture->getGPUTexture()) {
|
||||
_textureIsLoaded = true;
|
||||
bool prevAlphaTexture = _alphaTexture;
|
||||
_alphaTexture = _texture->getGPUTexture()->getUsage().isAlpha();
|
||||
if (_alphaTexture != prevAlphaTexture) {
|
||||
auto itemID = getRenderItemID();
|
||||
if (render::Item::isValidID(itemID)) {
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
transaction.updateItem(itemID);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch* batch = args->_batch;
|
||||
|
||||
|
@ -92,9 +110,9 @@ void Image3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
glm::vec2 topLeft(-x, -y);
|
||||
glm::vec2 bottomRight(x, y);
|
||||
glm::vec2 texCoordTopLeft(fromImage.x() / imageWidth, fromImage.y() / imageHeight);
|
||||
glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width()) / imageWidth,
|
||||
(fromImage.y() + fromImage.height()) / imageHeight);
|
||||
glm::vec2 texCoordTopLeft((fromImage.x() + 0.5f) / imageWidth, (fromImage.y() + 0.5f) / imageHeight);
|
||||
glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth,
|
||||
(fromImage.y() + fromImage.height() - 0.5f) / imageHeight);
|
||||
|
||||
const float MAX_COLOR = 255.0f;
|
||||
xColor color = getColor();
|
||||
|
@ -126,7 +144,7 @@ const render::ShapeKey Image3DOverlay::getShapeKey() {
|
|||
if (_emissive || shouldDrawHUDLayer()) {
|
||||
builder.withUnlit();
|
||||
}
|
||||
if (getAlpha() != 1.0f) {
|
||||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
return builder.build();
|
||||
|
|
|
@ -39,6 +39,7 @@ public:
|
|||
|
||||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
bool isTransparent() override { return Base3DOverlay::isTransparent() || _alphaTexture; }
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal) override;
|
||||
|
@ -48,6 +49,8 @@ public:
|
|||
private:
|
||||
QString _url;
|
||||
NetworkTexturePointer _texture;
|
||||
bool _textureIsLoaded { false };
|
||||
bool _alphaTexture { false };
|
||||
bool _emissive { false };
|
||||
|
||||
QRect _fromImage; // where from in the image to sample
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <GeometryCache.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include "AbstractViewStateInterface.h"
|
||||
|
||||
QString const Line3DOverlay::TYPE = "line3d";
|
||||
|
||||
|
@ -149,7 +150,7 @@ void Line3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
const render::ShapeKey Line3DOverlay::getShapeKey() {
|
||||
auto builder = render::ShapeKey::Builder().withOwnPipeline();
|
||||
if (getAlpha() != 1.0f || _glow > 0.0f) {
|
||||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
return builder.build();
|
||||
|
@ -222,9 +223,17 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
|
||||
auto glow = properties["glow"];
|
||||
if (glow.isValid()) {
|
||||
float prevGlow = _glow;
|
||||
setGlow(glow.toFloat());
|
||||
if (_glow > 0.0f) {
|
||||
_alpha = 0.5f;
|
||||
// Update our payload key if necessary to handle transparency
|
||||
if ((prevGlow <= 0.0f && _glow > 0.0f) || (prevGlow > 0.0f && _glow <= 0.0f)) {
|
||||
auto itemID = getRenderItemID();
|
||||
if (render::Item::isValidID(itemID)) {
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
transaction.updateItem(itemID);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
|
||||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
bool isTransparent() override { return Base3DOverlay::isTransparent() || _glow > 0.0f; }
|
||||
|
||||
virtual Line3DOverlay* createClone() const override;
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ public:
|
|||
bool isLoaded() { return _isLoaded; }
|
||||
bool getVisible() const { return _visible; }
|
||||
bool shouldDrawHUDLayer() const { return _drawHUDLayer; }
|
||||
virtual bool isTransparent() { return getAlphaPulse() != 0.0f || getAlpha() != 1.0f; };
|
||||
xColor getColor();
|
||||
float getAlpha();
|
||||
Anchor getAnchor() const { return _anchor; }
|
||||
|
|
|
@ -300,7 +300,12 @@ OverlayID Overlays::cloneOverlay(OverlayID id) {
|
|||
}
|
||||
|
||||
bool Overlays::editOverlay(OverlayID id, const QVariant& properties) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
auto thisOverlay = getOverlay(id);
|
||||
if (!thisOverlay) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!thisOverlay->is3D() && QThread::currentThread() != thread()) {
|
||||
// NOTE editOverlay can be called very frequently in scripts and can't afford to
|
||||
// block waiting on the main thread. Additionally, no script actually
|
||||
// examines the return value and does something useful with it, so use a non-blocking
|
||||
|
@ -309,22 +314,15 @@ bool Overlays::editOverlay(OverlayID id, const QVariant& properties) {
|
|||
return true;
|
||||
}
|
||||
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
if (thisOverlay) {
|
||||
thisOverlay->setProperties(properties.toMap());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
thisOverlay->setProperties(properties.toMap());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Overlays::editOverlays(const QVariant& propertiesById) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
// NOTE see comment on editOverlay for why this is not a blocking call
|
||||
QMetaObject::invokeMethod(this, "editOverlays", Q_ARG(QVariant, propertiesById));
|
||||
return true;
|
||||
}
|
||||
bool defer2DOverlays = QThread::currentThread() != thread();
|
||||
|
||||
QVariantMap map = propertiesById.toMap();
|
||||
QVariantMap deferrred;
|
||||
const QVariantMap map = propertiesById.toMap();
|
||||
bool success = true;
|
||||
for (const auto& key : map.keys()) {
|
||||
OverlayID id = OverlayID(key);
|
||||
|
@ -333,9 +331,21 @@ bool Overlays::editOverlays(const QVariant& propertiesById) {
|
|||
success = false;
|
||||
continue;
|
||||
}
|
||||
QVariant properties = map[key];
|
||||
|
||||
const QVariant& properties = map[key];
|
||||
if (defer2DOverlays && !thisOverlay->is3D()) {
|
||||
deferrred[key] = properties;
|
||||
continue;
|
||||
}
|
||||
thisOverlay->setProperties(properties.toMap());
|
||||
}
|
||||
|
||||
// For 2D/QML overlays, we need to perform the edit on the main thread
|
||||
if (defer2DOverlays && !deferrred.empty()) {
|
||||
// NOTE see comment on editOverlay for why this is not a blocking call
|
||||
QMetaObject::invokeMethod(this, "editOverlays", Q_ARG(QVariant, deferrred));
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace render {
|
|||
if (std::static_pointer_cast<Base3DOverlay>(overlay)->getDrawInFront()) {
|
||||
builder.withLayered();
|
||||
}
|
||||
if (overlay->getAlphaPulse() != 0.0f || overlay->getAlpha() != 1.0f) {
|
||||
if (overlay->isTransparent()) {
|
||||
builder.withTransparent();
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -110,7 +110,7 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
const render::ShapeKey Rectangle3DOverlay::getShapeKey() {
|
||||
auto builder = render::ShapeKey::Builder().withOwnPipeline();
|
||||
if (getAlpha() != 1.0f) {
|
||||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
return builder.build();
|
||||
|
|
|
@ -62,7 +62,7 @@ void Shape3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
const render::ShapeKey Shape3DOverlay::getShapeKey() {
|
||||
auto builder = render::ShapeKey::Builder();
|
||||
if (getAlpha() != 1.0f) {
|
||||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
if (!getIsSolid() || shouldDrawHUDLayer()) {
|
||||
|
|
|
@ -59,7 +59,7 @@ void Sphere3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
const render::ShapeKey Sphere3DOverlay::getShapeKey() {
|
||||
auto builder = render::ShapeKey::Builder();
|
||||
if (getAlpha() != 1.0f) {
|
||||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
if (!getIsSolid() || shouldDrawHUDLayer()) {
|
||||
|
|
|
@ -143,7 +143,7 @@ void Text3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
const render::ShapeKey Text3DOverlay::getShapeKey() {
|
||||
auto builder = render::ShapeKey::Builder();
|
||||
if (getAlpha() != 1.0f) {
|
||||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
return builder.build();
|
||||
|
|
|
@ -318,8 +318,8 @@ void Web3DOverlay::render(RenderArgs* args) {
|
|||
}
|
||||
|
||||
const render::ShapeKey Web3DOverlay::getShapeKey() {
|
||||
auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias();
|
||||
if (getAlpha() != 1.0f) {
|
||||
auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias().withOwnPipeline();
|
||||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
return builder.build();
|
||||
|
|
|
@ -32,6 +32,10 @@
|
|||
#include "AnimUtil.h"
|
||||
#include "IKTarget.h"
|
||||
|
||||
static int nextRigId = 1;
|
||||
static std::map<int, Rig*> rigRegistry;
|
||||
static std::mutex rigRegistryMutex;
|
||||
|
||||
static bool isEqual(const glm::vec3& u, const glm::vec3& v) {
|
||||
const float EPSILON = 0.0001f;
|
||||
return glm::length(u - v) / glm::length(u) <= EPSILON;
|
||||
|
@ -49,6 +53,26 @@ const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 0.9f, 0.0f);
|
|||
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 0.9f, 0.0f);
|
||||
const glm::vec3 DEFAULT_HEAD_POS(0.0f, 0.75f, 0.0f);
|
||||
|
||||
Rig::Rig() {
|
||||
// Ensure thread-safe access to the rigRegistry.
|
||||
std::lock_guard<std::mutex> guard(rigRegistryMutex);
|
||||
|
||||
// Insert this newly allocated rig into the rig registry
|
||||
_rigId = nextRigId;
|
||||
rigRegistry[_rigId] = this;
|
||||
nextRigId++;
|
||||
}
|
||||
|
||||
Rig::~Rig() {
|
||||
// Ensure thread-safe access to the rigRegstry, but also prevent the rig from being deleted
|
||||
// while Rig::animationStateHandlerResult is being invoked on a script thread.
|
||||
std::lock_guard<std::mutex> guard(rigRegistryMutex);
|
||||
auto iter = rigRegistry.find(_rigId);
|
||||
if (iter != rigRegistry.end()) {
|
||||
rigRegistry.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
|
||||
|
||||
UserAnimState::ClipNodeEnum clipNodeEnum;
|
||||
|
@ -935,8 +959,19 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh
|
|||
int identifier = data.key();
|
||||
StateHandler& value = data.value();
|
||||
QScriptValue& function = value.function;
|
||||
auto handleResult = [this, identifier](QScriptValue result) { // called in script thread to get the result back to us.
|
||||
animationStateHandlerResult(identifier, result);
|
||||
int rigId = _rigId;
|
||||
auto handleResult = [rigId, identifier](QScriptValue result) { // called in script thread to get the result back to us.
|
||||
// Hold the rigRegistryMutex to ensure thread-safe access to the rigRegistry, but
|
||||
// also to prevent the rig from being deleted while this lambda is being executed.
|
||||
std::lock_guard<std::mutex> guard(rigRegistryMutex);
|
||||
|
||||
// if the rig pointer is in the registry, it has not been deleted yet.
|
||||
auto iter = rigRegistry.find(rigId);
|
||||
if (iter != rigRegistry.end()) {
|
||||
Rig* rig = iter->second;
|
||||
assert(rig);
|
||||
rig->animationStateHandlerResult(identifier, result);
|
||||
}
|
||||
};
|
||||
// invokeMethod makes a copy of the args, and copies of AnimVariantMap do copy the underlying map, so this will correctly capture
|
||||
// the state of _animVars and allow continued changes to _animVars in this thread without conflict.
|
||||
|
|
|
@ -97,8 +97,8 @@ public:
|
|||
Hover
|
||||
};
|
||||
|
||||
Rig() {}
|
||||
virtual ~Rig() {}
|
||||
Rig();
|
||||
virtual ~Rig();
|
||||
|
||||
void destroyAnimGraph();
|
||||
|
||||
|
@ -372,6 +372,8 @@ protected:
|
|||
|
||||
glm::vec3 _prevLeftHandPoleVector { -Vectors::UNIT_Z };
|
||||
bool _prevLeftHandPoleVectorValid { false };
|
||||
|
||||
int _rigId;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Rig__) */
|
||||
|
|
|
@ -53,7 +53,6 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
|||
_viewState(viewState),
|
||||
_scriptingServices(scriptingServices),
|
||||
_displayModelBounds(false),
|
||||
_dontDoPrecisionPicking(false),
|
||||
_layeredZones(this)
|
||||
{
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
|
||||
|
@ -428,28 +427,6 @@ void EntityTreeRenderer::deleteReleasedModels() {
|
|||
}
|
||||
}
|
||||
|
||||
RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
|
||||
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude,
|
||||
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
|
||||
RayToEntityIntersectionResult result;
|
||||
if (_tree) {
|
||||
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
|
||||
OctreeElementPointer element;
|
||||
EntityItemPointer intersectedEntity = NULL;
|
||||
result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction,
|
||||
entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking,
|
||||
element, result.distance, result.face, result.surfaceNormal,
|
||||
(void**)&intersectedEntity, lockType, &result.accurate);
|
||||
if (result.intersects && intersectedEntity) {
|
||||
result.entityID = intersectedEntity->getEntityItemID();
|
||||
result.intersection = ray.origin + (ray.direction * result.distance);
|
||||
result.entity = intersectedEntity;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) {
|
||||
|
||||
connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity);
|
||||
|
@ -530,11 +507,10 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
|
|||
if (!_tree || _shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent");
|
||||
PickRay ray = _viewState->computePickRay(event->x(), event->y());
|
||||
|
||||
bool precisionPicking = !_dontDoPrecisionPicking;
|
||||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
||||
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
|
||||
if (rayPickResult.intersects) {
|
||||
//qCDebug(entitiesrenderer) << "mousePressEvent over entity:" << rayPickResult.entityID;
|
||||
|
||||
|
@ -579,11 +555,10 @@ void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) {
|
|||
if (!_tree || _shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
PerformanceTimer perfTimer("EntityTreeRenderer::mouseDoublePressEvent");
|
||||
PickRay ray = _viewState->computePickRay(event->x(), event->y());
|
||||
|
||||
bool precisionPicking = !_dontDoPrecisionPicking;
|
||||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
||||
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
|
||||
if (rayPickResult.intersects) {
|
||||
//qCDebug(entitiesrenderer) << "mouseDoublePressEvent over entity:" << rayPickResult.entityID;
|
||||
|
||||
|
@ -622,8 +597,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) {
|
|||
|
||||
PerformanceTimer perfTimer("EntityTreeRenderer::mouseReleaseEvent");
|
||||
PickRay ray = _viewState->computePickRay(event->x(), event->y());
|
||||
bool precisionPicking = !_dontDoPrecisionPicking;
|
||||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
||||
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
|
||||
if (rayPickResult.intersects) {
|
||||
//qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
||||
|
||||
|
@ -671,14 +645,11 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
|
|||
if (!_tree || _shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent");
|
||||
|
||||
PickRay ray = _viewState->computePickRay(event->x(), event->y());
|
||||
|
||||
bool precisionPicking = false; // for mouse moves we do not do precision picking
|
||||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
|
||||
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
|
||||
if (rayPickResult.intersects) {
|
||||
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
|
|
|
@ -59,6 +59,10 @@ public:
|
|||
float getEntityLoadingPriority(const EntityItem& item) const { return _calculateEntityLoadingPriorityFunc(item); }
|
||||
void setEntityLoadingPriorityFunction(CalculateEntityLoadingPriority fn) { this->_calculateEntityLoadingPriorityFunc = fn; }
|
||||
|
||||
void setMouseRayPickID(QUuid rayPickID) { _mouseRayPickID = rayPickID; }
|
||||
void setMouseRayPickResultOperator(std::function<RayToEntityIntersectionResult(QUuid)> getPrevRayPickResultOperator) { _getPrevRayPickResultOperator = getPrevRayPickResultOperator; }
|
||||
void setSetPrecisionPickingOperator(std::function<void(QUuid, bool)> setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; }
|
||||
|
||||
void shutdown();
|
||||
void update();
|
||||
|
||||
|
@ -130,7 +134,7 @@ public slots:
|
|||
|
||||
// optional slots that can be wired to menu items
|
||||
void setDisplayModelBounds(bool value) { _displayModelBounds = value; }
|
||||
void setDontDoPrecisionPicking(bool value) { _dontDoPrecisionPicking = value; }
|
||||
void setPrecisionPicking(bool value) { _setPrecisionPickingOperator(_mouseRayPickID, value); }
|
||||
|
||||
protected:
|
||||
virtual OctreePointer createTree() override {
|
||||
|
@ -150,10 +154,6 @@ private:
|
|||
void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false);
|
||||
|
||||
QList<ModelPointer> _releasedModels;
|
||||
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
|
||||
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude = QVector<EntityItemID>(),
|
||||
const QVector<EntityItemID>& entityIdsToDiscard = QVector<EntityItemID>(), bool visibleOnly=false,
|
||||
bool collidableOnly = false);
|
||||
|
||||
EntityItemID _currentHoverOverEntityID;
|
||||
EntityItemID _currentClickingOnEntityID;
|
||||
|
@ -176,12 +176,15 @@ private:
|
|||
AbstractViewStateInterface* _viewState;
|
||||
AbstractScriptingServicesInterface* _scriptingServices;
|
||||
bool _displayModelBounds;
|
||||
bool _dontDoPrecisionPicking;
|
||||
|
||||
bool _shuttingDown { false };
|
||||
|
||||
QMultiMap<QUrl, EntityItemID> _waitingOnPreload;
|
||||
|
||||
QUuid _mouseRayPickID;
|
||||
std::function<RayToEntityIntersectionResult(QUuid)> _getPrevRayPickResultOperator;
|
||||
std::function<void(QUuid, bool)> _setPrecisionPickingOperator;
|
||||
|
||||
class LayeredZone {
|
||||
public:
|
||||
LayeredZone(std::shared_ptr<ZoneEntityItem> zone, QUuid id, float volume) : zone(zone), id(id), volume(volume) {}
|
||||
|
|
|
@ -262,7 +262,7 @@ public:
|
|||
glm::vec3 getRegistrationPoint() const; /// registration point as ratio of entity
|
||||
|
||||
/// registration point as ratio of entity
|
||||
void setRegistrationPoint(const glm::vec3& value);
|
||||
virtual void setRegistrationPoint(const glm::vec3& value);
|
||||
|
||||
bool hasAngularVelocity() const { return getAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; }
|
||||
bool hasLocalAngularVelocity() const { return getLocalAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; }
|
||||
|
|
|
@ -280,24 +280,24 @@ void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
|||
}
|
||||
|
||||
void EntitySimulation::addDynamic(EntityDynamicPointer dynamic) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
QMutexLocker lock(&_dynamicsMutex);
|
||||
_dynamicsToAdd += dynamic;
|
||||
}
|
||||
|
||||
void EntitySimulation::removeDynamic(const QUuid dynamicID) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
QMutexLocker lock(&_dynamicsMutex);
|
||||
_dynamicsToRemove += dynamicID;
|
||||
}
|
||||
|
||||
void EntitySimulation::removeDynamics(QList<QUuid> dynamicIDsToRemove) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
QMutexLocker lock(&_dynamicsMutex);
|
||||
foreach(QUuid uuid, dynamicIDsToRemove) {
|
||||
_dynamicsToRemove.insert(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
void EntitySimulation::applyDynamicChanges() {
|
||||
QMutexLocker lock(&_mutex);
|
||||
QMutexLocker lock(&_dynamicsMutex);
|
||||
_dynamicsToAdd.clear();
|
||||
_dynamicsToRemove.clear();
|
||||
}
|
||||
|
|
|
@ -105,6 +105,7 @@ protected:
|
|||
SetOfEntities _simpleKinematicEntities; // entities undergoing non-colliding kinematic motion
|
||||
QList<EntityDynamicPointer> _dynamicsToAdd;
|
||||
QSet<QUuid> _dynamicsToRemove;
|
||||
QMutex _dynamicsMutex { QMutex::Recursive };
|
||||
|
||||
protected:
|
||||
SetOfEntities _entitiesToDelete; // entities simulation decided needed to be deleted (EntityTree will actually delete)
|
||||
|
|
|
@ -92,13 +92,12 @@ bool PolyLineEntityItem::appendPoint(const glm::vec3& point) {
|
|||
qCDebug(entities) << "MAX POINTS REACHED!";
|
||||
return false;
|
||||
}
|
||||
glm::vec3 halfBox = getDimensions() * 0.5f;
|
||||
if ((point.x < -halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < -halfBox.z || point.z > halfBox.z)) {
|
||||
qCDebug(entities) << "Point is outside entity's bounding box";
|
||||
return false;
|
||||
}
|
||||
|
||||
_points << point;
|
||||
_pointsChanged = true;
|
||||
|
||||
calculateScaleAndRegistrationPoint();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -141,23 +140,69 @@ bool PolyLineEntityItem::setLinePoints(const QVector<glm::vec3>& points) {
|
|||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
glm::vec3 point = points.at(i);
|
||||
glm::vec3 halfBox = getDimensions() * 0.5f;
|
||||
if ((point.x < -halfBox.x || point.x > halfBox.x) ||
|
||||
(point.y < -halfBox.y || point.y > halfBox.y) ||
|
||||
(point.z < -halfBox.z || point.z > halfBox.z)) {
|
||||
qCDebug(entities) << "Point is outside entity's bounding box";
|
||||
return;
|
||||
}
|
||||
}
|
||||
_points = points;
|
||||
|
||||
calculateScaleAndRegistrationPoint();
|
||||
|
||||
result = true;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void PolyLineEntityItem::calculateScaleAndRegistrationPoint() {
|
||||
glm::vec3 high(0.0f, 0.0f, 0.0f);
|
||||
glm::vec3 low(0.0f, 0.0f, 0.0f);
|
||||
for (int i = 0; i < _points.size(); i++) {
|
||||
glm::vec3 point = _points.at(i);
|
||||
|
||||
if (point.x > high.x) {
|
||||
high.x = point.x;
|
||||
} else if (point.x < low.x) {
|
||||
low.x = point.x;
|
||||
}
|
||||
|
||||
if (point.y > high.y) {
|
||||
high.y = point.y;
|
||||
} else if (point.y < low.y) {
|
||||
low.y = point.y;
|
||||
}
|
||||
|
||||
if (point.z > high.z) {
|
||||
high.z = point.z;
|
||||
} else if (point.z < low.z) {
|
||||
low.z = point.z;
|
||||
}
|
||||
}
|
||||
const float EPSILON = 0.0001f;
|
||||
if (_points.size() > 1) {
|
||||
// if all the points in the Polyline are at the same place in space, use default dimension settings
|
||||
if ((low - high).length() < EPSILON) {
|
||||
SpatiallyNestable::setScale(glm::vec3(1.0f, 1.0f, 1.0f));
|
||||
EntityItem::setRegistrationPoint(glm::vec3(0.5f));
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 result;
|
||||
const float halfLineWidth = 0.075f; // sadly _strokeWidths() don't seem to correspond to reality, so just use a flat assumption of the stroke width
|
||||
result.x = fabsf(high.x) + fabsf(low.x) + halfLineWidth;
|
||||
result.y = fabsf(high.y) + fabsf(low.y) + halfLineWidth;
|
||||
result.z = fabsf(high.z) + fabsf(low.z) + halfLineWidth;
|
||||
SpatiallyNestable::setScale(result);
|
||||
|
||||
// Center the poly line in the bounding box
|
||||
glm::vec3 point = _points.at(0);
|
||||
glm::vec3 startPointInScaleSpace = point - low;
|
||||
startPointInScaleSpace += glm::vec3(halfLineWidth * 0.5f);
|
||||
glm::vec3 newRegistrationPoint = startPointInScaleSpace / result;
|
||||
EntityItem::setRegistrationPoint(newRegistrationPoint);
|
||||
} else {
|
||||
// if Polyline has only one or fewer points, use default dimension settings
|
||||
SpatiallyNestable::setScale(glm::vec3(1.0f, 1.0f, 1.0f));
|
||||
EntityItem::setRegistrationPoint(glm::vec3(0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
|
|
|
@ -81,10 +81,17 @@ class PolyLineEntityItem : public EntityItem {
|
|||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking) const override { return false; }
|
||||
|
||||
// disable these external interfaces as PolyLineEntities caculate their own dimensions based on the points they contain
|
||||
virtual void setRegistrationPoint(const glm::vec3& value) override {};
|
||||
virtual void setScale(const glm::vec3& scale) override {};
|
||||
virtual void setScale(float value) override {};
|
||||
|
||||
virtual void debugDump() const override;
|
||||
static const float DEFAULT_LINE_WIDTH;
|
||||
static const int MAX_POINTS_PER_LINE;
|
||||
|
||||
private:
|
||||
void calculateScaleAndRegistrationPoint();
|
||||
|
||||
protected:
|
||||
rgbColor _color;
|
||||
float _lineWidth;
|
||||
|
|
|
@ -113,6 +113,18 @@ QString HifiSockAddr::toString() const {
|
|||
return _address.toString() + ":" + QString::number(_port);
|
||||
}
|
||||
|
||||
bool HifiSockAddr::hasPrivateAddress() const {
|
||||
// an address is private if it is loopback or falls in any of the RFC1918 address spaces
|
||||
const QPair<QHostAddress, int> TWENTY_FOUR_BIT_BLOCK = { QHostAddress("10.0.0.0"), 8 };
|
||||
const QPair<QHostAddress, int> TWENTY_BIT_BLOCK = { QHostAddress("172.16.0.0") , 12 };
|
||||
const QPair<QHostAddress, int> SIXTEEN_BIT_BLOCK = { QHostAddress("192.168.0.0"), 16 };
|
||||
|
||||
return _address.isLoopback()
|
||||
|| _address.isInSubnet(TWENTY_FOUR_BIT_BLOCK)
|
||||
|| _address.isInSubnet(TWENTY_BIT_BLOCK)
|
||||
|| _address.isInSubnet(SIXTEEN_BIT_BLOCK);
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr) {
|
||||
debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port;
|
||||
return debug.space();
|
||||
|
|
|
@ -55,6 +55,8 @@ public:
|
|||
|
||||
QString toString() const;
|
||||
|
||||
bool hasPrivateAddress() const; // checks if the address behind this sock addr is private per RFC 1918
|
||||
|
||||
friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr);
|
||||
friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr);
|
||||
friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr);
|
||||
|
|
|
@ -202,12 +202,12 @@ QUdpSocket& LimitedNodeList::getDTLSSocket() {
|
|||
return *_dtlsSocket;
|
||||
}
|
||||
|
||||
bool LimitedNodeList::isPacketVerified(const udt::Packet& packet) {
|
||||
bool LimitedNodeList::isPacketVerifiedWithSource(const udt::Packet& packet, Node* sourceNode) {
|
||||
// We track bandwidth when doing packet verification to avoid needing to do a node lookup
|
||||
// later when we already do it in packetSourceAndHashMatchAndTrackBandwidth. A node lookup
|
||||
// incurs a lock, so it is ideal to avoid needing to do it 2+ times for each packet
|
||||
// received.
|
||||
return packetVersionMatch(packet) && packetSourceAndHashMatchAndTrackBandwidth(packet);
|
||||
return packetVersionMatch(packet) && packetSourceAndHashMatchAndTrackBandwidth(packet, sourceNode);
|
||||
}
|
||||
|
||||
bool LimitedNodeList::packetVersionMatch(const udt::Packet& packet) {
|
||||
|
@ -256,7 +256,7 @@ bool LimitedNodeList::packetVersionMatch(const udt::Packet& packet) {
|
|||
}
|
||||
}
|
||||
|
||||
bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packet& packet) {
|
||||
bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packet& packet, Node* sourceNode) {
|
||||
|
||||
PacketType headerType = NLPacket::typeInHeader(packet);
|
||||
|
||||
|
@ -298,14 +298,18 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
|
|||
} else {
|
||||
QUuid sourceID = NLPacket::sourceIDInHeader(packet);
|
||||
|
||||
// figure out which node this is from
|
||||
SharedNodePointer matchingNode = nodeWithUUID(sourceID);
|
||||
// check if we were passed a sourceNode hint or if we need to look it up
|
||||
if (!sourceNode) {
|
||||
// figure out which node this is from
|
||||
SharedNodePointer matchingNode = nodeWithUUID(sourceID);
|
||||
sourceNode = matchingNode.data();
|
||||
}
|
||||
|
||||
if (matchingNode) {
|
||||
if (sourceNode) {
|
||||
if (!PacketTypeEnum::getNonVerifiedPackets().contains(headerType)) {
|
||||
|
||||
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
|
||||
QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, matchingNode->getConnectionSecret());
|
||||
QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, sourceNode->getConnectionSecret());
|
||||
|
||||
// check if the md5 hash in the header matches the hash we would expect
|
||||
if (packetHeaderHash != expectedHash) {
|
||||
|
@ -323,9 +327,9 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
|
|||
|
||||
// No matter if this packet is handled or not, we update the timestamp for the last time we heard
|
||||
// from this sending node
|
||||
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
sourceNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
emit dataReceived(matchingNode->getType(), packet.getPayloadSize());
|
||||
emit dataReceived(sourceNode->getType(), packet.getPayloadSize());
|
||||
|
||||
return true;
|
||||
|
||||
|
|
|
@ -286,7 +286,9 @@ public:
|
|||
|
||||
void setPacketFilterOperator(udt::PacketFilterOperator filterOperator) { _nodeSocket.setPacketFilterOperator(filterOperator); }
|
||||
bool packetVersionMatch(const udt::Packet& packet);
|
||||
bool isPacketVerified(const udt::Packet& packet);
|
||||
|
||||
bool isPacketVerifiedWithSource(const udt::Packet& packet, Node* sourceNode = nullptr);
|
||||
bool isPacketVerified(const udt::Packet& packet) { return isPacketVerifiedWithSource(packet); }
|
||||
|
||||
static void makeSTUNRequestPacket(char* stunRequestPacket);
|
||||
|
||||
|
@ -352,7 +354,7 @@ protected:
|
|||
|
||||
void setLocalSocket(const HifiSockAddr& sockAddr);
|
||||
|
||||
bool packetSourceAndHashMatchAndTrackBandwidth(const udt::Packet& packet);
|
||||
bool packetSourceAndHashMatchAndTrackBandwidth(const udt::Packet& packet, Node* sourceNode = nullptr);
|
||||
void processSTUNResponse(std::unique_ptr<udt::BasePacket> packet);
|
||||
|
||||
void handleNodeKill(const SharedNodePointer& node);
|
||||
|
|
|
@ -53,8 +53,12 @@ QVariant NodePermissions::toVariant(QHash<QUuid, GroupRank> groupRanks) {
|
|||
values["permissions_id"] = _id;
|
||||
if (_groupIDSet) {
|
||||
values["group_id"] = _groupID;
|
||||
if (groupRanks.contains(_rankID)) {
|
||||
|
||||
if (!_rankID.isNull()) {
|
||||
values["rank_id"] = _rankID;
|
||||
}
|
||||
|
||||
if (groupRanks.contains(_rankID)) {
|
||||
values["rank_name"] = groupRanks[_rankID].name;
|
||||
values["rank_order"] = groupRanks[_rankID].order;
|
||||
}
|
||||
|
|
|
@ -348,8 +348,7 @@ void PhysicalEntitySimulation::addDynamic(EntityDynamicPointer dynamic) {
|
|||
void PhysicalEntitySimulation::applyDynamicChanges() {
|
||||
QList<EntityDynamicPointer> dynamicsFailedToAdd;
|
||||
if (_physicsEngine) {
|
||||
// FIXME put fine grain locking into _physicsEngine
|
||||
QMutexLocker lock(&_mutex);
|
||||
QMutexLocker lock(&_dynamicsMutex);
|
||||
foreach(QUuid dynamicToRemove, _dynamicsToRemove) {
|
||||
_physicsEngine->removeDynamic(dynamicToRemove);
|
||||
}
|
||||
|
@ -360,9 +359,10 @@ void PhysicalEntitySimulation::applyDynamicChanges() {
|
|||
}
|
||||
}
|
||||
}
|
||||
// applyDynamicChanges will clear _dynamicsToRemove and _dynamicsToAdd
|
||||
EntitySimulation::applyDynamicChanges();
|
||||
}
|
||||
// applyDynamicChanges will clear _dynamicsToRemove and _dynamicsToAdd
|
||||
EntitySimulation::applyDynamicChanges();
|
||||
|
||||
// put back the ones that couldn't yet be added
|
||||
foreach (EntityDynamicPointer dynamicFailedToAdd, dynamicsFailedToAdd) {
|
||||
addDynamic(dynamicFailedToAdd);
|
||||
|
|
|
@ -28,7 +28,8 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
|||
"system/notifications.js",
|
||||
"system/dialTone.js",
|
||||
"system/firstPersonHMD.js",
|
||||
"system/tablet-ui/tabletUI.js"
|
||||
"system/tablet-ui/tabletUI.js",
|
||||
"system/commerce/wallet.js"
|
||||
];
|
||||
var DEFAULT_SCRIPTS_SEPARATE = [
|
||||
"system/controllers/controllerScripts.js",
|
||||
|
|
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
|
|
@ -1182,7 +1182,7 @@ function MyController(hand) {
|
|||
this.fullEnd = fullEnd;
|
||||
this.laserPointer = LaserPointers.createLaserPointer({
|
||||
joint: (hand == RIGHT_HAND) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND",
|
||||
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS,
|
||||
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS | RayPick.PICK_INCLUDE_NONCOLLIDABLE,
|
||||
maxDistance: PICK_MAX_DISTANCE,
|
||||
posOffset: getGrabPointSphereOffset(this.handToController()),
|
||||
renderStates: renderStates,
|
||||
|
@ -1191,7 +1191,7 @@ function MyController(hand) {
|
|||
});
|
||||
this.headLaserPointer = LaserPointers.createLaserPointer({
|
||||
joint: "Avatar",
|
||||
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS,
|
||||
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS | RayPick.PICK_INCLUDE_NONCOLLIDABLE,
|
||||
maxDistance: PICK_MAX_DISTANCE,
|
||||
renderStates: headRenderStates,
|
||||
faceAvatar: true,
|
||||
|
|
|
@ -115,7 +115,7 @@
|
|||
itemId: id,
|
||||
itemName: name,
|
||||
itemAuthor: author,
|
||||
itemPrice: price ? parseInt(price, 10) : Math.round(Math.random() * 50),
|
||||
itemPrice: price ? parseInt(price, 10) : 0,
|
||||
itemHref: href
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -178,8 +178,8 @@
|
|||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "emitShouldTrail",
|
||||
name: "Emit Should Trail",
|
||||
id: "emitterShouldTrail",
|
||||
name: "Emitter Should Trail",
|
||||
type: "Boolean"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -42,6 +42,8 @@
|
|||
});
|
||||
|
||||
function fromQml(message) {
|
||||
console.debug('tablet-goto::fromQml: message = ', JSON.stringify(message));
|
||||
|
||||
var response = {id: message.id, jsonrpc: "2.0"};
|
||||
switch (message.method) {
|
||||
case 'request':
|
||||
|
@ -98,6 +100,8 @@
|
|||
button.editProperties({isActive: shouldActivateButton});
|
||||
wireEventBridge(true);
|
||||
messagesWaiting(false);
|
||||
tablet.sendToQml({ method: 'refreshFeeds' })
|
||||
|
||||
} else {
|
||||
shouldActivateButton = false;
|
||||
onGotoScreen = false;
|
||||
|
|