diff --git a/interface/resources/qml/hifi/commerce/Checkout.qml b/interface/resources/qml/hifi/commerce/Checkout.qml deleted file mode 100644 index d9b0072917..0000000000 --- a/interface/resources/qml/hifi/commerce/Checkout.qml +++ /dev/null @@ -1,463 +0,0 @@ -// -// Checkout.qml -// qml/hifi/commerce -// -// Checkout -// -// Created by Zach Fox on 2017-08-07 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import Hifi 1.0 as Hifi -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit -import "../../controls" as HifiControls -import "./wallet" as HifiWallet - -// references XXX from root context - -Rectangle { - HifiConstants { id: hifi; } - - id: checkoutRoot; - property bool inventoryReceived: false; - property bool balanceReceived: false; - property string itemId: ""; - property string itemHref: ""; - property int balanceAfterPurchase: 0; - property bool alreadyOwned: false; - property int itemPriceFull: 0; - // Style - color: hifi.colors.baseGray; - Hifi.QmlCommerce { - id: commerce; - onBuyResult: { - if (result.status !== 'success') { - buyButton.text = result.message; - buyButton.enabled = false; - } else { - if (urlHandler.canHandleUrl(itemHref)) { - urlHandler.handleUrl(itemHref); - } - sendToScript({method: 'checkout_buySuccess', itemId: itemId}); - } - } - onBalanceResult: { - if (result.status !== 'success') { - console.log("Failed to get balance", result.message); - } else { - balanceReceived = true; - hfcBalanceText.text = parseFloat(result.data.balance/100).toFixed(2); - balanceAfterPurchase = parseFloat(result.data.balance/100) - parseFloat(checkoutRoot.itemPriceFull/100).toFixed(2); - } - } - onInventoryResult: { - if (result.status !== 'success') { - console.log("Failed to get inventory", result.message); - } else { - inventoryReceived = true; - console.log('inventory fixme', JSON.stringify(result)); - if (inventoryContains(result.data.assets, itemId)) { - alreadyOwned = true; - } else { - alreadyOwned = false; - } - } - } - } - - // - // TITLE BAR START - // - Item { - id: titleBarContainer; - // Size - width: checkoutRoot.width; - height: 50; - // Anchors - anchors.left: parent.left; - anchors.top: parent.top; - - // Title Bar text - RalewaySemiBold { - id: titleBarText; - text: "Checkout"; - // Text size - size: hifi.fontSizes.overlayTitle; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - anchors.leftMargin: 16; - anchors.bottom: parent.bottom; - width: paintedWidth; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - - // Separator - HifiControlsUit.Separator { - anchors.left: parent.left; - anchors.right: parent.right; - anchors.bottom: parent.bottom; - } - } - // - // TITLE BAR END - // - - // - // ITEM DESCRIPTION START - // - Item { - id: itemDescriptionContainer; - // Size - width: checkoutRoot.width; - height: childrenRect.height + 20; - // Anchors - anchors.left: parent.left; - anchors.top: titleBarContainer.bottom; - - // Item Name text - Item { - id: itemNameContainer; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 4; - anchors.left: parent.left; - anchors.leftMargin: 16; - anchors.right: parent.right; - anchors.rightMargin: 16; - height: childrenRect.height; - - RalewaySemiBold { - id: itemNameTextLabel; - text: "Item Name:"; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - width: paintedWidth; - // Text size - size: 16; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - RalewayRegular { - id: itemNameText; - // Text size - size: itemNameTextLabel.size; - // Anchors - anchors.top: parent.top; - anchors.left: itemNameTextLabel.right; - anchors.leftMargin: 16; - width: paintedWidth; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - } - - - // Item Author text - Item { - id: itemAuthorContainer; - // Anchors - anchors.top: itemNameContainer.bottom; - anchors.topMargin: 4; - anchors.left: parent.left; - anchors.leftMargin: 16; - anchors.right: parent.right; - anchors.rightMargin: 16; - height: childrenRect.height; - - RalewaySemiBold { - id: itemAuthorTextLabel; - text: "Item Author:"; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - width: paintedWidth; - // Text size - size: 16; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - RalewayRegular { - id: itemAuthorText; - // Text size - size: itemAuthorTextLabel.size; - // Anchors - anchors.top: parent.top; - anchors.left: itemAuthorTextLabel.right; - anchors.leftMargin: 16; - width: paintedWidth; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - } - - // HFC Balance text - Item { - id: hfcBalanceContainer; - // Anchors - anchors.top: itemAuthorContainer.bottom; - anchors.topMargin: 16; - anchors.left: parent.left; - anchors.leftMargin: 16; - anchors.right: parent.right; - anchors.rightMargin: 16; - height: childrenRect.height; - - RalewaySemiBold { - id: hfcBalanceTextLabel; - text: "HFC Balance:"; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - width: paintedWidth; - // Text size - size: 20; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - RalewayRegular { - id: hfcBalanceText; - text: "--"; - // Text size - size: hfcBalanceTextLabel.size; - // Anchors - anchors.top: parent.top; - anchors.left: hfcBalanceTextLabel.right; - anchors.leftMargin: 16; - width: paintedWidth; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - } - - // Item Price text - Item { - id: itemPriceContainer; - // Anchors - anchors.top: hfcBalanceContainer.bottom; - anchors.topMargin: 4; - anchors.left: parent.left; - anchors.leftMargin: 16; - anchors.right: parent.right; - anchors.rightMargin: 16; - height: childrenRect.height; - - RalewaySemiBold { - id: itemPriceTextLabel; - text: "Item Price:"; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - width: paintedWidth; - // Text size - size: 20; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - RalewayRegular { - id: itemPriceText; - // Text size - size: itemPriceTextLabel.size; - // Anchors - anchors.top: parent.top; - anchors.left: itemPriceTextLabel.right; - anchors.leftMargin: 16; - width: paintedWidth; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - } - - // HFC "Balance After Purchase" text - Item { - id: hfcBalanceAfterPurchaseContainer; - // Anchors - anchors.top: itemPriceContainer.bottom; - anchors.topMargin: 4; - anchors.left: parent.left; - anchors.leftMargin: 16; - anchors.right: parent.right; - anchors.rightMargin: 16; - height: childrenRect.height; - - RalewaySemiBold { - id: hfcBalanceAfterPurchaseTextLabel; - text: "HFC Balance After Purchase:"; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - width: paintedWidth; - // Text size - size: 20; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - RalewayRegular { - id: hfcBalanceAfterPurchaseText; - text: balanceAfterPurchase; - // Text size - size: hfcBalanceAfterPurchaseTextLabel.size; - // Anchors - anchors.top: parent.top; - anchors.left: hfcBalanceAfterPurchaseTextLabel.right; - anchors.leftMargin: 16; - width: paintedWidth; - // Style - color: (balanceAfterPurchase >= 0) ? hifi.colors.lightGrayText : hifi.colors.redHighlight; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - } - } - // - // ITEM DESCRIPTION END - // - - - // - // ACTION BUTTONS START - // - Item { - id: actionButtonsContainer; - // Size - width: checkoutRoot.width; - height: 40; - // Anchors - anchors.left: parent.left; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 8; - - // "Cancel" button - HifiControlsUit.Button { - id: cancelButton; - color: hifi.buttons.black; - colorScheme: hifi.colorSchemes.dark; - anchors.top: parent.top; - anchors.topMargin: 3; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 3; - anchors.left: parent.left; - anchors.leftMargin: 20; - width: parent.width/2 - anchors.leftMargin*2; - text: "Cancel" - onClicked: { - sendToScript({method: 'checkout_cancelClicked', params: itemId}); - } - } - - // "Buy" button - HifiControlsUit.Button { - id: buyButton; - enabled: balanceAfterPurchase >= 0 && inventoryReceived && balanceReceived; - color: hifi.buttons.black; - colorScheme: hifi.colorSchemes.dark; - anchors.top: parent.top; - anchors.topMargin: 3; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 3; - anchors.right: parent.right; - anchors.rightMargin: 20; - width: parent.width/2 - anchors.rightMargin*2; - text: (inventoryReceived && balanceReceived) ? (alreadyOwned ? "Already Owned: Get Item" : "Buy") : "--"; - onClicked: { - if (!alreadyOwned) { - commerce.buy(itemId, parseFloat(itemPriceText.text*100)); - } else { - if (urlHandler.canHandleUrl(itemHref)) { - urlHandler.handleUrl(itemHref); - } - sendToScript({method: 'checkout_buySuccess', itemId: itemId}); - } - } - } - } - // - // ACTION BUTTONS END - // - - // - // 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 'updateCheckoutQML': - itemId = message.params.itemId; - itemNameText.text = message.params.itemName; - itemAuthorText.text = message.params.itemAuthor; - checkoutRoot.itemPriceFull = message.params.itemPrice; - itemPriceText.text = parseFloat(checkoutRoot.itemPriceFull/100).toFixed(2); - itemHref = message.params.itemHref; - commerce.balance(); - commerce.inventory(); - break; - default: - console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message)); - } - } - signal sendToScript(var message); - - function inventoryContains(inventoryJson, id) { - for (var idx = 0; idx < inventoryJson.length; idx++) { - if(inventoryJson[idx].id === id) { - return true; - } - } - return false; - } - - // - // FUNCTION DEFINITIONS END - // -} diff --git a/interface/resources/qml/hifi/commerce/Inventory.qml b/interface/resources/qml/hifi/commerce/Inventory.qml deleted file mode 100644 index 20458f9f16..0000000000 --- a/interface/resources/qml/hifi/commerce/Inventory.qml +++ /dev/null @@ -1,280 +0,0 @@ -// -// Inventory.qml -// qml/hifi/commerce -// -// Inventory -// -// Created by Zach Fox on 2017-08-10 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import Hifi 1.0 as Hifi -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit -import "../../controls" as HifiControls -import "./wallet" as HifiWallet - -// references XXX from root context - -Rectangle { - HifiConstants { id: hifi; } - - id: inventoryRoot; - property string referrerURL: ""; - // Style - color: hifi.colors.baseGray; - Hifi.QmlCommerce { - id: commerce; - onBalanceResult: { - if (result.status !== 'success') { - console.log("Failed to get balance", result.message); - } else { - hfcBalanceText.text = parseFloat(result.data.balance/100).toFixed(2); - } - } - onInventoryResult: { - if (result.status !== 'success') { - console.log("Failed to get inventory", result.message); - } else { - inventoryContentsList.model = result.data.assets; - } - } - } - - // - // TITLE BAR START - // - Item { - id: titleBarContainer; - // Size - width: parent.width; - height: 50; - // Anchors - anchors.left: parent.left; - anchors.top: parent.top; - - // Title Bar text - RalewaySemiBold { - id: titleBarText; - text: "Inventory"; - // Text size - size: hifi.fontSizes.overlayTitle; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - anchors.leftMargin: 16; - anchors.bottom: parent.bottom; - width: paintedWidth; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - - // Separator - HifiControlsUit.Separator { - anchors.left: parent.left; - anchors.right: parent.right; - anchors.bottom: parent.bottom; - } - } - // - // TITLE BAR END - // - - // - // HFC BALANCE START - // - Item { - id: hfcBalanceContainer; - // Size - width: inventoryRoot.width; - height: childrenRect.height + 20; - // Anchors - anchors.left: parent.left; - anchors.leftMargin: 16; - anchors.top: titleBarContainer.bottom; - anchors.topMargin: 4; - - RalewaySemiBold { - id: hfcBalanceTextLabel; - text: "HFC Balance:"; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - width: paintedWidth; - // Text size - size: 20; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - RalewayRegular { - id: hfcBalanceText; - text: "--"; - // Text size - size: hfcBalanceTextLabel.size; - // Anchors - anchors.top: parent.top; - anchors.left: hfcBalanceTextLabel.right; - anchors.leftMargin: 16; - width: paintedWidth; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - } - // - // HFC BALANCE END - // - - // - // INVENTORY CONTENTS START - // - Item { - id: inventoryContentsContainer; - // Anchors - anchors.left: parent.left; - anchors.leftMargin: 16; - anchors.right: parent.right; - anchors.rightMargin: 16; - anchors.top: hfcBalanceContainer.bottom; - anchors.topMargin: 8; - anchors.bottom: actionButtonsContainer.top; - anchors.bottomMargin: 8; - - RalewaySemiBold { - id: inventoryContentsLabel; - text: "Inventory:"; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - width: paintedWidth; - // Text size - size: 24; - // Style - color: hifi.colors.lightGrayText; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - ListView { - id: inventoryContentsList; - clip: true; - // Anchors - anchors.top: inventoryContentsLabel.bottom; - anchors.topMargin: 8; - anchors.left: parent.left; - anchors.bottom: parent.bottom; - width: parent.width; - delegate: Item { - width: parent.width; - height: 30; - RalewayRegular { - id: thisItemId; - // Text size - size: 20; - // Style - color: hifi.colors.blueAccent; - text: modelData.title; - // Alignment - horizontalAlignment: Text.AlignHLeft; - } - MouseArea { - anchors.fill: parent; - hoverEnabled: enabled; - onClicked: { - sendToScript({method: 'inventory_itemClicked', itemId: modelData.id}); - } - onEntered: { - thisItemId.color = hifi.colors.blueHighlight; - } - onExited: { - thisItemId.color = hifi.colors.blueAccent; - } - } - } - } - } - // - // INVENTORY CONTENTS END - // - - // - // ACTION BUTTONS START - // - Item { - id: actionButtonsContainer; - // Size - width: inventoryRoot.width; - height: 40; - // Anchors - anchors.left: parent.left; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 8; - - // "Back" button - HifiControlsUit.Button { - id: backButton; - 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: "Back" - onClicked: { - sendToScript({method: 'inventory_backClicked', referrerURL: referrerURL}); - } - } - } - // - // ACTION BUTTONS END - // - - // - // 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 'updateInventory': - referrerURL = message.referrerURL; - commerce.balance(); - commerce.inventory(); - break; - default: - console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message)); - } - } - signal sendToScript(var message); - - // - // FUNCTION DEFINITIONS END - // -} diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml new file mode 100644 index 0000000000..5329099df5 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -0,0 +1,902 @@ +// +// Checkout.qml +// qml/hifi/commerce/checkout +// +// Checkout +// +// Created by Zach Fox on 2017-08-25 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 as Hifi +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "../../../styles-uit" +import "../../../controls-uit" as HifiControlsUit +import "../../../controls" as HifiControls +import "../wallet" as HifiWallet + +// references XXX from root context + +Rectangle { + HifiConstants { id: hifi; } + + id: root; + property string activeView: "initialize"; + property bool purchasesReceived: false; + property bool balanceReceived: false; + property string itemId: ""; + property string itemHref: ""; + property double balanceAfterPurchase: 0; + property bool alreadyOwned: false; + property int itemPriceFull: 0; + property bool itemIsJson: true; + property bool securityImageResultReceived: false; + property bool keyFilePathIfExistsResultReceived: false; + // Style + color: hifi.colors.baseGray; + Hifi.QmlCommerce { + id: commerce; + + onSecurityImageResult: { + securityImageResultReceived = true; + if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up" + root.activeView = "notSetUp"; + } else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") { + root.activeView = "checkoutMain"; + } else if (exists) { + // just set the source again (to be sure the change was noticed) + securityImage.source = ""; + securityImage.source = "image://security/securityImage"; + } + } + + onKeyFilePathIfExistsResult: { + keyFilePathIfExistsResultReceived = true; + if (path === "" && root.activeView !== "notSetUp") { + root.activeView = "notSetUp"; + } else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") { + root.activeView = "checkoutMain"; + } + } + + onBuyResult: { + if (result.status !== 'success') { + failureErrorText.text = "Here's some more info about the error:

