From e1064938cd38a770bc78d27e357fd134979c6708 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 10 Aug 2017 11:10:45 -0700 Subject: [PATCH 1/4] Add inventory; also fix some bugs --- .../resources/qml/hifi/commerce/Checkout.qml | 8 +- .../resources/qml/hifi/commerce/Inventory.qml | 188 ++++++++++++++++++ scripts/system/html/js/marketplacesInject.js | 21 ++ scripts/system/marketplaces/marketplaces.js | 20 +- 4 files changed, 230 insertions(+), 7 deletions(-) create mode 100644 interface/resources/qml/hifi/commerce/Inventory.qml diff --git a/interface/resources/qml/hifi/commerce/Checkout.qml b/interface/resources/qml/hifi/commerce/Checkout.qml index 48d5f5b1d5..78c3744a27 100644 --- a/interface/resources/qml/hifi/commerce/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/Checkout.qml @@ -269,7 +269,13 @@ Rectangle { if (buyFailed) { sendToScript({method: 'checkout_cancelClicked', params: itemId}); } else { - sendToScript({method: 'checkout_buyClicked', success: commerce.buy(itemId, parseInt(itemPriceText.text)), itemId: itemId, itemHref: itemHref}); + var success = commerce.buy(itemId, parseInt(itemPriceText.text)); + sendToScript({method: 'checkout_buyClicked', success: success, itemId: itemId, itemHref: itemHref}); + if (success) { + if (urlHandler.canHandleUrl(itemHref)) { + urlHandler.handleUrl(itemHref); + } + } } } } diff --git a/interface/resources/qml/hifi/commerce/Inventory.qml b/interface/resources/qml/hifi/commerce/Inventory.qml new file mode 100644 index 0000000000..fd488cfb2f --- /dev/null +++ b/interface/resources/qml/hifi/commerce/Inventory.qml @@ -0,0 +1,188 @@ +// +// 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 + +// references XXX from root context + +Rectangle { + HifiConstants { id: hifi; } + + id: inventoryRoot; + property string referrerURL: ""; + // Style + color: hifi.colors.baseGray; + Hifi.QmlCommerce { + id: commerce; + } + + // + // TITLE BAR START + // + Item { + id: titleBarContainer; + // Size + width: inventoryRoot.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.fill: parent; + anchors.leftMargin: 16; + // Style + color: hifi.colors.lightGrayText; + // Alignment + horizontalAlignment: Text.AlignHLeft; + verticalAlignment: Text.AlignVCenter; + } + + // Separator + HifiControlsUit.Separator { + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + } + } + // + // TITLE BAR END + // + + // + // 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 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 + // + + + // + // ACTION BUTTONS START + // + Item { + id: actionButtonsContainer; + // Size + width: inventoryRoot.width; + height: 40; + // Anchors + anchors.left: parent.left; + anchors.top: hfcBalanceContainer.bottom; + + // "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; + hfcBalanceText.text = message.hfcBalance; + break; + default: + console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message)); + } + } + signal sendToScript(var message); + + // + // FUNCTION DEFINITIONS END + // +} diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index a9b02e44dd..8368967c03 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -89,6 +89,25 @@ }); } + function addInventoryButton() { + // 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 () { + EventBridge.emitWebEvent(JSON.stringify({ + type: "INVENTORY", + referrerURL: window.location.href + })); + }); + } + function buyButtonClicked(id, name, author, price, href) { EventBridge.emitWebEvent(JSON.stringify({ type: "CHECKOUT", @@ -134,6 +153,7 @@ // since that doesn't trigger another AJAX request. injectBuyButtonOnMainPage(); } + addInventoryButton(); } function injectHiFiItemPageCode() { @@ -149,6 +169,7 @@ href); }); } + addInventoryButton(); } function updateClaraCode() { diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 97ed271009..0576e37d3e 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -20,6 +20,7 @@ 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 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"; @@ -86,7 +87,7 @@ function onScreenChanged(type, url) { onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL; - wireEventBridge(type === "QML" && url === MARKETPLACE_CHECKOUT_QML_PATH); + wireEventBridge(type === "QML" && (url === MARKETPLACE_CHECKOUT_QML_PATH || url === MARKETPLACE_INVENTORY_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) { @@ -139,6 +140,13 @@ action: "inspectionModeSetting", data: Settings.getValue("inspectionMode", false) })); + } else if (parsedJsonMessage.type === "INVENTORY") { + tablet.pushOntoStack(MARKETPLACE_INVENTORY_QML_PATH); + tablet.sendToQml({ + method: 'updateInventory', + referrerURL: parsedJsonMessage.referrerURL, + hfcBalance: 10 + }); } } } @@ -199,17 +207,17 @@ break; case 'checkout_buyClicked': if (message.success === true) { - tablet.gotoWebScreen(message.itemHref); - Script.setTimeout(function () { - tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); - }, 100); + tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); } else { tablet.sendToQml({ method: 'buyFailed' }); } //tablet.popFromStack(); break; + case 'inventory_backClicked': + tablet.gotoWebScreen(message.referrerURL, MARKETPLACES_INJECT_SCRIPT_URL); + break; default: - print('Unrecognized message from Checkout.qml: ' + JSON.stringify(message)); + print('Unrecognized message from Checkout.qml or Inventory.qml: ' + JSON.stringify(message)); } } From d2362e6329c9e30fea3e4d2fdd06e32f6d16d3d8 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 10 Aug 2017 12:29:28 -0700 Subject: [PATCH 2/4] addInventoryButton() under setting --- scripts/system/html/js/marketplacesInject.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 8368967c03..600159926e 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -151,9 +151,9 @@ // 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(); + injectBuyButtonOnMainPage; + addInventoryButton(); } - addInventoryButton(); } function injectHiFiItemPageCode() { @@ -168,8 +168,8 @@ 10, href); }); + addInventoryButton(); } - addInventoryButton(); } function updateClaraCode() { From 080c6da71a5aae200d61e7dec34a46a08522c406 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 11 Aug 2017 10:49:11 -0700 Subject: [PATCH 3/4] Get HFC balance from QmlCommerce API --- interface/resources/qml/hifi/commerce/Inventory.qml | 2 +- scripts/system/marketplaces/marketplaces.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/Inventory.qml b/interface/resources/qml/hifi/commerce/Inventory.qml index fd488cfb2f..f86be8b359 100644 --- a/interface/resources/qml/hifi/commerce/Inventory.qml +++ b/interface/resources/qml/hifi/commerce/Inventory.qml @@ -101,6 +101,7 @@ Rectangle { } RalewayRegular { id: hfcBalanceText; + text: commerce.balance(); // Text size size: hfcBalanceTextLabel.size; // Anchors @@ -174,7 +175,6 @@ Rectangle { switch (message.method) { case 'updateInventory': referrerURL = message.referrerURL; - hfcBalanceText.text = message.hfcBalance; break; default: console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message)); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 0576e37d3e..8aa36353c4 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -144,8 +144,7 @@ tablet.pushOntoStack(MARKETPLACE_INVENTORY_QML_PATH); tablet.sendToQml({ method: 'updateInventory', - referrerURL: parsedJsonMessage.referrerURL, - hfcBalance: 10 + referrerURL: parsedJsonMessage.referrerURL }); } } From a21ff75a49f0a4b037ebd2c64fa11619b77a86eb Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 11 Aug 2017 12:08:19 -0700 Subject: [PATCH 4/4] Big progress! --- .../resources/qml/hifi/commerce/Checkout.qml | 111 +++++++++++++++++- .../resources/qml/hifi/commerce/Inventory.qml | 74 +++++++++++- scripts/system/html/js/marketplacesInject.js | 2 +- scripts/system/marketplaces/marketplaces.js | 6 + 4 files changed, 187 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/Checkout.qml b/interface/resources/qml/hifi/commerce/Checkout.qml index 78c3744a27..82a652a7b9 100644 --- a/interface/resources/qml/hifi/commerce/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/Checkout.qml @@ -26,6 +26,8 @@ Rectangle { id: checkoutRoot; property string itemId; property string itemHref; + property int balanceAfterPurchase: commerce.balance() - parseInt(itemPriceText.text, 10); + property bool alreadyOwned: checkAlreadyOwned(itemId); // Style color: hifi.colors.baseGray; Hifi.QmlCommerce { @@ -172,11 +174,56 @@ Rectangle { } } + // 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: commerce.balance(); + // 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: itemAuthorContainer.bottom; + anchors.top: hfcBalanceContainer.bottom; anchors.topMargin: 4; anchors.left: parent.left; anchors.leftMargin: 16; @@ -215,6 +262,51 @@ Rectangle { 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 @@ -231,7 +323,8 @@ Rectangle { height: 40; // Anchors anchors.left: parent.left; - anchors.top: itemDescriptionContainer.bottom; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 8; // "Cancel" button HifiControlsUit.Button { @@ -255,6 +348,7 @@ Rectangle { HifiControlsUit.Button { property bool buyFailed: false; id: buyButton; + enabled: balanceAfterPurchase >= 0 && !alreadyOwned; color: hifi.buttons.black; colorScheme: hifi.colorSchemes.dark; anchors.top: parent.top; @@ -264,7 +358,7 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 20; width: parent.width/2 - anchors.rightMargin*2; - text: "Buy" + text: alreadyOwned ? "Already Owned" : "Buy"; onClicked: { if (buyFailed) { sendToScript({method: 'checkout_cancelClicked', params: itemId}); @@ -287,6 +381,16 @@ Rectangle { // // FUNCTION DEFINITIONS START // + + function checkAlreadyOwned(idToCheck) { + var inventory = commerce.inventory(); + if (inventory.indexOf(idToCheck) !== -1) { + return true; + } else { + return false; + } + } + // // Function Name: fromScript() // @@ -308,7 +412,6 @@ Rectangle { itemAuthorText.text = message.params.itemAuthor; itemPriceText.text = message.params.itemPrice; itemHref = message.params.itemHref; - buyButton.text = "Buy"; buyButton.buyFailed = false; break; case 'buyFailed': diff --git a/interface/resources/qml/hifi/commerce/Inventory.qml b/interface/resources/qml/hifi/commerce/Inventory.qml index f86be8b359..91fa3b9126 100644 --- a/interface/resources/qml/hifi/commerce/Inventory.qml +++ b/interface/resources/qml/hifi/commerce/Inventory.qml @@ -120,6 +120,77 @@ Rectangle { // 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; + // Anchors + anchors.top: inventoryContentsLabel.bottom; + anchors.topMargin: 8; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + width: parent.width; + model: commerce.inventory(); + delegate: Item { + width: parent.width; + height: 30; + RalewayRegular { + id: thisItemId; + // Text size + size: 20; + // Style + color: hifi.colors.blueAccent; + text: modelData; + // Alignment + horizontalAlignment: Text.AlignHLeft; + } + MouseArea { + anchors.fill: parent; + hoverEnabled: enabled; + onClicked: { + sendToScript({method: 'inventory_itemClicked', itemId: thisItemId.text}); + } + onEntered: { + thisItemId.color = hifi.colors.blueHighlight; + } + onExited: { + thisItemId.color = hifi.colors.blueAccent; + } + } + } + } + } + // + // INVENTORY CONTENTS END + // // // ACTION BUTTONS START @@ -131,7 +202,8 @@ Rectangle { height: 40; // Anchors anchors.left: parent.left; - anchors.top: hfcBalanceContainer.bottom; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 8; // "Back" button HifiControlsUit.Button { diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 600159926e..80c8f8a0e4 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -114,7 +114,7 @@ itemId: id, itemName: name, itemAuthor: author, - itemPrice: price, + itemPrice: Math.round(Math.random() * 50), itemHref: href })); } diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 8aa36353c4..e44e9fda94 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -212,6 +212,12 @@ } //tablet.popFromStack(); break; + case 'inventory_itemClicked': + 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;