diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml index fb8b410e3f..e5ce431de8 100644 --- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml +++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml @@ -30,24 +30,72 @@ Rectangle { property string activeView: "initialize"; property bool keyboardRaised: false; - property int category_index: -1; - property alias categoryChoices: categoriesModel; + property int currentSortIndex: 0; + property string sortString: ""; + property string categoryString: ""; + property string searchString: ""; anchors.fill: (typeof parent === undefined) ? undefined : parent; + function getMarketplaceItems() { + marketplaceItemView.visible = false; + itemsList.visible = true; + marketBrowseModel.getFirstPage(); + } + Component.onDestruction: { KeyboardScriptingInterface.raised = false; } Connections { - target: Marketplace; + target: MarketplaceScriptingInterface; onGetMarketplaceCategoriesResult: { if (result.status !== 'success') { console.log("Failed to get Marketplace Categories", result.data.message); } else { - - } + categoriesModel.clear(); + categoriesModel.append({ + id: -1, + name: "Everything" + }); + result.data.items.forEach(function(category) { + categoriesModel.append({ + id: category.id, + name: category.name + }); + }); + } + getMarketplaceItems(); + } + onGetMarketplaceItemsResult: { + marketBrowseModel.handlePage(result.status !== "success" && result.message, result); + } + + onGetMarketplaceItemResult: { + if (result.status !== 'success') { + console.log("Failed to get Marketplace Item", result.data.message); + } else { + + console.log(JSON.stringify(result.data)); + marketplaceItem.item_id = result.data.id; + marketplaceItem.image_url = result.data.thumbnail_url; + marketplaceItem.name = result.data.title; + marketplaceItem.likes = result.data.likes; + marketplaceItem.liked = result.data.has_liked; + marketplaceItem.creator = result.data.creator; + marketplaceItem.categories = result.data.categories; + marketplaceItem.price = result.data.cost; + marketplaceItem.description = result.data.description; + marketplaceItem.attributions = result.data.attributions; + marketplaceItem.license = result.data.license; + marketplaceItem.available = result.data.availability == "available"; + marketplaceItem.created_at = result.data.created_at; + console.log("HEIGHT: " + marketplaceItemContent.height); + marketplaceItemScrollView.contentHeight = marketplaceItemContent.height; + itemsList.visible = false; + marketplaceItemView.visible = true; + } } } @@ -60,15 +108,36 @@ Rectangle { // // HEADER BAR START // - Item { + + + Rectangle { + id: headerShadowBase; + anchors.fill: header; + color: "white"; + } + DropShadow { + anchors.fill: headerShadowBase; + source: headerShadowBase; + verticalOffset: 4; + horizontalOffset: 4; + radius: 6; + samples: 9; + color: hifi.colors.baseGrayShadow; + z:5; + } + Rectangle { id: header; visible: true; - width: parent.width; anchors.left: parent.left; anchors.top: parent.top; anchors.right: parent.right; - - Item { + anchors.topMargin: -1; + anchors.leftMargin: -1; + anchors.rightMargin: -1; + height: childrenRect.height+5; + z: 5; + + Rectangle { id: titleBarContainer; visible: true; // Size @@ -77,13 +146,14 @@ Rectangle { // Anchors anchors.left: parent.left; anchors.top: parent.top; - - // Wallet icon + + + // Marketplace icon Image { - id: walletIcon; + id: marketplaceIcon; source: "../../../../images/hifi-logo-blackish.svg"; height: 20 - width: walletIcon.height; + width: marketplaceIcon.height; anchors.left: parent.left; anchors.leftMargin: 8; anchors.verticalCenter: parent.verticalCenter; @@ -98,7 +168,7 @@ Rectangle { size: hifi.fontSizes.overlayTitle; // Anchors anchors.top: parent.top; - anchors.left: walletIcon.right; + anchors.left: marketplaceIcon.right; anchors.leftMargin: 6; anchors.bottom: parent.bottom; width: paintedWidth; @@ -109,9 +179,10 @@ Rectangle { } } - Item { + Rectangle { id: searchBarContainer; visible: true; + clip: false; // Size width: parent.width; anchors.top: titleBarContainer.bottom; @@ -125,10 +196,10 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter; height: 34; width: categoriesText.width + 25; - color: "white"; + color: hifi.colors.white; radius: 4; border.width: 1; - border.color: hifi.colors.lightGray; + border.color: hifi.colors.lightGrayText; // Categories Text @@ -136,9 +207,9 @@ Rectangle { id: categoriesText; text: "Categories"; // Text size - size: 18; + size: 14; // Style - color: hifi.colors.baseGray; + color: hifi.colors.lightGrayText; elide: Text.ElideRight; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; @@ -166,6 +237,8 @@ Rectangle { hoverEnabled: enabled; onClicked: { categoriesDropdown.visible = !categoriesDropdown.visible; + categoriesButton.color = categoriesDropdown.visible ? hifi.colors.lightGray : hifi.colors.white; + categoriesDropdown.forceActiveFocus(); } onEntered: categoriesText.color = hifi.colors.baseGrayShadow; onExited: categoriesText.color = hifi.colors.baseGray; @@ -173,77 +246,10 @@ Rectangle { Component.onCompleted: { console.log("Getting Marketplace Categories"); - console.log(JSON.stringify(Marketplace)); - Marketplace.getMarketplaceItems(); + MarketplaceScriptingInterface.getMarketplaceCategories(); } } - - Rectangle { - id: categoriesContainer; - visible: true; - height: 50 * categoriesModel.count; - width: parent.width; - anchors.top: categoriesButton.bottom; - anchors.left: categoriesButton.left; - color: hifi.colors.white; - - ListModel { - id: categoriesModel; - } - - ListView { - id: dropdownListView; - interactive: false; - anchors.fill: parent; - model: categoriesModel; - delegate: Item { - width: parent.width; - height: 50; - Rectangle { - id: dropDownButton; - color: hifi.colors.white; - width: parent.width; - height: 50; - visible: true; - - RalewaySemiBold { - id: dropDownButtonText; - text: model.displayName; - anchors.fill: parent; - anchors.topMargin: 2; - anchors.leftMargin: 12; - color: hifi.colors.baseGray; - horizontalAlignment: Text.AlignLeft; - verticalAlignment: Text.AlignVCenter; - size: 18; - } - - MouseArea { - anchors.fill: parent; - hoverEnabled: true; - propagateComposedEvents: false; - onEntered: { - dropDownButton.color = hifi.colors.blueHighlight; - } - onExited: { - dropDownButton.color = hifi.colors.white; - } - onClicked: { - root.category_index = index; - dropdownContainer.visible = false; - } - } - } - Rectangle { - height: 2; - width: parent.width; - color: hifi.colors.lightGray; - visible: model.separator - } - } - } - } - + // or RalewayRegular { id: orText; @@ -283,7 +289,6 @@ Rectangle { font.family: "FiraSans Regular"; font.pixelSize: hifi.fontSizes.textFieldInput; font.capitalization: Font.AllUppercase; - text: root.primaryFilter_displayName; } // workaround for https://bugreports.qt.io/browse/QTBUG-49297 @@ -295,20 +300,22 @@ Rectangle { // emit accepted signal manually if (acceptableInput) { - root.accepted(); - root.forceActiveFocus(); + searchField.accepted(); + searchField.forceActiveFocus(); } break; case Qt.Key_Backspace: - if (textField.text === "") { + if (searchField.text === "") { primaryFilter_index = -1; } break; } } - + onTextChanged: root.searchString = text; onAccepted: { - root.forceActiveFocus(); + root.searchString = searchField.text; + getMarketplaceItems(); + searchField.forceActiveFocus(); } onActiveFocusChanged: { @@ -320,17 +327,579 @@ Rectangle { } } } + // // HEADER BAR END // - DropShadow { - anchors.fill: header; - horizontalOffset: 0; - verticalOffset: 4; - radius: 4.0; - samples: 9 - color: Qt.rgba(0, 0, 0, 0.25); - source: header; - visible: header.visible; + + // + // CATEGORIES LIST START + // + Item { + id: categoriesDropdown; + anchors.fill: parent; + visible: false; + z: 10; + + MouseArea { + anchors.fill: parent; + propagateComposedEvents: true; + onClicked: { + categoriesDropdown.visible = false; + categoriesButton.color = hifi.colors.white; + } + } + + Rectangle { + anchors.left: parent.left; + anchors.bottom: parent.bottom; + anchors.top: parent.top; + anchors.topMargin: 100; + width: parent.width/3; + color: hifi.colors.white; + + ListModel { + id: categoriesModel; + } + + ListView { + id: categoriesListView; + anchors.fill: parent; + anchors.rightMargin: 10; + width: parent.width; + clip: true; + + model: categoriesModel; + delegate: ItemDelegate { + height: 34; + width: parent.width; + clip: true; + contentItem: Rectangle { + id: categoriesItem; + anchors.fill: parent; + color: hifi.colors.white; + visible: true; + + RalewayRegular { + id: categoriesItemText; + text: model.name; + anchors.leftMargin: 15; + anchors.fill:parent; + color: ListView.isCurrentItem ? hifi.colors.lightBlueHighlight : hifi.colors.baseGray; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + size: 14; + } + } + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + propagateComposedEvents: false; + z: 10; + onEntered: { + categoriesItem.color = ListView.isCurrentItem ? hifi.colors.white : hifi.colors.lightBlueHighlight; + } + onExited: { + categoriesItem.color = ListView.isCurrentItem ? hifi.colors.lightBlueHighlight : hifi.colors.white; + } + onClicked: { + categoriesListView.currentIndex = index; + categoriesText.text = categoriesItemText.text; + categoriesDropdown.visible = false; + categoriesButton.color = hifi.colors.white; + root.categoryString = categoriesItemText.text; + getMarketplaceItems(); + } + } + + } + ScrollBar.vertical: ScrollBar { + parent: categoriesListView.parent; + anchors.top: categoriesListView.top; + anchors.bottom: categoriesListView.bottom; + anchors.left: categoriesListView.right; + contentItem.opacity: 1; + } + } + } + } + // + // CATEGORIES LIST END + // + + // + // ITEMS LIST START + // + Item { + id: itemsList; + anchors.fill: parent; + anchors.topMargin: 100; + anchors.bottomMargin: 50; + visible: true; + + HifiModels.PSFListModel { + id: marketBrowseModel; + itemsPerPage: 7; + listModelName: 'marketBrowse'; + listView: marketBrowseList; + getPage: function () { + + MarketplaceScriptingInterface.getMarketplaceItems( + root.searchString == "" ? undefined : root.searchString, + "", + root.categoryString.toLowerCase(), + "", + "", + root.sortString, + false, + marketBrowseModel.currentPageToRetrieve, + marketBrowseModel.itemsPerPage + ); + } + processPage: function(data) { + console.log(JSON.stringify(data)); + data.items.forEach(function (item) { + console.log(JSON.stringify(item)); + }); + return data.items; + } + } + ListView { + id: marketBrowseList; + model: marketBrowseModel; + // Anchors + anchors.fill: parent; + anchors.rightMargin: 10; + orientation: ListView.Vertical; + focus: true; + clip: true; + + delegate: MarketplaceListItem { + item_id: model.id; + image_url:model.thumbnail_url; + name: model.title; + likes: model.likes; + liked: model.has_liked; + creator: model.creator; + category: model.primary_category; + price: model.cost; + available: model.availability == "available"; + anchors.topMargin: 10; + + + Component.onCompleted: { + console.log("Rendering marketplace list item " + model.id); + console.log(marketBrowseModel.count); + } + + onShowItem: { + MarketplaceScriptingInterface.getMarketplaceItem(item_id); + } + + onBuy: { + sendToScript({method: 'marketplace_checkout', itemId: item_id}); + } + + onCategoryClicked: { + root.categoryString = category; + categoriesText.text = category; + getMarketplaceItems(); + } + + onRequestReload: getMarketplaceItems(); + } + ScrollBar.vertical: ScrollBar { + parent: marketBrowseList.parent; + anchors.top: marketBrowseList.top; + anchors.bottom: marketBrowseList.bottom; + anchors.left: marketBrowseList.right; + contentItem.opacity: 1; + } + headerPositioning: ListView.InlineHeader; + header: Item { + id: itemsHeading; + + height: childrenRect.height; + width: parent.width; + + Item { + id: breadcrumbs; + anchors.left: parent.left; + anchors.right: parent.right; + height: 34; + visible: categoriesListView.currentIndex >= 0; + RalewayRegular { + id: categoriesItemText; + text: "Home /"; + anchors.leftMargin: 15; + anchors.fill:parent; + color: hifi.colors.blueHighlight; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + size: 18; + } + MouseArea { + anchors.fill: parent; + onClicked: { + categoriesListView.currentIndex = -1; + categoriesText.text = "Categories"; + root.categoryString = ""; + getMarketplaceItems(); + } + } + } + + Item { + id: searchScope; + anchors.top: breadcrumbs.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: 50; + + RalewaySemiBold { + id: searchScopeText; + text: "Featured"; + anchors.leftMargin: 15; + anchors.fill:parent; + anchors.topMargin: 10; + color: hifi.colors.baseGray; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + size: 22; + } + } + Item { + id: sort; + anchors.top: searchScope.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.topMargin: 10; + anchors.leftMargin: 15; + height: childrenRect.height; + RalewayRegular { + id: sortText; + text: "Sort:"; + anchors.leftMargin: 15; + anchors.rightMargin: 20; + height: 34; + color: hifi.colors.lightGrayText; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + size: 14; + } + + Rectangle { + radius: 4; + border.width: 1; + border.color: hifi.colors.faintGray; + anchors.left: sortText.right; + anchors.top: parent.top; + width: 322; + height: 36; + anchors.leftMargin: 20; + + + ListModel { + id: sortModel; + ListElement { + name: "Name"; + glyph: ";"; + sortString: "alpha"; + } + ListElement { + name: "Date"; + glyph: ";"; + sortString: "recent"; + } + ListElement { + name: "Popular"; + glyph: ";"; + sortString: "likes"; + } + ListElement { + name: "My Likes"; + glyph: ";"; + sortString: "my_likes"; + } + } + + ListView { + id: sortListView; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.topMargin:1; + anchors.bottomMargin:1; + anchors.leftMargin:1; + anchors.rightMargin:1; + width: childrenRect.width; + height: 34; + orientation: ListView.Horizontal; + focus: true; + clip: true; + highlightFollowsCurrentItem: false; + + delegate: SortButton { + width: 80; + height: parent.height; + glyph: model.glyph; + text: model.name; + + checked: { + ListView.isCurrentItem; + } + onClicked: { + root.currentSortIndex = index; + sortListView.positionViewAtIndex(index, ListView.Beginning); + sortListView.currentIndex = index; + root.sortString = model.sortString; + getMarketplaceItems(); + } + } + highlight: Rectangle { + width: 80; + height: parent.height; + color: hifi.colors.faintGray; + x: sortListView.currentItem.x; + } + model: sortModel; + } + } + } + } + } + } + + // + // ITEMS LIST END + // + + // + // ITEM START + // + Item { + id: marketplaceItemView; + anchors.fill: parent; + width: parent.width; + anchors.topMargin: 120; + visible: false; + + ScrollView { + id: marketplaceItemScrollView; + anchors.fill: parent; + + clip: true; + ScrollBar.vertical.policy: ScrollBar.AlwaysOn; + contentWidth: parent.width; + + Rectangle { + id: marketplaceItemContent; + width: parent.width; + height: childrenRect.height + 100; + + // Title Bar text + RalewaySemiBold { + id: backText; + text: "Back"; + // Text size + size: hifi.fontSizes.overlayTitle; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left + anchors.leftMargin: 15; + anchors.bottomMargin: 10; + width: paintedWidth; + height: paintedHeight; + // Style + color: hifi.colors.blueHighlight; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + MouseArea { + anchors.fill: backText; + + onClicked: { + getMarketplaceItems(); + } + } + + MarketplaceItem { + id: marketplaceItem; + anchors.top: backText.bottom; + width: parent.width; + height: childrenRect.height; + anchors.topMargin: 15; + + onBuy: { + sendToScript({method: 'marketplace_checkout', itemId: item_id}); + } + + onShowLicense: { + licenseInfoWebView.url = url; + licenseInfo.visible = true; + } + onCategoryClicked: { + root.categoryString = category; + categoriesText.text = category; + getMarketplaceItems(); + } + } + } + } + } + // + // ITEM END + // + + // + // FOOTER START + // + + Rectangle { + id: footer; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: 50; + + color: hifi.colors.blueHighlight; + + Item { + anchors.fill: parent; + anchors.rightMargin: 15; + anchors.leftMargin: 15; + + HiFiGlyphs { + id: footerGlyph; + text: hifi.glyphs.info; + // Size + size: 34; + // Anchors + anchors.left: parent.left; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + + anchors.rightMargin: 10; + // Style + color: hifi.colors.white; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + + RalewaySemiBold { + id: footerInfo; + text: "Get items from Clara.io!"; + anchors.left: footerGlyph.right; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + color: hifi.colors.white; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + size: 18; + } + + HifiControlsUit.Button { + text: "SEE ALL MARKETS"; + anchors.right: parent.right; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.topMargin: 10; + anchors.bottomMargin: 10; + anchors.leftMargin: 10; + anchors.rightMargin: 10; + width: 180; + + onClicked: { + sendToScript({method: 'marketplace_marketplaces'}); + } + } + } + } + + + // + // FOOTER END + // + + + // + // LICENSE START + // + Rectangle { + id: licenseInfo; + anchors.fill: root; + anchors.topMargin: 100; + anchors.bottomMargin: 0; + visible: false; + + + HifiControlsUit.WebView { + id: licenseInfoWebView; + anchors.bottomMargin: 1; + anchors.topMargin: 50; + anchors.leftMargin: 1; + anchors.rightMargin: 1; + anchors.fill: parent; + } + Item { + id: licenseClose; + anchors.top: parent.top; + anchors.right: parent.right; + anchors.topMargin: 10; + anchors.rightMargin: 10; + + width: 30; + height: 30; + HiFiGlyphs { + anchors.fill: parent; + height: 30; + text: hifi.glyphs.close; + // Size + size: 34; + // Anchors + anchors.rightMargin: 0; + anchors.verticalCenter: parent.verticalCenter; + horizontalAlignment: Text.AlignHCenter; + // Style + color: hifi.colors.baseGray; + } + + MouseArea { + anchors.fill: licenseClose; + onClicked: licenseInfo.visible = false; + } + } + } + // + // LICENSE END + // + + // + // 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) { + console.log("FROM SCRIPT " + JSON.stringify(message)); + switch (message.method) { + case 'updateMarketplaceQMLItem': + if (!message.params.itemId) { + console.log("A message with method 'updateMarketplaceQMLItem' was sent without an itemId!"); + return; + } + + MarketplaceScriptingInterface.getMarketplaceItem(message.params.itemId); + break; + } } } diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml new file mode 100644 index 0000000000..a7f2991920 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml @@ -0,0 +1,473 @@ +// +// MarketplaceListItem.qml +// qml/hifi/commerce/marketplace +// +// MarketplaceListItem +// +// Created by Roxanne Skelly on 2019-01-22 +// Copyright 2019 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.9 +import QtQuick.Controls 2.2 +import QtGraphicalEffects 1.0 +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit +import "../../../controls" as HifiControls +import "../common" as HifiCommerceCommon +import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. +import "../common/sendAsset" +import "../.." as HifiCommon + +Rectangle { + id: root; + property string item_id: ""; + property string image_url: ""; + property string name: ""; + property int likes: 0; + property bool liked: false; + property string creator: ""; + property var categories: []; + property int price: 0; + property var attributions: []; + property string description: ""; + property string license: ""; + property string posted: ""; + property bool available: false; + property string created_at: ""; + + onCategoriesChanged: { + categoriesListModel.clear(); + categories.forEach(function(category) { + console.log("category is " + category); + categoriesListModel.append({"category":category}); + }); + } + + signal buy(); + signal categoryClicked(string category); + signal showLicense(string url); + + HifiConstants { id: hifi; } + + Connections { + target: MarketplaceScriptingInterface; + + onMarketplaceItemLikeResult: { + if (result.status !== 'success') { + console.log("Failed to get Marketplace Categories", result.data.message); + } else { + root.liked = !root.liked; + root.likes = root.liked ? root.likes + 1 : root.likes - 1; + } + } + } + function getFormattedDate(timestamp) { + function addLeadingZero(n) { + return n < 10 ? '0' + n : '' + n; + } + + var a = new Date(timestamp); + + var year = a.getFullYear(); + var month = addLeadingZero(a.getMonth() + 1); + var day = addLeadingZero(a.getDate()); + var hour = a.getHours(); + var drawnHour = hour; + if (hour === 0) { + drawnHour = 12; + } else if (hour > 12) { + drawnHour -= 12; + } + drawnHour = addLeadingZero(drawnHour); + + var amOrPm = "AM"; + if (hour >= 12) { + amOrPm = "PM"; + } + + var min = addLeadingZero(a.getMinutes()); + var sec = addLeadingZero(a.getSeconds()); + return a.toDateString() + " " + drawnHour + ':' + min + amOrPm; + } + + anchors.left: parent.left; + anchors.right: parent.right; + anchors.leftMargin: 15; + anchors.rightMargin: 15; + height: childrenRect.height; + + + Rectangle { + id: header; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: parent.top; + + height: 50; + + RalewaySemiBold { + id: nameText; + text: root.name; + // Text size + size: 24; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + width: paintedWidth; + // Style + color: hifi.colors.baseGray; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + Item { + id: likes; + anchors.top: parent.top; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + anchors.rightMargin: 5; + + RalewaySemiBold { + id: heart; + text: "\u2665"; + // Size + size: 20; + // Anchors + anchors.top: parent.top; + anchors.right: parent.right; + anchors.rightMargin: 0; + anchors.verticalCenter: parent.verticalCenter; + horizontalAlignment: Text.AlignHCenter; + // Style + color: root.liked ? hifi.colors.redAccent : hifi.colors.lightGrayText; + } + + RalewaySemiBold { + id: likesText; + text: root.likes; + // Text size + size: hifi.fontSizes.overlayTitle; + // Anchors + anchors.top: parent.top; + anchors.right: heart.left; + anchors.rightMargin: 5; + anchors.leftMargin: 15; + anchors.bottom: parent.bottom; + width: paintedWidth; + // Style + color: hifi.colors.baseGray; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + MouseArea { + anchors.left: likesText.left; + anchors.right: heart.right; + anchors.top: likesText.top; + anchors.bottom: likesText.bottom; + + onClicked: { + MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, !root.liked); + } + } + } + } + Image { + id: itemImage; + source: root.image_url; + anchors.top: header.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: width*0.5625 + fillMode: Image.PreserveAspectCrop; + } + Item { + id: footer; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: itemImage.bottom; + height: childrenRect.height; + + HifiControlsUit.Button { + id: buyButton; + text: root.available ? (root.price ? root.price : "FREE") : "UNAVAILABLE (not for sale)"; + enabled: root.available; + buttonGlyph: root.available ? (root.price ? hifi.glyphs.hfc : "") : ""; + anchors.right: parent.right; + anchors.top: parent.top; + anchors.left: parent.left; + anchors.topMargin: 15; + height: 50; + color: hifi.buttons.blue; + + onClicked: root.buy(); + } + + Item { + id: creatorItem; + anchors.top: buyButton.bottom; + anchors.leftMargin: 15; + anchors.topMargin: 15; + width: parent.width; + height: childrenRect.height; + + RalewaySemiBold { + id: creatorLabel; + text: "CREATOR:"; + // Text size + size: 14; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + width: paintedWidth; + // Style + color: hifi.colors.lightGrayText; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + + RalewaySemiBold { + id: creatorText; + text: root.creator; + // Text size + size: 18; + // Anchors + anchors.top: creatorLabel.bottom; + anchors.left: parent.left; + anchors.topMargin: 10; + width: paintedWidth; + // Style + color: hifi.colors.blueHighlight; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + } + + Item { + id: posted; + anchors.top: creatorItem.bottom; + anchors.leftMargin: 15; + anchors.topMargin: 15; + width: parent.width; + height: childrenRect.height; + RalewaySemiBold { + id: postedLabel; + text: "POSTED:"; + // Text size + size: 14; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + + width: paintedWidth; + // Style + color: hifi.colors.lightGrayText; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + + RalewaySemiBold { + id: created_at; + text: { getFormattedDate(root.created_at); } + // Text size + size: 14; + // Anchors + anchors.top: postedLabel.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.topMargin: 10; + // Style + color: hifi.colors.lightGray; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + } + + Rectangle { + anchors.top: posted.bottom; + anchors.leftMargin: 15; + anchors.topMargin: 15; + width: parent.width; + height: 1; + color: hifi.colors.lightGray; + } + + Item { + id: licenseItem; + anchors.top: posted.bottom; + anchors.left: parent.left; + anchors.topMargin: 30; + width: parent.width; + height: childrenRect.height; + RalewaySemiBold { + id: licenseLabel; + text: "SHARED UNDER:"; + // Text size + size: 14; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + width: paintedWidth; + // Style + color: hifi.colors.lightGrayText; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + + RalewaySemiBold { + id: licenseText; + text: root.license; + // Text size + size: 14; + // Anchors + anchors.top: licenseLabel.bottom; + anchors.left: parent.left; + width: paintedWidth; + // Style + color: hifi.colors.lightGray; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + RalewaySemiBold { + id: licenseHelp; + text: "More about this license"; + // Text size + size: 14; + // Anchors + anchors.top: licenseText.bottom; + anchors.left: parent.left; + width: paintedWidth; + // Style + color: hifi.colors.blueHighlight; + // Alignment + verticalAlignment: Text.AlignVCenter; + + MouseArea { + anchors.fill: parent; + + onClicked: { + licenseInfo.visible = true; + var url; + if (root.license == "No Rights Reserved (CC0)") { + url = "https://creativecommons.org/publicdomain/zero/1.0/" + } else if (root.license == "Attribution (CC BY)") { + url = "https://creativecommons.org/licenses/by/4.0/" + } else if (root.license == "Attribution-ShareAlike (CC BY-SA)") { + url = "https://creativecommons.org/licenses/by-sa/4.0/" + } else if (root.license == "Attribution-NoDerivs (CC BY-ND)") { + url = "https://creativecommons.org/licenses/by-nd/4.0/" + } else if (root.license == "Attribution-NonCommercial (CC BY-NC)") { + url = "https://creativecommons.org/licenses/by-nc/4.0/" + } else if (root.license == "Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)") { + url = "https://creativecommons.org/licenses/by-nc-sa/4.0/" + } else if (root.license == "Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)") { + url = "https://creativecommons.org/licenses/by-nc-nd/4.0/" + } else if (root.license == "Proof of Provenance License (PoP License)") { + url = "https://digitalassetregistry.com/PoP-License/v1/" + } + if(url) { + licenseInfoWebView.url = url; + + } + } + } + } + } + + Item { + id: descriptionItem; + anchors.top: licenseItem.bottom; + anchors.topMargin: 15; + anchors.left: parent.left; + anchors.right: parent.right; + height: childrenRect.height; + RalewaySemiBold { + id: descriptionLabel; + text: "DESCRIPTION:"; + // Text size + size: 14; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + width: paintedWidth; + // Style + color: hifi.colors.lightGrayText; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + RalewaySemiBold { + id: descriptionText; + text: root.description; + // Text size + size: 14; + // Anchors + anchors.top: descriptionLabel.bottom; + anchors.left: parent.left; + width: parent.width; + // Style + color: hifi.colors.lightGray; + // Alignment + verticalAlignment: Text.AlignVCenter; + wrapMode: Text.Wrap; + } + } + + Item { + id: categoriesList; + anchors.top: descriptionItem.bottom; + anchors.topMargin: 15; + anchors.left: parent.left; + anchors.right: parent.right; + width: parent.width; + height: childrenRect.height; + RalewaySemiBold { + id: categoryLabel; + text: "IN:"; + // Text size + size: 14; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + width: paintedWidth; + // Style + color: hifi.colors.lightGrayText; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + ListModel { + id: categoriesListModel; + } + + ListView { + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: categoryLabel.bottom; + model: categoriesListModel; + height: 20*model.count; + delegate: RalewaySemiBold { + id: categoryText; + text: model.category; + // Text size + size: 14; + // Anchors + anchors.leftMargin: 15; + width: paintedWidth; + height: 20; + // Style + color: hifi.colors.blueHighlight; + // Alignment + verticalAlignment: Text.AlignVCenter; + + MouseArea { + anchors.fill: categoryText; + onClicked: root.categoryClicked(model.category); + } + } + } + } + } +} diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml new file mode 100644 index 0000000000..4b8471e8b9 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml @@ -0,0 +1,278 @@ +// +// MarketplaceListItem.qml +// qml/hifi/commerce/marketplace +// +// MarketplaceListItem +// +// Created by Roxanne Skelly on 2019-01-22 +// Copyright 2019 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.9 +import QtQuick.Controls 2.2 +import QtGraphicalEffects 1.0 +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit +import "../../../controls" as HifiControls +import "../common" as HifiCommerceCommon +import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. +import "../common/sendAsset" +import "../.." as HifiCommon + +Rectangle { + id: root; + property string item_id: ""; + property string image_url: ""; + property string name: ""; + property int likes: 0; + property bool liked: false; + property string creator: ""; + property string category: ""; + property int price: 0; + property bool available: false; + + signal buy(); + signal showItem(); + signal categoryClicked(string category); + signal requestReload(); + + HifiConstants { id: hifi; } + + width: parent.width; + height: childrenRect.height+50; + + DropShadow { + anchors.fill: shadowBase; + source: shadowBase; + verticalOffset: 4; + horizontalOffset: 4; + radius: 6; + samples: 9; + color: hifi.colors.baseGrayShadow; + } + + Rectangle { + id: shadowBase; + anchors.fill: itemRect; + color: "white"; + } + + Rectangle { + id: itemRect; + height: childrenRect.height; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: parent.top; + anchors.topMargin: 20; + anchors.bottomMargin: 10; + anchors.leftMargin: 15; + anchors.rightMargin: 15; + + MouseArea { + anchors.fill: parent; + onClicked: root.showItem(); + onEntered: { hoverRect.visible = true; console.log("entered"); } + onExited: { hoverRect.visible = false; console.log("exited"); } + hoverEnabled: true + } + + Rectangle { + id: header; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: parent.top; + + height: 50; + + RalewaySemiBold { + id: nameText; + text: root.name; + // Text size + size: hifi.fontSizes.overlayTitle; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.rightMargin: 50; + anchors.leftMargin: 15; + anchors.bottom: parent.bottom; + elide: Text.ElideRight; + width: paintedWidth; + // Style + color: hifi.colors.blueHighlight; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + Item { + id: likes; + anchors.top: parent.top; + anchors.right: parent.right; + anchors.rightMargin: 15; + anchors.bottom: parent.bottom; + width: childrenRect.width; + Connections { + target: MarketplaceScriptingInterface; + + onMarketplaceItemLikeResult: { + if (result.status !== 'success') { + console.log("Failed to get Marketplace Categories", result.data.message); + root.requestReload(); + } + } + } + + RalewaySemiBold { + id: heart; + text: "\u2665"; + // Size + size: 20; + // Anchors + anchors.top: parent.top; + anchors.right: parent.right; + anchors.rightMargin: 0; + anchors.verticalCenter: parent.verticalCenter; + horizontalAlignment: Text.AlignHCenter; + // Style + color: root.liked ? hifi.colors.redAccent : hifi.colors.lightGrayText; + } + + RalewaySemiBold { + id: likesText; + text: root.likes; + // Text size + size: hifi.fontSizes.overlayTitle; + // Anchors + anchors.top: parent.top; + anchors.right: heart.left; + anchors.rightMargin: 5; + anchors.leftMargin: 15; + anchors.bottom: parent.bottom; + width: paintedWidth; + // Style + color: hifi.colors.baseGray; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + MouseArea { + anchors.fill: parent; + onClicked: { + console.log("like " + root.item_id); + root.liked = !root.liked; + root.likes = root.liked ? root.likes + 1 : root.likes - 1; + MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, root.liked); + } + } + } + } + Image { + id: itemImage; + source: root.image_url; + anchors.top: header.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: width*0.5625 + fillMode: Image.PreserveAspectCrop; + } + Item { + id: footer; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: itemImage.bottom; + height: 60; + + RalewaySemiBold { + id: creatorLabel; + text: "CREATOR:"; + // Text size + size: 14; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.leftMargin: 15; + width: paintedWidth; + // Style + color: hifi.colors.lightGrayText; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + + RalewaySemiBold { + id: creatorText; + text: root.creator; + // Text size + size: 14; + // Anchors + anchors.top: creatorLabel.top; + anchors.left: creatorLabel.right; + anchors.leftMargin: 15; + width: paintedWidth; + // Style + color: hifi.colors.lightGray; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + RalewaySemiBold { + id: categoryLabel; + text: "IN:"; + // Text size + size: 14; + // Anchors + anchors.top: creatorLabel.bottom; + anchors.left: parent.left; + anchors.leftMargin: 15; + width: paintedWidth; + // Style + color: hifi.colors.lightGrayText; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + + RalewaySemiBold { + id: categoryText; + text: root.category; + // Text size + size: 14; + // Anchors + anchors.top: categoryLabel.top; + anchors.left: categoryLabel.right; + anchors.leftMargin: 15; + width: paintedWidth; + // Style + color: hifi.colors.blueHighlight; + // Alignment + verticalAlignment: Text.AlignVCenter; + + MouseArea { + anchors.fill: parent; + onClicked: root.categoryClicked(root.category); + } + } + + HifiControlsUit.Button { + text: root.price ? root.price : "FREE"; + buttonGlyph: root.price ? hifi.glyphs.hfc : ""; + anchors.right: parent.right; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.rightMargin: 15; + anchors.topMargin:10; + anchors.bottomMargin: 10; + color: hifi.buttons.blue; + + onClicked: root.buy(); + } + } + Rectangle { + id: hoverRect; + anchors.fill: parent; + border.color: hifi.colors.blueHighlight; + border.width: 2; + color: "#00000000"; + visible: false; + } + } +} diff --git a/interface/resources/qml/hifi/commerce/marketplace/SortButton.qml b/interface/resources/qml/hifi/commerce/marketplace/SortButton.qml new file mode 100644 index 0000000000..37ad2735ce --- /dev/null +++ b/interface/resources/qml/hifi/commerce/marketplace/SortButton.qml @@ -0,0 +1,87 @@ +// +// SortButton.qml +// qml/hifi/commerce/marketplace +// +// SortButton +// +// Created by Roxanne Skelly on 2019-01-18 +// Copyright 2019 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.9 +import QtQuick.Controls 2.2 +import QtGraphicalEffects 1.0 +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit +import "../../../controls" as HifiControls +import "../common" as HifiCommerceCommon +import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. +import "../common/sendAsset" +import "../.." as HifiCommon + +Item { + HifiConstants { id: hifi; } + + id: root; + + + property string glyph: ""; + property string text: ""; + property bool checked: false; + signal clicked(); + + width: childrenRect.width; + height: parent.height; + + Rectangle { + anchors.top: parent.top; + anchors.left: parent.left; + height: parent.height; + width: 2; + color: hifi.colors.faintGray; + visible: index > 0; + } + + HiFiGlyphs { + id: buttonGlyph; + text: root.glyph; + // Size + size: 14; + // Anchors + anchors.left: parent.left; + anchors.leftMargin: 0; + anchors.top: parent.top; + anchors.verticalCenter: parent.verticalCenter; + height: parent.height; + horizontalAlignment: Text.AlignHCenter; + // Style + color: hifi.colors.lightGray; + } + RalewayRegular { + id: buttonText; + text: root.text; + // Text size + size: 14; + // Style + color: hifi.colors.lightGray; + elide: Text.ElideRight; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + // Anchors + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.top: parent.top; + height: parent.height; + } + MouseArea { + anchors.fill: parent; + hoverEnabled: enabled; + onClicked: { + root.clicked(); + } + } +} \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1ec9c93e12..88fda37623 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2916,7 +2916,7 @@ void Application::initializeUi() { }, commerceCallback); QmlContextCallback marketplaceCallback = [](QQmlContext* context) { - context->setContextProperty("Marketplace", new QmlMarketplace()); + context->setContextProperty("MarketplaceScriptingInterface", new QmlMarketplace()); }; OffscreenQmlSurface::addWhitelistContextHandler({ QUrl{ "hifi/commerce/marketplace/Marketplace.qml" }, diff --git a/interface/src/commerce/QmlMarketplace.cpp b/interface/src/commerce/QmlMarketplace.cpp index 99d3bb1ae6..07a9e570bd 100644 --- a/interface/src/commerce/QmlMarketplace.cpp +++ b/interface/src/commerce/QmlMarketplace.cpp @@ -55,50 +55,54 @@ void QmlMarketplace::getMarketplaceItems( const int& perPage) { QString endpoint = "items"; - QJsonObject request; - request["q"] = q; - request["view"] = view; - request["category"] = category; - request["adminFilter"] = adminFilter; - request["adminFilterCost"] = adminFilterCost; - request["sort"] = sort; - request["isFree"] = isFree; - request["page"] = page; - request["perPage"] = perPage; + QUrlQuery request; + request.addQueryItem("q", q); + request.addQueryItem("view", view); + request.addQueryItem("category", category); + request.addQueryItem("adminFilter", adminFilter); + request.addQueryItem("adminFilterCost", adminFilterCost); + request.addQueryItem("sort", sort); + if (isFree) { + request.addQueryItem("isFree", "true"); + } + request.addQueryItem("page", QString::number(page)); + request.addQueryItem("perPage", QString::number(perPage)); send(endpoint, "getMarketplaceItemsSuccess", "getMarketplaceItemsFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::Optional, request); } void QmlMarketplace::getMarketplaceItem(const QString& marketplaceItemId) { QString endpoint = QString("items/") + marketplaceItemId; - QJsonObject request; + QUrlQuery request; send(endpoint, "getMarketplaceItemSuccess", "getMarketplaceItemFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::Optional, request); } void QmlMarketplace::marketplaceItemLike(const QString& marketplaceItemId, const bool like) { QString endpoint = QString("items/") + marketplaceItemId + "/like"; - QJsonObject request; - send(endpoint, "marketplaceItemLikeSuccess", "marketplaceItemLikeFailure", like ? QNetworkAccessManager::PutOperation : QNetworkAccessManager::DeleteOperation, AccountManagerAuth::Required, request); + QUrlQuery request; + send(endpoint, "marketplaceItemLikeSuccess", "marketplaceItemLikeFailure", like ? QNetworkAccessManager::PostOperation : QNetworkAccessManager::DeleteOperation, AccountManagerAuth::Required, request); } void QmlMarketplace::getMarketplaceCategories() { QString endpoint = "categories"; - QJsonObject request; + QUrlQuery request; send(endpoint, "getMarketplaceCategoriesSuccess", "getMarketplaceCategoriesFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::None, request); } -void QmlMarketplace::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request) { +void QmlMarketplace::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, const QUrlQuery & request) { auto accountManager = DependencyManager::get(); const QString URL = "/api/v1/marketplace/"; JSONCallbackParameters callbackParams(this, success, fail); -#if defined(DEV_BUILD) // Don't expose user's personal data in the wild. But during development this can be handy. - qCInfo(commerce) << "Sending" << QJsonDocument(request).toJson(QJsonDocument::Compact); -#endif + accountManager->sendRequest(URL + endpoint, authType, method, callbackParams, - QJsonDocument(request).toJson()); + QByteArray(), + NULL, + QVariantMap(), + request); + } QJsonObject QmlMarketplace::apiResponse(const QString& label, QNetworkReply* reply) { diff --git a/interface/src/commerce/QmlMarketplace.h b/interface/src/commerce/QmlMarketplace.h index 95a1aa3911..f954198371 100644 --- a/interface/src/commerce/QmlMarketplace.h +++ b/interface/src/commerce/QmlMarketplace.h @@ -60,7 +60,7 @@ signals: void marketplaceItemLikeResult(QJsonObject result); private: - void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request); + void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, const QUrlQuery & request); QJsonObject apiResponse(const QString& label, QNetworkReply* reply); QJsonObject failResponse(const QString& label, QNetworkReply* reply); }; diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 989661cb81..5be2c6d02e 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -208,7 +208,7 @@ void AccountManager::setSessionID(const QUuid& sessionID) { } } -QNetworkRequest AccountManager::createRequest(QString path, AccountManagerAuth::Type authType) { +QNetworkRequest AccountManager::createRequest(QString path, AccountManagerAuth::Type authType, const QUrlQuery & query) { QNetworkRequest networkRequest; networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter()); @@ -227,6 +227,7 @@ QNetworkRequest AccountManager::createRequest(QString path, AccountManagerAuth:: } else { requestURL.setPath("/" + path); } + requestURL.setQuery(query); if (authType != AccountManagerAuth::None ) { if (hasValidAccessToken()) { @@ -263,13 +264,14 @@ void AccountManager::sendRequest(const QString& path, Q_ARG(const JSONCallbackParameters&, callbackParams), Q_ARG(const QByteArray&, dataByteArray), Q_ARG(QHttpMultiPart*, dataMultiPart), - Q_ARG(QVariantMap, propertyMap)); + Q_ARG(QVariantMap, propertyMap), + Q_ARG(QUrlQuery, query)); return; } QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest = createRequest(path, authType); + QNetworkRequest networkRequest = createRequest(path, authType, query); if (VERBOSE_HTTP_REQUEST_DEBUGGING) { qCDebug(networking) << "Making a request to" << qPrintable(networkRequest.url().toString()); diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index ca2b826c98..36563a6ae0 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -61,7 +61,7 @@ class AccountManager : public QObject, public Dependency { public: AccountManager(UserAgentGetter userAgentGetter = DEFAULT_USER_AGENT_GETTER); - QNetworkRequest createRequest(QString path, AccountManagerAuth::Type authType); + QNetworkRequest createRequest(QString path, AccountManagerAuth::Type authType, const QUrlQuery & query = QUrlQuery()); Q_INVOKABLE void sendRequest(const QString& path, AccountManagerAuth::Type authType, QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation, diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 19efdc042c..7cacfd7935 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -379,21 +379,16 @@ function deleteSendMoneyParticleEffect() { function onUsernameChanged() { } -var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); -var METAVERSE_SERVER_URL = Account.metaverseServerURL; -var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. -function openMarketplace(optionalItemOrUrl) { - // This is a bit of a kluge, but so is the whole file. - // If given a whole path, use it with no cta. - // If given an id, build the appropriate url and use the id as the cta. - // Otherwise, use home and 'marketplace cta'. - // AND... if call onMarketplaceOpen to setupWallet if we need to. - var url = optionalItemOrUrl || MARKETPLACE_URL_INITIAL; - // If optionalItemOrUrl contains the metaverse base, then it's a url, not an item id. - if (optionalItemOrUrl && optionalItemOrUrl.indexOf(METAVERSE_SERVER_URL) === -1) { - url = MARKETPLACE_URL + '/items/' + optionalItemOrUrl; +var MARKETPLACE_QML_PATH = "hifi/commerce/marketplace/Marketplace.qml"; +function openMarketplace(optionalItem) { + ui.open(MARKETPLACE_QML_PATH); + + if (optionalItem) { + ui.tablet.sendToQml({ + method: 'updateMarketplaceQMLItem', + params: { itemId: optionalItem } + }); } - ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL); } function setCertificateInfo(itemCertificateId) { diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index f1931192e4..74bf8d3fec 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -19,6 +19,7 @@ var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; var GOTO_DIRECTORY = "GOTO_DIRECTORY"; + var GOTO_MARKETPLACE = "GOTO_MARKETPLACE"; var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; @@ -72,7 +73,13 @@ // Footer actions. $("#back-button").on("click", function () { - (document.referrer !== "") ? window.history.back() : window.location = (marketplaceBaseURL + "/marketplace?"); + if (document.referrer !== "") { + window.history.back(); + } else { + EventBridge.emitWebEvent(JSON.stringify({ + type: GOTO_MARKETPLACE + })); + } }); $("#all-markets").on("click", function () { EventBridge.emitWebEvent(JSON.stringify({ @@ -93,7 +100,9 @@ window.location = "https://clara.io/library?gameCheck=true&public=true"; }); $('#exploreHifiMarketplace').on('click', function () { - window.location = marketplaceBaseURL + "/marketplace?"; + EventBridge.emitWebEvent(JSON.stringify({ + type: GOTO_MARKETPLACE + })); }); } diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index db3b2e2107..2ca79b49f0 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -25,6 +25,7 @@ var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertif var MARKETPLACE_ITEM_TESTER_QML_PATH = "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml"; var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; // HRS FIXME "hifi/commerce/purchases/Purchases.qml"; var MARKETPLACE_WALLET_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; +var MARKETPLACE_QML_PATH = "hifi/commerce/marketplace/Marketplace.qml"; var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); var METAVERSE_SERVER_URL = Account.metaverseServerURL; @@ -36,6 +37,7 @@ var CLARA_IO_STATUS = "CLARA.IO STATUS"; var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; var GOTO_DIRECTORY = "GOTO_DIRECTORY"; +var GOTO_MARKETPLACE = "GOTO_MARKETPLACE"; var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; @@ -154,18 +156,15 @@ function onMarketplaceOpen(referrer) { } } -function openMarketplace(optionalItemOrUrl) { - // This is a bit of a kluge, but so is the whole file. - // If given a whole path, use it with no cta. - // If given an id, build the appropriate url and use the id as the cta. - // Otherwise, use home and 'marketplace cta'. - // AND... if call onMarketplaceOpen to setupWallet if we need to. - var url = optionalItemOrUrl || MARKETPLACE_URL_INITIAL; - // If optionalItemOrUrl contains the metaverse base, then it's a url, not an item id. - if (optionalItemOrUrl && optionalItemOrUrl.indexOf(METAVERSE_SERVER_URL) === -1) { - url = MARKETPLACE_URL + '/items/' + optionalItemOrUrl; +function openMarketplace(optionalItem) { + ui.open(MARKETPLACE_QML_PATH); + + if (optionalItem) { + ui.tablet.sendToQml({ + method: 'updateMarketplaceQMLItem', + params: { itemId: optionalItem } + }); } - ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL); } // Function Name: wireQmlEventBridge() @@ -439,7 +438,9 @@ var referrerURL; // Used for updating Purchases QML var filterText; // Used for updating Purchases QML function onWebEventReceived(message) { message = JSON.parse(message); - if (message.type === GOTO_DIRECTORY) { + if (message.type === GOTO_MARKETPLACE) { + openMarketplace(); + } else if (message.type === GOTO_DIRECTORY) { // This is the chooser between marketplaces. Only OUR markteplace // requires/makes-use-of wallet, so doesn't go through openMarketplace bottleneck. ui.open(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); @@ -518,6 +519,7 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { if (message.messageSrc === "HTML") { return; } + console.log(JSON.stringify(message)); switch (message.method) { case 'gotoBank': ui.close(); @@ -560,6 +562,18 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { case 'checkout_continue': openMarketplace(); break; + case 'marketplace_checkout': + ui.open(MARKETPLACE_CHECKOUT_QML_PATH); + ui.tablet.sendToQml({ + method: 'updateCheckoutQMLItemID', + params: message + }); + break; + case 'marketplace_marketplaces': + // This is the chooser between marketplaces. Only OUR markteplace + // requires/makes-use-of wallet, so doesn't go through openMarketplace bottleneck. + ui.open(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); + break; case 'checkout_rezClicked': case 'purchases_rezClicked': case 'tester_rezClicked': @@ -699,7 +713,9 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { var onMarketplaceItemTesterScreenNow = ( url.indexOf(MARKETPLACE_ITEM_TESTER_QML_PATH) !== -1 || url === MARKETPLACE_ITEM_TESTER_QML_PATH); - + var onMarketplaceScreenNow = ( + url.indexOf(MARKETPLACE_QML_PATH) !== -1 || + url === MARKETPLACE_QML_PATH); if ((!onWalletScreenNow && onWalletScreen) || (!onCommerceScreenNow && onCommerceScreen) || (!onMarketplaceItemTesterScreenNow && onMarketplaceScreen) @@ -711,7 +727,7 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { onCommerceScreen = onCommerceScreenNow; onWalletScreen = onWalletScreenNow; onMarketplaceItemTesterScreen = onMarketplaceItemTesterScreenNow; - wireQmlEventBridge(onCommerceScreen || onWalletScreen || onMarketplaceItemTesterScreen); + wireQmlEventBridge(onCommerceScreen || onWalletScreen || onMarketplaceItemTesterScreen || onMarketplaceScreenNow); if (url === MARKETPLACE_PURCHASES_QML_PATH) { // FIXME? Is there a race condition here in which the event @@ -769,14 +785,13 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { var BUTTON_NAME = "MARKET"; -var MARKETPLACE_QML_SOURCE = "hifi/commerce/marketplace/Marketplace.qml"; var ui; function startup() { ui = new AppUi({ buttonName: BUTTON_NAME, sortOrder: 9, - home: MARKETPLACE_QML_SOURCE, + home: MARKETPLACE_QML_PATH, onScreenChanged: onTabletScreenChanged, onMessage: onQmlMessageReceived });