" + (result.message); + root.activeView = "checkoutFailure"; + } else { + root.activeView = "checkoutSuccess"; + } + } + + onBalanceResult: { + if (result.status !== 'success') { + console.log("Failed to get balance", result.data.message); + } else { + root.balanceReceived = true; + hfcBalanceText.text = (parseFloat(result.data.balance/100).toFixed(2)) + " HFC"; + balanceAfterPurchase = parseFloat(result.data.balance/100) - root.itemPriceFull/100; + root.setBuyText(); + } + } + + onInventoryResult: { + if (result.status !== 'success') { + console.log("Failed to get purchases", result.data.message); + } else { + root.purchasesReceived = true; + if (purchasesContains(result.data.assets, itemId)) { + root.alreadyOwned = true; + } else { + root.alreadyOwned = false; + } + root.setBuyText(); + } + } + } + + HifiWallet.SecurityImageModel { + id: securityImageModel; + } + + // + // TITLE BAR START + // + Item { + id: titleBarContainer; + // Size + width: parent.width; + height: 50; + // Anchors + anchors.left: parent.left; + anchors.top: parent.top; + + // Title Bar text + RalewaySemiBold { + id: titleBarText; + text: "MARKETPLACE"; + // Text size + size: hifi.fontSizes.overlayTitle; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.leftMargin: 16; + anchors.bottom: parent.bottom; + width: paintedWidth; + // Style + color: hifi.colors.faintGray; + // Alignment + horizontalAlignment: Text.AlignHLeft; + verticalAlignment: Text.AlignVCenter; + } + + // Security Image (TEMPORARY!) + Image { + id: securityImage; + // Anchors + anchors.top: parent.top; + anchors.right: parent.right; + anchors.verticalCenter: parent.verticalCenter; + height: parent.height - 10; + width: height; + fillMode: Image.PreserveAspectFit; + mipmap: true; + cache: false; + source: "image://security/securityImage"; + } + + // Separator + HifiControlsUit.Separator { + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + } + } + // + // TITLE BAR END + // + + Rectangle { + id: initialize; + visible: root.activeView === "initialize"; + anchors.top: titleBarContainer.bottom; + anchors.bottom: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + color: hifi.colors.baseGray; + + Component.onCompleted: { + commerce.getSecurityImage(); + commerce.getKeyFilePathIfExists(); + } + } + + // + // "WALLET NOT SET UP" START + // + Item { + id: notSetUp; + visible: root.activeView === "notSetUp"; + anchors.top: titleBarContainer.bottom; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + + RalewayRegular { + id: notSetUpText; + text: "Your wallet isn't set up.

