mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 13:53:26 +02:00
Merging with master
This commit is contained in:
commit
d7333d3fa3
68 changed files with 1809 additions and 791 deletions
interface
resources/qml
Stats.qml
desktop
hifi/commerce
checkout
common
inspectionCertificate
purchases
wallet
windows
src
libraries
display-plugins/src/display-plugins
entities
networking/src/udt
render/src/render
shared/src/shared
ui/src
scripts/system
commerce
controllers/controllerModules
html/js
marketplaces
|
@ -55,7 +55,11 @@ Item {
|
|||
text: "Avatars: " + root.avatarCount
|
||||
}
|
||||
StatText {
|
||||
text: "Frame Rate: " + root.framerate.toFixed(2);
|
||||
text: "Game Rate: " + root.gameLoopRate
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded
|
||||
text: root.gameUpdateStats
|
||||
}
|
||||
StatText {
|
||||
text: "Render Rate: " + root.renderrate.toFixed(2);
|
||||
|
@ -64,21 +68,17 @@ Item {
|
|||
text: "Present Rate: " + root.presentrate.toFixed(2);
|
||||
}
|
||||
StatText {
|
||||
text: "Present New Rate: " + root.presentnewrate.toFixed(2);
|
||||
visible: root.expanded
|
||||
text: " Present New Rate: " + root.presentnewrate.toFixed(2);
|
||||
}
|
||||
StatText {
|
||||
text: "Present Drop Rate: " + root.presentdroprate.toFixed(2);
|
||||
visible: root.expanded
|
||||
text: " Present Drop Rate: " + root.presentdroprate.toFixed(2);
|
||||
}
|
||||
StatText {
|
||||
text: "Stutter Rate: " + root.stutterrate.toFixed(3);
|
||||
visible: root.stutterrate != -1;
|
||||
}
|
||||
StatText {
|
||||
text: "Simrate: " + root.simrate
|
||||
}
|
||||
StatText {
|
||||
text: "Avatar Simrate: " + root.avatarSimrate
|
||||
}
|
||||
StatText {
|
||||
text: "Missed Frame Count: " + root.appdropped;
|
||||
visible: root.appdropped > 0;
|
||||
|
@ -261,9 +261,6 @@ Item {
|
|||
StatText {
|
||||
text: "GPU: " + root.gpuFrameTime.toFixed(1) + " ms"
|
||||
}
|
||||
StatText {
|
||||
text: "Avatar: " + root.avatarSimulationTime.toFixed(1) + " ms"
|
||||
}
|
||||
StatText {
|
||||
text: "Triangles: " + root.triangles +
|
||||
" / Material Switches: " + root.materialSwitches
|
||||
|
|
|
@ -298,6 +298,23 @@ FocusScope {
|
|||
pinned = !pinned
|
||||
}
|
||||
|
||||
function isPointOnWindow(point) {
|
||||
for (var i = 0; i < desktop.visibleChildren.length; i++) {
|
||||
var child = desktop.visibleChildren[i];
|
||||
if (child.visible) {
|
||||
if (child.hasOwnProperty("modality")) {
|
||||
var mappedPoint = child.mapFromGlobal(point.x, point.y);
|
||||
var outLine = child.frame.children[2];
|
||||
var framePoint = outLine.mapFromGlobal(point.x, point.y);
|
||||
if (child.contains(mappedPoint) || outLine.contains(framePoint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setPinned(newPinned) {
|
||||
pinned = newPinned
|
||||
}
|
||||
|
|
|
@ -26,72 +26,57 @@ Rectangle {
|
|||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
objectName: "checkout"
|
||||
property string activeView: "initialize";
|
||||
property bool purchasesReceived: false;
|
||||
property bool balanceReceived: false;
|
||||
property bool securityImageResultReceived: false;
|
||||
property string itemName;
|
||||
property string itemId;
|
||||
property string itemPreviewImageUrl;
|
||||
property string itemHref;
|
||||
property double balanceAfterPurchase;
|
||||
property bool alreadyOwned: false;
|
||||
property int itemPrice: 0;
|
||||
property int itemPrice;
|
||||
property bool itemIsJson: true;
|
||||
property bool shouldBuyWithControlledFailure: false;
|
||||
property bool debugCheckoutSuccess: false;
|
||||
property bool canRezCertifiedItems: false;
|
||||
property bool canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
||||
onWalletStatusResult: {
|
||||
if (walletStatus === 0) {
|
||||
if (root.activeView !== "needsLogIn") {
|
||||
root.activeView = "needsLogIn";
|
||||
}
|
||||
} else if (walletStatus === 1) {
|
||||
if (root.activeView !== "notSetUp") {
|
||||
root.activeView = "notSetUp";
|
||||
notSetUpTimer.start();
|
||||
}
|
||||
} else if (walletStatus === 2) {
|
||||
if (root.activeView !== "passphraseModal") {
|
||||
root.activeView = "passphraseModal";
|
||||
}
|
||||
} else if (walletStatus === 3) {
|
||||
authSuccessStep();
|
||||
} else {
|
||||
console.log("ERROR in Checkout.qml: Unknown wallet status: " + walletStatus);
|
||||
}
|
||||
}
|
||||
|
||||
onLoginStatusResult: {
|
||||
if (!isLoggedIn && root.activeView !== "needsLogIn") {
|
||||
root.activeView = "needsLogIn";
|
||||
} else if (isLoggedIn) {
|
||||
root.activeView = "initialize";
|
||||
commerce.account();
|
||||
}
|
||||
}
|
||||
|
||||
onAccountResult: {
|
||||
if (result.status === "success") {
|
||||
commerce.getKeyFilePathIfExists();
|
||||
} else {
|
||||
// unsure how to handle a failure here. We definitely cannot proceed.
|
||||
}
|
||||
}
|
||||
|
||||
onKeyFilePathIfExistsResult: {
|
||||
if (path === "" && root.activeView !== "notSetUp") {
|
||||
root.activeView = "notSetUp";
|
||||
notSetUpTimer.start();
|
||||
} else if (path !== "" && root.activeView === "initialize") {
|
||||
commerce.getSecurityImage();
|
||||
}
|
||||
}
|
||||
|
||||
onSecurityImageResult: {
|
||||
securityImageResultReceived = true;
|
||||
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
|
||||
root.activeView = "notSetUp";
|
||||
notSetUpTimer.start();
|
||||
} else if (exists && root.activeView === "initialize") {
|
||||
commerce.getWalletAuthenticatedStatus();
|
||||
}
|
||||
}
|
||||
|
||||
onWalletAuthenticatedStatusResult: {
|
||||
if (!isAuthenticated && root.activeView !== "passphraseModal") {
|
||||
root.activeView = "passphraseModal";
|
||||
} else if (isAuthenticated) {
|
||||
authSuccessStep();
|
||||
commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
onBuyResult: {
|
||||
if (result.status !== 'success') {
|
||||
failureErrorText.text = "Here's some more info about the error:<br><br>" + (result.message);
|
||||
failureErrorText.text = result.message;
|
||||
root.activeView = "checkoutFailure";
|
||||
} else {
|
||||
root.activeView = "checkoutSuccess";
|
||||
|
@ -123,6 +108,19 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
onItemIdChanged: {
|
||||
commerce.inventory();
|
||||
itemPreviewImage.source = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/" + itemId + "/thumbnail/hifi-mp-" + itemId + ".jpg";
|
||||
}
|
||||
|
||||
onItemHrefChanged: {
|
||||
itemIsJson = root.itemHref.indexOf('.json') !== -1;
|
||||
}
|
||||
|
||||
onItemPriceChanged: {
|
||||
commerce.balance();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: notSetUpTimer;
|
||||
interval: 200;
|
||||
|
@ -176,6 +174,13 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
enabled: titleBarContainer.usernameDropdownVisible;
|
||||
anchors.fill: parent;
|
||||
onClicked: {
|
||||
titleBarContainer.usernameDropdownVisible = false;
|
||||
}
|
||||
}
|
||||
//
|
||||
// TITLE BAR END
|
||||
//
|
||||
|
@ -190,10 +195,9 @@ Rectangle {
|
|||
color: hifi.colors.white;
|
||||
|
||||
Component.onCompleted: {
|
||||
securityImageResultReceived = false;
|
||||
purchasesReceived = false;
|
||||
balanceReceived = false;
|
||||
commerce.getLoginStatus();
|
||||
commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,7 +285,6 @@ Rectangle {
|
|||
|
||||
Image {
|
||||
id: itemPreviewImage;
|
||||
source: root.itemPreviewImageUrl;
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
@ -291,6 +294,7 @@ Rectangle {
|
|||
|
||||
RalewaySemiBold {
|
||||
id: itemNameText;
|
||||
text: root.itemName;
|
||||
// Text size
|
||||
size: 26;
|
||||
// Anchors
|
||||
|
@ -315,19 +319,19 @@ Rectangle {
|
|||
anchors.top: parent.top;
|
||||
anchors.right: parent.right;
|
||||
height: 30;
|
||||
width: childrenRect.width;
|
||||
width: itemPriceTextLabel.width + itemPriceText.width + 20;
|
||||
|
||||
// "HFC" balance label
|
||||
HiFiGlyphs {
|
||||
id: itemPriceTextLabel;
|
||||
text: hifi.glyphs.hfc;
|
||||
// Size
|
||||
size: 36;
|
||||
size: 30;
|
||||
// Anchors
|
||||
anchors.right: itemPriceText.left;
|
||||
anchors.rightMargin: 4;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: -4;
|
||||
anchors.topMargin: 0;
|
||||
width: paintedWidth;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
|
@ -335,7 +339,7 @@ Rectangle {
|
|||
}
|
||||
FiraSansSemiBold {
|
||||
id: itemPriceText;
|
||||
text: "--";
|
||||
text: root.itemPrice;
|
||||
// Text size
|
||||
size: 26;
|
||||
// Anchors
|
||||
|
@ -405,7 +409,7 @@ Rectangle {
|
|||
verticalAlignment: Text.AlignTop;
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
RalewayRegular {
|
||||
id: buyText;
|
||||
// Text size
|
||||
size: 18;
|
||||
|
@ -424,7 +428,7 @@ Rectangle {
|
|||
verticalAlignment: Text.AlignVCenter;
|
||||
|
||||
onLinkActivated: {
|
||||
sendToScript({method: 'checkout_goToPurchases', filterText: itemNameText.text});
|
||||
sendToScript({method: 'checkout_goToPurchases', filterText: root.itemName});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -508,7 +512,7 @@ Rectangle {
|
|||
|
||||
RalewaySemiBold {
|
||||
id: completeText2;
|
||||
text: "The item " + '<font color="' + hifi.colors.blueAccent + '"><a href="#">' + itemNameText.text + '</a></font>' +
|
||||
text: "The item " + '<font color="' + hifi.colors.blueAccent + '"><a href="#">' + root.itemName + '</a></font>' +
|
||||
" has been added to your Purchases and a receipt will appear in your Wallet's transaction history.";
|
||||
// Text size
|
||||
size: 20;
|
||||
|
@ -709,7 +713,9 @@ Rectangle {
|
|||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.bottom: root.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
|
||||
RalewayRegular {
|
||||
id: failureHeaderText;
|
||||
|
@ -718,57 +724,65 @@ Rectangle {
|
|||
size: 24;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 80;
|
||||
anchors.topMargin: 40;
|
||||
height: paintedHeight;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
// Style
|
||||
color: hifi.colors.black;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: failureErrorText;
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
Rectangle {
|
||||
id: failureErrorTextContainer;
|
||||
anchors.top: failureHeaderText.bottom;
|
||||
anchors.topMargin: 35;
|
||||
height: paintedHeight;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
// Style
|
||||
color: hifi.colors.black;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
height: failureErrorText.height + 30;
|
||||
radius: 4;
|
||||
border.width: 2;
|
||||
border.color: "#F3808F";
|
||||
color: "#FFC3CD";
|
||||
|
||||
AnonymousProRegular {
|
||||
id: failureErrorText;
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 15;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 8;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 8;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.black;
|
||||
wrapMode: Text.Wrap;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: backToMarketplaceButtonContainer;
|
||||
// Size
|
||||
width: root.width;
|
||||
height: 130;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 8;
|
||||
anchors.bottomMargin: 16;
|
||||
// "Back to Marketplace" button
|
||||
HifiControlsUit.Button {
|
||||
id: backToMarketplaceButton;
|
||||
color: hifi.buttons.black;
|
||||
color: hifi.buttons.noneBorderlessGray;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
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;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
width: parent.width/2 - anchors.leftMargin*2;
|
||||
text: "Back to Marketplace";
|
||||
onClicked: {
|
||||
sendToScript({method: 'checkout_continueShopping', itemId: itemId});
|
||||
|
@ -814,15 +828,9 @@ Rectangle {
|
|||
switch (message.method) {
|
||||
case 'updateCheckoutQML':
|
||||
itemId = message.params.itemId;
|
||||
itemNameText.text = message.params.itemName;
|
||||
itemName = message.params.itemName;
|
||||
root.itemPrice = message.params.itemPrice;
|
||||
itemPriceText.text = root.itemPrice === 0 ? "Free" : root.itemPrice;
|
||||
itemHref = message.params.itemHref;
|
||||
itemPreviewImageUrl = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/" + itemId + "/thumbnail/hifi-mp-" + itemId + ".jpg";
|
||||
if (itemHref.indexOf('.json') === -1) {
|
||||
root.itemIsJson = false;
|
||||
}
|
||||
root.canRezCertifiedItems = message.canRezCertifiedItems;
|
||||
setBuyText();
|
||||
break;
|
||||
default:
|
||||
|
@ -845,10 +853,10 @@ Rectangle {
|
|||
if (root.purchasesReceived && root.balanceReceived) {
|
||||
if (root.balanceAfterPurchase < 0) {
|
||||
if (root.alreadyOwned) {
|
||||
buyText.text = "Your Wallet does not have sufficient funds to purchase this item again.<br>" +
|
||||
'<font color="' + hifi.colors.blueAccent + '"><a href="#">View the copy you own in My Purchases</a></font>';
|
||||
buyText.text = "<b>Your Wallet does not have sufficient funds to purchase this item again.<br>" +
|
||||
'<font color="' + hifi.colors.blueAccent + '"><a href="#">View the copy you own in My Purchases</a></font></b>';
|
||||
} else {
|
||||
buyText.text = "Your Wallet does not have sufficient funds to purchase this item.";
|
||||
buyText.text = "<b>Your Wallet does not have sufficient funds to purchase this item.</b>";
|
||||
}
|
||||
buyTextContainer.color = "#FFC3CD";
|
||||
buyTextContainer.border.color = "#F3808F";
|
||||
|
@ -856,8 +864,8 @@ Rectangle {
|
|||
buyGlyph.size = 54;
|
||||
} else {
|
||||
if (root.alreadyOwned) {
|
||||
buyText.text = 'You already own this item.<br>Purchasing it will buy another copy.<br><font color="'
|
||||
+ hifi.colors.blueAccent + '"><a href="#">View this item in My Purchases</a></font>';
|
||||
buyText.text = '<b>You already own this item.<br>Purchasing it will buy another copy.<br><font color="'
|
||||
+ hifi.colors.blueAccent + '"><a href="#">View this item in My Purchases</a></font></b>';
|
||||
buyTextContainer.color = "#FFD6AD";
|
||||
buyTextContainer.border.color = "#FAC07D";
|
||||
buyGlyph.text = hifi.glyphs.alert;
|
||||
|
@ -870,7 +878,7 @@ Rectangle {
|
|||
buyText.text = "";
|
||||
}
|
||||
} else {
|
||||
buyText.text = "This Marketplace item isn't an entity. It <b>will not</b> be added to your <b>Purchases</b>.";
|
||||
buyText.text = "This free item <b>will not</b> be added to your <b>Purchases</b>. Non-entities can't yet be purchased for HFC.";
|
||||
buyTextContainer.color = "#FFD6AD";
|
||||
buyTextContainer.border.color = "#FAC07D";
|
||||
buyGlyph.text = hifi.glyphs.alert;
|
||||
|
@ -884,12 +892,6 @@ Rectangle {
|
|||
} else {
|
||||
root.activeView = "checkoutSuccess";
|
||||
}
|
||||
if (!balanceReceived) {
|
||||
commerce.balance();
|
||||
}
|
||||
if (!purchasesReceived) {
|
||||
commerce.inventory();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -82,6 +82,7 @@ Rectangle {
|
|||
height: 140;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
cache: false;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
|
|
|
@ -27,23 +27,28 @@ Item {
|
|||
id: root;
|
||||
property string referrerURL: "https://metaverse.highfidelity.com/marketplace?";
|
||||
readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin;
|
||||
property alias usernameDropdownVisible: usernameDropdown.visible;
|
||||
|
||||
height: mainContainer.height + additionalDropdownHeight;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
||||
onLoginStatusResult: {
|
||||
if (!isLoggedIn) {
|
||||
onWalletStatusResult: {
|
||||
if (walletStatus === 0) {
|
||||
sendToParent({method: "needsLogIn"});
|
||||
} else if (walletStatus === 3) {
|
||||
commerce.getSecurityImage();
|
||||
} else {
|
||||
console.log("ERROR in EmulatedMarketplaceHeader.qml: Unknown wallet status: " + walletStatus);
|
||||
}
|
||||
}
|
||||
|
||||
onAccountResult: {
|
||||
if (result.status === "success") {
|
||||
commerce.getKeyFilePathIfExists();
|
||||
onLoginStatusResult: {
|
||||
if (!isLoggedIn) {
|
||||
sendToParent({method: "needsLogIn"});
|
||||
} else {
|
||||
// unsure how to handle a failure here. We definitely cannot proceed.
|
||||
commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,8 +61,7 @@ Item {
|
|||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
commerce.getLoginStatus();
|
||||
commerce.getSecurityImage();
|
||||
commerce.getWalletStatus();
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
@ -210,6 +214,7 @@ Item {
|
|||
anchors.bottomMargin: 16;
|
||||
width: height;
|
||||
mipmap: true;
|
||||
cache: false;
|
||||
|
||||
MouseArea {
|
||||
enabled: securityImage.visible;
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// SortableListModel.qml
|
||||
// qml/hifi/commerce/common
|
||||
//
|
||||
// SortableListModel
|
||||
//
|
||||
// Created by Zach Fox on 2017-09-28
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
|
||||
ListModel {
|
||||
id: root;
|
||||
property string sortColumnName: "";
|
||||
property bool isSortingDescending: true;
|
||||
|
||||
function swap(a, b) {
|
||||
if (a < b) {
|
||||
move(a, b, 1);
|
||||
move (b - 1, a, 1);
|
||||
} else if (a > b) {
|
||||
move(b, a, 1);
|
||||
move(a - 1, b, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function partition(begin, end, pivot) {
|
||||
var piv = get(pivot)[sortColumnName];
|
||||
swap(pivot, end - 1);
|
||||
var store = begin;
|
||||
|
||||
for (var i = begin; i < end - 1; ++i) {
|
||||
if (isSortingDescending) {
|
||||
if (get(i)[sortColumnName] < piv) {
|
||||
swap(store, i);
|
||||
++store;
|
||||
}
|
||||
} else {
|
||||
if (get(i)[sortColumnName] > piv) {
|
||||
swap(store, i);
|
||||
++store;
|
||||
}
|
||||
}
|
||||
}
|
||||
swap(end - 1, store);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
function qsort(begin, end) {
|
||||
if (end - 1 > begin) {
|
||||
var pivot = begin + Math.floor(Math.random() * (end - begin));
|
||||
|
||||
pivot = partition(begin, end, pivot);
|
||||
|
||||
qsort(begin, pivot);
|
||||
qsort(pivot + 1, end);
|
||||
}
|
||||
}
|
||||
|
||||
function quickSort() {
|
||||
qsort(0, count)
|
||||
}
|
||||
}
|
|
@ -30,12 +30,20 @@ Rectangle {
|
|||
property string itemOwner: "--";
|
||||
property string itemEdition: "--";
|
||||
property string dateOfPurchase: "";
|
||||
property bool closeGoesToPurchases: false;
|
||||
property bool isLightbox: false;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
Image {
|
||||
anchors.fill: parent;
|
||||
|
@ -262,7 +270,11 @@ Rectangle {
|
|||
height: 50;
|
||||
text: "close";
|
||||
onClicked: {
|
||||
sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases});
|
||||
if (root.isLightbox) {
|
||||
root.visible = false;
|
||||
} else {
|
||||
sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,7 +315,6 @@ Rectangle {
|
|||
switch (message.method) {
|
||||
case 'inspectionCertificate_setMarketplaceId':
|
||||
root.marketplaceId = message.marketplaceId;
|
||||
root.closeGoesToPurchases = message.closeGoesToPurchases;
|
||||
break;
|
||||
case 'inspectionCertificate_setItemInfo':
|
||||
root.itemName = message.itemName;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
|
@ -24,40 +25,133 @@ Rectangle {
|
|||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
property string activeView: "step_1";
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
property int activeView: 1;
|
||||
|
||||
Image {
|
||||
anchors.fill: parent;
|
||||
source: "images/Purchase-First-Run-" + root.activeView + ".jpg";
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: header;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: childrenRect.height;
|
||||
|
||||
Image {
|
||||
id: marketplaceHeaderImage;
|
||||
source: "../common/images/marketplaceHeaderImage.png";
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 2;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 8;
|
||||
width: 140;
|
||||
height: 58;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
visible: false;
|
||||
}
|
||||
ColorOverlay {
|
||||
anchors.fill: marketplaceHeaderImage;
|
||||
source: marketplaceHeaderImage;
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
RalewayRegular {
|
||||
id: introText1;
|
||||
text: "INTRODUCTION TO";
|
||||
// Text size
|
||||
size: 15;
|
||||
// Anchors
|
||||
anchors.top: marketplaceHeaderImage.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 12;
|
||||
anchors.right: parent.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
}
|
||||
RalewayRegular {
|
||||
id: introText2;
|
||||
text: "My Purchases";
|
||||
// Text size
|
||||
size: 28;
|
||||
// Anchors
|
||||
anchors.top: introText1.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 12;
|
||||
anchors.right: parent.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// "STEP 1" START
|
||||
//
|
||||
Item {
|
||||
id: step_1;
|
||||
visible: root.activeView === "step_1";
|
||||
anchors.top: parent.top;
|
||||
visible: root.activeView === 1;
|
||||
anchors.top: header.bottom;
|
||||
anchors.topMargin: 100;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 30;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: tutorialActionButtonsContainer.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
||||
RalewayRegular {
|
||||
id: step1text;
|
||||
text: "<b>This is the first-time Purchases tutorial.</b><br><br>Here is some <b>bold text</b> " +
|
||||
"inside Step 1.";
|
||||
text: "The <b>'REZ IT'</b> button makes your purchase appear in front of you.";
|
||||
// Text size
|
||||
size: 24;
|
||||
size: 20;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
width: 180;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
wrapMode: Text.WordWrap;
|
||||
}
|
||||
|
||||
// "Next" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: step1text.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
width: 150;
|
||||
height: 40;
|
||||
text: "Next";
|
||||
onClicked: {
|
||||
root.activeView++;
|
||||
}
|
||||
}
|
||||
|
||||
// "SKIP" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.noneBorderlessGray;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 32;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
width: 150;
|
||||
height: 40;
|
||||
text: "SKIP";
|
||||
onClicked: {
|
||||
sendSignalToParent({method: 'tutorial_finished'});
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
|
@ -69,127 +163,52 @@ Rectangle {
|
|||
//
|
||||
Item {
|
||||
id: step_2;
|
||||
visible: root.activeView === "step_2";
|
||||
anchors.top: parent.top;
|
||||
visible: root.activeView === 2;
|
||||
anchors.top: header.bottom;
|
||||
anchors.topMargin: 45;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 30;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: tutorialActionButtonsContainer.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
||||
RalewayRegular {
|
||||
id: step2text;
|
||||
text: "<b>STEP TWOOO!!!</b>";
|
||||
text: "If you rez an item twice, the first one will disappear.";
|
||||
// Text size
|
||||
size: 24;
|
||||
size: 20;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
width: 180;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
color: hifi.colors.white;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// "GOT IT" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: step2text.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
width: 150;
|
||||
height: 40;
|
||||
text: "GOT IT";
|
||||
onClicked: {
|
||||
sendSignalToParent({method: 'tutorial_finished'});
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// "STEP 2" END
|
||||
//
|
||||
|
||||
Item {
|
||||
id: tutorialActionButtonsContainer;
|
||||
// Size
|
||||
width: root.width;
|
||||
height: 70;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 24;
|
||||
|
||||
// "Skip" or "Back" button
|
||||
HifiControlsUit.Button {
|
||||
id: skipOrBackButton;
|
||||
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: root.activeView === "step_1" ? "Skip" : "Back";
|
||||
onClicked: {
|
||||
if (root.activeView === "step_1") {
|
||||
sendSignalToParent({method: 'tutorial_skipClicked'});
|
||||
} else {
|
||||
root.activeView = "step_" + (parseInt(root.activeView.split("_")[1]) - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "Next" or "Finish" button
|
||||
HifiControlsUit.Button {
|
||||
id: nextButton;
|
||||
color: hifi.buttons.blue;
|
||||
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: root.activeView === "step_2" ? "Finish" : "Next";
|
||||
onClicked: {
|
||||
// If this is the final step...
|
||||
if (root.activeView === "step_2") {
|
||||
sendSignalToParent({method: 'tutorial_finished'});
|
||||
} else {
|
||||
root.activeView = "step_" + (parseInt(root.activeView.split("_")[1]) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
//
|
||||
// Function Name: fromScript()
|
||||
//
|
||||
// Relevant Variables:
|
||||
// None
|
||||
//
|
||||
// Arguments:
|
||||
// message: The message sent from the JavaScript, in this case the Marketplaces 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) {
|
||||
case 'updatePurchases':
|
||||
referrerURL = message.referrerURL;
|
||||
break;
|
||||
case 'purchases_getIsFirstUseResult':
|
||||
if (message.isFirstUseOfPurchases && root.activeView !== "firstUseTutorial") {
|
||||
root.activeView = "firstUseTutorial";
|
||||
} else if (!message.isFirstUseOfPurchases && root.activeView === "initialize") {
|
||||
root.activeView = "purchasesMain";
|
||||
commerce.inventory();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
signal sendSignalToParent(var message);
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
|
|
|
@ -34,14 +34,19 @@ Item {
|
|||
property string itemId;
|
||||
property string itemPreviewImageUrl;
|
||||
property string itemHref;
|
||||
property int ownedItemCount;
|
||||
property int displayedItemCount;
|
||||
property int itemEdition;
|
||||
|
||||
property string originalStatusText;
|
||||
property string originalStatusColor;
|
||||
|
||||
height: 110;
|
||||
width: parent.width;
|
||||
|
||||
onPurchaseStatusChangedChanged: {
|
||||
if (root.purchaseStatusChanged === true && root.purchaseStatus === "confirmed") {
|
||||
root.originalStatusText = statusText.text;
|
||||
root.originalStatusColor = statusText.color;
|
||||
statusText.text = "CONFIRMED!";
|
||||
statusText.color = hifi.colors.blueAccent;
|
||||
confirmedTimer.start();
|
||||
|
@ -53,7 +58,8 @@ Item {
|
|||
id: confirmedTimer;
|
||||
interval: 3000;
|
||||
onTriggered: {
|
||||
root.purchaseStatus = "";
|
||||
statusText.text = root.originalStatusText;
|
||||
statusText.color = root.originalStatusColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,9 +180,30 @@ Item {
|
|||
}
|
||||
|
||||
Item {
|
||||
id: statusContainer;
|
||||
id: editionContainer;
|
||||
visible: root.displayedItemCount > 1 && !statusContainer.visible;
|
||||
anchors.left: itemName.left;
|
||||
anchors.top: certificateContainer.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.right: buttonContainer.left;
|
||||
anchors.rightMargin: 2;
|
||||
|
||||
visible: root.purchaseStatus || root.ownedItemCount > 1;
|
||||
FiraSansRegular {
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
text: "#" + root.itemEdition;
|
||||
size: 15;
|
||||
color: "#cc6a6a6a";
|
||||
verticalAlignment: Text.AlignTop;
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: statusContainer;
|
||||
visible: root.purchaseStatus === "pending" || root.purchaseStatus === "invalidated";
|
||||
anchors.left: itemName.left;
|
||||
anchors.top: certificateContainer.bottom;
|
||||
anchors.topMargin: 8;
|
||||
|
@ -195,8 +222,6 @@ Item {
|
|||
"PENDING..."
|
||||
} else if (root.purchaseStatus === "invalidated") {
|
||||
"INVALIDATED"
|
||||
} else if (root.ownedItemCount > 1) {
|
||||
"<font color='#6a6a6a'>(#" + root.itemEdition + ")</font> <u>You own " + root.ownedItemCount + " others</u>"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
@ -207,8 +232,6 @@ Item {
|
|||
hifi.colors.blueAccent
|
||||
} else if (root.purchaseStatus === "invalidated") {
|
||||
hifi.colors.redAccent
|
||||
} else if (root.ownedItemCount > 1) {
|
||||
hifi.colors.blueAccent
|
||||
} else {
|
||||
hifi.colors.baseGray
|
||||
}
|
||||
|
@ -240,8 +263,6 @@ Item {
|
|||
hifi.colors.blueAccent
|
||||
} else if (root.purchaseStatus === "invalidated") {
|
||||
hifi.colors.redAccent
|
||||
} else if (root.ownedItemCount > 1) {
|
||||
hifi.colors.blueAccent
|
||||
} else {
|
||||
hifi.colors.baseGray
|
||||
}
|
||||
|
@ -257,8 +278,6 @@ Item {
|
|||
sendToPurchases({method: 'showPendingLightbox'});
|
||||
} else if (root.purchaseStatus === "invalidated") {
|
||||
sendToPurchases({method: 'showInvalidatedLightbox'});
|
||||
} else if (root.ownedItemCount > 1) {
|
||||
sendToPurchases({method: 'setFilterText', filterText: root.itemName});
|
||||
}
|
||||
}
|
||||
onEntered: {
|
||||
|
@ -268,9 +287,6 @@ Item {
|
|||
} else if (root.purchaseStatus === "invalidated") {
|
||||
statusText.color = hifi.colors.redAccent;
|
||||
statusIcon.color = hifi.colors.redAccent;
|
||||
} else if (root.ownedItemCount > 1) {
|
||||
statusText.color = hifi.colors.blueHighlight;
|
||||
statusIcon.color = hifi.colors.blueHighlight;
|
||||
}
|
||||
}
|
||||
onExited: {
|
||||
|
@ -280,9 +296,6 @@ Item {
|
|||
} else if (root.purchaseStatus === "invalidated") {
|
||||
statusText.color = hifi.colors.redHighlight;
|
||||
statusIcon.color = hifi.colors.redHighlight;
|
||||
} else if (root.ownedItemCount > 1) {
|
||||
statusText.color = hifi.colors.blueAccent;
|
||||
statusIcon.color = hifi.colors.blueAccent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import "../../../controls-uit" as HifiControlsUit
|
|||
import "../../../controls" as HifiControls
|
||||
import "../wallet" as HifiWallet
|
||||
import "../common" as HifiCommerceCommon
|
||||
import "../inspectionCertificate" as HifiInspectionCertificate
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
|
@ -31,54 +32,46 @@ Rectangle {
|
|||
property bool securityImageResultReceived: false;
|
||||
property bool purchasesReceived: false;
|
||||
property bool punctuationMode: false;
|
||||
property bool canRezCertifiedItems: false;
|
||||
property bool canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified;
|
||||
property bool pendingInventoryReply: true;
|
||||
property bool isShowingMyItems: false;
|
||||
property bool isDebuggingFirstUseTutorial: false;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
||||
onWalletStatusResult: {
|
||||
if (walletStatus === 0) {
|
||||
if (root.activeView !== "needsLogIn") {
|
||||
root.activeView = "needsLogIn";
|
||||
}
|
||||
} else if (walletStatus === 1) {
|
||||
if (root.activeView !== "notSetUp") {
|
||||
root.activeView = "notSetUp";
|
||||
notSetUpTimer.start();
|
||||
}
|
||||
} else if (walletStatus === 2) {
|
||||
if (root.activeView !== "passphraseModal") {
|
||||
root.activeView = "passphraseModal";
|
||||
}
|
||||
} else if (walletStatus === 3) {
|
||||
if ((Settings.getValue("isFirstUseOfPurchases", true) || root.isDebuggingFirstUseTutorial) && root.activeView !== "firstUseTutorial") {
|
||||
root.activeView = "firstUseTutorial";
|
||||
} else if (!Settings.getValue("isFirstUseOfPurchases", true) && root.activeView === "initialize") {
|
||||
root.activeView = "purchasesMain";
|
||||
commerce.inventory();
|
||||
}
|
||||
} else {
|
||||
console.log("ERROR in Purchases.qml: Unknown wallet status: " + walletStatus);
|
||||
}
|
||||
}
|
||||
|
||||
onLoginStatusResult: {
|
||||
if (!isLoggedIn && root.activeView !== "needsLogIn") {
|
||||
root.activeView = "needsLogIn";
|
||||
} else if (isLoggedIn) {
|
||||
root.activeView = "initialize";
|
||||
commerce.account();
|
||||
}
|
||||
}
|
||||
|
||||
onAccountResult: {
|
||||
if (result.status === "success") {
|
||||
commerce.getKeyFilePathIfExists();
|
||||
} else {
|
||||
// unsure how to handle a failure here. We definitely cannot proceed.
|
||||
}
|
||||
}
|
||||
|
||||
onKeyFilePathIfExistsResult: {
|
||||
if (path === "" && root.activeView !== "notSetUp") {
|
||||
root.activeView = "notSetUp";
|
||||
notSetUpTimer.start();
|
||||
} else if (path !== "" && root.activeView === "initialize") {
|
||||
commerce.getSecurityImage();
|
||||
}
|
||||
}
|
||||
|
||||
onSecurityImageResult: {
|
||||
securityImageResultReceived = true;
|
||||
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
|
||||
root.activeView = "notSetUp";
|
||||
notSetUpTimer.start();
|
||||
} else if (exists && root.activeView === "initialize") {
|
||||
commerce.getWalletAuthenticatedStatus();
|
||||
}
|
||||
}
|
||||
|
||||
onWalletAuthenticatedStatusResult: {
|
||||
if (!isAuthenticated && root.activeView !== "passphraseModal") {
|
||||
root.activeView = "passphraseModal";
|
||||
} else if (isAuthenticated) {
|
||||
sendToScript({method: 'purchases_getIsFirstUse'});
|
||||
commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,6 +114,19 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
HifiInspectionCertificate.InspectionCertificate {
|
||||
id: inspectionCertificate;
|
||||
z: 999;
|
||||
visible: false;
|
||||
anchors.fill: parent;
|
||||
|
||||
Connections {
|
||||
onSendToScript: {
|
||||
sendToScript(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiCommerceCommon.CommerceLightbox {
|
||||
id: lightboxPopup;
|
||||
visible: false;
|
||||
|
@ -165,6 +171,13 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
enabled: titleBarContainer.usernameDropdownVisible;
|
||||
anchors.fill: parent;
|
||||
onClicked: {
|
||||
titleBarContainer.usernameDropdownVisible = false;
|
||||
}
|
||||
}
|
||||
//
|
||||
// TITLE BAR END
|
||||
//
|
||||
|
@ -182,7 +195,7 @@ Rectangle {
|
|||
Component.onCompleted: {
|
||||
securityImageResultReceived = false;
|
||||
purchasesReceived = false;
|
||||
commerce.getLoginStatus();
|
||||
commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,7 +231,7 @@ Rectangle {
|
|||
onSendSignalToParent: {
|
||||
if (msg.method === "authSuccess") {
|
||||
root.activeView = "initialize";
|
||||
sendToScript({method: 'purchases_getIsFirstUse'});
|
||||
commerce.getWalletStatus();
|
||||
} else {
|
||||
sendToScript(msg);
|
||||
}
|
||||
|
@ -228,19 +241,16 @@ Rectangle {
|
|||
|
||||
FirstUseTutorial {
|
||||
id: firstUseTutorial;
|
||||
z: 999;
|
||||
visible: root.activeView === "firstUseTutorial";
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.fill: parent;
|
||||
|
||||
Connections {
|
||||
onSendSignalToParent: {
|
||||
switch (message.method) {
|
||||
case 'tutorial_skipClicked':
|
||||
case 'tutorial_finished':
|
||||
sendToScript({method: 'purchases_setIsFirstUse'});
|
||||
Settings.setValue("isFirstUseOfPurchases", false);
|
||||
root.activeView = "purchasesMain";
|
||||
commerce.inventory();
|
||||
break;
|
||||
|
@ -278,7 +288,7 @@ Rectangle {
|
|||
anchors.topMargin: 4;
|
||||
|
||||
RalewayRegular {
|
||||
id: myPurchasesText;
|
||||
id: myText;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 10;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
@ -286,7 +296,7 @@ Rectangle {
|
|||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 4;
|
||||
width: paintedWidth;
|
||||
text: "My Purchases";
|
||||
text: isShowingMyItems ? "My Items" : "My Purchases";
|
||||
color: hifi.colors.baseGray;
|
||||
size: 28;
|
||||
}
|
||||
|
@ -296,7 +306,7 @@ Rectangle {
|
|||
colorScheme: hifi.colorSchemes.faintGray;
|
||||
hasClearButton: true;
|
||||
hasRoundedBorder: true;
|
||||
anchors.left: myPurchasesText.right;
|
||||
anchors.left: myText.right;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
@ -331,7 +341,7 @@ Rectangle {
|
|||
ListModel {
|
||||
id: previousPurchasesModel;
|
||||
}
|
||||
ListModel {
|
||||
HifiCommerceCommon.SortableListModel {
|
||||
id: filteredPurchasesModel;
|
||||
}
|
||||
|
||||
|
@ -400,7 +410,7 @@ Rectangle {
|
|||
|
||||
ListView {
|
||||
id: purchasesContentsList;
|
||||
visible: purchasesModel.count !== 0;
|
||||
visible: (root.isShowingMyItems && filteredPurchasesModel.count !== 0) || (!root.isShowingMyItems && filteredPurchasesModel.count !== 0);
|
||||
clip: true;
|
||||
model: filteredPurchasesModel;
|
||||
// Anchors
|
||||
|
@ -417,6 +427,8 @@ Rectangle {
|
|||
itemHref: root_file_url;
|
||||
purchaseStatus: status;
|
||||
purchaseStatusChanged: statusChanged;
|
||||
itemEdition: model.edition_number;
|
||||
displayedItemCount: model.displayedItemCount;
|
||||
anchors.topMargin: 12;
|
||||
anchors.bottomMargin: 12;
|
||||
|
||||
|
@ -425,6 +437,8 @@ Rectangle {
|
|||
if (msg.method === 'purchases_itemInfoClicked') {
|
||||
sendToScript({method: 'purchases_itemInfoClicked', itemId: itemId});
|
||||
} else if (msg.method === 'purchases_itemCertificateClicked') {
|
||||
inspectionCertificate.visible = true;
|
||||
inspectionCertificate.isLightbox = true;
|
||||
sendToScript(msg);
|
||||
} else if (msg.method === "showInvalidatedLightbox") {
|
||||
lightboxPopup.titleText = "Item Invalidated";
|
||||
|
@ -448,9 +462,55 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: noItemsAlertContainer;
|
||||
visible: !purchasesContentsList.visible && root.purchasesReceived && root.isShowingMyItems && filterBar.text === "";
|
||||
anchors.top: filterBarContainer.bottom;
|
||||
anchors.topMargin: 12;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: parent.width;
|
||||
|
||||
// Explanitory text
|
||||
RalewayRegular {
|
||||
id: noItemsYet;
|
||||
text: "<b>You haven't submitted anything to the Marketplace yet!</b><br><br>Submit an item to the Marketplace to add it to My Items.";
|
||||
// Text size
|
||||
size: 22;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 150;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 24;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 24;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
}
|
||||
|
||||
// "Go To Marketplace" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: noItemsYet.bottom;
|
||||
anchors.topMargin: 20;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
width: parent.width * 2 / 3;
|
||||
height: 50;
|
||||
text: "Visit Marketplace";
|
||||
onClicked: {
|
||||
sendToScript({method: 'purchases_goToMarketplaceClicked'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: noPurchasesAlertContainer;
|
||||
visible: !purchasesContentsList.visible && root.purchasesReceived;
|
||||
visible: !purchasesContentsList.visible && root.purchasesReceived && !root.isShowingMyItems && filterBar.text === "";
|
||||
anchors.top: filterBarContainer.bottom;
|
||||
anchors.topMargin: 12;
|
||||
anchors.left: parent.left;
|
||||
|
@ -478,7 +538,7 @@ Rectangle {
|
|||
horizontalAlignment: Text.AlignHCenter;
|
||||
}
|
||||
|
||||
// "Set Up" button
|
||||
// "Go To Marketplace" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
|
@ -530,17 +590,43 @@ Rectangle {
|
|||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
|
||||
function populateDisplayedItemCounts() {
|
||||
var itemCountDictionary = {};
|
||||
var currentItemId;
|
||||
for (var i = 0; i < filteredPurchasesModel.count; i++) {
|
||||
currentItemId = filteredPurchasesModel.get(i).id;
|
||||
if (itemCountDictionary[currentItemId] === undefined) {
|
||||
itemCountDictionary[currentItemId] = 1;
|
||||
} else {
|
||||
itemCountDictionary[currentItemId]++;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < filteredPurchasesModel.count; i++) {
|
||||
filteredPurchasesModel.setProperty(i, "displayedItemCount", itemCountDictionary[filteredPurchasesModel.get(i).id]);
|
||||
}
|
||||
}
|
||||
|
||||
function sortByDate() {
|
||||
filteredPurchasesModel.sortColumnName = "purchase_date";
|
||||
filteredPurchasesModel.isSortingDescending = false;
|
||||
filteredPurchasesModel.quickSort();
|
||||
}
|
||||
|
||||
function buildFilteredPurchasesModel() {
|
||||
filteredPurchasesModel.clear();
|
||||
for (var i = 0; i < purchasesModel.count; i++) {
|
||||
if (purchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) {
|
||||
if (purchasesModel.get(i).status !== "confirmed") {
|
||||
if (purchasesModel.get(i).status !== "confirmed" && !root.isShowingMyItems) {
|
||||
filteredPurchasesModel.insert(0, purchasesModel.get(i));
|
||||
} else {
|
||||
} else if ((root.isShowingMyItems && purchasesModel.get(i).edition_number === -1) || !root.isShowingMyItems) {
|
||||
filteredPurchasesModel.append(purchasesModel.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
populateDisplayedItemCounts();
|
||||
sortByDate();
|
||||
}
|
||||
|
||||
function checkIfAnyItemStatusChanged() {
|
||||
|
@ -581,16 +667,14 @@ Rectangle {
|
|||
case 'updatePurchases':
|
||||
referrerURL = message.referrerURL;
|
||||
titleBarContainer.referrerURL = message.referrerURL;
|
||||
root.canRezCertifiedItems = message.canRezCertifiedItems;
|
||||
filterBar.text = message.filterText ? message.filterText : "";
|
||||
break;
|
||||
case 'purchases_getIsFirstUseResult':
|
||||
if (message.isFirstUseOfPurchases && root.activeView !== "firstUseTutorial") {
|
||||
root.activeView = "firstUseTutorial";
|
||||
} else if (!message.isFirstUseOfPurchases && root.activeView === "initialize") {
|
||||
root.activeView = "purchasesMain";
|
||||
commerce.inventory();
|
||||
}
|
||||
case 'inspectionCertificate_setMarketplaceId':
|
||||
case 'inspectionCertificate_setItemInfo':
|
||||
inspectionCertificate.fromScript(message);
|
||||
break;
|
||||
case 'purchases_showMyItems':
|
||||
root.isShowingMyItems = true;
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
||||
|
|
Binary file not shown.
After ![]() (image error) Size: 72 KiB |
Binary file not shown.
After ![]() (image error) Size: 81 KiB |
|
@ -90,7 +90,7 @@ Item {
|
|||
} else {
|
||||
// Error submitting new passphrase
|
||||
resetSubmitButton();
|
||||
passphraseSelection.setErrorText("Backend error");
|
||||
passphraseSelection.setErrorText("Current passphrase incorrect - try again");
|
||||
}
|
||||
} else {
|
||||
sendSignalToWallet(msg);
|
||||
|
@ -137,9 +137,10 @@ Item {
|
|||
width: 150;
|
||||
text: "Submit";
|
||||
onClicked: {
|
||||
if (passphraseSelection.validateAndSubmitPassphrase()) {
|
||||
passphraseSubmitButton.text = "Submitting...";
|
||||
passphraseSubmitButton.enabled = false;
|
||||
passphraseSubmitButton.text = "Submitting...";
|
||||
passphraseSubmitButton.enabled = false;
|
||||
if (!passphraseSelection.validateAndSubmitPassphrase()) {
|
||||
resetSubmitButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,6 +129,7 @@ Item {
|
|||
width: height;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
cache: false;
|
||||
|
||||
MouseArea {
|
||||
enabled: titleBarSecurityImage.visible;
|
||||
|
|
|
@ -26,6 +26,7 @@ Item {
|
|||
id: root;
|
||||
property bool isChangingPassphrase: false;
|
||||
property bool isShowingTip: false;
|
||||
property bool shouldImmediatelyFocus: true;
|
||||
|
||||
// This object is always used in a popup.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
|
@ -42,8 +43,8 @@ Item {
|
|||
passphrasePageSecurityImage.source = "image://security/securityImage";
|
||||
}
|
||||
|
||||
onWalletAuthenticatedStatusResult: {
|
||||
sendMessageToLightbox({method: 'statusResult', status: isAuthenticated});
|
||||
onChangePassphraseStatusResult: {
|
||||
sendMessageToLightbox({method: 'statusResult', status: changeSuccess});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,10 +54,8 @@ Item {
|
|||
// TODO: Fix this unlikely bug
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
if (root.isChangingPassphrase) {
|
||||
currentPassphraseField.focus = true;
|
||||
} else {
|
||||
passphraseField.focus = true;
|
||||
if (root.shouldImmediatelyFocus) {
|
||||
focusFirstTextField();
|
||||
}
|
||||
sendMessageToLightbox({method: 'disableHmdPreview'});
|
||||
} else {
|
||||
|
@ -311,7 +310,7 @@ Item {
|
|||
passphraseFieldAgain.error = false;
|
||||
currentPassphraseField.error = false;
|
||||
setErrorText("");
|
||||
commerce.setPassphrase(passphraseField.text);
|
||||
commerce.changePassphrase(currentPassphraseField.text, passphraseField.text);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -327,5 +326,13 @@ Item {
|
|||
setErrorText("");
|
||||
}
|
||||
|
||||
function focusFirstTextField() {
|
||||
if (root.isChangingPassphrase) {
|
||||
currentPassphraseField.focus = true;
|
||||
} else {
|
||||
passphraseField.focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
signal sendMessageToLightbox(var msg);
|
||||
}
|
||||
|
|
|
@ -280,6 +280,34 @@ Item {
|
|||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: removeHmdContainer;
|
||||
z: 998;
|
||||
visible: false;
|
||||
color: hifi.colors.blueHighlight;
|
||||
anchors.fill: backupInstructionsButton;
|
||||
radius: 5;
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
}
|
||||
|
||||
RalewayBold {
|
||||
anchors.fill: parent;
|
||||
text: "INSTRUCTIONS OPEN ON DESKTOP";
|
||||
size: 15;
|
||||
color: hifi.colors.white;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: removeHmdContainerTimer;
|
||||
interval: 5000;
|
||||
onTriggered: removeHmdContainer.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: backupInstructionsButton;
|
||||
text: "View Backup Instructions";
|
||||
|
@ -293,6 +321,9 @@ Item {
|
|||
|
||||
onClicked: {
|
||||
Qt.openUrlExternally("https://www.highfidelity.com/");
|
||||
Qt.openUrlExternally("file:///" + root.keyFilePath.substring(0, root.keyFilePath.lastIndexOf('/')));
|
||||
removeHmdContainer.visible = true;
|
||||
removeHmdContainerTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ Item {
|
|||
anchors.verticalCenter: parent.verticalCenter;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
cache: false;
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
|
|
|
@ -38,48 +38,41 @@ Rectangle {
|
|||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
||||
onWalletStatusResult: {
|
||||
if (walletStatus === 0) {
|
||||
if (root.activeView !== "needsLogIn") {
|
||||
root.activeView = "needsLogIn";
|
||||
}
|
||||
} else if (walletStatus === 1) {
|
||||
if (root.activeView !== "walletSetup") {
|
||||
root.activeView = "walletSetup";
|
||||
}
|
||||
} else if (walletStatus === 2) {
|
||||
if (root.activeView !== "passphraseModal") {
|
||||
root.activeView = "passphraseModal";
|
||||
}
|
||||
} else if (walletStatus === 3) {
|
||||
root.activeView = "walletHome";
|
||||
commerce.getSecurityImage();
|
||||
} else {
|
||||
console.log("ERROR in Wallet.qml: Unknown wallet status: " + walletStatus);
|
||||
}
|
||||
}
|
||||
|
||||
onLoginStatusResult: {
|
||||
if (!isLoggedIn && root.activeView !== "needsLogIn") {
|
||||
root.activeView = "needsLogIn";
|
||||
} else if (isLoggedIn) {
|
||||
root.activeView = "initialize";
|
||||
commerce.account();
|
||||
}
|
||||
}
|
||||
|
||||
onAccountResult: {
|
||||
if (result.status === "success") {
|
||||
commerce.getKeyFilePathIfExists();
|
||||
} else {
|
||||
// unsure how to handle a failure here. We definitely cannot proceed.
|
||||
}
|
||||
}
|
||||
|
||||
onKeyFilePathIfExistsResult: {
|
||||
if (path === "" && root.activeView !== "walletSetup") {
|
||||
root.activeView = "walletSetup";
|
||||
} else if (path !== "" && root.activeView === "initialize") {
|
||||
commerce.getSecurityImage();
|
||||
commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
onSecurityImageResult: {
|
||||
if (!exists && root.activeView !== "walletSetup") { // "If security image is not set up"
|
||||
root.activeView = "walletSetup";
|
||||
} else if (exists && root.activeView === "initialize") {
|
||||
commerce.getWalletAuthenticatedStatus();
|
||||
if (exists) {
|
||||
titleBarSecurityImage.source = "";
|
||||
titleBarSecurityImage.source = "image://security/securityImage";
|
||||
}
|
||||
}
|
||||
|
||||
onWalletAuthenticatedStatusResult: {
|
||||
if (!isAuthenticated && passphraseModal && root.activeView !== "passphraseModal") {
|
||||
root.activeView = "passphraseModal";
|
||||
} else if (isAuthenticated) {
|
||||
root.activeView = "walletHome";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SecurityImageModel {
|
||||
|
@ -149,6 +142,7 @@ Rectangle {
|
|||
anchors.bottomMargin: 6;
|
||||
width: height;
|
||||
mipmap: true;
|
||||
cache: false;
|
||||
|
||||
MouseArea {
|
||||
enabled: titleBarSecurityImage.visible;
|
||||
|
@ -179,7 +173,7 @@ Rectangle {
|
|||
if (msg.method === 'walletSetup_finished') {
|
||||
if (msg.referrer === '') {
|
||||
root.activeView = "initialize";
|
||||
commerce.getLoginStatus();
|
||||
commerce.getWalletStatus();
|
||||
} else if (msg.referrer === 'purchases') {
|
||||
sendToScript({method: 'goToPurchases'});
|
||||
}
|
||||
|
@ -254,7 +248,7 @@ Rectangle {
|
|||
color: hifi.colors.baseGray;
|
||||
|
||||
Component.onCompleted: {
|
||||
commerce.getLoginStatus();
|
||||
commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ Item {
|
|||
|
||||
id: root;
|
||||
property bool historyReceived: false;
|
||||
property int pendingCount: 0;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
@ -39,6 +40,8 @@ Item {
|
|||
if (result.status === 'success') {
|
||||
transactionHistoryModel.clear();
|
||||
transactionHistoryModel.append(result.data.history);
|
||||
|
||||
calculatePendingAndInvalidated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,55 +203,74 @@ Item {
|
|||
model: transactionHistoryModel;
|
||||
delegate: Item {
|
||||
width: parent.width;
|
||||
height: transactionText.height + 30;
|
||||
height: (model.transaction_type === "pendingCount" && root.pendingCount !== 0) ? 40 : ((model.status === "confirmed" || model.status === "invalidated") ? transactionText.height + 30 : 0);
|
||||
|
||||
HifiControlsUit.Separator {
|
||||
visible: index === 0;
|
||||
colorScheme: 1;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.top: parent.top;
|
||||
}
|
||||
|
||||
AnonymousProRegular {
|
||||
id: dateText;
|
||||
text: getFormattedDate(model.created_at * 1000);
|
||||
// Style
|
||||
size: 18;
|
||||
Item {
|
||||
visible: model.transaction_type === "pendingCount" && root.pendingCount !== 0;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 15;
|
||||
width: 118;
|
||||
height: paintedHeight;
|
||||
color: hifi.colors.blueAccent;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignRight;
|
||||
}
|
||||
width: parent.width;
|
||||
height: visible ? parent.height : 0;
|
||||
|
||||
AnonymousProRegular {
|
||||
id: transactionText;
|
||||
text: model.text;
|
||||
size: 18;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 15;
|
||||
anchors.left: dateText.right;
|
||||
anchors.leftMargin: 20;
|
||||
anchors.right: parent.right;
|
||||
height: paintedHeight;
|
||||
color: hifi.colors.baseGrayHighlight;
|
||||
wrapMode: Text.WordWrap;
|
||||
|
||||
onLinkActivated: {
|
||||
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
|
||||
AnonymousProRegular {
|
||||
id: pendingCountText;
|
||||
anchors.fill: parent;
|
||||
text: root.pendingCount + ' Transaction' + (root.pendingCount > 1 ? 's' : '') + ' Pending';
|
||||
size: 18;
|
||||
color: hifi.colors.blueAccent;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Separator {
|
||||
colorScheme: 1;
|
||||
Item {
|
||||
visible: model.transaction_type !== "pendingCount" && (model.status === "confirmed" || model.status === "invalidated");
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: parent.width;
|
||||
height: visible ? parent.height : 0;
|
||||
|
||||
AnonymousProRegular {
|
||||
id: dateText;
|
||||
text: model.created_at ? getFormattedDate(model.created_at * 1000) : "";
|
||||
// Style
|
||||
size: 18;
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 15;
|
||||
width: 118;
|
||||
height: paintedHeight;
|
||||
color: hifi.colors.blueAccent;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignRight;
|
||||
}
|
||||
|
||||
AnonymousProRegular {
|
||||
id: transactionText;
|
||||
text: model.text ? (model.status === "invalidated" ? ("INVALIDATED: " + model.text) : model.text) : "";
|
||||
size: 18;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 15;
|
||||
anchors.left: dateText.right;
|
||||
anchors.leftMargin: 20;
|
||||
anchors.right: parent.right;
|
||||
height: paintedHeight;
|
||||
color: model.status === "invalidated" ? hifi.colors.redAccent : hifi.colors.baseGrayHighlight;
|
||||
wrapMode: Text.WordWrap;
|
||||
font.strikeout: model.status === "invalidated";
|
||||
|
||||
onLinkActivated: {
|
||||
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Separator {
|
||||
colorScheme: 1;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
onAtYEndChanged: {
|
||||
|
@ -299,6 +321,19 @@ Item {
|
|||
return year + '-' + month + '-' + day + '<br>' + drawnHour + ':' + min + amOrPm;
|
||||
}
|
||||
|
||||
|
||||
function calculatePendingAndInvalidated(startingPendingCount) {
|
||||
var pendingCount = startingPendingCount ? startingPendingCount : 0;
|
||||
for (var i = 0; i < transactionHistoryModel.count; i++) {
|
||||
if (transactionHistoryModel.get(i).status === "pending") {
|
||||
pendingCount++;
|
||||
}
|
||||
}
|
||||
|
||||
root.pendingCount = pendingCount;
|
||||
transactionHistoryModel.insert(0, {"transaction_type": "pendingCount"});
|
||||
}
|
||||
|
||||
//
|
||||
// Function Name: fromScript()
|
||||
//
|
||||
|
|
|
@ -43,7 +43,7 @@ Item {
|
|||
if (!exists && root.lastPage === "step_2") {
|
||||
// ERROR! Invalid security image.
|
||||
root.activeView = "step_2";
|
||||
} else {
|
||||
} else if (exists) {
|
||||
titleBarSecurityImage.source = "";
|
||||
titleBarSecurityImage.source = "image://security/securityImage";
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ Item {
|
|||
Image {
|
||||
id: titleBarSecurityImage;
|
||||
source: "";
|
||||
visible: !securityImageTip.visible && titleBarSecurityImage.source !== "";
|
||||
visible: !securityImageTip.visible && titleBarSecurityImage.source !== "" && root.activeView !== "step_1" && root.activeView !== "step_2";
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 6;
|
||||
anchors.top: parent.top;
|
||||
|
@ -125,6 +125,7 @@ Item {
|
|||
anchors.bottomMargin: 6;
|
||||
width: height;
|
||||
mipmap: true;
|
||||
cache: false;
|
||||
|
||||
MouseArea {
|
||||
enabled: titleBarSecurityImage.visible;
|
||||
|
@ -422,6 +423,7 @@ Item {
|
|||
onClicked: {
|
||||
root.hasShownSecurityImageTip = true;
|
||||
securityImageTip.visible = false;
|
||||
passphraseSelection.focusFirstTextField();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -466,6 +468,7 @@ Item {
|
|||
|
||||
PassphraseSelection {
|
||||
id: passphraseSelection;
|
||||
shouldImmediatelyFocus: root.hasShownSecurityImageTip;
|
||||
isShowingTip: securityImageTip.visible;
|
||||
anchors.top: passphraseTitleHelper.bottom;
|
||||
anchors.topMargin: 30;
|
||||
|
@ -680,6 +683,7 @@ Item {
|
|||
instructions02Container.visible = true;
|
||||
keysReadyPageFinishButton.visible = true;
|
||||
Qt.openUrlExternally("https://www.highfidelity.com/");
|
||||
Qt.openUrlExternally("file:///" + root.keyFilePath.substring(0, root.keyFilePath.lastIndexOf('/')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ Item {
|
|||
readonly property int frameMarginRight: frame.decoration ? frame.decoration.frameMarginRight : 0
|
||||
readonly property int frameMarginTop: frame.decoration ? frame.decoration.frameMarginTop : 0
|
||||
readonly property int frameMarginBottom: frame.decoration ? frame.decoration.frameMarginBottom : 0
|
||||
readonly property int offsetCorrection: 20
|
||||
|
||||
// Frames always fill their parents, but their decorations may extend
|
||||
// beyond the window via negative margin sizes
|
||||
|
@ -73,7 +74,7 @@ Item {
|
|||
Rectangle {
|
||||
id: sizeOutline
|
||||
x: -frameMarginLeft
|
||||
y: -frameMarginTop
|
||||
y: -frameMarginTop - offsetCorrection
|
||||
width: window ? window.width + frameMarginLeft + frameMarginRight + 2 : 0
|
||||
height: window ? window.height + frameMarginTop + frameMarginBottom + 2 : 0
|
||||
color: hifi.colors.baseGrayHighlight15
|
||||
|
|
|
@ -167,6 +167,7 @@
|
|||
#include "scripting/ControllerScriptingInterface.h"
|
||||
#include "scripting/RatesScriptingInterface.h"
|
||||
#include "scripting/SelectionScriptingInterface.h"
|
||||
#include "scripting/WalletScriptingInterface.h"
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
#include "SpeechRecognizer.h"
|
||||
#endif
|
||||
|
@ -686,6 +687,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::set<ContextOverlayInterface>();
|
||||
DependencyManager::set<Ledger>();
|
||||
DependencyManager::set<Wallet>();
|
||||
DependencyManager::set<WalletScriptingInterface>();
|
||||
|
||||
DependencyManager::set<FadeEffect>();
|
||||
|
||||
|
@ -1544,15 +1546,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||
|
||||
properties["fps"] = _frameCounter.rate();
|
||||
properties["target_frame_rate"] = getTargetFrameRate();
|
||||
properties["render_rate"] = displayPlugin->renderRate();
|
||||
properties["render_rate"] = _renderLoopCounter.rate();
|
||||
properties["target_render_rate"] = getTargetRenderFrameRate();
|
||||
properties["present_rate"] = displayPlugin->presentRate();
|
||||
properties["new_frame_present_rate"] = displayPlugin->newFramePresentRate();
|
||||
properties["dropped_frame_rate"] = displayPlugin->droppedFrameRate();
|
||||
properties["stutter_rate"] = displayPlugin->stutterRate();
|
||||
properties["sim_rate"] = getAverageSimsPerSecond();
|
||||
properties["avatar_sim_rate"] = getAvatarSimrate();
|
||||
properties["game_rate"] = getGameLoopRate();
|
||||
properties["has_async_reprojection"] = displayPlugin->hasAsyncReprojection();
|
||||
properties["hardware_stats"] = displayPlugin->getHardwareStats();
|
||||
|
||||
|
@ -2331,6 +2331,7 @@ void Application::initializeUi() {
|
|||
surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
|
||||
surfaceContext->setContextProperty("Selection", DependencyManager::get<SelectionScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
|
||||
surfaceContext->setContextProperty("Wallet", DependencyManager::get<WalletScriptingInterface>().data());
|
||||
|
||||
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
|
||||
surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get()));
|
||||
|
@ -2475,7 +2476,7 @@ void Application::updateCamera(RenderArgs& renderArgs) {
|
|||
}
|
||||
// Update camera position
|
||||
if (!isHMDMode()) {
|
||||
_myCamera.update(1.0f / _frameCounter.rate());
|
||||
_myCamera.update();
|
||||
}
|
||||
|
||||
renderArgs._cameraMode = (int8_t)_myCamera.getMode();
|
||||
|
@ -3843,7 +3844,7 @@ void Application::idle() {
|
|||
if (displayPlugin) {
|
||||
PROFILE_COUNTER_IF_CHANGED(app, "present", float, displayPlugin->presentRate());
|
||||
}
|
||||
PROFILE_COUNTER_IF_CHANGED(app, "fps", float, _frameCounter.rate());
|
||||
PROFILE_COUNTER_IF_CHANGED(app, "renderLoopRate", float, _renderLoopCounter.rate());
|
||||
PROFILE_COUNTER_IF_CHANGED(app, "currentDownloads", int, ResourceCache::getLoadingRequests().length());
|
||||
PROFILE_COUNTER_IF_CHANGED(app, "pendingDownloads", int, ResourceCache::getPendingRequestCount());
|
||||
PROFILE_COUNTER_IF_CHANGED(app, "currentProcessing", int, DependencyManager::get<StatTracker>()->getStat("Processing").toInt());
|
||||
|
@ -3879,8 +3880,6 @@ void Application::idle() {
|
|||
|
||||
Stats::getInstance()->updateStats();
|
||||
|
||||
_simCounter.increment();
|
||||
|
||||
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
|
||||
// details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing
|
||||
// details normally.
|
||||
|
@ -3960,6 +3959,7 @@ void Application::idle() {
|
|||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN));
|
||||
cameraMenuChanged();
|
||||
}
|
||||
_gameLoopCounter.increment();
|
||||
}
|
||||
|
||||
ivec2 Application::getMouse() const {
|
||||
|
@ -4646,12 +4646,7 @@ static bool domainLoadingInProgress = false;
|
|||
|
||||
void Application::update(float deltaTime) {
|
||||
|
||||
PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1);
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::update()");
|
||||
|
||||
updateLOD(deltaTime);
|
||||
PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_renderFrameCount + 1);
|
||||
|
||||
if (!_physicsEnabled) {
|
||||
if (!domainLoadingInProgress) {
|
||||
|
@ -4682,6 +4677,7 @@ void Application::update(float deltaTime) {
|
|||
PROFILE_ASYNC_END(app, "Scene Loading", "");
|
||||
}
|
||||
|
||||
auto myAvatar = getMyAvatar();
|
||||
{
|
||||
PerformanceTimer perfTimer("devices");
|
||||
|
||||
|
@ -4715,129 +4711,127 @@ void Application::update(float deltaTime) {
|
|||
_lastFaceTrackerUpdate = 0;
|
||||
}
|
||||
|
||||
}
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
|
||||
auto myAvatar = getMyAvatar();
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
controller::InputCalibrationData calibrationData = {
|
||||
myAvatar->getSensorToWorldMatrix(),
|
||||
createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()),
|
||||
myAvatar->getHMDSensorMatrix(),
|
||||
myAvatar->getCenterEyeCalibrationMat(),
|
||||
myAvatar->getHeadCalibrationMat(),
|
||||
myAvatar->getSpine2CalibrationMat(),
|
||||
myAvatar->getHipsCalibrationMat(),
|
||||
myAvatar->getLeftFootCalibrationMat(),
|
||||
myAvatar->getRightFootCalibrationMat(),
|
||||
myAvatar->getRightArmCalibrationMat(),
|
||||
myAvatar->getLeftArmCalibrationMat(),
|
||||
myAvatar->getRightHandCalibrationMat(),
|
||||
myAvatar->getLeftHandCalibrationMat()
|
||||
};
|
||||
|
||||
controller::InputCalibrationData calibrationData = {
|
||||
myAvatar->getSensorToWorldMatrix(),
|
||||
createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()),
|
||||
myAvatar->getHMDSensorMatrix(),
|
||||
myAvatar->getCenterEyeCalibrationMat(),
|
||||
myAvatar->getHeadCalibrationMat(),
|
||||
myAvatar->getSpine2CalibrationMat(),
|
||||
myAvatar->getHipsCalibrationMat(),
|
||||
myAvatar->getLeftFootCalibrationMat(),
|
||||
myAvatar->getRightFootCalibrationMat(),
|
||||
myAvatar->getRightArmCalibrationMat(),
|
||||
myAvatar->getLeftArmCalibrationMat(),
|
||||
myAvatar->getRightHandCalibrationMat(),
|
||||
myAvatar->getLeftHandCalibrationMat()
|
||||
};
|
||||
|
||||
InputPluginPointer keyboardMousePlugin;
|
||||
for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
|
||||
if (inputPlugin->getName() == KeyboardMouseDevice::NAME) {
|
||||
keyboardMousePlugin = inputPlugin;
|
||||
} else if (inputPlugin->isActive()) {
|
||||
inputPlugin->pluginUpdate(deltaTime, calibrationData);
|
||||
}
|
||||
}
|
||||
|
||||
userInputMapper->setInputCalibrationData(calibrationData);
|
||||
userInputMapper->update(deltaTime);
|
||||
|
||||
if (keyboardMousePlugin && keyboardMousePlugin->isActive()) {
|
||||
keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData);
|
||||
}
|
||||
|
||||
// Transfer the user inputs to the driveKeys
|
||||
// FIXME can we drop drive keys and just have the avatar read the action states directly?
|
||||
myAvatar->clearDriveKeys();
|
||||
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
|
||||
if (!_controllerScriptingInterface->areActionsCaptured()) {
|
||||
myAvatar->setDriveKey(MyAvatar::TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z));
|
||||
myAvatar->setDriveKey(MyAvatar::TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y));
|
||||
myAvatar->setDriveKey(MyAvatar::TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X));
|
||||
if (deltaTime > FLT_EPSILON) {
|
||||
myAvatar->setDriveKey(MyAvatar::PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH));
|
||||
myAvatar->setDriveKey(MyAvatar::YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW));
|
||||
myAvatar->setDriveKey(MyAvatar::STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW));
|
||||
InputPluginPointer keyboardMousePlugin;
|
||||
for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
|
||||
if (inputPlugin->getName() == KeyboardMouseDevice::NAME) {
|
||||
keyboardMousePlugin = inputPlugin;
|
||||
} else if (inputPlugin->isActive()) {
|
||||
inputPlugin->pluginUpdate(deltaTime, calibrationData);
|
||||
}
|
||||
}
|
||||
myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
|
||||
}
|
||||
|
||||
static const std::vector<controller::Action> avatarControllerActions = {
|
||||
controller::Action::LEFT_HAND,
|
||||
controller::Action::RIGHT_HAND,
|
||||
controller::Action::LEFT_FOOT,
|
||||
controller::Action::RIGHT_FOOT,
|
||||
controller::Action::HIPS,
|
||||
controller::Action::SPINE2,
|
||||
controller::Action::HEAD,
|
||||
controller::Action::LEFT_HAND_THUMB1,
|
||||
controller::Action::LEFT_HAND_THUMB2,
|
||||
controller::Action::LEFT_HAND_THUMB3,
|
||||
controller::Action::LEFT_HAND_THUMB4,
|
||||
controller::Action::LEFT_HAND_INDEX1,
|
||||
controller::Action::LEFT_HAND_INDEX2,
|
||||
controller::Action::LEFT_HAND_INDEX3,
|
||||
controller::Action::LEFT_HAND_INDEX4,
|
||||
controller::Action::LEFT_HAND_MIDDLE1,
|
||||
controller::Action::LEFT_HAND_MIDDLE2,
|
||||
controller::Action::LEFT_HAND_MIDDLE3,
|
||||
controller::Action::LEFT_HAND_MIDDLE4,
|
||||
controller::Action::LEFT_HAND_RING1,
|
||||
controller::Action::LEFT_HAND_RING2,
|
||||
controller::Action::LEFT_HAND_RING3,
|
||||
controller::Action::LEFT_HAND_RING4,
|
||||
controller::Action::LEFT_HAND_PINKY1,
|
||||
controller::Action::LEFT_HAND_PINKY2,
|
||||
controller::Action::LEFT_HAND_PINKY3,
|
||||
controller::Action::LEFT_HAND_PINKY4,
|
||||
controller::Action::RIGHT_HAND_THUMB1,
|
||||
controller::Action::RIGHT_HAND_THUMB2,
|
||||
controller::Action::RIGHT_HAND_THUMB3,
|
||||
controller::Action::RIGHT_HAND_THUMB4,
|
||||
controller::Action::RIGHT_HAND_INDEX1,
|
||||
controller::Action::RIGHT_HAND_INDEX2,
|
||||
controller::Action::RIGHT_HAND_INDEX3,
|
||||
controller::Action::RIGHT_HAND_INDEX4,
|
||||
controller::Action::RIGHT_HAND_MIDDLE1,
|
||||
controller::Action::RIGHT_HAND_MIDDLE2,
|
||||
controller::Action::RIGHT_HAND_MIDDLE3,
|
||||
controller::Action::RIGHT_HAND_MIDDLE4,
|
||||
controller::Action::RIGHT_HAND_RING1,
|
||||
controller::Action::RIGHT_HAND_RING2,
|
||||
controller::Action::RIGHT_HAND_RING3,
|
||||
controller::Action::RIGHT_HAND_RING4,
|
||||
controller::Action::RIGHT_HAND_PINKY1,
|
||||
controller::Action::RIGHT_HAND_PINKY2,
|
||||
controller::Action::RIGHT_HAND_PINKY3,
|
||||
controller::Action::RIGHT_HAND_PINKY4,
|
||||
controller::Action::LEFT_ARM,
|
||||
controller::Action::RIGHT_ARM,
|
||||
controller::Action::LEFT_SHOULDER,
|
||||
controller::Action::RIGHT_SHOULDER,
|
||||
controller::Action::LEFT_FORE_ARM,
|
||||
controller::Action::RIGHT_FORE_ARM,
|
||||
controller::Action::LEFT_LEG,
|
||||
controller::Action::RIGHT_LEG,
|
||||
controller::Action::LEFT_UP_LEG,
|
||||
controller::Action::RIGHT_UP_LEG,
|
||||
controller::Action::LEFT_TOE_BASE,
|
||||
controller::Action::RIGHT_TOE_BASE
|
||||
};
|
||||
userInputMapper->setInputCalibrationData(calibrationData);
|
||||
userInputMapper->update(deltaTime);
|
||||
|
||||
// copy controller poses from userInputMapper to myAvatar.
|
||||
glm::mat4 myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
|
||||
glm::mat4 worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix());
|
||||
glm::mat4 avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
|
||||
for (auto& action : avatarControllerActions) {
|
||||
controller::Pose pose = userInputMapper->getPoseState(action);
|
||||
myAvatar->setControllerPoseInSensorFrame(action, pose.transform(avatarToSensorMatrix));
|
||||
if (keyboardMousePlugin && keyboardMousePlugin->isActive()) {
|
||||
keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData);
|
||||
}
|
||||
|
||||
// Transfer the user inputs to the driveKeys
|
||||
// FIXME can we drop drive keys and just have the avatar read the action states directly?
|
||||
myAvatar->clearDriveKeys();
|
||||
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
|
||||
if (!_controllerScriptingInterface->areActionsCaptured()) {
|
||||
myAvatar->setDriveKey(MyAvatar::TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z));
|
||||
myAvatar->setDriveKey(MyAvatar::TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y));
|
||||
myAvatar->setDriveKey(MyAvatar::TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X));
|
||||
if (deltaTime > FLT_EPSILON) {
|
||||
myAvatar->setDriveKey(MyAvatar::PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH));
|
||||
myAvatar->setDriveKey(MyAvatar::YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW));
|
||||
myAvatar->setDriveKey(MyAvatar::STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW));
|
||||
}
|
||||
}
|
||||
myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
|
||||
}
|
||||
|
||||
static const std::vector<controller::Action> avatarControllerActions = {
|
||||
controller::Action::LEFT_HAND,
|
||||
controller::Action::RIGHT_HAND,
|
||||
controller::Action::LEFT_FOOT,
|
||||
controller::Action::RIGHT_FOOT,
|
||||
controller::Action::HIPS,
|
||||
controller::Action::SPINE2,
|
||||
controller::Action::HEAD,
|
||||
controller::Action::LEFT_HAND_THUMB1,
|
||||
controller::Action::LEFT_HAND_THUMB2,
|
||||
controller::Action::LEFT_HAND_THUMB3,
|
||||
controller::Action::LEFT_HAND_THUMB4,
|
||||
controller::Action::LEFT_HAND_INDEX1,
|
||||
controller::Action::LEFT_HAND_INDEX2,
|
||||
controller::Action::LEFT_HAND_INDEX3,
|
||||
controller::Action::LEFT_HAND_INDEX4,
|
||||
controller::Action::LEFT_HAND_MIDDLE1,
|
||||
controller::Action::LEFT_HAND_MIDDLE2,
|
||||
controller::Action::LEFT_HAND_MIDDLE3,
|
||||
controller::Action::LEFT_HAND_MIDDLE4,
|
||||
controller::Action::LEFT_HAND_RING1,
|
||||
controller::Action::LEFT_HAND_RING2,
|
||||
controller::Action::LEFT_HAND_RING3,
|
||||
controller::Action::LEFT_HAND_RING4,
|
||||
controller::Action::LEFT_HAND_PINKY1,
|
||||
controller::Action::LEFT_HAND_PINKY2,
|
||||
controller::Action::LEFT_HAND_PINKY3,
|
||||
controller::Action::LEFT_HAND_PINKY4,
|
||||
controller::Action::RIGHT_HAND_THUMB1,
|
||||
controller::Action::RIGHT_HAND_THUMB2,
|
||||
controller::Action::RIGHT_HAND_THUMB3,
|
||||
controller::Action::RIGHT_HAND_THUMB4,
|
||||
controller::Action::RIGHT_HAND_INDEX1,
|
||||
controller::Action::RIGHT_HAND_INDEX2,
|
||||
controller::Action::RIGHT_HAND_INDEX3,
|
||||
controller::Action::RIGHT_HAND_INDEX4,
|
||||
controller::Action::RIGHT_HAND_MIDDLE1,
|
||||
controller::Action::RIGHT_HAND_MIDDLE2,
|
||||
controller::Action::RIGHT_HAND_MIDDLE3,
|
||||
controller::Action::RIGHT_HAND_MIDDLE4,
|
||||
controller::Action::RIGHT_HAND_RING1,
|
||||
controller::Action::RIGHT_HAND_RING2,
|
||||
controller::Action::RIGHT_HAND_RING3,
|
||||
controller::Action::RIGHT_HAND_RING4,
|
||||
controller::Action::RIGHT_HAND_PINKY1,
|
||||
controller::Action::RIGHT_HAND_PINKY2,
|
||||
controller::Action::RIGHT_HAND_PINKY3,
|
||||
controller::Action::RIGHT_HAND_PINKY4,
|
||||
controller::Action::LEFT_ARM,
|
||||
controller::Action::RIGHT_ARM,
|
||||
controller::Action::LEFT_SHOULDER,
|
||||
controller::Action::RIGHT_SHOULDER,
|
||||
controller::Action::LEFT_FORE_ARM,
|
||||
controller::Action::RIGHT_FORE_ARM,
|
||||
controller::Action::LEFT_LEG,
|
||||
controller::Action::RIGHT_LEG,
|
||||
controller::Action::LEFT_UP_LEG,
|
||||
controller::Action::RIGHT_UP_LEG,
|
||||
controller::Action::LEFT_TOE_BASE,
|
||||
controller::Action::RIGHT_TOE_BASE
|
||||
};
|
||||
|
||||
// copy controller poses from userInputMapper to myAvatar.
|
||||
glm::mat4 myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
|
||||
glm::mat4 worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix());
|
||||
glm::mat4 avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
|
||||
for (auto& action : avatarControllerActions) {
|
||||
controller::Pose pose = userInputMapper->getPoseState(action);
|
||||
myAvatar->setControllerPoseInSensorFrame(action, pose.transform(avatarToSensorMatrix));
|
||||
}
|
||||
}
|
||||
|
||||
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
||||
|
@ -4845,117 +4839,123 @@ void Application::update(float deltaTime) {
|
|||
|
||||
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
|
||||
|
||||
if (_physicsEnabled) {
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation_physics, "Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
|
||||
PerformanceTimer perfTimer("physics");
|
||||
if (_physicsEnabled) {
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation_physics, "UpdateStates", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation_physics, "UpdateStates", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
PerformanceTimer perfTimer("updateStates)");
|
||||
static VectorOfMotionStates motionStates;
|
||||
_entitySimulation->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
_entitySimulation->deleteObjectsRemovedFromPhysics();
|
||||
|
||||
PerformanceTimer perfTimer("updateStates)");
|
||||
static VectorOfMotionStates motionStates;
|
||||
_entitySimulation->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
_entitySimulation->deleteObjectsRemovedFromPhysics();
|
||||
getEntities()->getTree()->withReadLock([&] {
|
||||
_entitySimulation->getObjectsToAddToPhysics(motionStates);
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
|
||||
getEntities()->getTree()->withReadLock([&] {
|
||||
_entitySimulation->getObjectsToAddToPhysics(motionStates);
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
|
||||
});
|
||||
getEntities()->getTree()->withReadLock([&] {
|
||||
_entitySimulation->getObjectsToChange(motionStates);
|
||||
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
|
||||
_entitySimulation->setObjectsToChange(stillNeedChange);
|
||||
});
|
||||
|
||||
_entitySimulation->applyDynamicChanges();
|
||||
|
||||
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
avatarManager->getObjectsToAddToPhysics(motionStates);
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
avatarManager->getObjectsToChange(motionStates);
|
||||
_physicsEngine->changeObjects(motionStates);
|
||||
|
||||
myAvatar->prepareForPhysicsSimulation();
|
||||
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
|
||||
dynamic->prepareForPhysicsSimulation();
|
||||
});
|
||||
}
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation_physics, "StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
PerformanceTimer perfTimer("stepSimulation");
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_physicsEngine->stepSimulation();
|
||||
});
|
||||
}
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation_physics, "HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
PerformanceTimer perfTimer("harvestChanges");
|
||||
if (_physicsEngine->hasOutgoingChanges()) {
|
||||
// grab the collision events BEFORE handleOutgoingChanges() because at this point
|
||||
// we have a better idea of which objects we own or should own.
|
||||
auto& collisionEvents = _physicsEngine->getCollisionEvents();
|
||||
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
PerformanceTimer perfTimer("handleOutgoingChanges");
|
||||
|
||||
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();
|
||||
_entitySimulation->handleChangedMotionStates(outgoingChanges);
|
||||
avatarManager->handleChangedMotionStates(outgoingChanges);
|
||||
|
||||
const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates();
|
||||
_entitySimulation->handleDeactivatedMotionStates(deactivations);
|
||||
});
|
||||
getEntities()->getTree()->withReadLock([&] {
|
||||
_entitySimulation->getObjectsToChange(motionStates);
|
||||
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
|
||||
_entitySimulation->setObjectsToChange(stillNeedChange);
|
||||
});
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
// handleCollisionEvents() AFTER handleOutgoinChanges()
|
||||
PerformanceTimer perfTimer("entities");
|
||||
avatarManager->handleCollisionEvents(collisionEvents);
|
||||
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
|
||||
// deadlock.)
|
||||
_entitySimulation->handleCollisionEvents(collisionEvents);
|
||||
_entitySimulation->applyDynamicChanges();
|
||||
|
||||
// NOTE: the getEntities()->update() call below will wait for lock
|
||||
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
|
||||
getEntities()->update(true); // update the models...
|
||||
}
|
||||
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
avatarManager->getObjectsToAddToPhysics(motionStates);
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
avatarManager->getObjectsToChange(motionStates);
|
||||
_physicsEngine->changeObjects(motionStates);
|
||||
|
||||
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) {
|
||||
_physicsEngine->harvestPerformanceStats();
|
||||
}
|
||||
// NOTE: the PhysicsEngine stats are written to stdout NOT to Qt log framework
|
||||
_physicsEngine->dumpStatsIfNecessary();
|
||||
myAvatar->prepareForPhysicsSimulation();
|
||||
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
|
||||
dynamic->prepareForPhysicsSimulation();
|
||||
});
|
||||
}
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation_physics, "StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
PerformanceTimer perfTimer("stepSimulation");
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_physicsEngine->stepSimulation();
|
||||
});
|
||||
}
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation_physics, "HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
PerformanceTimer perfTimer("harvestChanges");
|
||||
if (_physicsEngine->hasOutgoingChanges()) {
|
||||
// grab the collision events BEFORE handleOutgoingChanges() because at this point
|
||||
// we have a better idea of which objects we own or should own.
|
||||
auto& collisionEvents = _physicsEngine->getCollisionEvents();
|
||||
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
PerformanceTimer perfTimer("handleOutgoingChanges");
|
||||
|
||||
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();
|
||||
_entitySimulation->handleChangedMotionStates(outgoingChanges);
|
||||
avatarManager->handleChangedMotionStates(outgoingChanges);
|
||||
|
||||
const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates();
|
||||
_entitySimulation->handleDeactivatedMotionStates(deactivations);
|
||||
});
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
// handleCollisionEvents() AFTER handleOutgoinChanges()
|
||||
PerformanceTimer perfTimer("entities");
|
||||
avatarManager->handleCollisionEvents(collisionEvents);
|
||||
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
|
||||
// deadlock.)
|
||||
_entitySimulation->handleCollisionEvents(collisionEvents);
|
||||
|
||||
// NOTE: the getEntities()->update() call below will wait for lock
|
||||
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
|
||||
getEntities()->update(true); // update the models...
|
||||
}
|
||||
|
||||
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) {
|
||||
_physicsEngine->harvestPerformanceStats();
|
||||
}
|
||||
// NOTE: the PhysicsEngine stats are written to stdout NOT to Qt log framework
|
||||
_physicsEngine->dumpStatsIfNecessary();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// update the rendering without any simulation
|
||||
getEntities()->update(false);
|
||||
}
|
||||
} else {
|
||||
// update the rendering without any simulation
|
||||
getEntities()->update(false);
|
||||
}
|
||||
|
||||
// AvatarManager update
|
||||
{
|
||||
PerformanceTimer perfTimer("AvatarManager");
|
||||
_avatarSimCounter.increment();
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("otherAvatars");
|
||||
PROFILE_RANGE_EX(simulation, "OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
avatarManager->updateOtherAvatars(deltaTime);
|
||||
}
|
||||
|
||||
qApp->updateMyAvatarLookAtPosition();
|
||||
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation, "MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
PerformanceTimer perfTimer("MyAvatar");
|
||||
qApp->updateMyAvatarLookAtPosition();
|
||||
avatarManager->updateMyAvatar(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
PerformanceTimer perfTimer("misc");
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::update()");
|
||||
|
||||
updateLOD(deltaTime);
|
||||
|
||||
// TODO: break these out into distinct perfTimers when they prove interesting
|
||||
{
|
||||
PROFILE_RANGE(app, "RayPickManager");
|
||||
_rayPickManager.update();
|
||||
|
@ -5387,7 +5387,7 @@ bool Application::isHMDMode() const {
|
|||
return getActiveDisplayPlugin()->isHmd();
|
||||
}
|
||||
|
||||
float Application::getTargetFrameRate() const { return getActiveDisplayPlugin()->getTargetFrameRate(); }
|
||||
float Application::getTargetRenderFrameRate() const { return getActiveDisplayPlugin()->getTargetFrameRate(); }
|
||||
|
||||
QRect Application::getDesirableApplicationGeometry() const {
|
||||
QRect applicationGeometry = getWindow()->geometry();
|
||||
|
@ -5867,6 +5867,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance());
|
||||
scriptEngine->registerGlobalObject("Selection", DependencyManager::get<SelectionScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
|
||||
scriptEngine->registerGlobalObject("Wallet", DependencyManager::get<WalletScriptingInterface>().data());
|
||||
|
||||
qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue);
|
||||
|
||||
|
|
|
@ -194,10 +194,9 @@ public:
|
|||
|
||||
Overlays& getOverlays() { return _overlays; }
|
||||
|
||||
|
||||
size_t getFrameCount() const { return _frameCount; }
|
||||
float getFps() const { return _frameCounter.rate(); }
|
||||
float getTargetFrameRate() const; // frames/second
|
||||
size_t getRenderFrameCount() const { return _renderFrameCount; }
|
||||
float getRenderLoopRate() const { return _renderLoopCounter.rate(); }
|
||||
float getTargetRenderFrameRate() const; // frames/second
|
||||
|
||||
float getFieldOfView() { return _fieldOfView.get(); }
|
||||
void setFieldOfView(float fov);
|
||||
|
@ -268,8 +267,7 @@ public:
|
|||
|
||||
void updateMyAvatarLookAtPosition();
|
||||
|
||||
float getAvatarSimrate() const { return _avatarSimCounter.rate(); }
|
||||
float getAverageSimsPerSecond() const { return _simCounter.rate(); }
|
||||
float getGameLoopRate() const { return _gameLoopCounter.rate(); }
|
||||
|
||||
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f);
|
||||
void takeSecondaryCameraSnapshot();
|
||||
|
@ -531,12 +529,11 @@ private:
|
|||
QUndoStack _undoStack;
|
||||
UndoStackScriptingInterface _undoStackScriptingInterface;
|
||||
|
||||
uint32_t _frameCount { 0 };
|
||||
uint32_t _renderFrameCount { 0 };
|
||||
|
||||
// Frame Rate Measurement
|
||||
RateCounter<> _frameCounter;
|
||||
RateCounter<> _avatarSimCounter;
|
||||
RateCounter<> _simCounter;
|
||||
RateCounter<500> _renderLoopCounter;
|
||||
RateCounter<500> _gameLoopCounter;
|
||||
|
||||
FrameTimingsScriptingInterface _frameTimingsScriptingInterface;
|
||||
|
||||
|
|
|
@ -33,11 +33,11 @@ void Application::paintGL() {
|
|||
return;
|
||||
}
|
||||
|
||||
_frameCount++;
|
||||
_renderFrameCount++;
|
||||
_lastTimeRendered.start();
|
||||
|
||||
auto lastPaintBegin = usecTimestampNow();
|
||||
PROFILE_RANGE_EX(render, __FUNCTION__, 0xff0000ff, (uint64_t)_frameCount);
|
||||
PROFILE_RANGE_EX(render, __FUNCTION__, 0xff0000ff, (uint64_t)_renderFrameCount);
|
||||
PerformanceTimer perfTimer("paintGL");
|
||||
|
||||
if (nullptr == _displayPlugin) {
|
||||
|
@ -54,7 +54,7 @@ void Application::paintGL() {
|
|||
PROFILE_RANGE(render, "/pluginBeginFrameRender");
|
||||
// If a display plugin loses it's underlying support, it
|
||||
// needs to be able to signal us to not use it
|
||||
if (!displayPlugin->beginFrameRender(_frameCount)) {
|
||||
if (!displayPlugin->beginFrameRender(_renderFrameCount)) {
|
||||
updateDisplayMode();
|
||||
return;
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ void Application::paintGL() {
|
|||
|
||||
{
|
||||
PROFILE_RANGE(render, "/updateCompositor");
|
||||
getApplicationCompositor().setFrameInfo(_frameCount, eyeToWorld, sensorToWorld);
|
||||
getApplicationCompositor().setFrameInfo(_renderFrameCount, eyeToWorld, sensorToWorld);
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer finalFramebuffer;
|
||||
|
@ -141,7 +141,7 @@ void Application::paintGL() {
|
|||
}
|
||||
|
||||
auto frame = _gpuContext->endFrame();
|
||||
frame->frameIndex = _frameCount;
|
||||
frame->frameIndex = _renderFrameCount;
|
||||
frame->framebuffer = finalFramebuffer;
|
||||
frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer) {
|
||||
DependencyManager::get<FramebufferCache>()->releaseFramebuffer(framebuffer);
|
||||
|
@ -152,7 +152,7 @@ void Application::paintGL() {
|
|||
{
|
||||
PROFILE_RANGE(render, "/pluginOutput");
|
||||
PerformanceTimer perfTimer("pluginOutput");
|
||||
_frameCounter.increment();
|
||||
_renderLoopCounter.increment();
|
||||
displayPlugin->submitFrame(frame);
|
||||
}
|
||||
|
||||
|
|
|
@ -252,11 +252,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
qApp->getMain3DScene()->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
_avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC;
|
||||
_numAvatarsUpdated = numAvatarsUpdated;
|
||||
_numAvatarsNotUpdated = numAVatarsNotUpdated;
|
||||
|
||||
simulateAvatarFades(deltaTime);
|
||||
|
||||
_avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC;
|
||||
}
|
||||
|
||||
void AvatarManager::postUpdate(float deltaTime, const render::ScenePointer& scene) {
|
||||
|
|
|
@ -164,11 +164,7 @@ void Ledger::historySuccess(QNetworkReply& reply) {
|
|||
// turns out on my machine, toLocalTime convert to some weird timezone, yet the
|
||||
// systemTimeZone is correct. To avoid a strange bug with other's systems too, lets
|
||||
// be explicit
|
||||
#ifdef Q_OS_MAC
|
||||
QDateTime createdAt = QDateTime::fromTime_t(valueObject["created_at"].toInt(), Qt::UTC);
|
||||
#else
|
||||
QDateTime createdAt = QDateTime::fromSecsSinceEpoch(valueObject["created_at"].toInt(), Qt::UTC);
|
||||
#endif
|
||||
QDateTime localCreatedAt = createdAt.toTimeZone(QTimeZone::systemTimeZone());
|
||||
valueObject["text"] = QString("%1 sent %2 %3 with message \"%4\"").
|
||||
arg(from, to, coloredQuantityAndAssetTitle, valueObject["message"].toString());
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "Ledger.h"
|
||||
#include "Wallet.h"
|
||||
#include <AccountManager.h>
|
||||
#include "scripting/WalletScriptingInterface.h"
|
||||
|
||||
HIFI_QML_DEF(QmlCommerce)
|
||||
|
||||
|
@ -28,6 +29,37 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) {
|
|||
connect(ledger.data(), &Ledger::historyResult, this, &QmlCommerce::historyResult);
|
||||
connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult);
|
||||
connect(ledger.data(), &Ledger::accountResult, this, &QmlCommerce::accountResult);
|
||||
connect(ledger.data(), &Ledger::accountResult, this, [&]() {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
auto walletScriptingInterface = DependencyManager::get<WalletScriptingInterface>();
|
||||
uint status;
|
||||
|
||||
if (wallet->getKeyFilePath() == "" || !wallet->getSecurityImage()) {
|
||||
status = (uint)WalletStatus::WALLET_STATUS_NOT_SET_UP;
|
||||
} else if (!wallet->walletIsAuthenticatedWithPassphrase()) {
|
||||
status = (uint)WalletStatus::WALLET_STATUS_NOT_AUTHENTICATED;
|
||||
} else {
|
||||
status = (uint)WalletStatus::WALLET_STATUS_READY;
|
||||
}
|
||||
|
||||
walletScriptingInterface->setWalletStatus(status);
|
||||
emit walletStatusResult(status);
|
||||
});
|
||||
}
|
||||
|
||||
void QmlCommerce::getWalletStatus() {
|
||||
auto walletScriptingInterface = DependencyManager::get<WalletScriptingInterface>();
|
||||
uint status;
|
||||
|
||||
if (DependencyManager::get<AccountManager>()->isLoggedIn()) {
|
||||
// This will set account info for the wallet, allowing us to decrypt and display the security image.
|
||||
account();
|
||||
} else {
|
||||
status = (uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN;
|
||||
emit walletStatusResult(status);
|
||||
walletScriptingInterface->setWalletStatus(status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void QmlCommerce::getLoginStatus() {
|
||||
|
@ -36,7 +68,7 @@ void QmlCommerce::getLoginStatus() {
|
|||
|
||||
void QmlCommerce::getKeyFilePathIfExists() {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->sendKeyFilePathIfExists();
|
||||
emit keyFilePathIfExistsResult(wallet->getKeyFilePath());
|
||||
}
|
||||
|
||||
void QmlCommerce::getWalletAuthenticatedStatus() {
|
||||
|
@ -85,13 +117,18 @@ void QmlCommerce::history() {
|
|||
ledger->history(wallet->listPublicKeys());
|
||||
}
|
||||
|
||||
void QmlCommerce::changePassphrase(const QString& oldPassphrase, const QString& newPassphrase) {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
if ((wallet->getPassphrase()->isEmpty() || wallet->getPassphrase() == oldPassphrase) && !newPassphrase.isEmpty()) {
|
||||
emit changePassphraseStatusResult(wallet->changePassphrase(newPassphrase));
|
||||
} else {
|
||||
emit changePassphraseStatusResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
void QmlCommerce::setPassphrase(const QString& passphrase) {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
if(wallet->getPassphrase() && !wallet->getPassphrase()->isEmpty() && !passphrase.isEmpty()) {
|
||||
wallet->changePassphrase(passphrase);
|
||||
} else {
|
||||
wallet->setPassphrase(passphrase);
|
||||
}
|
||||
wallet->setPassphrase(passphrase);
|
||||
getWalletAuthenticatedStatus();
|
||||
}
|
||||
|
||||
|
|
|
@ -27,11 +27,21 @@ class QmlCommerce : public OffscreenQmlDialog {
|
|||
public:
|
||||
QmlCommerce(QQuickItem* parent = nullptr);
|
||||
|
||||
enum WalletStatus {
|
||||
WALLET_STATUS_NOT_LOGGED_IN = 0,
|
||||
WALLET_STATUS_NOT_SET_UP,
|
||||
WALLET_STATUS_NOT_AUTHENTICATED,
|
||||
WALLET_STATUS_READY
|
||||
};
|
||||
|
||||
signals:
|
||||
void walletStatusResult(uint walletStatus);
|
||||
|
||||
void loginStatusResult(bool isLoggedIn);
|
||||
void keyFilePathIfExistsResult(const QString& path);
|
||||
void securityImageResult(bool exists);
|
||||
void walletAuthenticatedStatusResult(bool isAuthenticated);
|
||||
void changePassphraseStatusResult(bool changeSuccess);
|
||||
|
||||
void buyResult(QJsonObject result);
|
||||
// Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and
|
||||
|
@ -42,6 +52,8 @@ signals:
|
|||
void accountResult(QJsonObject result);
|
||||
|
||||
protected:
|
||||
Q_INVOKABLE void getWalletStatus();
|
||||
|
||||
Q_INVOKABLE void getLoginStatus();
|
||||
Q_INVOKABLE void getKeyFilePathIfExists();
|
||||
Q_INVOKABLE void getSecurityImage();
|
||||
|
@ -49,6 +61,7 @@ protected:
|
|||
|
||||
Q_INVOKABLE void chooseSecurityImage(const QString& imageFile);
|
||||
Q_INVOKABLE void setPassphrase(const QString& passphrase);
|
||||
Q_INVOKABLE void changePassphrase(const QString& oldPassphrase, const QString& newPassphrase);
|
||||
|
||||
Q_INVOKABLE void buy(const QString& assetId, int cost, const bool controlledFailure = false);
|
||||
Q_INVOKABLE void balance();
|
||||
|
|
|
@ -284,7 +284,7 @@ Wallet::Wallet() {
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "verifyOwnerChallenge");
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket");
|
||||
}
|
||||
|
||||
Wallet::~Wallet() {
|
||||
|
@ -468,7 +468,7 @@ bool Wallet::generateKeyPair() {
|
|||
|
||||
// TODO: redo this soon -- need error checking and so on
|
||||
writeSecurityImage(_securityImage, keyFilePath());
|
||||
sendKeyFilePathIfExists();
|
||||
emit keyFilePathIfExistsResult(getKeyFilePath());
|
||||
QString oldKey = _publicKeys.count() == 0 ? "" : _publicKeys.last();
|
||||
QString key = keyPair.first->toBase64();
|
||||
_publicKeys.push_back(key);
|
||||
|
@ -559,14 +559,14 @@ void Wallet::chooseSecurityImage(const QString& filename) {
|
|||
emit securityImageResult(success);
|
||||
}
|
||||
|
||||
void Wallet::getSecurityImage() {
|
||||
bool Wallet::getSecurityImage() {
|
||||
unsigned char* data;
|
||||
int dataLen;
|
||||
|
||||
// if already decrypted, don't do it again
|
||||
if (_securityImage) {
|
||||
emit securityImageResult(true);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
|
@ -585,14 +585,15 @@ void Wallet::getSecurityImage() {
|
|||
success = true;
|
||||
}
|
||||
emit securityImageResult(success);
|
||||
return success;
|
||||
}
|
||||
void Wallet::sendKeyFilePathIfExists() {
|
||||
QString Wallet::getKeyFilePath() {
|
||||
QString filePath(keyFilePath());
|
||||
QFileInfo fileInfo(filePath);
|
||||
if (fileInfo.exists()) {
|
||||
emit keyFilePathIfExistsResult(filePath);
|
||||
return filePath;
|
||||
} else {
|
||||
emit keyFilePathIfExistsResult("");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,8 +32,8 @@ public:
|
|||
QStringList listPublicKeys();
|
||||
QString signWithKey(const QByteArray& text, const QString& key);
|
||||
void chooseSecurityImage(const QString& imageFile);
|
||||
void getSecurityImage();
|
||||
void sendKeyFilePathIfExists();
|
||||
bool getSecurityImage();
|
||||
QString getKeyFilePath();
|
||||
|
||||
void setSalt(const QByteArray& salt) { _salt = salt; }
|
||||
QByteArray getSalt() { return _salt; }
|
||||
|
|
|
@ -49,6 +49,10 @@ int main(int argc, const char* argv[]) {
|
|||
CrashReporter crashReporter { BUG_SPLAT_DATABASE, BUG_SPLAT_APPLICATION_NAME, BuildInfo::VERSION };
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
|
||||
#endif
|
||||
|
||||
disableQtBearerPoll(); // Fixes wifi ping spikes
|
||||
|
||||
QElapsedTimer startupTime;
|
||||
|
|
|
@ -22,16 +22,14 @@ class RatesScriptingInterface : public QObject {
|
|||
Q_PROPERTY(float newFrame READ getNewFrameRate)
|
||||
Q_PROPERTY(float dropped READ getDropRate)
|
||||
Q_PROPERTY(float simulation READ getSimulationRate)
|
||||
Q_PROPERTY(float avatar READ getAvatarRate)
|
||||
|
||||
public:
|
||||
RatesScriptingInterface(QObject* parent) : QObject(parent) {}
|
||||
float getRenderRate() { return qApp->getFps(); }
|
||||
float getRenderRate() { return qApp->getRenderLoopRate(); }
|
||||
float getPresentRate() { return qApp->getActiveDisplayPlugin()->presentRate(); }
|
||||
float getNewFrameRate() { return qApp->getActiveDisplayPlugin()->newFramePresentRate(); }
|
||||
float getDropRate() { return qApp->getActiveDisplayPlugin()->droppedFrameRate(); }
|
||||
float getSimulationRate() { return qApp->getAverageSimsPerSecond(); }
|
||||
float getAvatarRate() { return qApp->getAvatarSimrate(); }
|
||||
float getSimulationRate() { return qApp->getGameLoopRate(); }
|
||||
};
|
||||
|
||||
#endif // HIFI_INTERFACE_RATES_SCRIPTING_INTERFACE_H
|
||||
|
|
47
interface/src/scripting/WalletScriptingInterface.cpp
Normal file
47
interface/src/scripting/WalletScriptingInterface.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// WalletScriptingInterface.cpp
|
||||
// interface/src/scripting
|
||||
//
|
||||
// Created by Zach Fox on 2017-09-29.
|
||||
// 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 "WalletScriptingInterface.h"
|
||||
|
||||
CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qmlObject, parent) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
}
|
||||
|
||||
WalletScriptingInterface::WalletScriptingInterface() {
|
||||
}
|
||||
|
||||
static const QString CHECKOUT_QML_PATH = qApp->applicationDirPath() + "../../../qml/hifi/commerce/checkout/Checkout.qml";
|
||||
void WalletScriptingInterface::buy(const QString& name, const QString& id, const int& price, const QString& href) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "buy", Q_ARG(const QString&, name), Q_ARG(const QString&, id), Q_ARG(const int&, price), Q_ARG(const QString&, href));
|
||||
return;
|
||||
}
|
||||
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
|
||||
tablet->loadQMLSource(CHECKOUT_QML_PATH);
|
||||
DependencyManager::get<HMDScriptingInterface>()->openTablet();
|
||||
|
||||
QQuickItem* root = nullptr;
|
||||
if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !qApp->isHMDMode())) {
|
||||
root = DependencyManager::get<OffscreenUi>()->getRootItem();
|
||||
} else {
|
||||
root = tablet->getTabletRoot();
|
||||
}
|
||||
CheckoutProxy* checkout = new CheckoutProxy(root->findChild<QObject*>("checkout"));
|
||||
|
||||
// Example: Wallet.buy("Test Flaregun", "0d90d21c-ce7a-4990-ad18-e9d2cf991027", 17, "http://mpassets.highfidelity.com/0d90d21c-ce7a-4990-ad18-e9d2cf991027-v1/flaregun.json");
|
||||
checkout->writeProperty("itemName", name);
|
||||
checkout->writeProperty("itemId", id);
|
||||
checkout->writeProperty("itemPrice", price);
|
||||
checkout->writeProperty("itemHref", href);
|
||||
}
|
51
interface/src/scripting/WalletScriptingInterface.h
Normal file
51
interface/src/scripting/WalletScriptingInterface.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
|
||||
// WalletScriptingInterface.h
|
||||
// interface/src/scripting
|
||||
//
|
||||
// Created by Zach Fox on 2017-09-29.
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_WalletScriptingInterface_h
|
||||
#define hifi_WalletScriptingInterface_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "scripting/HMDScriptingInterface.h"
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
#include <ui/QmlWrapper.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include "Application.h"
|
||||
|
||||
class CheckoutProxy : public QmlWrapper {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CheckoutProxy(QObject* qmlObject, QObject* parent = nullptr);
|
||||
};
|
||||
|
||||
|
||||
class WalletScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(uint walletStatus READ getWalletStatus WRITE setWalletStatus NOTIFY walletStatusChanged)
|
||||
|
||||
public:
|
||||
WalletScriptingInterface();
|
||||
|
||||
Q_INVOKABLE uint getWalletStatus() { return _walletStatus; }
|
||||
void setWalletStatus(const uint& status) { _walletStatus = status; }
|
||||
|
||||
Q_INVOKABLE void buy(const QString& name, const QString& id, const int& price, const QString& href);
|
||||
|
||||
signals:
|
||||
void walletStatusChanged();
|
||||
|
||||
private:
|
||||
uint _walletStatus;
|
||||
};
|
||||
|
||||
#endif // hifi_WalletScriptingInterface_h
|
|
@ -171,6 +171,11 @@ void WindowScriptingInterface::setPreviousBrowseAssetLocation(const QString& loc
|
|||
Setting::Handle<QVariant>(LAST_BROWSE_ASSETS_LOCATION_SETTING).set(location);
|
||||
}
|
||||
|
||||
bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->isPointOnDesktopWindow(point);
|
||||
}
|
||||
|
||||
/// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and
|
||||
/// might be in same thread as a script that sets the reticle to invisible
|
||||
void WindowScriptingInterface::ensureReticleVisible() const {
|
||||
|
|
|
@ -72,6 +72,7 @@ public slots:
|
|||
void shareSnapshot(const QString& path, const QUrl& href = QUrl(""));
|
||||
bool isPhysicsEnabled();
|
||||
bool setDisplayTexture(const QString& name);
|
||||
bool isPointOnDesktopWindow(QVariant point);
|
||||
|
||||
int openMessageBox(QString title, QString text, int buttons, int defaultButton);
|
||||
void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "Stats.h"
|
||||
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
#include <QFontDatabase>
|
||||
|
||||
|
@ -116,12 +117,6 @@ void Stats::updateStats(bool force) {
|
|||
}
|
||||
}
|
||||
|
||||
bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::Stats) && isExpanded();
|
||||
if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) {
|
||||
PerformanceTimer::setActive(shouldDisplayTimingDetail);
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
// we need to take one avatar out so we don't include ourselves
|
||||
|
@ -129,7 +124,7 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(updatedAvatarCount, avatarManager->getNumAvatarsUpdated());
|
||||
STAT_UPDATE(notUpdatedAvatarCount, avatarManager->getNumAvatarsNotUpdated());
|
||||
STAT_UPDATE(serverCount, (int)nodeList->size());
|
||||
STAT_UPDATE_FLOAT(framerate, qApp->getFps(), 0.1f);
|
||||
STAT_UPDATE_FLOAT(renderrate, qApp->getRenderLoopRate(), 0.1f);
|
||||
if (qApp->getActiveDisplayPlugin()) {
|
||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||
auto stats = displayPlugin->getHardwareStats();
|
||||
|
@ -137,7 +132,6 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(longrenders, stats["long_render_count"].toInt());
|
||||
STAT_UPDATE(longsubmits, stats["long_submit_count"].toInt());
|
||||
STAT_UPDATE(longframes, stats["long_frame_count"].toInt());
|
||||
STAT_UPDATE_FLOAT(renderrate, displayPlugin->renderRate(), 0.1f);
|
||||
STAT_UPDATE_FLOAT(presentrate, displayPlugin->presentRate(), 0.1f);
|
||||
STAT_UPDATE_FLOAT(presentnewrate, displayPlugin->newFramePresentRate(), 0.1f);
|
||||
STAT_UPDATE_FLOAT(presentdroprate, displayPlugin->droppedFrameRate(), 0.1f);
|
||||
|
@ -150,8 +144,7 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(presentnewrate, -1);
|
||||
STAT_UPDATE(presentdroprate, -1);
|
||||
}
|
||||
STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond());
|
||||
STAT_UPDATE(avatarSimrate, (int)qApp->getAvatarSimrate());
|
||||
STAT_UPDATE(gameLoopRate, (int)qApp->getGameLoopRate());
|
||||
|
||||
auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
|
||||
STAT_UPDATE(packetInCount, (int)bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond());
|
||||
|
@ -406,14 +399,21 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText());
|
||||
}
|
||||
|
||||
bool performanceTimerIsActive = PerformanceTimer::isActive();
|
||||
bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails);
|
||||
if (displayPerf && performanceTimerIsActive) {
|
||||
if (!_timingExpanded) {
|
||||
_timingExpanded = true;
|
||||
|
||||
bool performanceTimerShouldBeActive = Menu::getInstance()->isOptionChecked(MenuOption::Stats) && _expanded;
|
||||
if (performanceTimerShouldBeActive != PerformanceTimer::isActive()) {
|
||||
PerformanceTimer::setActive(performanceTimerShouldBeActive);
|
||||
}
|
||||
if (performanceTimerShouldBeActive) {
|
||||
PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up
|
||||
}
|
||||
|
||||
if (performanceTimerShouldBeActive &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails)) {
|
||||
if (!_showTimingDetails) {
|
||||
_showTimingDetails = true;
|
||||
emit timingExpandedChanged();
|
||||
}
|
||||
PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up
|
||||
|
||||
// we will also include room for 1 line per timing record and a header of 4 lines
|
||||
// Timing details...
|
||||
|
@ -453,10 +453,55 @@ void Stats::updateStats(bool force) {
|
|||
}
|
||||
_timingStats = perfLines;
|
||||
emit timingStatsChanged();
|
||||
} else if (_timingExpanded) {
|
||||
_timingExpanded = false;
|
||||
} else if (_showTimingDetails) {
|
||||
_showTimingDetails = false;
|
||||
emit timingExpandedChanged();
|
||||
}
|
||||
|
||||
if (_expanded && performanceTimerShouldBeActive) {
|
||||
if (!_showGameUpdateStats) {
|
||||
_showGameUpdateStats = true;
|
||||
}
|
||||
class SortableStat {
|
||||
public:
|
||||
SortableStat(QString a, float p) : message(a), priority(p) {}
|
||||
QString message;
|
||||
float priority;
|
||||
bool operator<(const SortableStat& other) const { return priority < other.priority; }
|
||||
};
|
||||
|
||||
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
|
||||
std::priority_queue<SortableStat> idleUpdateStats;
|
||||
auto itr = allRecords.find("/idle/update");
|
||||
if (itr != allRecords.end()) {
|
||||
float dt = (float)itr.value().getMovingAverage() / (float)USECS_PER_MSEC;
|
||||
_gameUpdateStats = QString("/idle/update = %1 ms").arg(dt);
|
||||
|
||||
QVector<QString> categories = { "devices", "physics", "otherAvatars", "MyAvatar", "misc" };
|
||||
for (int32_t j = 0; j < categories.size(); ++j) {
|
||||
QString recordKey = "/idle/update/" + categories[j];
|
||||
itr = allRecords.find(recordKey);
|
||||
if (itr != allRecords.end()) {
|
||||
float dt = (float)itr.value().getMovingAverage() / (float)USECS_PER_MSEC;
|
||||
QString message = QString("\n %1 = %2").arg(categories[j]).arg(dt);
|
||||
idleUpdateStats.push(SortableStat(message, dt));
|
||||
}
|
||||
}
|
||||
while (!idleUpdateStats.empty()) {
|
||||
SortableStat stat = idleUpdateStats.top();
|
||||
_gameUpdateStats += stat.message;
|
||||
idleUpdateStats.pop();
|
||||
}
|
||||
emit gameUpdateStatsChanged();
|
||||
} else if (_gameUpdateStats != "") {
|
||||
_gameUpdateStats = "";
|
||||
emit gameUpdateStatsChanged();
|
||||
}
|
||||
} else if (_showGameUpdateStats) {
|
||||
_showGameUpdateStats = false;
|
||||
_gameUpdateStats = "";
|
||||
emit gameUpdateStatsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Stats::setRenderDetails(const render::RenderDetails& details) {
|
||||
|
|
|
@ -32,8 +32,6 @@ class Stats : public QQuickItem {
|
|||
|
||||
STATS_PROPERTY(int, serverCount, 0)
|
||||
// How often the app is creating new gpu::Frames
|
||||
STATS_PROPERTY(float, framerate, 0)
|
||||
// How often the display plugin is executing a given frame
|
||||
STATS_PROPERTY(float, renderrate, 0)
|
||||
// How often the display plugin is presenting to the device
|
||||
STATS_PROPERTY(float, presentrate, 0)
|
||||
|
@ -47,8 +45,7 @@ class Stats : public QQuickItem {
|
|||
|
||||
STATS_PROPERTY(float, presentnewrate, 0)
|
||||
STATS_PROPERTY(float, presentdroprate, 0)
|
||||
STATS_PROPERTY(int, simrate, 0)
|
||||
STATS_PROPERTY(int, avatarSimrate, 0)
|
||||
STATS_PROPERTY(int, gameLoopRate, 0)
|
||||
STATS_PROPERTY(int, avatarCount, 0)
|
||||
STATS_PROPERTY(int, updatedAvatarCount, 0)
|
||||
STATS_PROPERTY(int, notUpdatedAvatarCount, 0)
|
||||
|
@ -108,6 +105,7 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(QString, packetStats, QString())
|
||||
STATS_PROPERTY(QString, lodStatus, QString())
|
||||
STATS_PROPERTY(QString, timingStats, QString())
|
||||
STATS_PROPERTY(QString, gameUpdateStats, QString())
|
||||
STATS_PROPERTY(int, serverElements, 0)
|
||||
STATS_PROPERTY(int, serverInternal, 0)
|
||||
STATS_PROPERTY(int, serverLeaves, 0)
|
||||
|
@ -148,7 +146,7 @@ public:
|
|||
void updateStats(bool force = false);
|
||||
|
||||
bool isExpanded() { return _expanded; }
|
||||
bool isTimingExpanded() { return _timingExpanded; }
|
||||
bool isTimingExpanded() { return _showTimingDetails; }
|
||||
|
||||
void setExpanded(bool expanded) {
|
||||
if (_expanded != expanded) {
|
||||
|
@ -167,7 +165,6 @@ signals:
|
|||
void longrendersChanged();
|
||||
void longframesChanged();
|
||||
void appdroppedChanged();
|
||||
void framerateChanged();
|
||||
void expandedChanged();
|
||||
void timingExpandedChanged();
|
||||
void serverCountChanged();
|
||||
|
@ -176,8 +173,7 @@ signals:
|
|||
void presentnewrateChanged();
|
||||
void presentdroprateChanged();
|
||||
void stutterrateChanged();
|
||||
void simrateChanged();
|
||||
void avatarSimrateChanged();
|
||||
void gameLoopRateChanged();
|
||||
void avatarCountChanged();
|
||||
void updatedAvatarCountChanged();
|
||||
void notUpdatedAvatarCountChanged();
|
||||
|
@ -242,6 +238,7 @@ signals:
|
|||
void localInternalChanged();
|
||||
void localLeavesChanged();
|
||||
void timingStatsChanged();
|
||||
void gameUpdateStatsChanged();
|
||||
void glContextSwapchainMemoryChanged();
|
||||
void qmlTextureMemoryChanged();
|
||||
void texturePendingTransfersChanged();
|
||||
|
@ -267,7 +264,8 @@ private:
|
|||
int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process
|
||||
bool _resetRecentMaxPacketsSoon{ true };
|
||||
bool _expanded{ false };
|
||||
bool _timingExpanded{ false };
|
||||
bool _showTimingDetails{ false };
|
||||
bool _showGameUpdateStats{ false };
|
||||
QString _monospaceFont;
|
||||
const AudioIOStats* _audioStats;
|
||||
QStringList _downloadUrls = QStringList();
|
||||
|
|
|
@ -201,7 +201,8 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt
|
|||
void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) {
|
||||
qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID;
|
||||
if (_commerceSettingSwitch.get()) {
|
||||
Setting::Handle<bool> _settingSwitch{ "commerce", false };
|
||||
if (_settingSwitch.get()) {
|
||||
openInspectionCertificate();
|
||||
} else {
|
||||
openMarketplace();
|
||||
|
|
|
@ -79,8 +79,6 @@ private:
|
|||
|
||||
bool _isInMarketplaceInspectionMode { false };
|
||||
|
||||
Setting::Handle<bool> _commerceSettingSwitch{ "commerce", false };
|
||||
|
||||
void openInspectionCertificate();
|
||||
void openMarketplace();
|
||||
void enableEntityHighlight(const EntityItemID& entityItemID);
|
||||
|
|
|
@ -129,10 +129,10 @@ protected:
|
|||
bool _vsyncEnabled { true };
|
||||
QThread* _presentThread{ nullptr };
|
||||
std::queue<gpu::FramePointer> _newFrameQueue;
|
||||
RateCounter<100> _droppedFrameRate;
|
||||
RateCounter<100> _newFrameRate;
|
||||
RateCounter<100> _presentRate;
|
||||
RateCounter<100> _renderRate;
|
||||
RateCounter<200> _droppedFrameRate;
|
||||
RateCounter<200> _newFrameRate;
|
||||
RateCounter<200> _presentRate;
|
||||
RateCounter<200> _renderRate;
|
||||
|
||||
gpu::FramePointer _currentFrame;
|
||||
gpu::Frame* _lastFrame { nullptr };
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
set(TARGET_NAME entities)
|
||||
setup_hifi_library(Network Script)
|
||||
include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
|
||||
link_hifi_libraries(shared networking octree avatars)
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtEndian>
|
||||
#include <QJsonDocument>
|
||||
#include <openssl/rsa.h> // see comments for DEBUG_CERT
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
|
@ -34,6 +38,7 @@
|
|||
|
||||
Q_DECLARE_METATYPE(EntityItemPointer);
|
||||
int entityItemPointernMetaTypeId = qRegisterMetaType<EntityItemPointer>();
|
||||
|
||||
int EntityItem::_maxActionsDataSize = 800;
|
||||
quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND;
|
||||
|
||||
|
@ -95,7 +100,19 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
|||
requestedProperties += PROP_DYNAMIC;
|
||||
requestedProperties += PROP_LOCKED;
|
||||
requestedProperties += PROP_USER_DATA;
|
||||
|
||||
// Certifiable properties
|
||||
requestedProperties += PROP_ITEM_NAME;
|
||||
requestedProperties += PROP_ITEM_DESCRIPTION;
|
||||
requestedProperties += PROP_ITEM_CATEGORIES;
|
||||
requestedProperties += PROP_ITEM_ARTIST;
|
||||
requestedProperties += PROP_ITEM_LICENSE;
|
||||
requestedProperties += PROP_LIMITED_RUN;
|
||||
requestedProperties += PROP_MARKETPLACE_ID;
|
||||
requestedProperties += PROP_EDITION_NUMBER;
|
||||
requestedProperties += PROP_ENTITY_INSTANCE_NUMBER;
|
||||
requestedProperties += PROP_CERTIFICATE_ID;
|
||||
|
||||
requestedProperties += PROP_NAME;
|
||||
requestedProperties += PROP_HREF;
|
||||
requestedProperties += PROP_DESCRIPTION;
|
||||
|
@ -240,7 +257,19 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
APPEND_ENTITY_PROPERTY(PROP_DYNAMIC, getDynamic());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked());
|
||||
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData());
|
||||
|
||||
// Certifiable Properties
|
||||
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, getMarketplaceID());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ITEM_NAME, getItemName());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ITEM_DESCRIPTION, getItemDescription());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ITEM_CATEGORIES, getItemCategories());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ITEM_ARTIST, getItemArtist());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ITEM_LICENSE, getItemLicense());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LIMITED_RUN, getLimitedRun());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EDITION_NUMBER, getEditionNumber());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, getEntityInstanceNumber());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, getCertificateID());
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_NAME, getName());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
|
||||
|
@ -791,6 +820,17 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_MARKETPLACE_ID) {
|
||||
READ_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
|
||||
}
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_CERTIFICATE_PROPERTIES) {
|
||||
READ_ENTITY_PROPERTY(PROP_ITEM_NAME, QString, setItemName);
|
||||
READ_ENTITY_PROPERTY(PROP_ITEM_DESCRIPTION, QString, setItemDescription);
|
||||
READ_ENTITY_PROPERTY(PROP_ITEM_CATEGORIES, QString, setItemCategories);
|
||||
READ_ENTITY_PROPERTY(PROP_ITEM_ARTIST, QString, setItemArtist);
|
||||
READ_ENTITY_PROPERTY(PROP_ITEM_LICENSE, QString, setItemLicense);
|
||||
READ_ENTITY_PROPERTY(PROP_LIMITED_RUN, quint32, setLimitedRun);
|
||||
READ_ENTITY_PROPERTY(PROP_EDITION_NUMBER, quint32, setEditionNumber);
|
||||
READ_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, quint32, setEntityInstanceNumber);
|
||||
READ_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, QString, setCertificateID);
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_NAME, QString, setName);
|
||||
READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
|
||||
|
@ -1208,7 +1248,19 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dynamic, getDynamic);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData);
|
||||
|
||||
// Certifiable Properties
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(itemName, getItemName);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(itemDescription, getItemDescription);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(itemCategories, getItemCategories);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(itemArtist, getItemArtist);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(itemLicense, getItemLicense);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(limitedRun, getLimitedRun);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(marketplaceID, getMarketplaceID);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(editionNumber, getEditionNumber);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(entityInstanceNumber, getEntityInstanceNumber);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(certificateID, getCertificateID);
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription);
|
||||
|
@ -1303,7 +1355,19 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
|
||||
|
||||
// Certifiable Properties
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(itemName, setItemName);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(itemDescription, setItemDescription);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(itemCategories, setItemCategories);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(itemArtist, setItemArtist);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(itemLicense, setItemLicense);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(limitedRun, setLimitedRun);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(editionNumber, setEditionNumber);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(entityInstanceNumber, setEntityInstanceNumber);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(certificateID, setCertificateID);
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription);
|
||||
|
@ -1510,6 +1574,107 @@ float EntityItem::getRadius() const {
|
|||
return 0.5f * glm::length(getDimensions());
|
||||
}
|
||||
|
||||
// Checking Certifiable Properties
|
||||
#define ADD_STRING_PROPERTY(n, N) if (!propertySet.get##N().isEmpty()) json[#n] = propertySet.get##N()
|
||||
#define ADD_ENUM_PROPERTY(n, N) json[#n] = propertySet.get##N##AsString()
|
||||
#define ADD_INT_PROPERTY(n, N) if (propertySet.get##N() != 0) json[#n] = (propertySet.get##N() == (quint32) -1) ? -1.0 : ((double) propertySet.get##N())
|
||||
QByteArray EntityItem::getStaticCertificateJSON() const {
|
||||
// Produce a compact json of every non-default static certificate property, with the property names in alphabetical order.
|
||||
// The static certificate properties include all an only those properties that cannot be changed without altering the identity
|
||||
// of the entity as reviewed during the certification submission.
|
||||
|
||||
QJsonObject json;
|
||||
EntityItemProperties propertySet = getProperties(); // Note: neither EntityItem nor EntityitemProperties "properties" are QObject "properties"!
|
||||
// It is important that this be reproducible in the same order each time. Since we also generate these on the server, we do it alphabetically
|
||||
// to help maintainence in two different code bases.
|
||||
if (!propertySet.getAnimation().getURL().isEmpty()) {
|
||||
json["animation.url"] = propertySet.getAnimation().getURL();
|
||||
}
|
||||
ADD_STRING_PROPERTY(collisionSoundURL, CollisionSoundURL);
|
||||
ADD_STRING_PROPERTY(compoundShapeURL, CompoundShapeURL);
|
||||
ADD_INT_PROPERTY(editionNumber, EditionNumber);
|
||||
ADD_INT_PROPERTY(entityInstanceNumber, EntityInstanceNumber);
|
||||
ADD_STRING_PROPERTY(itemArtist, ItemArtist);
|
||||
ADD_STRING_PROPERTY(itemCategories, ItemCategories);
|
||||
ADD_STRING_PROPERTY(itemDescription, ItemDescription);
|
||||
ADD_STRING_PROPERTY(itemLicense, ItemLicense);
|
||||
ADD_STRING_PROPERTY(itemName, ItemName);
|
||||
ADD_INT_PROPERTY(limitedRun, LimitedRun);
|
||||
ADD_STRING_PROPERTY(marketplaceID, MarketplaceID);
|
||||
ADD_STRING_PROPERTY(modelURL, ModelURL);
|
||||
ADD_STRING_PROPERTY(script, Script);
|
||||
ADD_ENUM_PROPERTY(shapeType, ShapeType);
|
||||
json["type"] = EntityTypes::getEntityTypeName(propertySet.getType());
|
||||
|
||||
return QJsonDocument(json).toJson(QJsonDocument::Compact);
|
||||
}
|
||||
QByteArray EntityItem::getStaticCertificateHash() const {
|
||||
return QCryptographicHash::hash(getStaticCertificateJSON(), QCryptographicHash::Sha256);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CERT
|
||||
QString EntityItem::computeCertificateID() {
|
||||
// Until the marketplace generates it, compute and answer the certificateID here.
|
||||
// Does not set it, as that will have to be done from script engine in order to update server, etc.
|
||||
const auto hash = getStaticCertificateHash();
|
||||
const auto text = reinterpret_cast<const unsigned char*>(hash.constData());
|
||||
const unsigned int textLength = hash.length();
|
||||
|
||||
const char privateKey[] = "-----BEGIN RSA PRIVATE KEY-----\n\
|
||||
MIIBOQIBAAJBALCoBiDAZOClO26tC5pd7JikBL61WIgpAqbcNnrV/TcG6LPI7Zbi\n\
|
||||
MjdUixmTNvYMRZH3Wlqtl2IKG1W68y3stKECAwEAAQJABvOlwhYwIhL+gr12jm2R\n\
|
||||
yPPzZ9nVEQ6kFxLlZfIT09119fd6OU1X5d4sHWfMfSIEgjwQIDS3ZU1kY3XKo87X\n\
|
||||
zQIhAOPHlYa1OC7BLhaTouy68qIU2vCKLP8mt4S31/TT0UOnAiEAxor6gU6yupTQ\n\
|
||||
yuyV3yHvr5LkZKBGqhjmOTmDfgtX7ncCIChGbgX3nQuHVOLhD/nTxHssPNozVGl5\n\
|
||||
KxHof+LmYSYZAiB4U+yEh9SsXdq40W/3fpLMPuNq1PRezJ5jGidGMcvF+wIgUNec\n\
|
||||
3Kg2U+CVZr8/bDT/vXRrsKj1zfobYuvbfVH02QY=\n\
|
||||
-----END RSA PRIVATE KEY-----";
|
||||
BIO* bio = BIO_new_mem_buf((void*)privateKey, sizeof(privateKey));
|
||||
RSA* rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
|
||||
|
||||
QByteArray signature(RSA_size(rsa), 0);
|
||||
unsigned int signatureLength = 0;
|
||||
const int signOK = RSA_sign(NID_sha256, text, textLength, reinterpret_cast<unsigned char*>(signature.data()), &signatureLength, rsa);
|
||||
BIO_free(bio);
|
||||
RSA_free(rsa);
|
||||
if (!signOK) {
|
||||
qCWarning(entities) << "Unable to compute signature for" << getName() << getEntityItemID();
|
||||
return "";
|
||||
}
|
||||
return signature.toBase64();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool EntityItem::verifyStaticCertificateProperties() {
|
||||
// True IIF a non-empty certificateID matches the static certificate json.
|
||||
// I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash.
|
||||
|
||||
if (getCertificateID().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
const auto signatureBytes = QByteArray::fromBase64(getCertificateID().toLatin1());
|
||||
const auto signature = reinterpret_cast<const unsigned char*>(signatureBytes.constData());
|
||||
const unsigned int signatureLength = signatureBytes.length();
|
||||
|
||||
const auto hash = getStaticCertificateHash();
|
||||
const auto text = reinterpret_cast<const unsigned char*>(hash.constData());
|
||||
const unsigned int textLength = hash.length();
|
||||
|
||||
// After DEBUG_CERT ends, we will get/cache this once from the marketplace when needed, and it likely won't be RSA.
|
||||
const char publicKey[] = "-----BEGIN PUBLIC KEY-----\n\
|
||||
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALCoBiDAZOClO26tC5pd7JikBL61WIgp\n\
|
||||
AqbcNnrV/TcG6LPI7ZbiMjdUixmTNvYMRZH3Wlqtl2IKG1W68y3stKECAwEAAQ==\n\
|
||||
-----END PUBLIC KEY-----";
|
||||
BIO *bio = BIO_new_mem_buf((void*)publicKey, sizeof(publicKey));
|
||||
EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
|
||||
RSA* rsa = EVP_PKEY_get1_RSA(evp_key);
|
||||
bool answer = RSA_verify(NID_sha256, text, textLength, signature, signatureLength, rsa);
|
||||
BIO_free(bio);
|
||||
RSA_free(rsa);
|
||||
EVP_PKEY_free(evp_key);
|
||||
return answer;
|
||||
}
|
||||
|
||||
void EntityItem::adjustShapeInfoByRegistration(ShapeInfo& info) const {
|
||||
if (_registrationPoint != ENTITY_ITEM_DEFAULT_REGISTRATION_POINT) {
|
||||
glm::mat4 scale = glm::scale(getDimensions());
|
||||
|
@ -2762,19 +2927,33 @@ void EntityItem::setUserData(const QString& value) {
|
|||
});
|
||||
}
|
||||
|
||||
QString EntityItem::getMarketplaceID() const {
|
||||
QString result;
|
||||
withReadLock([&] {
|
||||
result = _marketplaceID;
|
||||
});
|
||||
return result;
|
||||
// Certifiable Properties
|
||||
#define DEFINE_PROPERTY_GETTER(type, accessor, var) \
|
||||
type EntityItem::get##accessor() const { \
|
||||
type result; \
|
||||
withReadLock([&] { \
|
||||
result = _##var; \
|
||||
}); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
void EntityItem::setMarketplaceID(const QString& value) {
|
||||
withWriteLock([&] {
|
||||
_marketplaceID = value;
|
||||
});
|
||||
#define DEFINE_PROPERTY_SETTER(type, accessor, var) \
|
||||
void EntityItem::set##accessor(const type & value) { \
|
||||
withWriteLock([&] { \
|
||||
_##var = value; \
|
||||
}); \
|
||||
}
|
||||
#define DEFINE_PROPERTY_ACCESSOR(type, accessor, var) DEFINE_PROPERTY_GETTER(type, accessor, var) DEFINE_PROPERTY_SETTER(type, accessor, var)
|
||||
DEFINE_PROPERTY_ACCESSOR(QString, ItemName, itemName)
|
||||
DEFINE_PROPERTY_ACCESSOR(QString, ItemDescription, itemDescription)
|
||||
DEFINE_PROPERTY_ACCESSOR(QString, ItemCategories, itemCategories)
|
||||
DEFINE_PROPERTY_ACCESSOR(QString, ItemArtist, itemArtist)
|
||||
DEFINE_PROPERTY_ACCESSOR(QString, ItemLicense, itemLicense)
|
||||
DEFINE_PROPERTY_ACCESSOR(quint32, LimitedRun, limitedRun)
|
||||
DEFINE_PROPERTY_ACCESSOR(QString, MarketplaceID, marketplaceID)
|
||||
DEFINE_PROPERTY_ACCESSOR(quint32, EditionNumber, editionNumber)
|
||||
DEFINE_PROPERTY_ACCESSOR(quint32, EntityInstanceNumber, entityInstanceNumber)
|
||||
DEFINE_PROPERTY_ACCESSOR(QString, CertificateID, certificateID)
|
||||
|
||||
uint32_t EntityItem::getDirtyFlags() const {
|
||||
uint32_t result;
|
||||
|
|
|
@ -36,6 +36,9 @@
|
|||
#include "SimulationFlags.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
|
||||
// FIXME: The server-side marketplace will soon create the certificateID. At that point, all of the DEBUG_CERT stuff will go away.
|
||||
#define DEBUG_CERT 1
|
||||
|
||||
class EntitySimulation;
|
||||
class EntityTreeElement;
|
||||
class EntityTreeElementExtraEncodeData;
|
||||
|
@ -304,8 +307,33 @@ public:
|
|||
uint8_t getPendingOwnershipPriority() const { return _simulationOwner.getPendingPriority(); }
|
||||
void rememberHasSimulationOwnershipBid() const;
|
||||
|
||||
// Certifiable Properties
|
||||
QString getItemName() const;
|
||||
void setItemName(const QString& value);
|
||||
QString getItemDescription() const;
|
||||
void setItemDescription(const QString& value);
|
||||
QString getItemCategories() const;
|
||||
void setItemCategories(const QString& value);
|
||||
QString getItemArtist() const;
|
||||
void setItemArtist(const QString& value);
|
||||
QString getItemLicense() const;
|
||||
void setItemLicense(const QString& value);
|
||||
quint32 getLimitedRun() const;
|
||||
void setLimitedRun(const quint32&);
|
||||
QString getMarketplaceID() const;
|
||||
void setMarketplaceID(const QString& value);
|
||||
quint32 getEditionNumber() const;
|
||||
void setEditionNumber(const quint32&);
|
||||
quint32 getEntityInstanceNumber() const;
|
||||
void setEntityInstanceNumber(const quint32&);
|
||||
QString getCertificateID() const;
|
||||
void setCertificateID(const QString& value);
|
||||
QByteArray getStaticCertificateJSON() const;
|
||||
QByteArray getStaticCertificateHash() const;
|
||||
bool verifyStaticCertificateProperties();
|
||||
#ifdef DEBUG_CERT
|
||||
QString computeCertificateID();
|
||||
#endif
|
||||
|
||||
// TODO: get rid of users of getRadius()...
|
||||
float getRadius() const;
|
||||
|
@ -524,12 +552,24 @@ protected:
|
|||
bool _locked { ENTITY_ITEM_DEFAULT_LOCKED };
|
||||
QString _userData { ENTITY_ITEM_DEFAULT_USER_DATA };
|
||||
SimulationOwner _simulationOwner;
|
||||
QString _marketplaceID { ENTITY_ITEM_DEFAULT_MARKETPLACE_ID };
|
||||
bool _shouldHighlight { false };
|
||||
QString _name { ENTITY_ITEM_DEFAULT_NAME };
|
||||
QString _href; //Hyperlink href
|
||||
QString _description; //Hyperlink description
|
||||
|
||||
// Certifiable Properties
|
||||
QString _itemName { ENTITY_ITEM_DEFAULT_ITEM_NAME };
|
||||
QString _itemDescription { ENTITY_ITEM_DEFAULT_ITEM_DESCRIPTION };
|
||||
QString _itemCategories { ENTITY_ITEM_DEFAULT_ITEM_CATEGORIES };
|
||||
QString _itemArtist { ENTITY_ITEM_DEFAULT_ITEM_ARTIST };
|
||||
QString _itemLicense { ENTITY_ITEM_DEFAULT_ITEM_LICENSE };
|
||||
quint32 _limitedRun { ENTITY_ITEM_DEFAULT_LIMITED_RUN };
|
||||
QString _certificateID { ENTITY_ITEM_DEFAULT_CERTIFICATE_ID };
|
||||
quint32 _editionNumber { ENTITY_ITEM_DEFAULT_EDITION_NUMBER };
|
||||
quint32 _entityInstanceNumber { ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER };
|
||||
QString _marketplaceID { ENTITY_ITEM_DEFAULT_MARKETPLACE_ID };
|
||||
|
||||
|
||||
// NOTE: Damping is applied like this: v *= pow(1 - damping, dt)
|
||||
//
|
||||
// Hence the damping coefficient must range from 0 (no damping) to 1 (immediate stop).
|
||||
|
|
|
@ -288,7 +288,19 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_RADIUS_SPREAD, radiusSpread);
|
||||
CHECK_PROPERTY_CHANGE(PROP_RADIUS_START, radiusStart);
|
||||
CHECK_PROPERTY_CHANGE(PROP_RADIUS_FINISH, radiusFinish);
|
||||
|
||||
// Certifiable Properties
|
||||
CHECK_PROPERTY_CHANGE(PROP_ITEM_NAME, itemName);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ITEM_DESCRIPTION, itemDescription);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ITEM_CATEGORIES, itemCategories);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ITEM_ARTIST, itemArtist);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ITEM_LICENSE, itemLicense);
|
||||
CHECK_PROPERTY_CHANGE(PROP_LIMITED_RUN, limitedRun);
|
||||
CHECK_PROPERTY_CHANGE(PROP_MARKETPLACE_ID, marketplaceID);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EDITION_NUMBER, editionNumber);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ENTITY_INSTANCE_NUMBER, entityInstanceNumber);
|
||||
CHECK_PROPERTY_CHANGE(PROP_CERTIFICATE_ID, certificateID);
|
||||
|
||||
CHECK_PROPERTY_CHANGE(PROP_NAME, name);
|
||||
CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode);
|
||||
CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl);
|
||||
|
@ -405,7 +417,19 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ACTION_DATA, actionData);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCKED, locked);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_DATA, userData);
|
||||
|
||||
// Certifiable Properties
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ITEM_NAME, itemName);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ITEM_DESCRIPTION, itemDescription);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ITEM_CATEGORIES, itemCategories);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ITEM_ARTIST, itemArtist);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ITEM_LICENSE, itemLicense);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LIMITED_RUN, limitedRun);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MARKETPLACE_ID, marketplaceID);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EDITION_NUMBER, editionNumber);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ENTITY_INSTANCE_NUMBER, entityInstanceNumber);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CERTIFICATE_ID, certificateID);
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_SOUND_URL, collisionSoundURL);
|
||||
|
||||
|
@ -671,7 +695,19 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusSpread, float, setRadiusSpread);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusStart, float, setRadiusStart);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusFinish, float, setRadiusFinish);
|
||||
|
||||
// Certifiable Properties
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(itemName, QString, setItemName);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(itemDescription, QString, setItemDescription);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(itemCategories, QString, setItemCategories);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(itemArtist, QString, setItemArtist);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(itemLicense, QString, setItemLicense);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(limitedRun, quint32, setLimitedRun);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(marketplaceID, QString, setMarketplaceID);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(editionNumber, quint32, setEditionNumber);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(entityInstanceNumber, quint32, setEntityInstanceNumber);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(certificateID, QString, setCertificateID);
|
||||
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionSoundURL, QString, setCollisionSoundURL);
|
||||
|
||||
|
@ -809,7 +845,19 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
|||
COPY_PROPERTY_IF_CHANGED(radiusSpread);
|
||||
COPY_PROPERTY_IF_CHANGED(radiusStart);
|
||||
COPY_PROPERTY_IF_CHANGED(radiusFinish);
|
||||
|
||||
// Certifiable Properties
|
||||
COPY_PROPERTY_IF_CHANGED(itemName);
|
||||
COPY_PROPERTY_IF_CHANGED(itemDescription);
|
||||
COPY_PROPERTY_IF_CHANGED(itemCategories);
|
||||
COPY_PROPERTY_IF_CHANGED(itemArtist);
|
||||
COPY_PROPERTY_IF_CHANGED(itemLicense);
|
||||
COPY_PROPERTY_IF_CHANGED(limitedRun);
|
||||
COPY_PROPERTY_IF_CHANGED(marketplaceID);
|
||||
COPY_PROPERTY_IF_CHANGED(editionNumber);
|
||||
COPY_PROPERTY_IF_CHANGED(entityInstanceNumber);
|
||||
COPY_PROPERTY_IF_CHANGED(certificateID);
|
||||
|
||||
COPY_PROPERTY_IF_CHANGED(name);
|
||||
COPY_PROPERTY_IF_CHANGED(collisionSoundURL);
|
||||
|
||||
|
@ -981,7 +1029,19 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
|||
ADD_PROPERTY_TO_MAP(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_RADIUS_START, RadiusStart, radiusStart, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float);
|
||||
|
||||
// Certifiable Properties
|
||||
ADD_PROPERTY_TO_MAP(PROP_ITEM_NAME, ItemName, itemName, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_ITEM_CATEGORIES, ItemCategories, itemCategories, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_ITEM_ARTIST, ItemArtist, itemArtist, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_ITEM_LICENSE, ItemLicense, itemLicense, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_LIMITED_RUN, LimitedRun, limitedRun, quint32);
|
||||
ADD_PROPERTY_TO_MAP(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_EDITION_NUMBER, EditionNumber, editionNumber, quint32);
|
||||
ADD_PROPERTY_TO_MAP(PROP_ENTITY_INSTANCE_NUMBER, EntityInstanceNumber, entityInstanceNumber, quint32);
|
||||
ADD_PROPERTY_TO_MAP(PROP_CERTIFICATE_ID, CertificateID, certificateID, QString);
|
||||
|
||||
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor);
|
||||
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLightAmbientIntensity, keyLightAmbientIntensity, float);
|
||||
|
@ -1334,11 +1394,22 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
properties.getType() == EntityTypes::Sphere) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape());
|
||||
}
|
||||
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID());
|
||||
APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
|
||||
|
||||
// Certifiable Properties
|
||||
APPEND_ENTITY_PROPERTY(PROP_ITEM_NAME, properties.getItemName());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ITEM_DESCRIPTION, properties.getItemDescription());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ITEM_CATEGORIES, properties.getItemCategories());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ITEM_ARTIST, properties.getItemArtist());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ITEM_LICENSE, properties.getItemLicense());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LIMITED_RUN, properties.getLimitedRun());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EDITION_NUMBER, properties.getEditionNumber());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, properties.getEntityInstanceNumber());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, properties.getCertificateID());
|
||||
}
|
||||
|
||||
if (propertyCount > 0) {
|
||||
|
@ -1632,12 +1703,23 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE, QString, setShape);
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACTION_DATA, QByteArray, setActionData);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
|
||||
|
||||
// Certifiable Properties
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ITEM_NAME, QString, setItemName);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ITEM_DESCRIPTION, QString, setItemDescription);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ITEM_CATEGORIES, QString, setItemCategories);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ITEM_ARTIST, QString, setItemArtist);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ITEM_LICENSE, QString, setItemLicense);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIMITED_RUN, quint32, setLimitedRun);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EDITION_NUMBER, quint32, setEditionNumber);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ENTITY_INSTANCE_NUMBER, quint32, setEntityInstanceNumber);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CERTIFICATE_ID, QString, setCertificateID);
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
@ -1746,7 +1828,17 @@ void EntityItemProperties::markAllChanged() {
|
|||
//_alphaStartChanged = true;
|
||||
//_alphaFinishChanged = true;
|
||||
|
||||
// Certifiable Properties
|
||||
_itemNameChanged = true;
|
||||
_itemDescriptionChanged = true;
|
||||
_itemCategoriesChanged = true;
|
||||
_itemArtistChanged = true;
|
||||
_itemLicenseChanged = true;
|
||||
_limitedRunChanged = true;
|
||||
_marketplaceIDChanged = true;
|
||||
_editionNumberChanged = true;
|
||||
_entityInstanceNumberChanged = true;
|
||||
_certificateIDChanged = true;
|
||||
|
||||
_keyLight.markAllChanged();
|
||||
|
||||
|
@ -2053,9 +2145,39 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
if (radiusFinishChanged()) {
|
||||
out += "radiusFinish";
|
||||
}
|
||||
|
||||
// Certifiable Properties
|
||||
if (itemNameChanged()) {
|
||||
out += "itemName";
|
||||
}
|
||||
if (itemDescriptionChanged()) {
|
||||
out += "itemDescription";
|
||||
}
|
||||
if (itemCategoriesChanged()) {
|
||||
out += "itemCategories";
|
||||
}
|
||||
if (itemArtistChanged()) {
|
||||
out += "itemArtist";
|
||||
}
|
||||
if (itemLicenseChanged()) {
|
||||
out += "itemLicense";
|
||||
}
|
||||
if (limitedRunChanged()) {
|
||||
out += "limitedRun";
|
||||
}
|
||||
if (marketplaceIDChanged()) {
|
||||
out += "marketplaceID";
|
||||
}
|
||||
if (editionNumberChanged()) {
|
||||
out += "editionNumber";
|
||||
}
|
||||
if (entityInstanceNumberChanged()) {
|
||||
out += "entityInstanceNumber";
|
||||
}
|
||||
if (certificateIDChanged()) {
|
||||
out += "certificateID";
|
||||
}
|
||||
|
||||
if (backgroundModeChanged()) {
|
||||
out += "backgroundMode";
|
||||
}
|
||||
|
|
|
@ -170,7 +170,6 @@ public:
|
|||
DEFINE_PROPERTY(PROP_RADIUS_START, RadiusStart, radiusStart, float, particle::DEFAULT_RADIUS_START);
|
||||
DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, particle::DEFAULT_RADIUS_FINISH);
|
||||
DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, particle::DEFAULT_EMITTER_SHOULD_TRAIL);
|
||||
DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID);
|
||||
DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup);
|
||||
DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE);
|
||||
DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray, PolyVoxEntityItem::DEFAULT_VOXEL_DATA);
|
||||
|
@ -203,6 +202,18 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube());
|
||||
DEFINE_PROPERTY_REF(PROP_SHAPE, Shape, shape, QString, "Sphere");
|
||||
|
||||
// Certifiable Properties - related to Proof of Purchase certificates
|
||||
DEFINE_PROPERTY_REF(PROP_ITEM_NAME, ItemName, itemName, QString, ENTITY_ITEM_DEFAULT_ITEM_NAME);
|
||||
DEFINE_PROPERTY_REF(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString, ENTITY_ITEM_DEFAULT_ITEM_DESCRIPTION);
|
||||
DEFINE_PROPERTY_REF(PROP_ITEM_CATEGORIES, ItemCategories, itemCategories, QString, ENTITY_ITEM_DEFAULT_ITEM_CATEGORIES);
|
||||
DEFINE_PROPERTY_REF(PROP_ITEM_ARTIST, ItemArtist, itemArtist, QString, ENTITY_ITEM_DEFAULT_ITEM_ARTIST);
|
||||
DEFINE_PROPERTY_REF(PROP_ITEM_LICENSE, ItemLicense, itemLicense, QString, ENTITY_ITEM_DEFAULT_ITEM_LICENSE);
|
||||
DEFINE_PROPERTY_REF(PROP_LIMITED_RUN, LimitedRun, limitedRun, quint32, ENTITY_ITEM_DEFAULT_LIMITED_RUN);
|
||||
DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID);
|
||||
DEFINE_PROPERTY_REF(PROP_EDITION_NUMBER, EditionNumber, editionNumber, quint32, ENTITY_ITEM_DEFAULT_EDITION_NUMBER);
|
||||
DEFINE_PROPERTY_REF(PROP_ENTITY_INSTANCE_NUMBER, EntityInstanceNumber, entityInstanceNumber, quint32, ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER);
|
||||
DEFINE_PROPERTY_REF(PROP_CERTIFICATE_ID, CertificateID, certificateID, QString, ENTITY_ITEM_DEFAULT_CERTIFICATE_ID);
|
||||
|
||||
// these are used when bouncing location data into and out of scripts
|
||||
DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glmVec3, ENTITY_ITEM_ZERO_VEC3);
|
||||
DEFINE_PROPERTY_REF(PROP_LOCAL_ROTATION, LocalRotation, localRotation, glmQuat, ENTITY_ITEM_DEFAULT_ROTATION);
|
||||
|
@ -426,7 +437,19 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
|||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, RadiusSpread, radiusSpread, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, RadiusStart, radiusStart, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, RadiusFinish, radiusFinish, "");
|
||||
|
||||
// Certifiable Properties
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ItemName, itemName, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ItemDescription, itemDescription, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ItemCategories, itemCategories, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ItemArtist, itemArtist, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ItemLicense, itemLicense, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LimitedRun, limitedRun, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, MarketplaceID, marketplaceID, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EditionNumber, editionNumber, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityInstanceNumber, entityInstanceNumber, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CertificateID, certificateID, "");
|
||||
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundMode, backgroundMode, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, "");
|
||||
|
|
|
@ -26,9 +26,20 @@ const glm::vec3 ENTITY_ITEM_HALF_VEC3 = glm::vec3(0.5f);
|
|||
|
||||
const bool ENTITY_ITEM_DEFAULT_LOCKED = false;
|
||||
const QString ENTITY_ITEM_DEFAULT_USER_DATA = QString("");
|
||||
const QString ENTITY_ITEM_DEFAULT_MARKETPLACE_ID = QString("");
|
||||
const QUuid ENTITY_ITEM_DEFAULT_SIMULATOR_ID = QUuid();
|
||||
|
||||
// Certifiable Properties
|
||||
const QString ENTITY_ITEM_DEFAULT_ITEM_NAME = QString("");
|
||||
const QString ENTITY_ITEM_DEFAULT_ITEM_DESCRIPTION = QString("");
|
||||
const QString ENTITY_ITEM_DEFAULT_ITEM_CATEGORIES = QString("");
|
||||
const QString ENTITY_ITEM_DEFAULT_ITEM_ARTIST = QString("");
|
||||
const QString ENTITY_ITEM_DEFAULT_ITEM_LICENSE = QString("");
|
||||
const quint32 ENTITY_ITEM_DEFAULT_LIMITED_RUN = -1;
|
||||
const QString ENTITY_ITEM_DEFAULT_MARKETPLACE_ID = QString("");
|
||||
const quint32 ENTITY_ITEM_DEFAULT_EDITION_NUMBER = 0;
|
||||
const quint32 ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER = 0;
|
||||
const QString ENTITY_ITEM_DEFAULT_CERTIFICATE_ID = QString("");
|
||||
|
||||
const float ENTITY_ITEM_DEFAULT_ALPHA = 1.0f;
|
||||
const float ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA = 1.0f;
|
||||
const bool ENTITY_ITEM_DEFAULT_VISIBLE = true;
|
||||
|
|
|
@ -187,7 +187,19 @@ enum EntityPropertyList {
|
|||
PROP_SERVER_SCRIPTS,
|
||||
|
||||
PROP_FILTER_URL,
|
||||
|
||||
|
||||
// Certificable Properties
|
||||
PROP_ITEM_NAME,
|
||||
PROP_ITEM_DESCRIPTION,
|
||||
PROP_ITEM_CATEGORIES,
|
||||
PROP_ITEM_ARTIST,
|
||||
PROP_ITEM_LICENSE,
|
||||
PROP_LIMITED_RUN,
|
||||
// PROP_MARKETPLACE_ID is above
|
||||
PROP_EDITION_NUMBER,
|
||||
PROP_ENTITY_INSTANCE_NUMBER,
|
||||
PROP_CERTIFICATE_ID,
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ATTENTION: add new properties to end of list just ABOVE this line
|
||||
PROP_AFTER_LAST_ITEM,
|
||||
|
|
|
@ -1765,3 +1765,31 @@ glm::mat4 EntityScriptingInterface::getEntityLocalTransform(const QUuid& entityI
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::verifyStaticCertificateProperties(const QUuid& entityID) {
|
||||
bool result = false;
|
||||
if (_entityTree) {
|
||||
_entityTree->withReadLock([&] {
|
||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID));
|
||||
if (entity) {
|
||||
result = entity->verifyStaticCertificateProperties();
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CERT
|
||||
QString EntityScriptingInterface::computeCertificateID(const QUuid& entityID) {
|
||||
QString result { "" };
|
||||
if (_entityTree) {
|
||||
_entityTree->withReadLock([&] {
|
||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID));
|
||||
if (entity) {
|
||||
result = entity->computeCertificateID();
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
|
@ -386,6 +386,11 @@ public slots:
|
|||
*/
|
||||
Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID);
|
||||
|
||||
Q_INVOKABLE bool verifyStaticCertificateProperties(const QUuid& entityID);
|
||||
#ifdef DEBUG_CERT
|
||||
Q_INVOKABLE QString computeCertificateID(const QUuid& entityID);
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
|
||||
|
|
|
@ -215,16 +215,18 @@ void ModelEntityItem::debugDump() const {
|
|||
}
|
||||
|
||||
void ModelEntityItem::setShapeType(ShapeType type) {
|
||||
if (type != _shapeType) {
|
||||
if (type == SHAPE_TYPE_STATIC_MESH && _dynamic) {
|
||||
// dynamic and STATIC_MESH are incompatible
|
||||
// since the shape is being set here we clear the dynamic bit
|
||||
_dynamic = false;
|
||||
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
|
||||
withWriteLock([&] {
|
||||
if (type != _shapeType) {
|
||||
if (type == SHAPE_TYPE_STATIC_MESH && _dynamic) {
|
||||
// dynamic and STATIC_MESH are incompatible
|
||||
// since the shape is being set here we clear the dynamic bit
|
||||
_dynamic = false;
|
||||
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
|
||||
}
|
||||
_shapeType = type;
|
||||
_dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
|
||||
}
|
||||
_shapeType = type;
|
||||
_dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ShapeType ModelEntityItem::getShapeType() const {
|
||||
|
@ -257,13 +259,15 @@ void ModelEntityItem::setModelURL(const QString& url) {
|
|||
}
|
||||
|
||||
void ModelEntityItem::setCompoundShapeURL(const QString& url) {
|
||||
if (_compoundShapeURL != url) {
|
||||
ShapeType oldType = computeTrueShapeType();
|
||||
_compoundShapeURL = url;
|
||||
if (oldType != computeTrueShapeType()) {
|
||||
_dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
|
||||
withWriteLock([&] {
|
||||
if (_compoundShapeURL.get() != url) {
|
||||
ShapeType oldType = computeTrueShapeType();
|
||||
_compoundShapeURL.set(url);
|
||||
if (oldType != computeTrueShapeType()) {
|
||||
_dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::setAnimationURL(const QString& url) {
|
||||
|
@ -492,10 +496,8 @@ bool ModelEntityItem::hasModel() const {
|
|||
return !_modelURL.isEmpty();
|
||||
});
|
||||
}
|
||||
bool ModelEntityItem::hasCompoundShapeURL() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return !_compoundShapeURL.isEmpty();
|
||||
});
|
||||
bool ModelEntityItem::hasCompoundShapeURL() const {
|
||||
return _compoundShapeURL.get().isEmpty();
|
||||
}
|
||||
|
||||
QString ModelEntityItem::getModelURL() const {
|
||||
|
@ -505,9 +507,7 @@ QString ModelEntityItem::getModelURL() const {
|
|||
}
|
||||
|
||||
QString ModelEntityItem::getCompoundShapeURL() const {
|
||||
return resultWithReadLock<QString>([&] {
|
||||
return _compoundShapeURL;
|
||||
});
|
||||
return _compoundShapeURL.get();
|
||||
}
|
||||
|
||||
void ModelEntityItem::setColor(const rgbColor& value) {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "EntityItem.h"
|
||||
#include <JointData.h>
|
||||
#include <ThreadSafeValueCache.h>
|
||||
#include "AnimationPropertyGroup.h"
|
||||
|
||||
class ModelEntityItem : public EntityItem {
|
||||
|
@ -153,7 +154,8 @@ protected:
|
|||
|
||||
rgbColor _color;
|
||||
QString _modelURL;
|
||||
QString _compoundShapeURL;
|
||||
|
||||
ThreadSafeValueCache<QString> _compoundShapeURL;
|
||||
|
||||
AnimationPropertyGroup _animationProperties;
|
||||
|
||||
|
|
|
@ -633,10 +633,12 @@ void ParticleEffectEntityItem::debugDump() const {
|
|||
}
|
||||
|
||||
void ParticleEffectEntityItem::setShapeType(ShapeType type) {
|
||||
if (type != _shapeType) {
|
||||
_shapeType = type;
|
||||
_dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
|
||||
}
|
||||
withWriteLock([&] {
|
||||
if (type != _shapeType) {
|
||||
_shapeType = type;
|
||||
_dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setMaxParticles(quint32 maxParticles) {
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
static void setDrawZoneBoundaries(bool value) { _drawZoneBoundaries = value; }
|
||||
|
||||
virtual bool isReadyToComputeShape() const override { return false; }
|
||||
void setShapeType(ShapeType type) override { _shapeType = type; }
|
||||
void setShapeType(ShapeType type) override { withWriteLock([&] { _shapeType = type; }); }
|
||||
virtual ShapeType getShapeType() const override;
|
||||
|
||||
virtual bool hasCompoundShapeURL() const;
|
||||
|
|
|
@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityData:
|
||||
case PacketType::EntityPhysics:
|
||||
return VERSION_ENTITIES_ANIMATION_ALLOW_TRANSLATION_PROPERTIES;
|
||||
return VERSION_ENTITIES_HAS_CERTIFICATE_PROPERTIES;
|
||||
case PacketType::EntityQuery:
|
||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree);
|
||||
case PacketType::AvatarIdentity:
|
||||
|
|
|
@ -267,6 +267,7 @@ const PacketVersion VERSION_ENTITIES_BULLET_DYNAMICS = 70;
|
|||
const PacketVersion VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT = 71;
|
||||
const PacketVersion VERSION_ENTITIES_HAS_HIGHLIGHT_SCRIPTING_INTERFACE = 72;
|
||||
const PacketVersion VERSION_ENTITIES_ANIMATION_ALLOW_TRANSLATION_PROPERTIES = 73;
|
||||
const PacketVersion VERSION_ENTITIES_HAS_CERTIFICATE_PROPERTIES = 74;
|
||||
|
||||
enum class EntityQueryPacketVersion: PacketVersion {
|
||||
JSONFilter = 18,
|
||||
|
|
|
@ -151,7 +151,7 @@ void DrawSceneOctree::run(const RenderContextPointer& renderContext, const ItemS
|
|||
float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust));
|
||||
Transform crosshairModel;
|
||||
crosshairModel.setTranslation(glm::vec3(0.0, 0.0, -1000.0));
|
||||
crosshairModel.setScale(1000.0 * tan(glm::radians(angle))); // Scaling at the actual tan of the lod angle => Multiplied by TWO
|
||||
crosshairModel.setScale(1000.0f * tanf(glm::radians(angle))); // Scaling at the actual tan of the lod angle => Multiplied by TWO
|
||||
batch.resetViewTransform();
|
||||
batch.setModelTransform(crosshairModel);
|
||||
batch.setPipeline(getDrawLODReticlePipeline());
|
||||
|
|
|
@ -45,7 +45,7 @@ Camera::Camera() :
|
|||
{
|
||||
}
|
||||
|
||||
void Camera::update(float deltaTime) {
|
||||
void Camera::update() {
|
||||
if (_isKeepLookingAt) {
|
||||
lookAt(_lookingAt);
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ public:
|
|||
|
||||
void initialize(); // instantly put the camera at the ideal position and orientation.
|
||||
|
||||
void update( float deltaTime );
|
||||
void update();
|
||||
|
||||
CameraMode getMode() const { return _mode; }
|
||||
void setMode(CameraMode m);
|
||||
|
|
|
@ -35,19 +35,19 @@ public:
|
|||
uint32_t interval() const { return INTERVAL; }
|
||||
|
||||
private:
|
||||
mutable uint64_t _start { usecTimestampNow() };
|
||||
mutable uint64_t _expiry { usecTimestampNow() + INTERVAL * USECS_PER_MSEC};
|
||||
mutable size_t _count { 0 };
|
||||
const float _scale { powf(10, PRECISION) };
|
||||
mutable std::atomic<float> _rate;
|
||||
|
||||
void checkRate() const {
|
||||
auto now = usecTimestampNow();
|
||||
float currentIntervalMs = (now - _start) / (float)USECS_PER_MSEC;
|
||||
if (currentIntervalMs > (float)INTERVAL) {
|
||||
float currentCount = _count;
|
||||
float intervalSeconds = currentIntervalMs / (float)MSECS_PER_SECOND;
|
||||
_rate = roundf(currentCount / intervalSeconds * _scale) / _scale;
|
||||
_start = now;
|
||||
if (now > _expiry) {
|
||||
float MSECS_PER_USEC = 0.001f;
|
||||
float SECS_PER_MSEC = 0.001f;
|
||||
float intervalSeconds = ((float)INTERVAL + (float)(now - _expiry) * MSECS_PER_USEC) * SECS_PER_MSEC;
|
||||
_rate = roundf((float)_count / intervalSeconds * _scale) / _scale;
|
||||
_expiry = now + INTERVAL * USECS_PER_MSEC;
|
||||
_count = 0;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -136,6 +136,14 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function<voi
|
|||
shownProperty.write(!shownProperty.read().toBool());
|
||||
}
|
||||
|
||||
bool OffscreenUi::isPointOnDesktopWindow(QVariant point) {
|
||||
QVariant result;
|
||||
BLOCKING_INVOKE_METHOD(_desktop, "isPointOnWindow",
|
||||
Q_RETURN_ARG(QVariant, result),
|
||||
Q_ARG(QVariant, point));
|
||||
return result.toBool();
|
||||
}
|
||||
|
||||
void OffscreenUi::hide(const QString& name) {
|
||||
QQuickItem* item = getRootItem()->findChild<QQuickItem*>(name);
|
||||
if (item) {
|
||||
|
|
|
@ -78,6 +78,7 @@ public:
|
|||
bool eventFilter(QObject* originalDestination, QEvent* event) override;
|
||||
void addMenuInitializer(std::function<void(VrMenu*)> f);
|
||||
QObject* getFlags();
|
||||
Q_INVOKABLE bool isPointOnDesktopWindow(QVariant point);
|
||||
QQuickItem* getDesktop();
|
||||
QQuickItem* getToolWindow();
|
||||
QObject* getRootMenu();
|
||||
|
|
|
@ -61,7 +61,7 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info)
|
|||
|
||||
// During the period in which we have HFC commerce in the system, but not applied everywhere:
|
||||
const QString tokenStringCommerce{ "Chrome/48.0 (HighFidelityInterface WithHFC)" };
|
||||
static Setting::Handle<bool> _settingSwitch{ "commerce", false };
|
||||
Setting::Handle<bool> _settingSwitch{ "commerce", false };
|
||||
bool isMoney = _settingSwitch.get();
|
||||
|
||||
const QString tokenString = !isAuthable ? tokenStringMobile : (isMoney ? tokenStringCommerce : tokenStringMetaverse);
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
onButtonClicked();
|
||||
break;
|
||||
case 'walletReset':
|
||||
Settings.setValue("isFirstUseOfPurchases", true);
|
||||
onButtonClicked();
|
||||
onButtonClicked();
|
||||
break;
|
||||
|
|
|
@ -108,13 +108,17 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
"userData"
|
||||
];
|
||||
|
||||
|
||||
var MARGIN = 25;
|
||||
function FarActionGrabEntity(hand) {
|
||||
this.hand = hand;
|
||||
this.grabbedThingID = null;
|
||||
this.actionID = null; // action this script created...
|
||||
this.entityWithContextOverlay = false;
|
||||
this.contextOverlayTimer = false;
|
||||
this.reticleMinX = MARGIN;
|
||||
this.reticleMaxX;
|
||||
this.reticleMinY = MARGIN;
|
||||
this.reticleMaxY;
|
||||
|
||||
var ACTION_TTL = 15; // seconds
|
||||
|
||||
|
@ -344,12 +348,28 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
this.grabbedThingID = null;
|
||||
};
|
||||
|
||||
this.updateRecommendedArea = function() {
|
||||
var dims = Controller.getViewportDimensions();
|
||||
this.reticleMaxX = dims.x - MARGIN;
|
||||
this.reticleMaxY = dims.y - MARGIN;
|
||||
};
|
||||
|
||||
this.calculateNewReticlePosition = function(intersection) {
|
||||
this.updateRecommendedArea();
|
||||
var point2d = HMD.overlayFromWorldPoint(intersection);
|
||||
point2d.x = Math.max(this.reticleMinX, Math.min(point2d.x, this.reticleMaxX));
|
||||
point2d.y = Math.max(this.reticleMinY, Math.min(point2d.y, this.reticleMaxY));
|
||||
return point2d;
|
||||
};
|
||||
|
||||
this.notPointingAtEntity = function(controllerData) {
|
||||
var intersection = controllerData.rayPicks[this.hand];
|
||||
var entityProperty = Entities.getEntityProperties(intersection.objectID);
|
||||
var entityType = entityProperty.type;
|
||||
var hudRayPick = controllerData.hudRayPicks[this.hand];
|
||||
var point2d = this.calculateNewReticlePosition(hudRayPick.intersection);
|
||||
if ((intersection.type === RayPick.INTERSECTED_ENTITY && entityType === "Web") ||
|
||||
intersection.type === RayPick.INTERSECTED_OVERLAY) {
|
||||
intersection.type === RayPick.INTERSECTED_OVERLAY || Window.isPointOnDesktopWindow(point2d)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -178,11 +178,11 @@
|
|||
}
|
||||
var hudRayPick = controllerData.hudRayPicks[this.hand];
|
||||
var point2d = this.calculateNewReticlePosition(hudRayPick.intersection);
|
||||
this.setReticlePosition(point2d);
|
||||
if (!Reticle.isPointingAtSystemOverlay(point2d)) {
|
||||
if (!Window.isPointOnDesktopWindow(point2d) && !this.triggerClicked) {
|
||||
this.exitModule();
|
||||
return false;
|
||||
}
|
||||
this.setReticlePosition(point2d);
|
||||
Reticle.visible = false;
|
||||
this.movedAway = false;
|
||||
this.triggerClicked = controllerData.triggerClicks[this.hand];
|
||||
|
@ -239,7 +239,7 @@
|
|||
|
||||
function cleanup() {
|
||||
ControllerDispatcherUtils.disableDispatcherModule("LeftHudOverlayPointer");
|
||||
ControllerDispatcherUtils.disbaleDispatcherModule("RightHudOverlayPointer");
|
||||
ControllerDispatcherUtils.disableDispatcherModule("RightHudOverlayPointer");
|
||||
}
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
var confirmAllPurchases = false; // Set this to "true" to cause Checkout.qml to popup for all items, even if free
|
||||
var userIsLoggedIn = false;
|
||||
var walletNeedsSetup = false;
|
||||
|
||||
function injectCommonCode(isDirectoryPage) {
|
||||
|
||||
|
@ -91,6 +92,48 @@
|
|||
});
|
||||
}
|
||||
|
||||
emitWalletSetupEvent = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "WALLET_SETUP"
|
||||
}));
|
||||
}
|
||||
|
||||
function maybeAddSetupWalletButton() {
|
||||
if (userIsLoggedIn && walletNeedsSetup) {
|
||||
var resultsElement = document.getElementById('results');
|
||||
var setupWalletElement = document.createElement('div');
|
||||
setupWalletElement.classList.add("row");
|
||||
setupWalletElement.id = "setupWalletDiv";
|
||||
setupWalletElement.style = "height:60px;margin:20px 10px 10px 10px;padding:12px 5px;" +
|
||||
"background-color:#D6F4D8;border-color:#aee9b2;border-width:2px;border-style:solid;border-radius:5px;";
|
||||
|
||||
var span = document.createElement('span');
|
||||
span.style = "margin:10px 5px;color:#1b6420;font-size:15px;";
|
||||
span.innerHTML = "<a href='#' onclick='emitWalletSetupEvent(); return false;'>Setup your Wallet</a> to get money and shop in Marketplace.";
|
||||
|
||||
var xButton = document.createElement('a');
|
||||
xButton.id = "xButton";
|
||||
xButton.setAttribute('href', "#");
|
||||
xButton.style = "width:50px;height:100%;margin:0;color:#ccc;font-size:20px;";
|
||||
xButton.innerHTML = "X";
|
||||
xButton.onclick = function () {
|
||||
setupWalletElement.remove();
|
||||
dummyRow.remove();
|
||||
};
|
||||
|
||||
setupWalletElement.appendChild(span);
|
||||
setupWalletElement.appendChild(xButton);
|
||||
|
||||
resultsElement.insertBefore(setupWalletElement, resultsElement.firstChild);
|
||||
|
||||
// Dummy row for padding
|
||||
var dummyRow = document.createElement('div');
|
||||
dummyRow.classList.add("row");
|
||||
dummyRow.style = "height:15px;";
|
||||
resultsElement.insertBefore(dummyRow, resultsElement.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
function maybeAddLogInButton() {
|
||||
if (!userIsLoggedIn) {
|
||||
var resultsElement = document.getElementById('results');
|
||||
|
@ -149,7 +192,7 @@
|
|||
var dropDownElement = document.getElementById('user-dropdown');
|
||||
purchasesElement.id = "purchasesButton";
|
||||
purchasesElement.setAttribute('href', "#");
|
||||
purchasesElement.innerHTML = "MY PURCHASES";
|
||||
purchasesElement.innerHTML = "My Purchases";
|
||||
// FRONTEND WEBDEV RANT: The username dropdown should REALLY not be programmed to be on the same
|
||||
// line as the search bar, overlaid on top of the search bar, floated right, and then relatively bumped up using "top:-50px".
|
||||
purchasesElement.style = "height:100%;margin-top:18px;font-weight:bold;float:right;margin-right:" + (dropDownElement.offsetWidth + 30) +
|
||||
|
@ -164,12 +207,34 @@
|
|||
}
|
||||
}
|
||||
|
||||
function changeDropdownMenu() {
|
||||
var logInOrOutButton = document.createElement('a');
|
||||
logInOrOutButton.id = "logInOrOutButton";
|
||||
logInOrOutButton.setAttribute('href', "#");
|
||||
logInOrOutButton.innerHTML = userIsLoggedIn ? "Log Out" : "Log In";
|
||||
logInOrOutButton.onclick = function () {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "LOGIN"
|
||||
}));
|
||||
};
|
||||
|
||||
$($('.dropdown-menu').find('li')[0]).append(logInOrOutButton);
|
||||
|
||||
$('a[href="/marketplace?view=mine"]').each(function () {
|
||||
$(this).attr('href', '#');
|
||||
$(this).on('click', function () {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "MY_ITEMS"
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function buyButtonClicked(id, name, author, price, href) {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "CHECKOUT",
|
||||
itemId: id,
|
||||
itemName: name,
|
||||
itemAuthor: author,
|
||||
itemPrice: price ? parseInt(price, 10) : 0,
|
||||
itemHref: href
|
||||
}));
|
||||
|
@ -235,9 +300,13 @@
|
|||
}
|
||||
|
||||
function injectHiFiCode() {
|
||||
if (confirmAllPurchases) {
|
||||
if (!$('body').hasClass("code-injected") && confirmAllPurchases) {
|
||||
|
||||
$('body').addClass("code-injected");
|
||||
|
||||
maybeAddLogInButton();
|
||||
maybeAddSetupWalletButton();
|
||||
changeDropdownMenu();
|
||||
|
||||
var target = document.getElementById('templated-items');
|
||||
// MutationObserver is necessary because the DOM is populated after the page is loaded.
|
||||
|
@ -260,9 +329,13 @@
|
|||
}
|
||||
|
||||
function injectHiFiItemPageCode() {
|
||||
if (confirmAllPurchases) {
|
||||
if (!$('body').hasClass("code-injected") && confirmAllPurchases) {
|
||||
|
||||
$('body').addClass("code-injected");
|
||||
|
||||
maybeAddLogInButton();
|
||||
maybeAddSetupWalletButton();
|
||||
changeDropdownMenu();
|
||||
|
||||
var purchaseButton = $('#side-info').find('.btn').first();
|
||||
|
||||
|
@ -551,7 +624,8 @@
|
|||
if (parsedJsonMessage.type === "marketplaces") {
|
||||
if (parsedJsonMessage.action === "commerceSetting") {
|
||||
confirmAllPurchases = !!parsedJsonMessage.data.commerceMode;
|
||||
userIsLoggedIn = !!parsedJsonMessage.data.userIsLoggedIn
|
||||
userIsLoggedIn = !!parsedJsonMessage.data.userIsLoggedIn;
|
||||
walletNeedsSetup = !!parsedJsonMessage.data.walletNeedsSetup;
|
||||
injectCode();
|
||||
}
|
||||
}
|
||||
|
@ -567,4 +641,5 @@
|
|||
|
||||
// Load / unload.
|
||||
window.addEventListener("load", onLoad); // More robust to Web site issues than using $(document).ready().
|
||||
window.addEventListener("page:change", onLoad); // Triggered after Marketplace HTML is changed
|
||||
}());
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page.
|
||||
var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html");
|
||||
var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js");
|
||||
var MARKETPLACE_CHECKOUT_QML_PATH = Script.resourcesPath() + "qml/hifi/commerce/checkout/Checkout.qml";
|
||||
var MARKETPLACE_CHECKOUT_QML_PATH_BASE = "qml/hifi/commerce/checkout/Checkout.qml";
|
||||
var MARKETPLACE_CHECKOUT_QML_PATH = Script.resourcesPath() + MARKETPLACE_CHECKOUT_QML_PATH_BASE;
|
||||
var MARKETPLACE_PURCHASES_QML_PATH = Script.resourcesPath() + "qml/hifi/commerce/purchases/Purchases.qml";
|
||||
var MARKETPLACE_WALLET_QML_PATH = Script.resourcesPath() + "qml/hifi/commerce/wallet/Wallet.qml";
|
||||
var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "commerce/inspectionCertificate/InspectionCertificate.qml";
|
||||
|
@ -60,6 +61,7 @@
|
|||
var onCommerceScreen = false;
|
||||
|
||||
var debugCheckout = false;
|
||||
var debugError = false;
|
||||
function showMarketplace() {
|
||||
if (!debugCheckout) {
|
||||
UserActivityLogger.openedMarketplace();
|
||||
|
@ -70,8 +72,7 @@
|
|||
method: 'updateCheckoutQML', params: {
|
||||
itemId: '0d90d21c-ce7a-4990-ad18-e9d2cf991027',
|
||||
itemName: 'Test Flaregun',
|
||||
itemAuthor: 'hifiDave',
|
||||
itemPrice: 17,
|
||||
itemPrice: (debugError ? 10 : 17),
|
||||
itemHref: 'http://mpassets.highfidelity.com/0d90d21c-ce7a-4990-ad18-e9d2cf991027-v1/flaregun.json',
|
||||
},
|
||||
canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified
|
||||
|
@ -106,8 +107,8 @@
|
|||
var referrerURL; // Used for updating Purchases QML
|
||||
var filterText; // Used for updating Purchases QML
|
||||
function onScreenChanged(type, url) {
|
||||
onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL;
|
||||
onCommerceScreen = type === "QML" && (url === MARKETPLACE_CHECKOUT_QML_PATH || url === MARKETPLACE_PURCHASES_QML_PATH || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1);
|
||||
onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1;
|
||||
onCommerceScreen = type === "QML" && (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH_BASE) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1);
|
||||
wireEventBridge(onCommerceScreen);
|
||||
|
||||
if (url === MARKETPLACE_PURCHASES_QML_PATH) {
|
||||
|
@ -128,12 +129,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
function setCertificateInfo(currentEntityWithContextOverlay, itemMarketplaceId, closeGoesToPurchases) {
|
||||
function setCertificateInfo(currentEntityWithContextOverlay, itemMarketplaceId) {
|
||||
wireEventBridge(true);
|
||||
tablet.sendToQml({
|
||||
method: 'inspectionCertificate_setMarketplaceId',
|
||||
marketplaceId: itemMarketplaceId || Entities.getEntityProperties(currentEntityWithContextOverlay, ['marketplaceID']).marketplaceID,
|
||||
closeGoesToPurchases: closeGoesToPurchases
|
||||
marketplaceId: itemMarketplaceId || Entities.getEntityProperties(currentEntityWithContextOverlay, ['marketplaceID']).marketplaceID
|
||||
});
|
||||
// ZRF FIXME! Make a call to the endpoint to get item info instead of this silliness
|
||||
Script.setTimeout(function () {
|
||||
|
@ -203,7 +203,8 @@
|
|||
action: "commerceSetting",
|
||||
data: {
|
||||
commerceMode: Settings.getValue("commerce", false),
|
||||
userIsLoggedIn: Account.loggedIn
|
||||
userIsLoggedIn: Account.loggedIn,
|
||||
walletNeedsSetup: Wallet.walletStatus === 1
|
||||
}
|
||||
}));
|
||||
} else if (parsedJsonMessage.type === "PURCHASES") {
|
||||
|
@ -212,6 +213,16 @@
|
|||
tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH);
|
||||
} else if (parsedJsonMessage.type === "LOGIN") {
|
||||
openLoginWindow();
|
||||
} else if (parsedJsonMessage.type === "WALLET_SETUP") {
|
||||
tablet.pushOntoStack(MARKETPLACE_WALLET_QML_PATH);
|
||||
} else if (parsedJsonMessage.type === "MY_ITEMS") {
|
||||
referrerURL = MARKETPLACE_URL_INITIAL;
|
||||
filterText = "";
|
||||
tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH);
|
||||
wireEventBridge(true);
|
||||
tablet.sendToQml({
|
||||
method: 'purchases_showMyItems'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -325,36 +336,26 @@
|
|||
case 'maybeEnableHmdPreview':
|
||||
Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled);
|
||||
break;
|
||||
case 'purchases_getIsFirstUse':
|
||||
tablet.sendToQml({
|
||||
method: 'purchases_getIsFirstUseResult',
|
||||
isFirstUseOfPurchases: Settings.getValue("isFirstUseOfPurchases", true)
|
||||
});
|
||||
break;
|
||||
case 'purchases_setIsFirstUse':
|
||||
Settings.setValue("isFirstUseOfPurchases", false);
|
||||
break;
|
||||
case 'purchases_openGoTo':
|
||||
tablet.loadQMLSource("TabletAddressDialog.qml");
|
||||
break;
|
||||
case 'purchases_itemCertificateClicked':
|
||||
tablet.loadQMLSource("../commerce/inspectionCertificate/InspectionCertificate.qml");
|
||||
setCertificateInfo("", message.itemMarketplaceId, true);
|
||||
setCertificateInfo("", message.itemMarketplaceId);
|
||||
break;
|
||||
case 'inspectionCertificate_closeClicked':
|
||||
if (message.closeGoesToPurchases) {
|
||||
referrerURL = MARKETPLACE_URL_INITIAL;
|
||||
filterText = "";
|
||||
tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH);
|
||||
} else {
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
tablet.gotoHomeScreen();
|
||||
break;
|
||||
case 'inspectionCertificate_showInMarketplaceClicked':
|
||||
tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL);
|
||||
break;
|
||||
case 'header_myItemsClicked':
|
||||
tablet.gotoWebScreen(MARKETPLACE_URL + '?view=mine', MARKETPLACES_INJECT_SCRIPT_URL);
|
||||
referrerURL = MARKETPLACE_URL_INITIAL;
|
||||
filterText = "";
|
||||
tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH);
|
||||
wireEventBridge(true);
|
||||
tablet.sendToQml({
|
||||
method: 'purchases_showMyItems'
|
||||
});
|
||||
break;
|
||||
default:
|
||||
print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message));
|
||||
|
|
Loading…
Reference in a new issue