Merge pull request #11495 from zfox23/pop_interfaceChanges2

Commerce: One step closer!
This commit is contained in:
Zach Fox 2017-10-03 11:56:00 -07:00 committed by GitHub
commit ea3da6c9dd
32 changed files with 950 additions and 448 deletions

View file

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

View file

@ -82,6 +82,7 @@ Rectangle {
height: 140;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
}
RalewayRegular {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View file

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

View file

@ -129,6 +129,7 @@ Item {
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
MouseArea {
enabled: titleBarSecurityImage.visible;

View file

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

View file

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

View file

@ -65,6 +65,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
}
}
MouseArea {

View file

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

View file

@ -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()
//

View file

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

View file

@ -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
@ -687,6 +688,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<ContextOverlayInterface>();
DependencyManager::set<Ledger>();
DependencyManager::set<Wallet>();
DependencyManager::set<WalletScriptingInterface>();
DependencyManager::set<FadeEffect>();
@ -2331,6 +2333,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()));
@ -6058,6 +6061,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);

View file

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

View file

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

View file

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

View file

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

View file

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

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

View 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

View file

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

View file

@ -79,8 +79,6 @@ private:
bool _isInMarketplaceInspectionMode { false };
Setting::Handle<bool> _commerceSettingSwitch{ "commerce", false };
void openInspectionCertificate();
void openMarketplace();
void enableEntityHighlight(const EntityItemID& entityItemID);

View file

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

View file

@ -78,6 +78,7 @@
onButtonClicked();
break;
case 'walletReset':
Settings.setValue("isFirstUseOfPurchases", true);
onButtonClicked();
onButtonClicked();
break;

View file

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

View file

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