Set up your Wallet (no credit card necessary) to claim your free HFC " + + "and get items from the Marketplace."; + // Text size + size: 24; + // Anchors + anchors.top: parent.top; + anchors.bottom: notSetUpActionButtonsContainer.top; + anchors.left: parent.left; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + // Style + color: hifi.colors.faintGray; + wrapMode: Text.WordWrap; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + + Item { + id: notSetUpActionButtonsContainer; + // Size + width: root.width; + height: 70; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 24; + + // "Cancel" button + HifiControlsUit.Button { + id: cancelButton; + color: hifi.buttons.black; + colorScheme: hifi.colorSchemes.dark; + anchors.top: parent.top; + anchors.topMargin: 3; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 3; + anchors.left: parent.left; + anchors.leftMargin: 20; + width: parent.width/2 - anchors.leftMargin*2; + text: "Cancel" + onClicked: { + sendToScript({method: 'checkout_cancelClicked', params: itemId}); + } + } + + // "Set Up" button + HifiControlsUit.Button { + id: setUpButton; + 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: "Set Up Wallet" + onClicked: { + sendToScript({method: 'checkout_setUpClicked'}); + } + } + } + } + // + // "WALLET NOT SET UP" END + // + + // + // CHECKOUT CONTENTS START + // + Item { + id: checkoutContents; + visible: root.activeView === "checkoutMain"; + anchors.top: titleBarContainer.bottom; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + + onVisibleChanged: { + if (visible) { + commerce.balance(); + commerce.inventory(); + } + } + + // + // ITEM DESCRIPTION START + // + Item { + id: itemDescriptionContainer; + // Anchors + anchors.left: parent.left; + anchors.leftMargin: 32; + anchors.right: parent.right; + anchors.rightMargin: 32; + anchors.top: parent.top; + anchors.bottom: checkoutActionButtonsContainer.top; + + // HFC Balance text + Item { + id: hfcBalanceContainer; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 30; + anchors.left: parent.left; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + height: childrenRect.height; + + RalewaySemiBold { + id: hfcBalanceTextLabel; + text: "Balance:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + width: paintedWidth; + height: paintedHeight; + // Text size + size: 30; + // Style + color: hifi.colors.lightGrayText; + // Alignment + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + } + RalewayRegular { + id: hfcBalanceText; + text: "-- HFC"; + // Text size + size: hfcBalanceTextLabel.size; + // Anchors + anchors.top: parent.top; + anchors.left: hfcBalanceTextLabel.right; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + height: paintedHeight; + // Style + color: hifi.colors.lightGrayText; + // Alignment + horizontalAlignment: Text.AlignRight; + verticalAlignment: Text.AlignVCenter; + } + } + + // Item Name text + Item { + id: itemNameContainer; + // Anchors + anchors.top: hfcBalanceContainer.bottom; + anchors.topMargin: 32; + anchors.left: parent.left; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + height: childrenRect.height; + + RalewaySemiBold { + id: itemNameTextLabel; + text: "Item:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + width: paintedWidth; + height: paintedHeight; + // Text size + size: 20; + // Style + color: hifi.colors.lightGrayText; + // Alignment + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + } + RalewayRegular { + id: itemNameText; + // Text size + size: itemNameTextLabel.size; + // Anchors + anchors.top: parent.top; + anchors.left: itemNameTextLabel.right; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + height: paintedHeight; + // Style + color: hifi.colors.lightGrayText; + elide: Text.ElideRight; + // Alignment + horizontalAlignment: Text.AlignRight; + verticalAlignment: Text.AlignVCenter; + } + } + + + // Item Author text + Item { + id: itemAuthorContainer; + // Anchors + anchors.top: itemNameContainer.bottom; + anchors.topMargin: 4; + anchors.left: parent.left; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + height: childrenRect.height; + + RalewaySemiBold { + id: itemAuthorTextLabel; + text: "Author:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + width: paintedWidth; + height: paintedHeight; + // Text size + size: 20; + // Style + color: hifi.colors.lightGrayText; + // Alignment + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + } + RalewayRegular { + id: itemAuthorText; + // Text size + size: itemAuthorTextLabel.size; + // Anchors + anchors.top: parent.top; + anchors.left: itemAuthorTextLabel.right; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + height: paintedHeight; + // Style + color: hifi.colors.lightGrayText; + elide: Text.ElideRight; + // Alignment + horizontalAlignment: Text.AlignRight; + verticalAlignment: Text.AlignVCenter; + } + } + + // "Item Price" container + Item { + id: itemPriceContainer; + // Anchors + anchors.top: itemAuthorContainer.bottom; + anchors.topMargin: 32; + anchors.left: parent.left; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + height: childrenRect.height; + + RalewaySemiBold { + id: itemPriceTextLabel; + text: "Item Price:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + width: paintedWidth; + height: paintedHeight; + // Text size + size: 30; + // Style + color: hifi.colors.lightGrayText; + // Alignment + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + } + RalewayRegular { + id: itemPriceText; + text: "-- HFC"; + // Text size + size: itemPriceTextLabel.size; + // Anchors + anchors.top: parent.top; + anchors.left: itemPriceTextLabel.right; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + height: paintedHeight; + // Style + color: (balanceAfterPurchase >= 0) ? hifi.colors.lightGrayText : hifi.colors.redHighlight; + // Alignment + horizontalAlignment: Text.AlignRight; + verticalAlignment: Text.AlignVCenter; + } + } + + // "Balance After Purchase" container + Item { + id: balanceAfterPurchaseContainer; + // Anchors + anchors.top: itemPriceContainer.bottom; + anchors.topMargin: 16; + anchors.left: parent.left; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + height: childrenRect.height; + + RalewaySemiBold { + id: balanceAfterPurchaseTextLabel; + text: "Balance After Purchase:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + width: paintedWidth; + height: paintedHeight; + // Text size + size: 20; + // Style + color: hifi.colors.lightGrayText; + // Alignment + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + } + RalewayRegular { + id: balanceAfterPurchaseText; + text: balanceAfterPurchase.toFixed(2) + " HFC"; + // Text size + size: balanceAfterPurchaseTextLabel.size; + // Anchors + anchors.top: parent.top; + anchors.left: balanceAfterPurchaseTextLabel.right; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + height: paintedHeight; + // Style + color: (balanceAfterPurchase >= 0) ? hifi.colors.lightGrayText : hifi.colors.redHighlight; + // Alignment + horizontalAlignment: Text.AlignRight; + verticalAlignment: Text.AlignVCenter; + } + } + } + // + // ITEM DESCRIPTION END + // + + + // + // ACTION BUTTONS AND TEXT START + // + Item { + id: checkoutActionButtonsContainer; + // Size + width: root.width; + height: 200; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 8; + + // "Cancel" button + HifiControlsUit.Button { + id: cancelPurchaseButton; + color: hifi.buttons.black; + colorScheme: hifi.colorSchemes.dark; + anchors.top: parent.top; + anchors.topMargin: 3; + height: 40; + anchors.left: parent.left; + anchors.leftMargin: 20; + width: parent.width/2 - anchors.leftMargin*2; + text: "Cancel" + onClicked: { + sendToScript({method: 'checkout_cancelClicked', params: itemId}); + } + } + + // "Buy" button + HifiControlsUit.Button { + id: buyButton; + enabled: (balanceAfterPurchase >= 0 && purchasesReceived && balanceReceived) || !itemIsJson; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; + anchors.top: parent.top; + anchors.topMargin: 3; + height: 40; + anchors.right: parent.right; + anchors.rightMargin: 20; + width: parent.width/2 - anchors.rightMargin*2; + text: (itemIsJson ? ((purchasesReceived && balanceReceived) ? (root.alreadyOwned ? "Buy Another" : "Buy"): "--") : "Get Item"); + onClicked: { + if (itemIsJson) { + buyButton.enabled = false; + commerce.buy(itemId, itemPriceFull); + } else { + if (urlHandler.canHandleUrl(itemHref)) { + urlHandler.handleUrl(itemHref); + } + } + } + } + + // "Purchases" button + HifiControlsUit.Button { + id: goToPurchasesButton; + color: hifi.buttons.black; + colorScheme: hifi.colorSchemes.dark; + anchors.top: buyButton.bottom; + anchors.topMargin: 20; + anchors.bottomMargin: 7; + height: 40; + anchors.left: parent.left; + anchors.leftMargin: 20; + width: parent.width - anchors.leftMargin*2; + text: "View Purchases" + onClicked: { + sendToScript({method: 'checkout_goToPurchases'}); + } + } + + RalewayRegular { + id: buyText; + // Text size + size: 20; + // Anchors + anchors.bottom: parent.bottom; + anchors.bottomMargin: 10; + height: paintedHeight; + anchors.left: parent.left; + anchors.leftMargin: 10; + anchors.right: parent.right; + anchors.rightMargin: 10; + // Style + color: hifi.colors.faintGray; + wrapMode: Text.WordWrap; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + } + // + // ACTION BUTTONS END + // + } + // + // CHECKOUT CONTENTS END + // + + // + // CHECKOUT SUCCESS START + // + Item { + id: checkoutSuccess; + visible: root.activeView === "checkoutSuccess"; + anchors.top: titleBarContainer.bottom; + anchors.bottom: root.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + + RalewayRegular { + id: completeText; + text: "Purchase Complete!

You bought " + (itemNameText.text) + " by " + (itemAuthorText.text) + ""; + // Text size + size: 24; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 40; + height: paintedHeight; + anchors.left: parent.left; + anchors.right: parent.right; + // Style + color: hifi.colors.faintGray; + wrapMode: Text.WordWrap; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + + Item { + id: checkoutSuccessActionButtonsContainer; + // Size + width: root.width; + height: 70; + // Anchors + anchors.top: completeText.bottom; + anchors.topMargin: 30; + anchors.left: parent.left; + anchors.right: parent.right; + + // "Purchases" button + HifiControlsUit.Button { + id: purchasesButton; + 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: "View Purchases"; + onClicked: { + sendToScript({method: 'checkout_goToPurchases'}); + } + } + + // "Rez Now!" button + HifiControlsUit.Button { + id: rezNowButton; + 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: "Rez Now!" + onClicked: { + if (urlHandler.canHandleUrl(itemHref)) { + urlHandler.handleUrl(itemHref); + } + } + } + } + + Item { + id: continueShoppingButtonContainer; + // Size + width: root.width; + height: 70; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 8; + // "Continue Shopping" button + HifiControlsUit.Button { + id: continueShoppingButton; + 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.rightMargin*2; + text: "Continue Shopping"; + onClicked: { + sendToScript({method: 'checkout_continueShopping', itemId: itemId}); + } + } + } + } + // + // CHECKOUT SUCCESS END + // + + // + // CHECKOUT FAILURE START + // + Item { + id: checkoutFailure; + visible: root.activeView === "checkoutFailure"; + anchors.top: titleBarContainer.bottom; + anchors.bottom: root.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + + RalewayRegular { + id: failureHeaderText; + text: "Purchase Failed.
Your Purchases and HFC balance haven't changed."; + // Text size + size: 24; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 80; + height: paintedHeight; + anchors.left: parent.left; + anchors.right: parent.right; + // Style + color: hifi.colors.faintGray; + wrapMode: Text.WordWrap; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + + RalewayRegular { + id: failureErrorText; + // Text size + size: 16; + // Anchors + anchors.top: failureHeaderText.bottom; + anchors.topMargin: 35; + height: paintedHeight; + anchors.left: parent.left; + anchors.right: parent.right; + // Style + color: hifi.colors.faintGray; + wrapMode: Text.WordWrap; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + + Item { + id: backToMarketplaceButtonContainer; + // Size + width: root.width; + height: 130; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 8; + // "Back to Marketplace" button + HifiControlsUit.Button { + id: backToMarketplaceButton; + color: hifi.buttons.black; + colorScheme: hifi.colorSchemes.dark; + anchors.top: parent.top; + anchors.topMargin: 3; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 3; + anchors.right: parent.right; + anchors.rightMargin: 20; + width: parent.width/2 - anchors.rightMargin*2; + text: "Back to Marketplace"; + onClicked: { + sendToScript({method: 'checkout_continueShopping', itemId: itemId}); + } + } + } + } + // + // CHECKOUT FAILURE END + // + + // + // 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 'updateCheckoutQML': + itemId = message.params.itemId; + itemNameText.text = message.params.itemName; + itemAuthorText.text = message.params.itemAuthor; + root.itemPriceFull = message.params.itemPrice; + itemPriceText.text = root.itemPriceFull === 0 ? "Free" : "" + (parseFloat(root.itemPriceFull/100).toFixed(2)) + " HFC"; + itemHref = message.params.itemHref; + if (itemHref.indexOf('.json') === -1) { + root.itemIsJson = false; + } + setBuyText(); + break; + default: + console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message)); + } + } + signal sendToScript(var message); + + function purchasesContains(purchasesJson, id) { + for (var idx = 0; idx < purchasesJson.length; idx++) { + if(purchasesJson[idx].id === id) { + return true; + } + } + return false; + } + + function setBuyText() { + if (root.itemIsJson) { + if (root.purchasesReceived && root.balanceReceived) { + if (root.balanceAfterPurchase < 0) { + if (root.alreadyOwned) { + buyText.text = "You do not have enough HFC to purchase this item again. Go to your Purchases to view the copy you own."; + } else { + buyText.text = "You do not have enough HFC to purchase this item."; + } + } else { + if (root.alreadyOwned) { + buyText.text = "You already own this item. If you buy it again, you'll be able to use multiple copies of it at once."; + } else { + buyText.text = "This item will be added to your Purchases, which can be accessed from Marketplace."; + } + } + } else { + buyText.text = ""; + } + } else { + buyText.text = "This Marketplace item isn't an entity. It will not be added to your Purchases."; + } + } + + // + // FUNCTION DEFINITIONS END + // +} diff --git a/interface/resources/qml/hifi/commerce/images/01cat.jpg b/interface/resources/qml/hifi/commerce/images/01cat.jpg deleted file mode 100644 index 6e7897cb82..0000000000 Binary files a/interface/resources/qml/hifi/commerce/images/01cat.jpg and /dev/null differ diff --git a/interface/resources/qml/hifi/commerce/images/02car.jpg b/interface/resources/qml/hifi/commerce/images/02car.jpg deleted file mode 100644 index 5dd8091e57..0000000000 Binary files a/interface/resources/qml/hifi/commerce/images/02car.jpg and /dev/null differ diff --git a/interface/resources/qml/hifi/commerce/images/03dog.jpg b/interface/resources/qml/hifi/commerce/images/03dog.jpg deleted file mode 100644 index 4a85b80c0c..0000000000 Binary files a/interface/resources/qml/hifi/commerce/images/03dog.jpg and /dev/null differ diff --git a/interface/resources/qml/hifi/commerce/images/04stars.jpg b/interface/resources/qml/hifi/commerce/images/04stars.jpg deleted file mode 100644 index 8f2bf62f83..0000000000 Binary files a/interface/resources/qml/hifi/commerce/images/04stars.jpg and /dev/null differ diff --git a/interface/resources/qml/hifi/commerce/images/05plane.jpg b/interface/resources/qml/hifi/commerce/images/05plane.jpg deleted file mode 100644 index 6504459d8b..0000000000 Binary files a/interface/resources/qml/hifi/commerce/images/05plane.jpg and /dev/null differ diff --git a/interface/resources/qml/hifi/commerce/images/06gingerbread.jpg b/interface/resources/qml/hifi/commerce/images/06gingerbread.jpg deleted file mode 100644 index 54c37faa2f..0000000000 Binary files a/interface/resources/qml/hifi/commerce/images/06gingerbread.jpg and /dev/null differ diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml new file mode 100644 index 0000000000..af32f5cfb7 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -0,0 +1,143 @@ +// +// PurchasedItem.qml +// qml/hifi/commerce/purchases +// +// PurchasedItem +// +// Created by Zach Fox on 2017-08-25 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 as Hifi +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "../../../styles-uit" +import "../../../controls-uit" as HifiControlsUit +import "../../../controls" as HifiControls +import "../wallet" as HifiWallet + +// references XXX from root context + +Rectangle { + HifiConstants { id: hifi; } + + id: root; + property string itemName: ""; + property string itemId: ""; + property string itemPreviewImageUrl: ""; + property string itemHref: ""; + // Style + color: hifi.colors.white; + // Size + width: parent.width; + height: 120; + + Image { + id: itemPreviewImage; + source: root.itemPreviewImageUrl; + anchors.left: parent.left; + anchors.leftMargin: 8; + anchors.top: parent.top; + anchors.topMargin: 8; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 8; + width: 180; + fillMode: Image.PreserveAspectFit; + + MouseArea { + anchors.fill: parent; + onClicked: { + sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId}); + } + } + } + + + RalewayRegular { + id: itemName; + anchors.top: itemPreviewImage.top; + anchors.left: itemPreviewImage.right; + anchors.leftMargin: 8; + anchors.right: parent.right; + anchors.rightMargin: 8; + height: 30; + // Text size + size: 20; + // Style + color: hifi.colors.blueAccent; + text: root.itemName; + elide: Text.ElideRight; + // Alignment + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + + MouseArea { + anchors.fill: parent; + hoverEnabled: enabled; + onClicked: { + sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId}); + } + onEntered: { + itemName.color = hifi.colors.blueHighlight; + } + onExited: { + itemName.color = hifi.colors.blueAccent; + } + } + } + + Item { + id: buttonContainer; + anchors.top: itemName.bottom; + anchors.topMargin: 8; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 8; + anchors.left: itemPreviewImage.right; + anchors.leftMargin: 8; + anchors.right: parent.right; + anchors.rightMargin: 8; + + // "Rez" button + HifiControlsUit.Button { + id: rezButton; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; + anchors.top: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + height: parent.height/2 - 4; + text: "Rez Item" + onClicked: { + if (urlHandler.canHandleUrl(root.itemHref)) { + urlHandler.handleUrl(root.itemHref); + } + } + } + + // "More Info" button + HifiControlsUit.Button { + id: moreInfoButton; + color: hifi.buttons.black; + colorScheme: hifi.colorSchemes.dark; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: parent.height/2 - 4; + text: "More Info" + onClicked: { + sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId}); + } + } + } + + // + // FUNCTION DEFINITIONS START + // + signal sendToPurchases(var msg); + // + // FUNCTION DEFINITIONS END + // +} diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml new file mode 100644 index 0000000000..5060b65e0b --- /dev/null +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -0,0 +1,414 @@ +// +// Purchases.qml +// qml/hifi/commerce/purchases +// +// Purchases +// +// Created by Zach Fox on 2017-08-25 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 as Hifi +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "../../../styles-uit" +import "../../../controls-uit" as HifiControlsUit +import "../../../controls" as HifiControls +import "../wallet" as HifiWallet + +// references XXX from root context + +Rectangle { + HifiConstants { id: hifi; } + + id: root; + property string activeView: "initialize"; + property string referrerURL: ""; + property bool securityImageResultReceived: false; + property bool keyFilePathIfExistsResultReceived: false; + property bool punctuationMode: false; + // Style + color: hifi.colors.baseGray; + Hifi.QmlCommerce { + id: commerce; + + onSecurityImageResult: { + securityImageResultReceived = true; + if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up" + root.activeView = "notSetUp"; + } else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") { + root.activeView = "purchasesMain"; + } else if (exists) { + // just set the source again (to be sure the change was noticed) + securityImage.source = ""; + securityImage.source = "image://security/securityImage"; + } + } + + onKeyFilePathIfExistsResult: { + keyFilePathIfExistsResultReceived = true; + if (path === "" && root.activeView !== "notSetUp") { + root.activeView = "notSetUp"; + } else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") { + root.activeView = "purchasesMain"; + } + } + + onInventoryResult: { + if (result.status !== 'success') { + console.log("Failed to get purchases", result.message); + } else { + purchasesModel.append(result.data.assets); + filteredPurchasesModel.append(result.data.assets); + } + } + } + + HifiWallet.SecurityImageModel { + id: securityImageModel; + } + + // + // TITLE BAR START + // + Item { + id: titleBarContainer; + // Size + height: 50; + // Anchors + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: parent.top; + + // Title Bar text + RalewaySemiBold { + id: titleBarText; + text: "PURCHASES"; + // Text size + size: hifi.fontSizes.overlayTitle; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.leftMargin: 16; + anchors.bottom: parent.bottom; + width: paintedWidth; + // Style + color: hifi.colors.faintGray; + // Alignment + horizontalAlignment: Text.AlignHLeft; + verticalAlignment: Text.AlignVCenter; + } + + // Security Image (TEMPORARY!) + Image { + id: securityImage; + // Anchors + anchors.top: parent.top; + anchors.right: parent.right; + anchors.verticalCenter: parent.verticalCenter; + height: parent.height - 10; + width: height; + fillMode: Image.PreserveAspectFit; + mipmap: true; + cache: false; + source: "image://security/securityImage"; + } + + // Separator + HifiControlsUit.Separator { + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + } + } + // + // TITLE BAR END + // + + Rectangle { + id: initialize; + visible: root.activeView === "initialize"; + anchors.top: titleBarContainer.bottom; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + color: hifi.colors.baseGray; + + Component.onCompleted: { + commerce.getSecurityImage(); + commerce.getKeyFilePathIfExists(); + } + } + + // + // "WALLET NOT SET UP" START + // + Item { + id: notSetUp; + visible: root.activeView === "notSetUp"; + anchors.top: titleBarContainer.bottom; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + + RalewayRegular { + id: notSetUpText; + text: "Your wallet isn't set up.

Set up your Wallet (no credit card necessary) to claim your free HFC " + + "and get items from the Marketplace."; + // Text size + size: 24; + // Anchors + anchors.top: parent.top; + anchors.bottom: notSetUpActionButtonsContainer.top; + anchors.left: parent.left; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + // Style + color: hifi.colors.faintGray; + wrapMode: Text.WordWrap; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + + Item { + id: notSetUpActionButtonsContainer; + // Size + width: root.width; + height: 70; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 24; + + // "Cancel" button + HifiControlsUit.Button { + id: cancelButton; + color: hifi.buttons.black; + colorScheme: hifi.colorSchemes.dark; + anchors.top: parent.top; + anchors.topMargin: 3; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 3; + anchors.left: parent.left; + anchors.leftMargin: 20; + width: parent.width/2 - anchors.leftMargin*2; + text: "Cancel" + onClicked: { + sendToScript({method: 'purchases_backClicked', referrerURL: referrerURL}); + } + } + + // "Set Up" button + HifiControlsUit.Button { + id: setUpButton; + 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: "Set Up Wallet" + onClicked: { + sendToScript({method: 'checkout_setUpClicked'}); + } + } + } + } + // + // "WALLET NOT SET UP" END + // + + // + // PURCHASES CONTENTS START + // + Item { + id: purchasesContentsContainer; + visible: root.activeView === "purchasesMain"; + // Anchors + anchors.left: parent.left; + anchors.leftMargin: 4; + anchors.right: parent.right; + anchors.rightMargin: 4; + anchors.top: titleBarContainer.bottom; + anchors.topMargin: 8; + anchors.bottom: actionButtonsContainer.top; + anchors.bottomMargin: 8; + + onVisibleChanged: { + if (visible) { + commerce.balance(); + commerce.inventory(); + } + } + + // + // FILTER BAR START + // + Item { + id: filterBarContainer; + // Size + height: 40; + // Anchors + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: parent.top; + anchors.topMargin: 4; + + HifiControlsUit.TextField { + id: filterBar; + property int previousLength: 0; + anchors.fill: parent; + placeholderText: "Filter"; + + onTextChanged: { + if (filterBar.text.length < previousLength) { + filteredPurchasesModel.clear(); + + for (var i = 0; i < purchasesModel.count; i++) { + filteredPurchasesModel.append(purchasesModel.get(i)); + } + } + + for (var i = 0; i < filteredPurchasesModel.count; i++) { + if (filteredPurchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) === -1) { + filteredPurchasesModel.remove(i); + i--; + } + } + previousLength = filterBar.text.length; + } + + onAccepted: { + focus = false; + } + } + } + // + // FILTER BAR END + // + + ListModel { + id: purchasesModel; + } + ListModel { + id: filteredPurchasesModel; + } + + ListView { + id: purchasesContentsList; + clip: true; + model: filteredPurchasesModel; + // Anchors + anchors.top: filterBarContainer.bottom; + anchors.topMargin: 12; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + width: parent.width; + delegate: PurchasedItem { + itemName: title; + itemId: id; + itemPreviewImageUrl: preview; + itemHref: root_file_url; + anchors.topMargin: 12; + anchors.bottomMargin: 12; + + Connections { + onSendToPurchases: { + if (msg.method === 'purchases_itemInfoClicked') { + sendToScript({method: 'purchases_itemInfoClicked', itemId: itemId}); + } + } + } + } + } + } + // + // PURCHASES CONTENTS END + // + + // + // ACTION BUTTONS START + // + Item { + id: actionButtonsContainer; + visible: purchasesContentsContainer.visible; + // Size + width: parent.width; + height: 40; + // Anchors + anchors.left: parent.left; + anchors.bottom: keyboard.top; + anchors.bottomMargin: 8; + + // "Back" button + HifiControlsUit.Button { + id: backButton; + 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: "Back" + onClicked: { + sendToScript({method: 'purchases_backClicked', referrerURL: referrerURL}); + } + } + } + // + // ACTION BUTTONS END + // + + HifiControlsUit.Keyboard { + id: keyboard; + raised: HMD.mounted && filterBar.focus; + numeric: parent.punctuationMode; + anchors { + bottom: parent.bottom; + left: parent.left; + right: parent.right; + } + } + + // + // 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; + default: + console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message)); + } + } + signal sendToScript(var message); + + // + // FUNCTION DEFINITIONS END + // +} diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index 88f939d393..33faacd0ab 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -39,6 +39,13 @@ Item { onBalanceResult : { balanceText.text = parseFloat(result.data.balance/100).toFixed(2); } + + onHistoryResult : { + if (result.status === 'success') { + var txt = result.data.history.map(function (h) { return h.text; }).join("
"); + transactionHistoryText.text = txt; + } + } } SecurityImageModel { @@ -105,6 +112,7 @@ Item { onVisibleChanged: { if (visible) { commerce.balance(); + commerce.history(); } } } @@ -227,17 +235,14 @@ Item { anchors.right: parent.right; // some placeholder stuff - RalewayRegular { - text: homeMessage.visible ? "you CANNOT scroll through this." : "you CAN scroll through this"; - // Text size - size: 16; - // Anchors + TextArea { + id: transactionHistoryText; + text: "
history unavailable
"; + textFormat: TextEdit.AutoText; + font.pointSize: 10; anchors.fill: parent; - // Style - color: hifi.colors.darkGray; - // Alignment - horizontalAlignment: Text.AlignHCenter; - verticalAlignment: Text.AlignVCenter; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignTop; } } @@ -351,6 +356,7 @@ Item { } } signal sendSignalToWallet(var msg); + // // FUNCTION DEFINITIONS END // diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 55ee0e16e9..dddfae6455 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -45,6 +45,7 @@ Handler(buy) Handler(receiveAt) Handler(balance) Handler(inventory) +Handler(history) void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, QJsonObject request) { auto accountManager = DependencyManager::get(); @@ -107,3 +108,6 @@ void Ledger::inventory(const QStringList& keys) { keysQuery("inventory", "inventorySuccess", "inventoryFailure"); } +void Ledger::history(const QStringList& keys) { + keysQuery("history", "historySuccess", "historyFailure"); +} diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index e43b8453b9..7d3fdef0c0 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -28,12 +28,14 @@ public: bool receiveAt(const QString& hfc_key, const QString& old_key); void balance(const QStringList& keys); void inventory(const QStringList& keys); + void history(const QStringList& keys); signals: void buyResult(QJsonObject result); void receiveAtResult(QJsonObject result); void balanceResult(QJsonObject result); void inventoryResult(QJsonObject result); + void historyResult(QJsonObject result); public slots: void buySuccess(QNetworkReply& reply); @@ -44,6 +46,8 @@ public slots: void balanceFailure(QNetworkReply& reply); void inventorySuccess(QNetworkReply& reply); void inventoryFailure(QNetworkReply& reply); + void historySuccess(QNetworkReply& reply); + void historyFailure(QNetworkReply& reply); private: QJsonObject apiResponse(const QString& label, QNetworkReply& reply); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index a5ae008a36..bf6bcc221c 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -25,6 +25,7 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) { connect(ledger.data(), &Ledger::balanceResult, this, &QmlCommerce::balanceResult); connect(ledger.data(), &Ledger::inventoryResult, this, &QmlCommerce::inventoryResult); connect(wallet.data(), &Wallet::securityImageResult, this, &QmlCommerce::securityImageResult); + connect(ledger.data(), &Ledger::historyResult, this, &QmlCommerce::historyResult); connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult); } @@ -46,26 +47,37 @@ void QmlCommerce::balance() { auto wallet = DependencyManager::get(); ledger->balance(wallet->listPublicKeys()); } + void QmlCommerce::inventory() { auto ledger = DependencyManager::get(); auto wallet = DependencyManager::get(); ledger->inventory(wallet->listPublicKeys()); } +void QmlCommerce::history() { + auto ledger = DependencyManager::get(); + auto wallet = DependencyManager::get(); + ledger->history(wallet->listPublicKeys()); +} + void QmlCommerce::chooseSecurityImage(const QString& imageFile) { auto wallet = DependencyManager::get(); wallet->chooseSecurityImage(imageFile); } + void QmlCommerce::getSecurityImage() { auto wallet = DependencyManager::get(); wallet->getSecurityImage(); } + void QmlCommerce::getLoginStatus() { emit loginStatusResult(DependencyManager::get()->isLoggedIn()); } + void QmlCommerce::setPassphrase(const QString& passphrase) { emit passphraseSetupStatusResult(true); } + void QmlCommerce::getPassphraseSetupStatus() { emit passphraseSetupStatusResult(false); } diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index 0c3ed64c81..fd913ae4b7 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -36,12 +36,14 @@ signals: void securityImageResult(bool exists); void loginStatusResult(bool isLoggedIn); void passphraseSetupStatusResult(bool passphraseIsSetup); + void historyResult(QJsonObject result); void keyFilePathIfExistsResult(const QString& path); protected: Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = ""); Q_INVOKABLE void balance(); Q_INVOKABLE void inventory(); + Q_INVOKABLE void history(); Q_INVOKABLE void chooseSecurityImage(const QString& imageFile); Q_INVOKABLE void getSecurityImage(); Q_INVOKABLE void getLoginStatus(); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 1d64733f49..9e04694a24 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -347,7 +347,7 @@ bool Wallet::createIfNeeded() { qCDebug(commerce) << "read private key"; RSA_free(key); // K -- add the public key since we have a legit private key associated with it - _publicKeys.push_back(QUrl::toPercentEncoding(publicKey.toBase64())); + _publicKeys.push_back(publicKey.toBase64()); return false; } } @@ -419,7 +419,7 @@ void Wallet::chooseSecurityImage(const QString& filename) { } // temporary... QString path = qApp->applicationDirPath(); - path.append("/resources/qml/hifi/commerce/"); + path.append("/resources/qml/hifi/commerce/wallet/"); path.append(filename); // now create a new security image pixmap _securityImage = new QPixmap(); diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 6f1167cfc9..c0278a6496 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -26,8 +26,7 @@ Base3DOverlay::Base3DOverlay() : _isSolid(DEFAULT_IS_SOLID), _isDashedLine(DEFAULT_IS_DASHED_LINE), _ignoreRayIntersection(false), - _drawInFront(false), - _isAA(true) + _drawInFront(false) { } @@ -39,7 +38,6 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) : _isDashedLine(base3DOverlay->_isDashedLine), _ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection), _drawInFront(base3DOverlay->_drawInFront), - _isAA(base3DOverlay->_isAA), _isGrabbable(base3DOverlay->_isGrabbable) { setTransform(base3DOverlay->getTransform()); @@ -191,13 +189,6 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { needRenderItemUpdate = true; } - auto isAA = properties["isAA"]; - if (isAA.isValid()) { - bool value = isAA.toBool(); - setIsAA(value); - needRenderItemUpdate = true; - } - // Communicate changes to the renderItem if needed if (needRenderItemUpdate) { auto itemID = getRenderItemID(); @@ -253,9 +244,6 @@ QVariant Base3DOverlay::getProperty(const QString& property) { if (property == "parentJointIndex") { return getParentJointIndex(); } - if (property == "isAA") { - return _isAA; - } return Overlay::getProperty(property); } diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 29d4c093a9..6377b46d7d 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -43,14 +43,11 @@ public: bool getDrawInFront() const { return _drawInFront; } bool getIsGrabbable() const { return _isGrabbable; } - virtual bool isAA() const { return _isAA; } - void setLineWidth(float lineWidth) { _lineWidth = lineWidth; } void setIsSolid(bool isSolid) { _isSolid = isSolid; } void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; } void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; } void setDrawInFront(bool value) { _drawInFront = value; } - void setIsAA(bool value) { _isAA = value; } void setIsGrabbable(bool value) { _isGrabbable = value; } virtual AABox getBounds() const override = 0; @@ -75,7 +72,6 @@ protected: bool _isDashedLine; bool _ignoreRayIntersection; bool _drawInFront; - bool _isAA; bool _isGrabbable { false }; QString _name; diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index f0b01827c7..104082dee4 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -335,9 +335,9 @@ void Web3DOverlay::render(RenderArgs* args) { batch.setModelTransform(transform); auto geometryCache = DependencyManager::get(); if (color.a < OPAQUE_ALPHA_THRESHOLD) { - geometryCache->bindTransparentWebBrowserProgram(batch, _isAA); + geometryCache->bindWebBrowserProgram(batch, true); } else { - geometryCache->bindOpaqueWebBrowserProgram(batch, _isAA); + geometryCache->bindWebBrowserProgram(batch); } geometryCache->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color, _geometryId); batch.setResourceTexture(0, nullptr); // restore default white color after me diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 113739d99b..9f4a9a3c4a 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -47,6 +47,11 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf _displayModelBounds(false), _layeredZones(this) { + setMouseRayPickResultOperator([](QUuid rayPickID) { + RayToEntityIntersectionResult entityResult; + return entityResult; + }); + setSetPrecisionPickingOperator([](QUuid rayPickID, bool value) {}); EntityRenderer::initEntityRenderers(); _currentHoverOverEntityID = UNKNOWN_ENTITY_ID; _currentClickingOnEntityID = UNKNOWN_ENTITY_ID; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 8590d6932c..8b5feb15f0 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -182,11 +182,10 @@ void WebEntityRenderer::doRender(RenderArgs* args) { float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); - const bool IS_AA = true; if (fadeRatio < OPAQUE_ALPHA_THRESHOLD) { - DependencyManager::get()->bindTransparentWebBrowserProgram(batch, IS_AA); + DependencyManager::get()->bindWebBrowserProgram(batch, true); } else { - DependencyManager::get()->bindOpaqueWebBrowserProgram(batch, IS_AA); + DependencyManager::get()->bindWebBrowserProgram(batch); } DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio), _geometryId); } diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 16efb96ce9..de0e4ddee5 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -132,10 +132,13 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, if (args.bitstreamVersion < VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP) { READ_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, QString, setAnimationSettings); } else { - // Note: since we've associated our _animationProperties with our _animationLoop, the readEntitySubclassDataFromBuffer() - // will automatically read into the animation loop - int bytesFromAnimation = _animationProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, animationPropertiesChanged); + int bytesFromAnimation; + withWriteLock([&] { + // Note: since we've associated our _animationProperties with our _animationLoop, the readEntitySubclassDataFromBuffer() + // will automatically read into the animation loop + bytesFromAnimation = _animationProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, animationPropertiesChanged); + }); bytesRead += bytesFromAnimation; dataAt += bytesFromAnimation; @@ -188,8 +191,10 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL()); APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures()); - _animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); + withReadLock([&] { + _animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + }); APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); @@ -241,12 +246,14 @@ ShapeType ModelEntityItem::computeTrueShapeType() const { } void ModelEntityItem::setModelURL(const QString& url) { - if (_modelURL != url) { - _modelURL = url; - if (_shapeType == SHAPE_TYPE_STATIC_MESH) { - _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; + withWriteLock([&] { + if (_modelURL != url) { + _modelURL = url; + if (_shapeType == SHAPE_TYPE_STATIC_MESH) { + _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; + } } - } + }); } void ModelEntityItem::setCompoundShapeURL(const QString& url) { @@ -261,7 +268,9 @@ void ModelEntityItem::setCompoundShapeURL(const QString& url) { void ModelEntityItem::setAnimationURL(const QString& url) { _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; - _animationProperties.setURL(url); + withWriteLock([&] { + _animationProperties.setURL(url); + }); } void ModelEntityItem::setAnimationSettings(const QString& value) { diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 286674488b..3bf83d08c9 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1923,7 +1923,7 @@ inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) { return a.getRaw() == b.getRaw(); } -static void buildWebShader(const std::string& vertShaderText, const std::string& fragShaderText, bool blendEnable, bool isAA, +static void buildWebShader(const std::string& vertShaderText, const std::string& fragShaderText, bool blendEnable, gpu::ShaderPointer& shaderPointerOut, gpu::PipelinePointer& pipelinePointerOut) { auto VS = gpu::Shader::createVertex(vertShaderText); auto PS = gpu::Shader::createPixel(fragShaderText); @@ -1939,43 +1939,23 @@ static void buildWebShader(const std::string& vertShaderText, const std::string& gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - if (isAA) { - blendEnable ? PrepareStencil::testMask(*state) : PrepareStencil::testMaskDrawShape(*state); - } else { - PrepareStencil::testMaskDrawShapeNoAA(*state); - } + PrepareStencil::testMaskDrawShapeNoAA(*state); pipelinePointerOut = gpu::Pipeline::create(shaderPointerOut, state); } -void GeometryCache::bindOpaqueWebBrowserProgram(gpu::Batch& batch, bool isAA) { - batch.setPipeline(getOpaqueWebBrowserProgram(isAA)); +void GeometryCache::bindWebBrowserProgram(gpu::Batch& batch, bool transparent) { + batch.setPipeline(getWebBrowserProgram(transparent)); } -gpu::PipelinePointer GeometryCache::getOpaqueWebBrowserProgram(bool isAA) { +gpu::PipelinePointer GeometryCache::getWebBrowserProgram(bool transparent) { static std::once_flag once; std::call_once(once, [&]() { - const bool BLEND_ENABLE = false; - buildWebShader(simple_vert, simple_opaque_web_browser_frag, BLEND_ENABLE, true, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipeline); - buildWebShader(simple_vert, simple_opaque_web_browser_frag, BLEND_ENABLE, false, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipelineNoAA); + buildWebShader(simple_vert, simple_opaque_web_browser_frag, false, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipelineNoAA); + buildWebShader(simple_vert, simple_transparent_web_browser_frag, true, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipelineNoAA); }); - return isAA ? _simpleOpaqueWebBrowserPipeline : _simpleOpaqueWebBrowserPipelineNoAA; -} - -void GeometryCache::bindTransparentWebBrowserProgram(gpu::Batch& batch, bool isAA) { - batch.setPipeline(getTransparentWebBrowserProgram(isAA)); -} - -gpu::PipelinePointer GeometryCache::getTransparentWebBrowserProgram(bool isAA) { - static std::once_flag once; - std::call_once(once, [&]() { - const bool BLEND_ENABLE = true; - buildWebShader(simple_vert, simple_transparent_web_browser_frag, BLEND_ENABLE, true, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipeline); - buildWebShader(simple_vert, simple_transparent_web_browser_frag, BLEND_ENABLE, false, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipelineNoAA); - }); - - return isAA ? _simpleTransparentWebBrowserPipeline : _simpleTransparentWebBrowserPipelineNoAA; + return transparent ? _simpleTransparentWebBrowserPipelineNoAA : _simpleOpaqueWebBrowserPipelineNoAA; } void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased) { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 37ba54ea80..a90842403b 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -166,11 +166,8 @@ public: static gpu::PipelinePointer getSimplePipeline(bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false, bool fading = false); - void bindOpaqueWebBrowserProgram(gpu::Batch& batch, bool isAA); - gpu::PipelinePointer getOpaqueWebBrowserProgram(bool isAA); - - void bindTransparentWebBrowserProgram(gpu::Batch& batch, bool isAA); - gpu::PipelinePointer getTransparentWebBrowserProgram(bool isAA); + void bindWebBrowserProgram(gpu::Batch& batch, bool transparent = false); + gpu::PipelinePointer getWebBrowserProgram(bool transparent); static void initializeShapePipelines(); @@ -459,10 +456,8 @@ private: static QHash _simplePrograms; gpu::ShaderPointer _simpleOpaqueWebBrowserShader; - gpu::PipelinePointer _simpleOpaqueWebBrowserPipeline; gpu::PipelinePointer _simpleOpaqueWebBrowserPipelineNoAA; gpu::ShaderPointer _simpleTransparentWebBrowserShader; - gpu::PipelinePointer _simpleTransparentWebBrowserPipeline; gpu::PipelinePointer _simpleTransparentWebBrowserPipelineNoAA; static render::ShapePipelinePointer getShapePipeline(bool textured = false, bool transparent = false, bool culled = true, diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 2889a1514a..62a4dcf369 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -90,20 +90,20 @@ }); } - function addInventoryButton() { + function addPurchasesButton() { // Why isn't this an id?! This really shouldn't be a class on the website, but it is. var navbarBrandElement = document.getElementsByClassName('navbar-brand')[0]; - var inventoryElement = document.createElement('a'); - inventoryElement.classList.add("btn"); - inventoryElement.classList.add("btn-default"); - inventoryElement.id = "inventoryButton"; - inventoryElement.setAttribute('href', "#"); - inventoryElement.innerHTML = "INVENTORY"; - inventoryElement.style = "height:100%;margin-top:0;padding:15px 15px;"; - navbarBrandElement.parentNode.insertAdjacentElement('beforeend', inventoryElement); - $('#inventoryButton').on('click', function () { + var purchasesElement = document.createElement('a'); + purchasesElement.classList.add("btn"); + purchasesElement.classList.add("btn-default"); + purchasesElement.id = "purchasesButton"; + purchasesElement.setAttribute('href', "#"); + purchasesElement.innerHTML = "PURCHASES"; + purchasesElement.style = "height:100%;margin-top:0;padding:15px 15px;"; + navbarBrandElement.parentNode.insertAdjacentElement('beforeend', purchasesElement); + $('#purchasesButton').on('click', function () { EventBridge.emitWebEvent(JSON.stringify({ - type: "INVENTORY", + type: "PURCHASES", referrerURL: window.location.href })); }); @@ -121,11 +121,19 @@ } function injectBuyButtonOnMainPage() { + var cost; + $('.grid-item').find('#price-or-edit').find('a').each(function() { $(this).attr('data-href', $(this).attr('href')); $(this).attr('href', '#'); - }); - $('.grid-item').find('#price-or-edit').find('.price').text("BUY"); + cost = $(this).closest('.col-xs-3').find('.item-cost').text(); + + if (parseInt(cost) > 0) { + $(this).find('.price').text("BUY"); + } + }); + + $('.grid-item').find('#price-or-edit').find('a').on('click', function () { buyButtonClicked($(this).closest('.grid-item').attr('data-item-id'), $(this).closest('.grid-item').find('.item-title').text(), @@ -153,7 +161,7 @@ // Try this here in case it works (it will if the user just pressed the "back" button, // since that doesn't trigger another AJAX request. injectBuyButtonOnMainPage; - addInventoryButton(); + addPurchasesButton(); } } @@ -161,15 +169,21 @@ if (confirmAllPurchases) { var href = $('#side-info').find('.btn').attr('href'); $('#side-info').find('.btn').attr('href', '#'); - $('#side-info').find('.btn').html('Buy Item '); + + var cost = $('.item-cost').text(); + + if (parseInt(cost) > 0) { + $('#side-info').find('.btn').html('Buy Item '); + } + $('#side-info').find('.btn').on('click', function () { buyButtonClicked(window.location.pathname.split("/")[3], $('#top-center').find('h1').text(), $('#creator').find('.value').text(), - $('.item-cost').text(), + cost, href); }); - addInventoryButton(); + addPurchasesButton(); } } diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index c3d55d5875..3dc62c9e34 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -167,7 +167,6 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { parentID: this.tabletEntityID, parentJointIndex: -1, showKeyboardFocusHighlight: false, - isAA: HMD.active, visible: visible }); @@ -453,10 +452,6 @@ WebTablet.prototype.onHmdChanged = function () { this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); // TODO -- is this still needed? // Entities.editEntity(this.tabletEntityID, tabletProperties); - - // Full scene FXAA should be disabled on the overlay when the tablet in desktop mode. - // This should make the text more readable. - Overlays.editOverlay(this.webOverlayID, { isAA: HMD.active }); }; WebTablet.prototype.pickle = function () { diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 84d7d44689..7b479cdcad 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -19,9 +19,9 @@ 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.qml"; - var MARKETPLACE_INVENTORY_QML_PATH = Script.resourcesPath() + "qml/hifi/commerce/Inventory.qml"; - var MARKETPLACE_SECURITY_QML_PATH = Script.resourcesPath() + "qml/hifi/commerce/SecurityImageSelection.qml"; + var MARKETPLACE_CHECKOUT_QML_PATH = Script.resourcesPath() + "qml/hifi/commerce/checkout/Checkout.qml"; + 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 HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; // var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; @@ -88,7 +88,7 @@ function onScreenChanged(type, url) { onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL; - wireEventBridge(type === "QML" && (url === MARKETPLACE_CHECKOUT_QML_PATH || url === MARKETPLACE_INVENTORY_QML_PATH || url === MARKETPLACE_SECURITY_QML_PATH)); + wireEventBridge(type === "QML" && (url === MARKETPLACE_CHECKOUT_QML_PATH || url === MARKETPLACE_PURCHASES_QML_PATH)); // for toolbar mode: change button to active when window is first openend, false otherwise. marketplaceButton.editProperties({ isActive: onMarketplaceScreen }); if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) { @@ -141,10 +141,10 @@ action: "inspectionModeSetting", data: Settings.getValue("inspectionMode", false) })); - } else if (parsedJsonMessage.type === "INVENTORY") { - tablet.pushOntoStack(MARKETPLACE_INVENTORY_QML_PATH); + } else if (parsedJsonMessage.type === "PURCHASES") { + tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); tablet.sendToQml({ - method: 'updateInventory', + method: 'updatePurchases', referrerURL: parsedJsonMessage.referrerURL }); } @@ -199,30 +199,37 @@ // in the format "{method, params}", like json-rpc. function fromQml(message) { switch (message.method) { + case 'checkout_setUpClicked': + tablet.pushOntoStack(MARKETPLACE_WALLET_QML_PATH); + break; case 'checkout_cancelClicked': tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.params, MARKETPLACES_INJECT_SCRIPT_URL); // TODO: Make Marketplace a QML app that's a WebView wrapper so we can use the app stack. // I don't think this is trivial to do since we also want to inject some JS into the DOM. //tablet.popFromStack(); break; - case 'checkout_buySuccess': + case 'checkout_goToPurchases': + tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); + tablet.sendToQml({ + method: 'updatePurchases', + referrerURL: MARKETPLACE_URL_INITIAL + }); + break; + case 'checkout_continueShopping': tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); //tablet.popFromStack(); break; - case 'inventory_itemClicked': + case 'purchases_itemInfoClicked': var itemId = message.itemId; if (itemId && itemId !== "") { tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + itemId, MARKETPLACES_INJECT_SCRIPT_URL); } break; - case 'inventory_backClicked': - tablet.gotoWebScreen(message.referrerURL, MARKETPLACES_INJECT_SCRIPT_URL); - break; - case 'securityImageSelection_cancelClicked': + case 'purchases_backClicked': tablet.gotoWebScreen(message.referrerURL, MARKETPLACES_INJECT_SCRIPT_URL); break; default: - print('Unrecognized message from Checkout.qml, Inventory.qml, or SecurityImageSelection.qml: ' + JSON.stringify(message)); + print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message)); } }