From abdd50fb455feacb2c27306a6322c5e4387e5b14 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 9 Feb 2018 14:58:48 -0800 Subject: [PATCH 01/25] it's a start --- .gitignore | 5 ++- .../hifi/commerce/common/FirstUseTutorial.qml | 4 +- .../hifi/commerce/purchases/PurchasedItem.qml | 42 ++++++++++-------- .../qml/hifi/commerce/purchases/Purchases.qml | 24 ++++++---- interface/resources/qml/js/Utils.jsc | Bin 6516 -> 6596 bytes scripts/system/commerce/wallet.js | 2 +- scripts/system/marketplaces/marketplaces.js | 5 ++- 7 files changed, 50 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index f45572c388..1ffb93fe80 100644 --- a/.gitignore +++ b/.gitignore @@ -85,4 +85,7 @@ npm-debug.log android/app/src/main/assets # Resource binary file -interface/compiledResources \ No newline at end of file +interface/compiledResources + +# GPUCache +interface/resources/GPUCache/* \ No newline at end of file diff --git a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml index bcc641acd5..9944838e03 100644 --- a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml +++ b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml @@ -90,11 +90,11 @@ Rectangle { id: introText2; text: "My Purchases"; // Text size - size: 28; + size: 22; // Anchors anchors.top: introText1.bottom; anchors.left: parent.left; - anchors.leftMargin: 12; + anchors.leftMargin: 24; anchors.right: parent.right; height: paintedHeight; // Style diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 05e280b839..361b9931a4 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -39,7 +39,10 @@ Item { property int itemEdition; property int numberSold; property int limitedRun; - property bool isWearable; + property string itemType; + property var itemTypesArray: ["entity", "wearable", "contentSet", "app", "avatar"]; + property var buttonTextNormal: ["REZ", "WEAR", "SWAP", "INSTALL", "WEAR"]; + property var buttonTextClicked: ["REZZED", "WORN", "SWAPPED", "INSTALLED", "WORN"] property string originalStatusText; property string originalStatusColor; @@ -73,10 +76,10 @@ Item { color: hifi.colors.white; // Size anchors.left: parent.left; - anchors.leftMargin: 8; + anchors.leftMargin: 16; anchors.right: parent.right; - anchors.rightMargin: 8; - anchors.top: parent.top; + anchors.rightMargin: 16; + anchors.verticalCenter: parent.verticalCenter; height: root.height - 10; Image { @@ -311,7 +314,7 @@ Item { id: rezzedNotifContainer; z: 998; visible: false; - color: hifi.colors.blueHighlight; + color: "#1FC6A6"; anchors.fill: buttonContainer; MouseArea { anchors.fill: parent; @@ -321,7 +324,7 @@ Item { RalewayBold { anchors.fill: parent; - text: "REZZED"; + text: (root.buttonTextClicked)[itemTypesArray.indexOf(root.itemType)]; size: 18; color: hifi.colors.white; verticalAlignment: Text.AlignVCenter; @@ -337,25 +340,28 @@ Item { Button { id: buttonContainer; - property int color: hifi.buttons.red; + property int color: hifi.buttons.blue; property int colorScheme: hifi.colorSchemes.light; anchors.top: parent.top; + anchors.topMargin: 4; anchors.bottom: parent.bottom; + anchors.bottomMargin: 4; anchors.right: parent.right; + anchors.rightMargin: 4; width: height; - enabled: (root.canRezCertifiedItems || root.isWearable) && root.purchaseStatus !== "invalidated"; + enabled: (root.canRezCertifiedItems || root.itemType === "wearable") && root.purchaseStatus !== "invalidated"; onClicked: { - sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); + sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start(); - UserActivityLogger.commerceEntityRezzed(root.itemId, "purchases", root.isWearable ? "rez" : "wear"); + UserActivityLogger.commerceEntityRezzed(root.itemId, "purchases", root.itemType === "wearable" ? "rez" : "wear"); } style: ButtonStyle { - background: Rectangle { + radius: 4; gradient: Gradient { GradientStop { position: 0.2 @@ -413,10 +419,10 @@ Item { font.capitalization: Font.AllUppercase color: enabled ? hifi.buttons.textColor[control.color] : hifi.buttons.disabledTextColor[control.colorScheme] - size: 16; + size: 15; verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - text: root.isWearable ? "Wear It" : "Rez It" + text: (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)] } } } @@ -425,11 +431,11 @@ Item { DropShadow { anchors.fill: mainContainer; - horizontalOffset: 3; - verticalOffset: 3; - radius: 8.0; - samples: 17; - color: "#80000000"; + horizontalOffset: 0; + verticalOffset: 4; + radius: 4.0; + samples: 9 + color: Qt.rgba(0, 0, 0, 0.25); source: mainContainer; } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 2743677683..922ba54822 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -308,11 +308,11 @@ Rectangle { anchors.bottom: parent.bottom; anchors.bottomMargin: 10; anchors.left: parent.left; - anchors.leftMargin: 4; + anchors.leftMargin: 16; width: paintedWidth; text: isShowingMyItems ? "My Items" : "My Purchases"; color: hifi.colors.baseGray; - size: 28; + size: 22; } HifiControlsUit.TextField { @@ -323,8 +323,8 @@ Rectangle { hasRoundedBorder: true; anchors.left: myText.right; anchors.leftMargin: 16; - anchors.top: parent.top; - anchors.bottom: parent.bottom; + height: 39; + anchors.verticalCenter: parent.verticalCenter; anchors.right: parent.right; placeholderText: "filter items"; @@ -454,16 +454,24 @@ Rectangle { numberSold: model.number_sold; limitedRun: model.limited_run; displayedItemCount: model.displayedItemCount; - isWearable: model.categories.indexOf("Wearables") > -1; - anchors.topMargin: 12; - anchors.bottomMargin: 12; + itemType: { + if (model.download_url.indexOf(".fst") > -1) { + "avatar"; + } else if (model.categories.indexOf("Wearables") > -1) { + "wearable"; + } else { + "entity"; + } + } + anchors.topMargin: 10; + anchors.bottomMargin: 10; Connections { onSendToPurchases: { if (msg.method === 'purchases_itemInfoClicked') { sendToScript({method: 'purchases_itemInfoClicked', itemId: itemId}); } else if (msg.method === "purchases_rezClicked") { - sendToScript({method: 'purchases_rezClicked', itemHref: itemHref, isWearable: isWearable}); + sendToScript({method: 'purchases_rezClicked', itemHref: itemHref, itemType: itemType}); } else if (msg.method === 'purchases_itemCertificateClicked') { inspectionCertificate.visible = true; inspectionCertificate.isLightbox = true; diff --git a/interface/resources/qml/js/Utils.jsc b/interface/resources/qml/js/Utils.jsc index 8da68e4e192018ee2b4f3d835ec91f36f0161eca..ab20e996b9469915ac6a89901da175143e6b5024 100644 GIT binary patch delta 604 zcmexjbi`Pvu*@VmC9xz?kb!}Lfs>VC&A*2iV;C72bXXY}mb_f{tI)LMb*EnX9J{G= zWjE?fVPxWFpL~eXjmbb{GZ&K)lcEI!149}^CPNNGDuWe+J{UTKSV!qu$P@xQv&;%5r0+3J# zickeer~z51p@D&c!LzePrQ-kp|4@m^A9y8vAm%nqfJ$c|q`_`zm;seYK$lnmm56{y zI39Phm>j})kdv7eWRZ)?jLG}?J(xDJOx`D`JXuG8Z}JL$VaC6c_wai&DZm7I_&)Iq z-167~3SS0>5B8H41jOnE9)IGGd+4!a)=UuiXrJc6pLZgSU+*D*n>GUj15EV<2e8B^ z{2Kn8s9?FPGD3MA9~ zMq$_Fat`Uu69ftv87(F=3MGm|!vW+*kU5~R2l;h#rBDLn4g!mi%G zd85u0My4%nlMgYvF+C98%*ABHq-@E+z>vm}$&ka4%3#Hy4~8yaRtiHBLn1>mL-yoY zme9>xSavcq8f?zvxX;LFu-T8xhB-~cvzy7I+abZT(?w;5Z?~9lcMw!410*y7MW_HI z)PW*Y0TOCJ7HViFnI&N2cysApZo@sr}K+4E}OiT- Date: Mon, 12 Feb 2018 17:06:57 -0800 Subject: [PATCH 02/25] Lots of progress --- interface/resources/fonts/hifi-glyphs.ttf | Bin 31232 -> 32536 bytes .../qml/hifi/commerce/checkout/Checkout.qml | 69 +++++++++++++----- .../hifi/commerce/purchases/PurchasedItem.qml | 57 +++++++++++---- .../qml/hifi/commerce/purchases/Purchases.qml | 16 +++- .../qml/styles-uit/HifiConstants.qml | 4 + interface/src/Application.cpp | 34 +++++---- interface/src/Application.h | 2 + interface/src/commerce/QmlCommerce.cpp | 13 ++++ interface/src/commerce/QmlCommerce.h | 4 + .../entities/src/EntityScriptingInterface.cpp | 5 ++ .../entities/src/EntityScriptingInterface.h | 6 ++ scripts/system/marketplaces/marketplaces.js | 17 +++-- 12 files changed, 174 insertions(+), 53 deletions(-) diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 4cc5a0fe4f098a287c1df067c8bff37695f96d34..8db0377f88864810e7f893132449ad8e258e68cd 100644 GIT binary patch delta 1778 zcmX|BYit}>6+Y+AoqK2QJl2nSB>S*;W_D-2b{(&GXJ_2H>o!qq1dW}@Z9){4lvT5h zqu7p>-KGvdyya2)h89Z*g%o}O?VnUUtN=f%mWR~PN+2OYkx)Sq5&Ym!Bm`1H$U(EI zxYE7fxku-Gy65UU=jngqH}7dS0U-a02OeC6w}Am{ ze+U4@BM;BikGyrb2Y@F4;P~U~?ahl1e$WOW9|Dk#Gwtr??b{#$Z2t}blrw9Wp0Lk- z?+O6h1lW9TwbMRrK2iK70Hl2J>T0LorZv0)fEEChR@Zx5#n0{g5&-@H04LYZKi+=l zzCWD;kPic3X}!I*3AgYR0M4Zk+GwwL{yul@IKah^0C0Ene79$>Tz&{(>nZ@77!-#9 z<3CKEc`S49zd$39t`EL*>*gIKw{Ct7TlnhE0)7T(cCTO)kG0@#ScG|49K=chgPk9c z0j`2Hu<}6ha9SlOaEEpK#?0;8gBJt$^ znvmp2b9C;3-4pEExjusyJc7T#t6)GGns5-7;5Yy_>bAqJ!1J5KQ3G2Tg@Na@0*RA& z9GzqUa@}co^(3x@m5?*eDhrr|l`t^TwGxK~EUZLvQg=e1*_K<6Bg89Z#LC`#l`0;h z#@=uBzN{(xM>AZvI$0~X_cy8%oG>J&S1V%3xyDQ}sVTa|T^*mr{OD+IaFj<3TNrq? z{N-oE3FOtif1AHCsI0%KQ&}I$2qqeal{<}0(JNJr>nEuzTZ{{dkGS~8Xby8$$|UJ( z-zK`1>hW$!@H)N=8sx!;DVT*A=HY($BpiWba1u5Ez-N}zaGML*jCe~9D{$s_9_tW&CAU7uQ@i9 z%?_9DEe)>)_Q(RF@H4i%-x2t3@AV!c_EsJ~AtNH8L0%S8c!sU#R&MhlsSyhF4MDS(ChHj+vx|Y|pyrw&rWeM?l zbwU;4@6~<^B_a!Xh;e2rjC1MR5>p7HgfWG4E~`|buMwt5hC+oRs~JU32gE#lAK%9> z!W7KFJpkpj0|Va| zMi2?-dZ8eX>=)2VCq~@DD76F6f3$^(eMb&XqkbPQVC4X2 zuo?NE&DthbO*^aZ{Xu=r(-c*UWr;~KMJj2un7wH%o3?E(8^*G#F8}nKIaO%9CP_6( zqZDcGvazdQ&~bPcYA_A;!LGq9%{Kk4`qt~TRwKq=Fr#kHm{Om@z*IrxOdFxUE RU)lNNrJVNvNq_6+{{ZQFEu#Pc delta 506 zcmX|-Pe>GT6vsbrW?a_PN=#T{p%p?1S=w31NpL;s+9A-lJ#L?N1OHfx?@7&K_$jRKEZ}fzEtBH)|UysezmWF6K*xFSHt&=rbf$Ti>;Q8YrX zncAgVM-V91wi+jx!~?YvL%)~ zq-iG8DbuR-xJ$C&e|BTy|Mj+vesu*20yIT_8i=W3*y}t;FhB|8So(HmEqr(Ac;|op E2i!<=3IG5A diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 809f48361d..c141b48d32 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -37,11 +37,14 @@ Rectangle { property double balanceAfterPurchase; property bool alreadyOwned: false; property int itemPrice: -1; - property bool itemIsJson: true; + property bool isCertified; + property string itemType; + property var itemTypesArray: ["entity", "wearable", "contentSet", "app", "avatar"]; + property var buttonTextNormal: ["REZ IT", "WEAR IT", "SWAP CONTENT SET", "INSTALL IT", "WEAR IT"]; + property var buttonTextClicked: ["REZZED", "WORN", "SWAPPED", "INSTALLED", "WORN"] property bool shouldBuyWithControlledFailure: false; property bool debugCheckoutSuccess: false; property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified(); - property bool isWearable; property string referrer; // Style color: hifi.colors.white; @@ -85,7 +88,9 @@ Rectangle { UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned, result.message); } else { root.itemHref = result.data.download_url; - root.isWearable = result.data.categories.indexOf("Wearables") > -1; + if (result.data.categories.indexOf("Wearables") > -1) { + root.itemType = "wearable"; + } root.activeView = "checkoutSuccess"; UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned); } @@ -122,7 +127,25 @@ Rectangle { } onItemHrefChanged: { - itemIsJson = root.itemHref.endsWith('.json'); + if (root.itemHref.indexOf(".fst") > -1) { + root.itemType = "avatar"; + } else if (root.itemHref.endsWith('.json.gz')) { + root.itemType = "contentSet"; + } else if (root.itemHref.endsWith('.json')) { + root.itemType = "entity"; // "wearable" type handled later + } else if (root.itemHref.endsWith('.js')) { + root.itemType = "app"; + } else { + root.itemType = "entity"; + } + } + + onItemTypeChanged: { + if (root.itemType === "entity" || root.itemType === "wearable" || root.itemType === "contentSet") { + root.isCertified = true; + } else { + root.isCertified = false; + } } onItemPriceChanged: { @@ -464,7 +487,7 @@ Rectangle { // "Buy" button HifiControlsUit.Button { id: buyButton; - enabled: (root.balanceAfterPurchase >= 0 && purchasesReceived && balanceReceived) || !itemIsJson; + enabled: (root.balanceAfterPurchase >= 0 && purchasesReceived && balanceReceived) || (!root.isCertified); color: hifi.buttons.blue; colorScheme: hifi.colorSchemes.light; anchors.top: checkoutActionButtonsContainer.top; @@ -472,9 +495,9 @@ Rectangle { height: 40; anchors.left: parent.left; anchors.right: parent.right; - text: (itemIsJson ? ((purchasesReceived && balanceReceived) ? "Confirm Purchase" : "--") : "Get Item"); + text: ((root.isCertified) ? ((purchasesReceived && balanceReceived) ? "Confirm Purchase" : "--") : "Get Item"); onClicked: { - if (itemIsJson) { + if (root.isCertified) { buyButton.enabled = false; if (!root.shouldBuyWithControlledFailure) { Commerce.buy(itemId, itemPrice); @@ -576,7 +599,7 @@ Rectangle { RalewayBold { anchors.fill: parent; - text: "REZZED"; + text: (root.buttonTextClicked)[itemTypesArray.indexOf(root.itemType)]; size: 18; color: hifi.colors.white; verticalAlignment: Text.AlignVCenter; @@ -592,7 +615,7 @@ Rectangle { // "Rez" button HifiControlsUit.Button { id: rezNowButton; - enabled: root.canRezCertifiedItems || root.isWearable; + enabled: root.canRezCertifiedItems || root.itemType === "wearable"; buttonGlyph: hifi.glyphs.lightning; color: hifi.buttons.red; colorScheme: hifi.colorSchemes.light; @@ -601,17 +624,27 @@ Rectangle { height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: root.isWearable ? "Wear It" : "Rez It" + text: (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)]; onClicked: { - sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); - rezzedNotifContainer.visible = true; - rezzedNotifContainerTimer.start(); - UserActivityLogger.commerceEntityRezzed(root.itemId, "checkout", root.isWearable ? "rez" : "wear"); + if (root.itemType === "contentSet") { + lightboxPopup.titleText = "Replace Content"; + lightboxPopup.bodyText = "Rezzing this content set will replace the existing environment and all of the items in this domain."; + lightboxPopup.button1text = "CANCEL"; + lightboxPopup.button1method = "root.visible = false;" + lightboxPopup.button2text = "CONFIRM"; + lightboxPopup.button2method = "sendToScript({method: 'checkout_rezClicked', itemHref: " + root.itemHref + ", itemType: " + root.itemType + "});"; + lightboxPopup.visible = true; + } else { + sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); + rezzedNotifContainer.visible = true; + rezzedNotifContainerTimer.start(); + UserActivityLogger.commerceEntityRezzed(root.itemId, "checkout", root.itemType); + } } } RalewaySemiBold { id: noPermissionText; - visible: !root.canRezCertifiedItems && !root.isWearable; + visible: !root.canRezCertifiedItems && root.itemType !== "wearable"; text: 'You do not have Certified Rez permissions in this domain.' // Text size size: 16; @@ -640,7 +673,7 @@ Rectangle { } RalewaySemiBold { id: explainRezText; - visible: !root.isWearable; + visible: root.itemType === "entity"; text: 'What does "Rez" mean?' // Text size size: 16; @@ -851,7 +884,7 @@ Rectangle { buyButton.color = hifi.buttons.red; root.shouldBuyWithControlledFailure = true; } else { - buyButton.text = (itemIsJson ? ((purchasesReceived && balanceReceived) ? (root.alreadyOwned ? "Buy Another" : "Buy"): "--") : "Get Item"); + buyButton.text = (root.isCertified ? ((purchasesReceived && balanceReceived) ? (root.alreadyOwned ? "Buy Another" : "Buy"): "--") : "Get Item"); buyButton.color = hifi.buttons.blue; root.shouldBuyWithControlledFailure = false; } @@ -901,7 +934,7 @@ Rectangle { } function setBuyText() { - if (root.itemIsJson) { + if (root.isCertified) { if (root.purchasesReceived && root.balanceReceived) { if (root.balanceAfterPurchase < 0) { if (root.alreadyOwned) { diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 361b9931a4..41f2d919cd 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -41,8 +41,11 @@ Item { property int limitedRun; property string itemType; property var itemTypesArray: ["entity", "wearable", "contentSet", "app", "avatar"]; - property var buttonTextNormal: ["REZ", "WEAR", "SWAP", "INSTALL", "WEAR"]; - property var buttonTextClicked: ["REZZED", "WORN", "SWAPPED", "INSTALLED", "WORN"] + property var buttonTextNormal: ["REZ", "WEAR", "REPLACE", "INSTALL", "WEAR"]; + property var buttonTextClicked: ["REZZED", "WORN", "REPLACED", "INSTALLED", "WORN"] + property var buttonGlyph: [hifi.glyphs.wand, hifi.glyphs.hat, hifi.glyphs.globe, hifi.glyphs.install, hifi.glyphs.avatar]; + property bool showConfirmation: false; + property bool hasPermissionToRezThis; property string originalStatusText; property string originalStatusColor; @@ -50,6 +53,23 @@ Item { height: 110; width: parent.width; + Connections { + target: Commerce; + + onContentSetChanged: { + if (contentSetMarketplaceID === root.itemId) { + showConfirmation = true; + } + } + } + + onItemTypeChanged: { + if ((itemType === "entity" && (!Entities.canRezCertified() && !Entities.canRezTmpCertified())) || + (itemType === "contentSet" && !Entities.canReplaceContent())) { + root.hasPermissionToRezThis = false; + } + } + onPurchaseStatusChangedChanged: { if (root.purchaseStatusChanged === true && root.purchaseStatus === "confirmed") { root.originalStatusText = statusText.text; @@ -60,6 +80,15 @@ Item { } } + onShowConfirmationChanged: { + if (root.showConfirmation) { + rezzedNotifContainer.visible = true; + rezzedNotifContainerTimer.start(); + UserActivityLogger.commerceEntityRezzed(root.itemId, "purchases", root.itemType); + root.showConfirmation = false; + } + } + Timer { id: confirmedTimer; interval: 3000; @@ -325,7 +354,7 @@ Item { RalewayBold { anchors.fill: parent; text: (root.buttonTextClicked)[itemTypesArray.indexOf(root.itemType)]; - size: 18; + size: 15; color: hifi.colors.white; verticalAlignment: Text.AlignVCenter; horizontalAlignment: Text.AlignHCenter; @@ -353,10 +382,12 @@ Item { enabled: (root.canRezCertifiedItems || root.itemType === "wearable") && root.purchaseStatus !== "invalidated"; onClicked: { - sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); - rezzedNotifContainer.visible = true; - rezzedNotifContainerTimer.start(); - UserActivityLogger.commerceEntityRezzed(root.itemId, "purchases", root.itemType === "wearable" ? "rez" : "wear"); + if (root.itemType === "contentSet") { + sendToPurchases({method: 'showReplaceContentLightbox', itemId: root.itemId, itemHref: root.itemHref}); + } else { + sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); + root.showConfirmation = true; + } } style: ButtonStyle { @@ -396,13 +427,13 @@ Item { label: Item { HiFiGlyphs { - id: lightningIcon; - text: hifi.glyphs.lightning; + id: rezIcon; + text: (root.buttonGlyph)[itemTypesArray.indexOf(root.itemType)]; // Size - size: 32; + size: 60; // Anchors anchors.top: parent.top; - anchors.topMargin: 12; + anchors.topMargin: 0; anchors.left: parent.left; anchors.right: parent.right; horizontalAlignment: Text.AlignHCenter; @@ -411,8 +442,8 @@ Item { : hifi.buttons.disabledTextColor[control.colorScheme] } RalewayBold { - anchors.top: lightningIcon.bottom; - anchors.topMargin: -20; + anchors.top: rezIcon.bottom; + anchors.topMargin: -4; anchors.right: parent.right; anchors.left: parent.left; anchors.bottom: parent.bottom; diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 922ba54822..956bfa7c26 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -455,10 +455,16 @@ Rectangle { limitedRun: model.limited_run; displayedItemCount: model.displayedItemCount; itemType: { - if (model.download_url.indexOf(".fst") > -1) { + if (model.root_file_url.indexOf(".fst") > -1) { "avatar"; } else if (model.categories.indexOf("Wearables") > -1) { "wearable"; + } else if (model.root_file_url.endsWith('.json.gz')) { + "contentSet"; + } else if (model.root_file_url.endsWith('.json')) { + "entity"; + } else if (model.root_file_url.endsWith('.js')) { + "app"; } else { "entity"; } @@ -490,6 +496,14 @@ Rectangle { lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1method = "root.visible = false;" lightboxPopup.visible = true; + } else if (msg.method === "showReplaceContentLightbox") { + lightboxPopup.titleText = "Replace Content"; + lightboxPopup.bodyText = "Rezzing this content set will replace the existing environment and all of the items in this domain."; + lightboxPopup.button1text = "CANCEL"; + lightboxPopup.button1method = "root.visible = false;" + lightboxPopup.button2text = "CONFIRM"; + lightboxPopup.button2method = "Commerce.replaceContentSet('" + msg.itemId + "', '" + msg.itemHref + "'); root.visible = false;"; + lightboxPopup.visible = true; } else if (msg.method === "setFilterText") { filterBar.text = msg.filterText; } diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index 5da587ea57..16b74f6b54 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -354,5 +354,9 @@ Item { readonly property string wallet: "\ue027" readonly property string paperPlane: "\ue028" readonly property string passphrase: "\ue029" + readonly property string globe: "\ue02c" + readonly property string wand: "\ue02d" + readonly property string hat: "\ue02e" + readonly property string install: "\ue02f" } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index be2a54b8e9..576d080947 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6176,6 +6176,24 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) { return true; } +void Application::replaceDomainContent(const QString& url) { + qCDebug(interfaceapp) << "Attempting to replace domain content: " << url; + QByteArray urlData(url.toUtf8()); + auto limitedNodeList = DependencyManager::get(); + limitedNodeList->eachMatchingNode([](const SharedNodePointer& node) { + return node->getType() == NodeType::EntityServer && node->getActiveSocket(); + }, [&urlData, limitedNodeList](const SharedNodePointer& octreeNode) { + auto octreeFilePacket = NLPacket::create(PacketType::OctreeFileReplacementFromUrl, urlData.size(), true); + octreeFilePacket->write(urlData); + limitedNodeList->sendPacket(std::move(octreeFilePacket), *octreeNode); + }); + auto addressManager = DependencyManager::get(); + addressManager->handleLookupString(DOMAIN_SPAWNING_POINT); + QString newHomeAddress = addressManager->getHost() + DOMAIN_SPAWNING_POINT; + qCDebug(interfaceapp) << "Setting new home bookmark to: " << newHomeAddress; + DependencyManager::get()->setHomeLocationToAddress(newHomeAddress); +} + bool Application::askToReplaceDomainContent(const QString& url) { QString methodDetails; const int MAX_CHARACTERS_PER_LINE = 90; @@ -6195,21 +6213,7 @@ bool Application::askToReplaceDomainContent(const QString& url) { QString details; if (static_cast(answer.toInt()) == QMessageBox::Yes) { // Given confirmation, send request to domain server to replace content - qCDebug(interfaceapp) << "Attempting to replace domain content: " << url; - QByteArray urlData(url.toUtf8()); - auto limitedNodeList = DependencyManager::get(); - limitedNodeList->eachMatchingNode([](const SharedNodePointer& node) { - return node->getType() == NodeType::EntityServer && node->getActiveSocket(); - }, [&urlData, limitedNodeList](const SharedNodePointer& octreeNode) { - auto octreeFilePacket = NLPacket::create(PacketType::OctreeFileReplacementFromUrl, urlData.size(), true); - octreeFilePacket->write(urlData); - limitedNodeList->sendPacket(std::move(octreeFilePacket), *octreeNode); - }); - auto addressManager = DependencyManager::get(); - addressManager->handleLookupString(DOMAIN_SPAWNING_POINT); - QString newHomeAddress = addressManager->getHost() + DOMAIN_SPAWNING_POINT; - qCDebug(interfaceapp) << "Setting new home bookmark to: " << newHomeAddress; - DependencyManager::get()->setHomeLocationToAddress(newHomeAddress); + replaceDomainContent(url); details = "SuccessfulRequestToReplaceContent"; } else { details = "UserDeclinedToReplaceContent"; diff --git a/interface/src/Application.h b/interface/src/Application.h index ddb8ce11e5..2dd83f39eb 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -284,6 +284,8 @@ public: bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; } void saveNextPhysicsStats(QString filename); + void replaceDomainContent(const QString& url); + signals: void svoImportRequested(const QString& url); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index c9caa393ce..ba52edba4f 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -15,6 +15,8 @@ #include "Ledger.h" #include "Wallet.h" #include +#include +#include QmlCommerce::QmlCommerce() { auto ledger = DependencyManager::get(); @@ -163,3 +165,14 @@ void QmlCommerce::transferHfcToUsername(const QString& username, const int& amou QString key = keys[0]; ledger->transferHfcToUsername(key, username, amount, optionalMessage); } + +void QmlCommerce::replaceContentSet(const QString& id, const QString& url) { + qApp->replaceDomainContent(url); + QJsonObject messageProperties = { + { "status", "SuccessfulRequestToReplaceContent" }, + { "content_set_url", url } + }; + UserActivityLogger::getInstance().logAction("replace_domain_content", messageProperties); + + emit contentSetChanged(id); +} diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index b261ee6de6..aa939a439d 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -48,6 +48,8 @@ signals: void transferHfcToNodeResult(QJsonObject result); void transferHfcToUsernameResult(QJsonObject result); + void contentSetChanged(const QString& contentSetMarketplaceID); + protected: Q_INVOKABLE void getWalletStatus(); @@ -71,6 +73,8 @@ protected: Q_INVOKABLE void transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage); Q_INVOKABLE void transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage); + + Q_INVOKABLE void replaceContentSet(const QString& id, const QString& url); }; #endif // hifi_QmlCommerce_h diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 4342f0e683..f9449cca34 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -101,6 +101,11 @@ bool EntityScriptingInterface::canWriteAssets() { return nodeList->getThisNodeCanWriteAssets(); } +bool EntityScriptingInterface::canReplaceContent() { + auto nodeList = DependencyManager::get(); + return nodeList->getThisNodeCanReplaceContent(); +} + void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) { if (_entityTree) { disconnect(_entityTree.get(), &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index d1b321dbca..30e1e6ce1c 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -144,6 +144,12 @@ public slots: */ Q_INVOKABLE bool canWriteAssets(); + /**jsdoc + * @function Entities.canReplaceContent + * @return {bool} `true` if the DomainServer will allow this Node/Avatar to replace the domain's content set + */ + Q_INVOKABLE bool canReplaceContent(); + /**jsdoc * Add a new entity with the specified properties. If `clientOnly` is true, the entity will * not be sent to the server and will only be visible/accessible on the local client. diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index b43fe545fa..95d9294063 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -65,7 +65,7 @@ var selectionDisplay = null; // for gridTool.js to ignore var onMarketplaceScreen = false; var onCommerceScreen = false; - var debugCheckout = false; + var debugCheckout = true; var debugError = false; function showMarketplace() { if (!debugCheckout) { @@ -75,11 +75,11 @@ var selectionDisplay = null; // for gridTool.js to ignore tablet.pushOntoStack(MARKETPLACE_CHECKOUT_QML_PATH); tablet.sendToQml({ method: 'updateCheckoutQML', params: { - itemId: '0d90d21c-ce7a-4990-ad18-e9d2cf991027', - itemName: 'Test Flaregun', - itemPrice: (debugError ? 10 : 17), - itemHref: 'http://mpassets.highfidelity.com/0d90d21c-ce7a-4990-ad18-e9d2cf991027-v1/flaregun.json', - categories: ["Wearables", "Miscellaneous"] + itemId: 'e197e3d7-eafc-4aa5-9341-acee57174fe9', + itemName: 'Oasis', + itemPrice: (debugError ? 10 : 11), + itemHref: 'http://mpassets-staging.highfidelity.com/e197e3d7-eafc-4aa5-9341-acee57174fe9-v1/oasis_Aug15.json.gz', + categories: ["Miscellaneous"] } }); } @@ -240,6 +240,11 @@ var selectionDisplay = null; // for gridTool.js to ignore var wearableLocalDimensions = null; var wearableDimensions = null; + if (itemType === "contentSet") { + console.log("Item is a content set; codepath shouldn't go here.") + return; + } + if (isWearable) { var wearableTransforms = Settings.getValue("io.highfidelity.avatarStore.checkOut.transforms"); if (!wearableTransforms) { From 0809210dc3cb92948f0173828e244d405681b743 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 13 Feb 2018 11:16:48 -0800 Subject: [PATCH 03/25] No permission card --- .../hifi/commerce/purchases/PurchasedItem.qml | 100 +++++++++++++++++- .../qml/hifi/commerce/purchases/Purchases.qml | 84 +++------------ .../AccountServicesScriptingInterface.h | 2 +- 3 files changed, 113 insertions(+), 73 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 41f2d919cd..9db847eada 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -29,7 +29,6 @@ Item { id: root; property string purchaseStatus; property bool purchaseStatusChanged; - property bool canRezCertifiedItems: false; property string itemName; property string itemId; property string itemPreviewImageUrl; @@ -67,6 +66,8 @@ Item { if ((itemType === "entity" && (!Entities.canRezCertified() && !Entities.canRezTmpCertified())) || (itemType === "contentSet" && !Entities.canReplaceContent())) { root.hasPermissionToRezThis = false; + } else { + root.hasPermissionToRezThis = true; } } @@ -128,15 +129,19 @@ Item { } } - + TextMetrics { + id: itemNameTextMetrics; + font: itemName.font; + text: itemName.text; + } RalewaySemiBold { id: itemName; anchors.top: itemPreviewImage.top; anchors.topMargin: 4; anchors.left: itemPreviewImage.right; anchors.leftMargin: 8; - anchors.right: buttonContainer.left; - anchors.rightMargin: 8; + width: root.hasPermissionToRezThis ? (buttonContainer.x - itemPreviewImage.x - itemPreviewImage.width) : + Math.min(itemNameTextMetrics.tightBoundingRect.width + 2, buttonContainer.x - itemPreviewImage.x - itemPreviewImage.width - noPermissionGlyph.width + 2); height: paintedHeight; // Text size size: 24; @@ -162,6 +167,91 @@ Item { } } } + HiFiGlyphs { + id: noPermissionGlyph; + visible: !root.hasPermissionToRezThis; + anchors.verticalCenter: itemName.verticalCenter; + anchors.left: itemName.right; + anchors.leftMargin: itemName.truncated ? -14 : -2; + text: hifi.glyphs.info; + // Size + size: 40; + width: 32; + // Style + color: hifi.colors.redAccent; + + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + + onEntered: { + noPermissionGlyph.color = hifi.colors.redHighlight; + } + onExited: { + noPermissionGlyph.color = hifi.colors.redAccent; + } + onClicked: { + permissionExplanationCard.visible = true; + } + } + } + Rectangle { + id: permissionExplanationCard; + z: 995; + visible: false; + anchors.fill: parent; + color: hifi.colors.white; + + RalewayRegular { + id: permissionExplanationText; + text: { + if (root.itemType === "contentSet") { + "You do not have 'Replace Content' permissions in this domain. Learn more"; + } else if (root.itemType === "entity") { + "You do not have 'Rez Certified' permissions in this domain. Learn more"; + } + } + size: 16; + anchors.left: parent.left; + anchors.leftMargin: 30; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.right: permissionExplanationGlyph.left; + color: hifi.colors.baseGray; + wrapMode: Text.WordWrap; + verticalAlignment: Text.AlignVCenter; + + onLinkActivated: { + sendToPurchases({method: 'showPermissionsExplanation', itemType: root.itemType}); + } + } + // "Close" button + HiFiGlyphs { + id: permissionExplanationGlyph; + text: hifi.glyphs.close; + color: hifi.colors.baseGray; + size: 26; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.right: parent.right; + width: 77; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + onEntered: { + parent.text = hifi.glyphs.closeInverted; + } + onExited: { + parent.text = hifi.glyphs.close; + } + onClicked: { + permissionExplanationCard.visible = false; + } + } + } + } Item { id: certificateContainer; @@ -379,7 +469,7 @@ Item { anchors.right: parent.right; anchors.rightMargin: 4; width: height; - enabled: (root.canRezCertifiedItems || root.itemType === "wearable") && root.purchaseStatus !== "invalidated"; + enabled: root.hasPermissionToRezThis && root.purchaseStatus !== "invalidated"; onClicked: { if (root.itemType === "contentSet") { diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 956bfa7c26..daa23d29c8 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -32,7 +32,6 @@ Rectangle { property bool securityImageResultReceived: false; property bool purchasesReceived: false; property bool punctuationMode: false; - property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified(); property bool pendingInventoryReply: true; property bool isShowingMyItems: false; property bool isDebuggingFirstUseTutorial: false; @@ -365,69 +364,6 @@ Rectangle { id: filteredPurchasesModel; } - Rectangle { - id: cantRezCertified; - visible: !root.canRezCertifiedItems; - color: "#FFC3CD"; - radius: 4; - border.color: hifi.colors.redAccent; - border.width: 1; - anchors.top: separator.bottom; - anchors.topMargin: 12; - anchors.left: parent.left; - anchors.leftMargin: 16; - anchors.right: parent.right; - anchors.rightMargin: 16; - height: 80; - - HiFiGlyphs { - id: lightningIcon; - text: hifi.glyphs.lightning; - // Size - size: 36; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 18; - anchors.left: parent.left; - anchors.leftMargin: 12; - horizontalAlignment: Text.AlignHCenter; - // Style - color: hifi.colors.lightGray; - } - - RalewayRegular { - text: "You don't have permission to rez certified items in this domain. " + - 'Learn More'; - // Text size - size: 18; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 4; - anchors.left: lightningIcon.right; - anchors.leftMargin: 8; - anchors.right: parent.right; - anchors.rightMargin: 8; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 4; - // Style - color: hifi.colors.baseGray; - wrapMode: Text.WordWrap; - // Alignment - verticalAlignment: Text.AlignVCenter; - - onLinkActivated: { - lightboxPopup.titleText = "Rez Permission Required"; - lightboxPopup.bodyText = "You don't have permission to rez certified items in this domain.

" + - "Use the GOTO app to visit another domain or go to your own sandbox."; - lightboxPopup.button1text = "CLOSE"; - lightboxPopup.button1method = "root.visible = false;" - lightboxPopup.button2text = "OPEN GOTO"; - lightboxPopup.button2method = "sendToParent({method: 'purchases_openGoTo'});"; - lightboxPopup.visible = true; - } - } - } - ListView { id: purchasesContentsList; visible: (root.isShowingMyItems && filteredPurchasesModel.count !== 0) || (!root.isShowingMyItems && filteredPurchasesModel.count !== 0); @@ -436,13 +372,12 @@ Rectangle { snapMode: ListView.SnapToItem; highlightRangeMode: ListView.StrictlyEnforceRange; // Anchors - anchors.top: root.canRezCertifiedItems ? separator.bottom : cantRezCertified.bottom; + anchors.top: separator.bottom; anchors.topMargin: 12; anchors.left: parent.left; anchors.bottom: parent.bottom; width: parent.width; delegate: PurchasedItem { - canRezCertifiedItems: root.canRezCertifiedItems; itemName: title; itemId: id; itemPreviewImageUrl: preview; @@ -466,7 +401,7 @@ Rectangle { } else if (model.root_file_url.endsWith('.js')) { "app"; } else { - "entity"; + "unknown"; } } anchors.topMargin: 10; @@ -504,6 +439,21 @@ Rectangle { lightboxPopup.button2text = "CONFIRM"; lightboxPopup.button2method = "Commerce.replaceContentSet('" + msg.itemId + "', '" + msg.itemHref + "'); root.visible = false;"; lightboxPopup.visible = true; + } else if (msg.method === "showPermissionsExplanation") { + if (msg.itemType === "entity") { + lightboxPopup.titleText = "Rez Certified Permission"; + lightboxPopup.bodyText = "You don't have permission to rez certified items in this domain.

" + + "Use the GOTO app to visit another domain or go to your own sandbox."; + lightboxPopup.button2text = "OPEN GOTO"; + lightboxPopup.button2method = "sendToParent({method: 'purchases_openGoTo'});"; + } else if (msg.itemType === "contentSet") { + lightboxPopup.titleText = "Replace Content Permission"; + lightboxPopup.bodyText = "You do not have the permission 'Replace Content' in this domain's server settings. The domain owner " + + "must enable it for you before you can replace content sets in this domain."; + } + lightboxPopup.button1text = "CLOSE"; + lightboxPopup.button1method = "root.visible = false;" + lightboxPopup.visible = true; } else if (msg.method === "setFilterText") { filterBar.text = msg.filterText; } diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h index 012c37305d..cfa51697af 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.h +++ b/interface/src/scripting/AccountServicesScriptingInterface.h @@ -38,7 +38,7 @@ class AccountServicesScriptingInterface : public QObject { Q_PROPERTY(QString username READ getUsername NOTIFY myUsernameChanged) Q_PROPERTY(bool loggedIn READ loggedIn NOTIFY loggedInChanged) Q_PROPERTY(QString findableBy READ getFindableBy WRITE setFindableBy NOTIFY findableByChanged) - Q_PROPERTY(QUrl metaverseServerURL READ getMetaverseServerURL) + Q_PROPERTY(QUrl metaverseServerURL READ getMetaverseServerURL CONSTANT) public: static AccountServicesScriptingInterface* getInstance(); From 1b7253dc7f8d20d8e2acc5a66c1d752cf556061f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 13 Feb 2018 13:54:50 -0800 Subject: [PATCH 04/25] Already Owned endpoint --- interface/src/commerce/Ledger.cpp | 11 +++++++++++ interface/src/commerce/Ledger.h | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 10ddd4c110..f319881035 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -336,3 +336,14 @@ void Ledger::transferHfcToUsername(const QString& hfc_key, const QString& userna auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); signedSend("transaction", transactionString, hfc_key, "transfer_hfc_to_user", "transferHfcToUsernameSuccess", "transferHfcToUsernameFailure"); } + +void Ledger::alreadyOwnedSuccess(QNetworkReply& reply) { apiResponse("alreadyOwned", reply); } +void Ledger::alreadyOwnedFailure(QNetworkReply& reply) { failResponse("alreadyOwned", reply); } +void Ledger::alreadyOwned(const QString& marketplaceId) { + auto wallet = DependencyManager::get(); + QString endpoint = "already_owned"; + QJsonObject request; + request["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys()); + request["marketplace_item_id"] = marketplaceId; + send(endpoint, "alreadyOwnedSuccess", "alreadyOwnedFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::None, request); +} diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 1b56c893ab..ac9fe950d9 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -35,6 +35,7 @@ public: void certificateInfo(const QString& certificateId); void transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage); void transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage); + void alreadyOwned(const QString& marketplaceId); enum CertificateStatus { CERTIFICATE_STATUS_UNKNOWN = 0, @@ -55,6 +56,7 @@ signals: void certificateInfoResult(QJsonObject result); void transferHfcToNodeResult(QJsonObject result); void transferHfcToUsernameResult(QJsonObject result); + void alreadyOwnedResult(QJsonObject result); void updateCertificateStatus(const QString& certID, uint certStatus); @@ -79,6 +81,8 @@ public slots: void transferHfcToNodeFailure(QNetworkReply& reply); void transferHfcToUsernameSuccess(QNetworkReply& reply); void transferHfcToUsernameFailure(QNetworkReply& reply); + void alreadyOwnedSuccess(QNetworkReply& reply); + void alreadyOwnedFailure(QNetworkReply& reply); private: QJsonObject apiResponse(const QString& label, QNetworkReply& reply); From 69a06bf1b75ebecc57d61899b509e7fbd86325da Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 13 Feb 2018 14:42:03 -0800 Subject: [PATCH 05/25] Wrong fonts file --- interface/resources/fonts/hifi-glyphs.ttf | Bin 31500 -> 32536 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 8907cf7858dd304cdad024bfa1ff4ca5cd94caf9..8db0377f88864810e7f893132449ad8e258e68cd 100644 GIT binary patch delta 1476 zcmXw3TWl0n82_O*-HyEv#{Om?#?c=y-?cjWd2_BDjG<32kZU z0)>aJHxg7(5MvC2Cf=S*R2sY_24jqXMB|I%3mOul55AC?Xm~JSX2Fx3|Cay%a{mAP z`A+;pZ``Fi2tWi+L&3(u!N-S=9DDY4VD?`C$%odi8^k3DVD<)p%!WZQ2gd(%?ap z)rrwRmtNcg9J&M0+|>T*nl*CvQDEjnK(sZQBOv{We{4r=#osU(f^+@d+qdp(cKg<= z)K90+4A7I*J$H_LTDA;>=)+(mQvn(`*?_+q>s!7})Cg)YND5)L}i3lDu*g@>^Lo3Ra(032c3rFdn4 zDrMQPx&_a-P3acAf?KfTLYmUeRXpF8o^QuRp%TObQSf}rk!9a5Sx%7d3+cw~cwkvU zbuLv`OJ-b1w^9x&Y2>|w2S?b*^k$}63m3$#gw{MSS$3isqcxXH>r0$@wz=quMe`jy zo}E8q<};moHsiMF-PuZOQzFrvUy*Mf&sp;Zh;%1r#TVK-U9NplBcj^KrmZRwF`i4R zddIdlLv20YUQfn4#Ar4vW=PF_=d=si^Vd+igWnk<-tk*UdT2`~tIqK33!SlyE52^e zH}%NqP_m0{=ZuKdR90&4;w5c5&jz0p!XO6eGrELkw4)cxu?CMcW;hEhD!8)XN|7cr zO?Ij1dxc!RQqDOk$~l(VN0wPC_EV`?DaW019~I3`*3g!)oqj48Q&fJSrBiSVOUU=q zWXEkO0^c8CzUO-lS4iOnA@6=9Mncl38#R3>r0e>S+^9)iM6V{pM!m%_T8wbgXw&sB zUH`pJ*V}cr!!%8Em@}dCCMji9Xi_LCu1LXDm1$fPn&cr><8Nt9*H}bXgeKLPuIwAu z*l;MMhc@VPScs;Z0esj<8b;Cx+ooyi`ZJ-nknaB6ka<>Db+t)I5!Hm0%G-+2nBYtZ zO-iYTxW>;jp(zoK>zW#hX=*7ZHunSh*mcwTfh?e)obz#S(M3WB&v#N(>yK|BA1Af zWU**?{K}OpR-#-28mx?QCfC@_}MwPPjRKkit? zY8||{r>l78vD#~NdhU-}f;4)OUZN9p^1`8Sc{)5gd1&u}{gV@;leKAnxH`FuA0Dk9 nkb5VmYt`}bGgptM3a3EezyoRtC>r1Qzw_gL&ibFbZr}P3tv~vd delta 456 zcmYL`ze^hd6vsdB?ivGWt%HPM4=mM1Mf4Q>MRcjgr6GbPb*Xy9=nXkNNR&$k^$&vx3S#6gf)5_=7w`MNU*3mXsb0$t0z6?CmAO>v zb-HZFZ=iAo=(E!^DSRSOIRTz$<_Cr{Utc*O3qUq!o6cs>=>RCdL0Mfhi%z3~3Y8x~ zbgfWct-r~%Kx6?5bt`YKw5AVxfIq;9W#xBFZBWiZ+JSD%F1ekTi8hc=ATJ6VIdg8@ zw%}0`M7K@X;X)Qc#sWg?rk%f-_?(4eu;V!!#nQ^lLmf)rAtIqTfoI>cf0v#n{}EFN z?CiZCZ$`zBzmrMXt4+yU>GM9ys2IUSy>~r?K$cO%Oh=RHKpRn-Slj1)-ec7eZ$|4< zJSXy{;Qil{TQ{F+p7&5{YGuv-IqP@m)jxURryGp4VfT~21*Fs3M9g2P^(lC UG`K$#yosNa2ggBt;P1!p0BG@JE&u=k From a477a3da7004f3c3d3a0b14ffb1f95536f8bccad Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 13 Feb 2018 15:04:31 -0800 Subject: [PATCH 06/25] Done with My Purchases flow for now --- .../qml/hifi/commerce/common/CommerceLightbox.qml | 4 ++++ .../qml/hifi/commerce/purchases/Purchases.qml | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml index 8b0ec17232..b24af716ad 100644 --- a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml +++ b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml @@ -100,6 +100,10 @@ Rectangle { size: 20; verticalAlignment: Text.AlignTop; wrapMode: Text.WordWrap; + + onLinkActivated: { + sendToParent({ method: 'commerceLightboxLinkClicked', linkUrl: link }); + } } Item { diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index daa23d29c8..91fed207bc 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -147,7 +147,11 @@ Rectangle { Connections { onSendToParent: { - sendToScript(msg); + if (msg.method === 'commerceLightboxLinkClicked') { + Qt.openUrlExternally(msg.linkUrl); + } else { + sendToScript(msg); + } } } } @@ -433,7 +437,11 @@ Rectangle { lightboxPopup.visible = true; } else if (msg.method === "showReplaceContentLightbox") { lightboxPopup.titleText = "Replace Content"; - lightboxPopup.bodyText = "Rezzing this content set will replace the existing environment and all of the items in this domain."; + lightboxPopup.bodyText = "Rezzing this content set will replace the existing environment and all of the items in this domain. " + + "If you want to save the state of the content in this domain, create a backup before proceeding.

" + + "For more information about backing up and restoring content, " + + "" + + "click here to open info on your desktop browser."; lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = "root.visible = false;" lightboxPopup.button2text = "CONFIRM"; From 0fca843667fa1f5723849674ce3da22d4561c999 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 14 Feb 2018 10:24:34 -0800 Subject: [PATCH 07/25] Integrate 'alreadyOwned' endpoint --- .../qml/hifi/commerce/checkout/Checkout.qml | 38 ++++++++----------- interface/src/commerce/QmlCommerce.cpp | 7 ++++ interface/src/commerce/QmlCommerce.h | 3 ++ 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index c141b48d32..bd3f706004 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -28,7 +28,7 @@ Rectangle { id: root; objectName: "checkout" property string activeView: "initialize"; - property bool purchasesReceived: false; + property bool ownershipStatusReceived: false; property bool balanceReceived: false; property string itemName; property string itemId; @@ -106,14 +106,15 @@ Rectangle { } } - onInventoryResult: { + onAlreadyOwnedResult: { if (result.status !== 'success') { - console.log("Failed to get purchases", result.data.message); + console.log("Failed to get Already Owned status", result.data.message); } else { - root.purchasesReceived = true; - if (purchasesContains(result.data.assets, itemId)) { - root.alreadyOwned = true; + root.ownershipStatusReceived = true; + if (result.data.marketplace_item_id === root.itemId) { + root.alreadyOwned = result.data.already_owned; } else { + console.log("WARNING - Received 'Already Owned' status about different Marketplace ID!"); root.alreadyOwned = false; } root.setBuyText(); @@ -122,7 +123,8 @@ Rectangle { } onItemIdChanged: { - Commerce.inventory(); + root.ownershipStatusReceived = false; + Commerce.alreadyOwned(root.itemId); itemPreviewImage.source = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/" + itemId + "/thumbnail/hifi-mp-" + itemId + ".jpg"; } @@ -136,6 +138,7 @@ Rectangle { } else if (root.itemHref.endsWith('.js')) { root.itemType = "app"; } else { + console.log("WARNING - Item type is UNKNOWN!"); root.itemType = "entity"; } } @@ -226,7 +229,7 @@ Rectangle { color: hifi.colors.white; Component.onCompleted: { - purchasesReceived = false; + ownershipStatusReceived = false; balanceReceived = false; Commerce.getWalletStatus(); } @@ -487,7 +490,7 @@ Rectangle { // "Buy" button HifiControlsUit.Button { id: buyButton; - enabled: (root.balanceAfterPurchase >= 0 && purchasesReceived && balanceReceived) || (!root.isCertified); + enabled: (root.balanceAfterPurchase >= 0 && ownershipStatusReceived && balanceReceived) || (!root.isCertified); color: hifi.buttons.blue; colorScheme: hifi.colorSchemes.light; anchors.top: checkoutActionButtonsContainer.top; @@ -495,7 +498,7 @@ Rectangle { height: 40; anchors.left: parent.left; anchors.right: parent.right; - text: ((root.isCertified) ? ((purchasesReceived && balanceReceived) ? "Confirm Purchase" : "--") : "Get Item"); + text: ((root.isCertified) ? ((ownershipStatusReceived && balanceReceived) ? "Confirm Purchase" : "--") : "Get Item"); onClicked: { if (root.isCertified) { buyButton.enabled = false; @@ -884,7 +887,7 @@ Rectangle { buyButton.color = hifi.buttons.red; root.shouldBuyWithControlledFailure = true; } else { - buyButton.text = (root.isCertified ? ((purchasesReceived && balanceReceived) ? (root.alreadyOwned ? "Buy Another" : "Buy"): "--") : "Get Item"); + buyButton.text = (root.isCertified ? ((ownershipStatusReceived && balanceReceived) ? (root.alreadyOwned ? "Buy Another" : "Buy"): "--") : "Get Item"); buyButton.color = hifi.buttons.blue; root.shouldBuyWithControlledFailure = false; } @@ -924,18 +927,9 @@ Rectangle { } 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.isCertified) { - if (root.purchasesReceived && root.balanceReceived) { + if (root.ownershipStatusReceived && root.balanceReceived) { if (root.balanceAfterPurchase < 0) { if (root.alreadyOwned) { buyText.text = "Your Wallet does not have sufficient funds to purchase this item again.
" + @@ -978,7 +972,7 @@ Rectangle { root.activeView = "checkoutSuccess"; } root.balanceReceived = false; - root.purchasesReceived = false; + root.ownershipStatusReceived = false; Commerce.inventory(); Commerce.balance(); } diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index ba52edba4f..5458925d55 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -30,9 +30,11 @@ QmlCommerce::QmlCommerce() { connect(ledger.data(), &Ledger::accountResult, this, &QmlCommerce::accountResult); connect(wallet.data(), &Wallet::walletStatusResult, this, &QmlCommerce::walletStatusResult); connect(ledger.data(), &Ledger::certificateInfoResult, this, &QmlCommerce::certificateInfoResult); + connect(ledger.data(), &Ledger::alreadyOwnedResult, this, &QmlCommerce::alreadyOwnedResult); connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus); connect(ledger.data(), &Ledger::transferHfcToNodeResult, this, &QmlCommerce::transferHfcToNodeResult); connect(ledger.data(), &Ledger::transferHfcToUsernameResult, this, &QmlCommerce::transferHfcToUsernameResult); + connect(ledger.data(), &Ledger::transferHfcToUsernameResult, this, &QmlCommerce::transferHfcToUsernameResult); auto accountManager = DependencyManager::get(); connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { @@ -176,3 +178,8 @@ void QmlCommerce::replaceContentSet(const QString& id, const QString& url) { emit contentSetChanged(id); } + +void QmlCommerce::alreadyOwned(const QString& marketplaceId) { + auto ledger = DependencyManager::get(); + ledger->alreadyOwned(marketplaceId); +} diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index aa939a439d..dc38dcad19 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -42,6 +42,7 @@ signals: void historyResult(QJsonObject result); void accountResult(QJsonObject result); void certificateInfoResult(QJsonObject result); + void alreadyOwnedResult(QJsonObject result); void updateCertificateStatus(const QString& certID, uint certStatus); @@ -70,10 +71,12 @@ protected: Q_INVOKABLE void account(); Q_INVOKABLE void certificateInfo(const QString& certificateId); + Q_INVOKABLE void alreadyOwned(const QString& marketplaceId); Q_INVOKABLE void transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage); Q_INVOKABLE void transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage); + Q_INVOKABLE void replaceContentSet(const QString& id, const QString& url); }; From 5a8b8d4ac84068c47d5a06d66632cb3fd21aaa4c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 14 Feb 2018 16:44:02 -0800 Subject: [PATCH 08/25] Lots of changes --- .../qml/hifi/commerce/checkout/Checkout.qml | 132 +++++++++++++----- .../InspectionCertificate.qml | 1 + .../hifi/commerce/purchases/PurchasedItem.qml | 2 + interface/src/commerce/Ledger.cpp | 5 +- 4 files changed, 105 insertions(+), 35 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index bd3f706004..981d5c5b9a 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -40,8 +40,9 @@ Rectangle { property bool isCertified; property string itemType; property var itemTypesArray: ["entity", "wearable", "contentSet", "app", "avatar"]; - property var buttonTextNormal: ["REZ IT", "WEAR IT", "SWAP CONTENT SET", "INSTALL IT", "WEAR IT"]; - property var buttonTextClicked: ["REZZED", "WORN", "SWAPPED", "INSTALLED", "WORN"] + property var buttonTextNormal: ["REZ IT", "WEAR IT", "REPLACE CONTENT SET", "INSTALL IT", "WEAR IT"]; + property var buttonTextClicked: ["REZZED", "WORN", "CONTENT SET REPLACED!", "INSTALLED", "WORN"] + property var buttonGlyph: [hifi.glyphs.wand, hifi.glyphs.hat, hifi.glyphs.globe, hifi.glyphs.install, hifi.glyphs.avatar]; property bool shouldBuyWithControlledFailure: false; property bool debugCheckoutSuccess: false; property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified(); @@ -102,7 +103,7 @@ Rectangle { } else { root.balanceReceived = true; root.balanceAfterPurchase = result.data.balance - root.itemPrice; - root.setBuyText(); + root.refreshBuyUI(); } } @@ -117,7 +118,7 @@ Rectangle { console.log("WARNING - Received 'Already Owned' status about different Marketplace ID!"); root.alreadyOwned = false; } - root.setBuyText(); + root.refreshBuyUI(); } } } @@ -131,11 +132,11 @@ Rectangle { onItemHrefChanged: { if (root.itemHref.indexOf(".fst") > -1) { root.itemType = "avatar"; - } else if (root.itemHref.endsWith('.json.gz')) { + } else if (root.itemHref.indexOf('.json.gz') > -1) { root.itemType = "contentSet"; - } else if (root.itemHref.endsWith('.json')) { + } else if (root.itemHref.indexOf('.json') > -1) { root.itemType = "entity"; // "wearable" type handled later - } else if (root.itemHref.endsWith('.js')) { + } else if (root.itemHref.indexOf('.js') > -1) { root.itemType = "app"; } else { console.log("WARNING - Item type is UNKNOWN!"); @@ -304,6 +305,31 @@ Rectangle { anchors.left: parent.left; anchors.right: parent.right; + Rectangle { + id: loading; + z: 997; + visible: !root.ownershipStatusReceived || !root.balanceReceived; + anchors.fill: parent; + color: Qt.rgba(0.0, 0.0, 0.0, 0.7); + + // This object is always used in a popup. + // This MouseArea is used to prevent a user from being + // able to click on a button/mouseArea underneath the popup/section. + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + propagateComposedEvents: false; + } + + AnimatedImage { + source: "../common/images/loader.gif" + width: 96; + height: width; + anchors.verticalCenter: parent.verticalCenter; + anchors.horizontalCenter: parent.horizontalCenter; + } + } + RalewayRegular { id: confirmPurchaseText; anchors.top: parent.top; @@ -437,7 +463,7 @@ Rectangle { Rectangle { id: buyTextContainer; visible: buyText.text !== ""; - anchors.top: cancelPurchaseButton.bottom; + anchors.top: parent.top; anchors.topMargin: 16; anchors.left: parent.left; anchors.right: parent.right; @@ -480,10 +506,23 @@ Rectangle { // Alignment horizontalAlignment: Text.AlignLeft; verticalAlignment: Text.AlignVCenter; + } + } - onLinkActivated: { - sendToScript({method: 'checkout_goToPurchases', filterText: root.itemName}); - } + // "View in My Purchases" button + HifiControlsUit.Button { + id: viewInMyPurchasesButton; + visible: false; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.light; + anchors.top: buyTextContainer.visible ? buyTextContainer.bottom : checkoutActionButtonsContainer.top; + anchors.topMargin: 16; + height: 40; + anchors.left: parent.left; + anchors.right: parent.right; + text: "VIEW THIS ITEM IN MY PURCHASES"; + onClicked: { + sendToScript({method: 'checkout_goToPurchases', filterText: root.itemName}); } } @@ -491,21 +530,38 @@ Rectangle { HifiControlsUit.Button { id: buyButton; enabled: (root.balanceAfterPurchase >= 0 && ownershipStatusReceived && balanceReceived) || (!root.isCertified); - color: hifi.buttons.blue; + color: viewInMyPurchasesButton.visible ? hifi.buttons.white : hifi.buttons.blue; colorScheme: hifi.colorSchemes.light; - anchors.top: checkoutActionButtonsContainer.top; + anchors.top: viewInMyPurchasesButton.visible ? viewInMyPurchasesButton.bottom : + (buyTextContainer.visible ? buyTextContainer.bottom : checkoutActionButtonsContainer.top); anchors.topMargin: 16; height: 40; anchors.left: parent.left; anchors.right: parent.right; - text: ((root.isCertified) ? ((ownershipStatusReceived && balanceReceived) ? "Confirm Purchase" : "--") : "Get Item"); + text: ((root.isCertified) ? ((ownershipStatusReceived && balanceReceived) ? + (viewInMyPurchasesButton.visible ? "Buy It Again" : "Confirm Purchase") : "--") : "Get Item"); onClicked: { if (root.isCertified) { - buyButton.enabled = false; if (!root.shouldBuyWithControlledFailure) { - Commerce.buy(itemId, itemPrice); + if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { + lightboxPopup.titleText = "Purchase Content Set"; + lightboxPopup.bodyText = "You will not be able to replace this domain's content with " + root.itemName + + " until the server owner gives you 'Replace Content' permissions.

Are you sure you want to purchase this content set?"; + lightboxPopup.button1text = "CANCEL"; + lightboxPopup.button1method = "root.visible = false;" + lightboxPopup.button2text = "CONFIRM"; + lightboxPopup.button2method = "Commerce.buy('" + root.itemId + "', " + root.itemPrice + ");" + + "root.visible = false; buyButton.enabled = false; loading.visible = true;"; + lightboxPopup.visible = true; + } else { + buyButton.enabled = false; + loading.visible = true; + Commerce.buy(root.itemId, root.itemPrice); + } } else { - Commerce.buy(itemId, itemPrice, true); + buyButton.enabled = false; + loading.visible = true; + Commerce.buy(root.itemId, root.itemPrice, true); } } else { if (urlHandler.canHandleUrl(itemHref)) { @@ -618,8 +674,10 @@ Rectangle { // "Rez" button HifiControlsUit.Button { id: rezNowButton; - enabled: root.canRezCertifiedItems || root.itemType === "wearable"; - buttonGlyph: hifi.glyphs.lightning; + enabled: (root.itemType === "entity" && root.canRezCertifiedItems) || + (root.itemType === "contentSet" && Entities.canReplaceContent()) || + root.itemType === "wearable"; + buttonGlyph: (root.buttonGlyph)[itemTypesArray.indexOf(root.itemType)]; color: hifi.buttons.red; colorScheme: hifi.colorSchemes.light; anchors.top: completeText2.bottom; @@ -631,11 +689,17 @@ Rectangle { onClicked: { if (root.itemType === "contentSet") { lightboxPopup.titleText = "Replace Content"; - lightboxPopup.bodyText = "Rezzing this content set will replace the existing environment and all of the items in this domain."; + lightboxPopup.bodyText = "Rezzing this content set will replace the existing environment and all of the items in this domain. " + + "If you want to save the state of the content in this domain, create a backup before proceeding.

" + + "For more information about backing up and restoring content, " + + "
" + + "click here to open info on your desktop browser."; lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = "root.visible = false;" lightboxPopup.button2text = "CONFIRM"; - lightboxPopup.button2method = "sendToScript({method: 'checkout_rezClicked', itemHref: " + root.itemHref + ", itemType: " + root.itemType + "});"; + lightboxPopup.button2method = "Commerce.replaceContentSet('" + root.itemId + "', '" + root.itemHref + "');" + + "root.visible = false;rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start();" + + "UserActivityLogger.commerceEntityRezzed('" + root.itemId + "', 'checkout', '" + root.itemType + "');"; lightboxPopup.visible = true; } else { sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); @@ -919,7 +983,7 @@ Rectangle { itemHref = message.params.itemHref; referrer = message.params.referrer; itemAuthor = message.params.itemAuthor; - setBuyText(); + refreshBuyUI(); break; default: console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message)); @@ -927,13 +991,13 @@ Rectangle { } signal sendToScript(var message); - function setBuyText() { + function refreshBuyUI() { if (root.isCertified) { if (root.ownershipStatusReceived && root.balanceReceived) { if (root.balanceAfterPurchase < 0) { if (root.alreadyOwned) { - buyText.text = "Your Wallet does not have sufficient funds to purchase this item again.
" + - '
View the copy you own in My Purchases'; + buyText.text = "Your Wallet does not have sufficient funds to purchase this item again."; + viewInMyPurchasesButton.visible = true; } else { buyText.text = "Your Wallet does not have sufficient funds to purchase this item."; } @@ -943,15 +1007,19 @@ Rectangle { buyGlyph.size = 54; } else { if (root.alreadyOwned) { - buyText.text = 'You already own this item.
Purchasing it will buy another copy.
View this item in My Purchases
'; - buyTextContainer.color = "#FFD6AD"; - buyTextContainer.border.color = "#FAC07D"; - buyGlyph.text = hifi.glyphs.alert; - buyGlyph.size = 46; + viewInMyPurchasesButton.visible = true; } else { buyText.text = ""; } + + if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { + buyText.text = "The domain owner must enable 'Replace Content' permissions for you in this " + + "domain's server settings before you can replace this domain's content with " + root.itemName + ""; + buyTextContainer.color = "#FFC3CD"; + buyTextContainer.border.color = "#F3808F"; + buyGlyph.text = hifi.glyphs.alert; + buyGlyph.size = 54; + } } } else { buyText.text = ""; @@ -973,7 +1041,7 @@ Rectangle { } root.balanceReceived = false; root.ownershipStatusReceived = false; - Commerce.inventory(); + Commerce.alreadyOwned(root.itemId); Commerce.balance(); } diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index f493747c5e..a622349d00 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -208,6 +208,7 @@ Rectangle { // able to click on a button/mouseArea underneath the popup/section. MouseArea { anchors.fill: parent; + hoverEnabled: true; propagateComposedEvents: false; } diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 9db847eada..d19a16e74e 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -209,6 +209,8 @@ Item { "You do not have 'Replace Content' permissions in this domain. Learn more"; } else if (root.itemType === "entity") { "You do not have 'Rez Certified' permissions in this domain. Learn more"; + } else { + "" } } size: 16; diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index f319881035..dff441f840 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -49,6 +49,7 @@ Handler(balance) Handler(inventory) Handler(transferHfcToNode) Handler(transferHfcToUsername) +Handler(alreadyOwned) void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request) { auto accountManager = DependencyManager::get(); @@ -337,13 +338,11 @@ void Ledger::transferHfcToUsername(const QString& hfc_key, const QString& userna signedSend("transaction", transactionString, hfc_key, "transfer_hfc_to_user", "transferHfcToUsernameSuccess", "transferHfcToUsernameFailure"); } -void Ledger::alreadyOwnedSuccess(QNetworkReply& reply) { apiResponse("alreadyOwned", reply); } -void Ledger::alreadyOwnedFailure(QNetworkReply& reply) { failResponse("alreadyOwned", reply); } void Ledger::alreadyOwned(const QString& marketplaceId) { auto wallet = DependencyManager::get(); QString endpoint = "already_owned"; QJsonObject request; request["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys()); request["marketplace_item_id"] = marketplaceId; - send(endpoint, "alreadyOwnedSuccess", "alreadyOwnedFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::None, request); + send(endpoint, "alreadyOwnedSuccess", "alreadyOwnedFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, request); } From b8fb08dc74d80cd43469338640a579680637fe2e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 15 Feb 2018 11:24:22 -0800 Subject: [PATCH 09/25] Avatar support --- .../qml/hifi/commerce/checkout/Checkout.qml | 15 ++++++++++----- .../qml/hifi/commerce/purchases/PurchasedItem.qml | 2 ++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 981d5c5b9a..916e67e37f 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -40,8 +40,9 @@ Rectangle { property bool isCertified; property string itemType; property var itemTypesArray: ["entity", "wearable", "contentSet", "app", "avatar"]; - property var buttonTextNormal: ["REZ IT", "WEAR IT", "REPLACE CONTENT SET", "INSTALL IT", "WEAR IT"]; - property var buttonTextClicked: ["REZZED", "WORN", "CONTENT SET REPLACED!", "INSTALLED", "WORN"] + property var itemTypesText: ["entity", "wearable", "content set", "app", "avatar"]; + property var buttonTextNormal: ["REZ", "WEAR", "REPLACE CONTENT SET", "INSTALL", "WEAR"]; + property var buttonTextClicked: ["REZZED!", "WORN!", "CONTENT SET REPLACED!", "INSTALLED!", "AVATAR CHANGED!"] property var buttonGlyph: [hifi.glyphs.wand, hifi.glyphs.hat, hifi.glyphs.globe, hifi.glyphs.install, hifi.glyphs.avatar]; property bool shouldBuyWithControlledFailure: false; property bool debugCheckoutSuccess: false; @@ -529,6 +530,7 @@ Rectangle { // "Buy" button HifiControlsUit.Button { id: buyButton; + visible: !(root.itemType === "avatar" && viewInMyPurchasesButton.visible) enabled: (root.balanceAfterPurchase >= 0 && ownershipStatusReceived && balanceReceived) || (!root.isCertified); color: viewInMyPurchasesButton.visible ? hifi.buttons.white : hifi.buttons.blue; colorScheme: hifi.colorSchemes.light; @@ -576,7 +578,7 @@ Rectangle { id: cancelPurchaseButton; color: hifi.buttons.noneBorderlessGray; colorScheme: hifi.colorSchemes.light; - anchors.top: buyButton.bottom; + anchors.top: buyButton.visible ? buyButton.bottom : viewInMyPurchasesButton.bottom; anchors.topMargin: 16; height: 40; anchors.left: parent.left; @@ -622,8 +624,9 @@ Rectangle { RalewaySemiBold { id: completeText2; - text: "The item " + '' + root.itemName + '' + - " has been added to your Purchases and a receipt will appear in your Wallet's transaction history."; + text: "The " + (root.itemTypesText)[itemTypesArray.indexOf(root.itemType)] + + '' + root.itemName + '' + + " has been added to your Purchases and a receipt will appear in your Wallet's transaction history."; // Text size size: 20; // Anchors @@ -701,6 +704,8 @@ Rectangle { "root.visible = false;rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start();" + "UserActivityLogger.commerceEntityRezzed('" + root.itemId + "', 'checkout', '" + root.itemType + "');"; lightboxPopup.visible = true; + } else if (root.itemType === "avatar") { + Avatar.skeletonModelURL = root.itemHref; } else { sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); rezzedNotifContainer.visible = true; diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index d19a16e74e..7b2cae5188 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -476,6 +476,8 @@ Item { onClicked: { if (root.itemType === "contentSet") { sendToPurchases({method: 'showReplaceContentLightbox', itemId: root.itemId, itemHref: root.itemHref}); + } else if (root.itemType === "avatar") { + Avatar.skeletonModelURL = root.itemHref; } else { sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); root.showConfirmation = true; From 807237282263e941235ab48b525a4079c712259d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 15 Feb 2018 13:14:56 -0800 Subject: [PATCH 10/25] Lots of changes --- .../resources/qml/controls-uit/Separator.qml | 4 +- .../qml/hifi/commerce/checkout/Checkout.qml | 69 ++++++++++--------- .../hifi/commerce/purchases/PurchasedItem.qml | 62 +++++++++++++---- .../qml/hifi/commerce/purchases/Purchases.qml | 16 +++-- interface/src/commerce/QmlCommerce.cpp | 8 +-- interface/src/commerce/QmlCommerce.h | 4 +- libraries/avatars/src/AvatarData.h | 3 +- libraries/avatars/src/ScriptAvatarData.cpp | 1 + libraries/avatars/src/ScriptAvatarData.h | 3 +- 9 files changed, 109 insertions(+), 61 deletions(-) diff --git a/interface/resources/qml/controls-uit/Separator.qml b/interface/resources/qml/controls-uit/Separator.qml index 5e2d278454..3350764ae9 100644 --- a/interface/resources/qml/controls-uit/Separator.qml +++ b/interface/resources/qml/controls-uit/Separator.qml @@ -14,8 +14,8 @@ import "../styles-uit" Item { property int colorScheme: 0; - readonly property var topColor: [ hifi.colors.baseGrayShadow, hifi.colors.faintGray ]; - readonly property var bottomColor: [ hifi.colors.baseGrayHighlight, hifi.colors.faintGray ]; + readonly property var topColor: [ hifi.colors.baseGrayShadow, hifi.colors.faintGray, "#89858C" ]; + readonly property var bottomColor: [ hifi.colors.baseGrayHighlight, hifi.colors.faintGray, "#89858C" ]; // Size height: colorScheme === 0 ? 2 : 1; diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 916e67e37f..08b100a64c 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -339,8 +339,8 @@ Rectangle { anchors.leftMargin: 16; width: paintedWidth; height: paintedHeight; - text: "Confirm Purchase:"; - color: hifi.colors.baseGray; + text: "Review Purchase:"; + color: hifi.colors.black; size: 28; } @@ -453,7 +453,7 @@ Rectangle { width: root.width; // Anchors anchors.top: separator2.bottom; - anchors.topMargin: 16; + anchors.topMargin: 0; anchors.left: parent.left; anchors.leftMargin: 16; anchors.right: parent.right; @@ -465,7 +465,7 @@ Rectangle { id: buyTextContainer; visible: buyText.text !== ""; anchors.top: parent.top; - anchors.topMargin: 16; + anchors.topMargin: 10; anchors.left: parent.left; anchors.right: parent.right; height: buyText.height + 30; @@ -517,8 +517,8 @@ Rectangle { color: hifi.buttons.blue; colorScheme: hifi.colorSchemes.light; anchors.top: buyTextContainer.visible ? buyTextContainer.bottom : checkoutActionButtonsContainer.top; - anchors.topMargin: 16; - height: 40; + anchors.topMargin: 10; + height: 50; anchors.left: parent.left; anchors.right: parent.right; text: "VIEW THIS ITEM IN MY PURCHASES"; @@ -536,8 +536,8 @@ Rectangle { colorScheme: hifi.colorSchemes.light; anchors.top: viewInMyPurchasesButton.visible ? viewInMyPurchasesButton.bottom : (buyTextContainer.visible ? buyTextContainer.bottom : checkoutActionButtonsContainer.top); - anchors.topMargin: 16; - height: 40; + anchors.topMargin: 10; + height: 50; anchors.left: parent.left; anchors.right: parent.right; text: ((root.isCertified) ? ((ownershipStatusReceived && balanceReceived) ? @@ -579,8 +579,8 @@ Rectangle { color: hifi.buttons.noneBorderlessGray; colorScheme: hifi.colorSchemes.light; anchors.top: buyButton.visible ? buyButton.bottom : viewInMyPurchasesButton.bottom; - anchors.topMargin: 16; - height: 40; + anchors.topMargin: 10; + height: 50; anchors.left: parent.left; anchors.right: parent.right; text: "Cancel" @@ -606,32 +606,32 @@ Rectangle { anchors.top: titleBarContainer.bottom; anchors.bottom: root.bottom; anchors.left: parent.left; - anchors.leftMargin: 16; + anchors.leftMargin: 20; anchors.right: parent.right; - anchors.rightMargin: 16; + anchors.rightMargin: 20; RalewayRegular { id: completeText; anchors.top: parent.top; - anchors.topMargin: 30; + anchors.topMargin: 18; anchors.left: parent.left; width: paintedWidth; height: paintedHeight; text: "Thank you for your order!"; color: hifi.colors.baseGray; - size: 28; + size: 36; } RalewaySemiBold { id: completeText2; text: "The " + (root.itemTypesText)[itemTypesArray.indexOf(root.itemType)] + - '' + root.itemName + '' + + ' ' + root.itemName + '' + " has been added to your Purchases and a receipt will appear in your Wallet's transaction history."; // Text size - size: 20; + size: 18; // Anchors anchors.top: completeText.bottom; - anchors.topMargin: 10; + anchors.topMargin: 15; height: paintedHeight; anchors.left: parent.left; anchors.right: parent.right; @@ -684,7 +684,7 @@ Rectangle { color: hifi.buttons.red; colorScheme: hifi.colorSchemes.light; anchors.top: completeText2.bottom; - anchors.topMargin: 30; + anchors.topMargin: 27; height: 50; anchors.left: parent.left; anchors.right: parent.right; @@ -700,12 +700,18 @@ Rectangle { lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = "root.visible = false;" lightboxPopup.button2text = "CONFIRM"; - lightboxPopup.button2method = "Commerce.replaceContentSet('" + root.itemId + "', '" + root.itemHref + "');" + + lightboxPopup.button2method = "Commerce.replaceContentSet('" + root.itemHref + "');" + "root.visible = false;rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start();" + "UserActivityLogger.commerceEntityRezzed('" + root.itemId + "', 'checkout', '" + root.itemType + "');"; lightboxPopup.visible = true; } else if (root.itemType === "avatar") { - Avatar.skeletonModelURL = root.itemHref; + lightboxPopup.titleText = "Change Avatar"; + lightboxPopup.bodyText = "This will change your current avatar to " + root.itemName + " while retaining your wearables."; + lightboxPopup.button1text = "CANCEL"; + lightboxPopup.button1method = "root.visible = false;" + lightboxPopup.button2text = "CONFIRM"; + lightboxPopup.button2method = "Avatar.skeletonModelURL('" + root.itemHref + "'); root.visible = false;"; + lightboxPopup.visible = true; } else { sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); rezzedNotifContainer.visible = true; @@ -768,9 +774,9 @@ Rectangle { RalewaySemiBold { id: myPurchasesLink; - text: 'View this item in My Purchases'; + text: 'View this item in My Purchases'; // Text size - size: 20; + size: 18; // Anchors anchors.top: explainRezText.visible ? explainRezText.bottom : (noPermissionText.visible ? noPermissionText.bottom : rezNowButton.bottom); anchors.topMargin: 40; @@ -790,12 +796,12 @@ Rectangle { RalewaySemiBold { id: walletLink; - text: 'View receipt in Wallet'; + text: 'View receipt in Wallet'; // Text size - size: 20; + size: 18; // Anchors anchors.top: myPurchasesLink.bottom; - anchors.topMargin: 20; + anchors.topMargin: 16; height: paintedHeight; anchors.left: parent.left; anchors.right: parent.right; @@ -813,12 +819,12 @@ Rectangle { RalewayRegular { id: pendingText; text: 'Your item is marked "pending" while your purchase is being confirmed. ' + - 'Learn More'; + 'Learn More'; // Text size - size: 20; + size: 18; // Anchors anchors.top: walletLink.bottom; - anchors.topMargin: 60; + anchors.topMargin: 32; height: paintedHeight; anchors.left: parent.left; anchors.right: parent.right; @@ -844,11 +850,10 @@ Rectangle { color: hifi.buttons.noneBorderlessGray; colorScheme: hifi.colorSchemes.light; anchors.bottom: parent.bottom; - anchors.bottomMargin: 20; + anchors.bottomMargin: 54; anchors.right: parent.right; - anchors.rightMargin: 14; - width: parent.width/2 - anchors.rightMargin; - height: 60; + width: 193; + height: 44; text: "Continue Shopping"; onClicked: { sendToScript({method: 'checkout_continueShopping', itemId: itemId}); diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 7b2cae5188..be460ea4ef 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -20,6 +20,7 @@ import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls import "../wallet" as HifiWallet +import TabletScriptingInterface 1.0 // references XXX from root context @@ -56,7 +57,17 @@ Item { target: Commerce; onContentSetChanged: { - if (contentSetMarketplaceID === root.itemId) { + if (contentSetHref === root.itemHref) { + showConfirmation = true; + } + } + } + + Connections { + target: MyAvatar; + + onSkeletonModelURLChanged: { + if (skeletonModelURL === root.itemHref) { showConfirmation = true; } } @@ -275,19 +286,19 @@ Item { anchors.bottom: parent.bottom; width: 32; // Style - color: hifi.colors.lightGray; + color: hifi.colors.black; } RalewayRegular { id: viewCertificateText; text: "VIEW CERTIFICATE"; - size: 14; + size: 13; anchors.left: certificateIcon.right; anchors.leftMargin: 4; anchors.top: parent.top; anchors.bottom: parent.bottom; anchors.right: parent.right; - color: hifi.colors.lightGray; + color: hifi.colors.black; } MouseArea { @@ -297,13 +308,13 @@ Item { sendToPurchases({method: 'purchases_itemCertificateClicked', itemCertificateId: root.certificateId}); } onEntered: { - certificateIcon.color = hifi.colors.black; - viewCertificateText.color = hifi.colors.black; - } - onExited: { certificateIcon.color = hifi.colors.lightGray; viewCertificateText.color = hifi.colors.lightGray; } + onExited: { + certificateIcon.color = hifi.colors.black; + viewCertificateText.color = hifi.colors.black; + } } } @@ -317,14 +328,14 @@ Item { anchors.right: buttonContainer.left; anchors.rightMargin: 2; - FiraSansRegular { + RalewayRegular { anchors.left: parent.left; anchors.top: parent.top; anchors.bottom: parent.bottom; width: paintedWidth; text: "#" + root.itemEdition; - size: 15; - color: "#cc6a6a6a"; + size: 13; + color: hifi.colors.black; verticalAlignment: Text.AlignTop; } } @@ -471,13 +482,28 @@ Item { anchors.right: parent.right; anchors.rightMargin: 4; width: height; - enabled: root.hasPermissionToRezThis && root.purchaseStatus !== "invalidated"; + enabled: root.hasPermissionToRezThis && + root.purchaseStatus !== "invalidated" && + MyAvatar.skeletonModelURL !== root.itemHref; + + onHoveredChanged: { + if (hovered) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + + onFocusChanged: { + if (focus) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); if (root.itemType === "contentSet") { - sendToPurchases({method: 'showReplaceContentLightbox', itemId: root.itemId, itemHref: root.itemHref}); + sendToPurchases({method: 'showReplaceContentLightbox', itemHref: root.itemHref}); } else if (root.itemType === "avatar") { - Avatar.skeletonModelURL = root.itemHref; + sendToPurchases({method: 'showChangeAvatarLightbox', itemName: root.itemName, itemHref: root.itemHref}); } else { sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); root.showConfirmation = true; @@ -547,7 +573,13 @@ Item { size: 15; verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - text: (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)] + text: { + if (MyAvatar.skeletonModelURL === root.itemHref) { + "CURRENT"; + } else { + (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)]; + } + } } } } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 91fed207bc..df65a0a1ac 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -300,7 +300,7 @@ Rectangle { anchors.left: parent.left; anchors.leftMargin: 8; anchors.right: parent.right; - anchors.rightMargin: 12; + anchors.rightMargin: 16; anchors.top: parent.top; anchors.topMargin: 4; @@ -314,7 +314,7 @@ Rectangle { anchors.leftMargin: 16; width: paintedWidth; text: isShowingMyItems ? "My Items" : "My Purchases"; - color: hifi.colors.baseGray; + color: hifi.colors.black; size: 22; } @@ -348,7 +348,7 @@ Rectangle { HifiControlsUit.Separator { id: separator; - colorScheme: 1; + colorScheme: 2; anchors.left: parent.left; anchors.right: parent.right; anchors.top: filterBarContainer.bottom; @@ -445,7 +445,15 @@ Rectangle { lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = "root.visible = false;" lightboxPopup.button2text = "CONFIRM"; - lightboxPopup.button2method = "Commerce.replaceContentSet('" + msg.itemId + "', '" + msg.itemHref + "'); root.visible = false;"; + lightboxPopup.button2method = "Commerce.replaceContentSet('" + msg.itemHref + "'); root.visible = false;"; + lightboxPopup.visible = true; + } else if (msg.method === "showChangeAvatarLightbox") { + lightboxPopup.titleText = "Change Avatar"; + lightboxPopup.bodyText = "This will change your current avatar to " + msg.itemName + " while retaining your wearables."; + lightboxPopup.button1text = "CANCEL"; + lightboxPopup.button1method = "root.visible = false;" + lightboxPopup.button2text = "CONFIRM"; + lightboxPopup.button2method = "MyAvatar.skeletonModelURL('" + msg.itemHref + "'); root.visible = false;"; lightboxPopup.visible = true; } else if (msg.method === "showPermissionsExplanation") { if (msg.itemType === "entity") { diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 5458925d55..36c1e422c5 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -168,15 +168,15 @@ void QmlCommerce::transferHfcToUsername(const QString& username, const int& amou ledger->transferHfcToUsername(key, username, amount, optionalMessage); } -void QmlCommerce::replaceContentSet(const QString& id, const QString& url) { - qApp->replaceDomainContent(url); +void QmlCommerce::replaceContentSet(const QString& itemHref) { + qApp->replaceDomainContent(itemHref); QJsonObject messageProperties = { { "status", "SuccessfulRequestToReplaceContent" }, - { "content_set_url", url } + { "content_set_url", itemHref } }; UserActivityLogger::getInstance().logAction("replace_domain_content", messageProperties); - emit contentSetChanged(id); + emit contentSetChanged(itemHref); } void QmlCommerce::alreadyOwned(const QString& marketplaceId) { diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index dc38dcad19..b621608190 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -49,7 +49,7 @@ signals: void transferHfcToNodeResult(QJsonObject result); void transferHfcToUsernameResult(QJsonObject result); - void contentSetChanged(const QString& contentSetMarketplaceID); + void contentSetChanged(const QString& contentSetHref); protected: Q_INVOKABLE void getWalletStatus(); @@ -77,7 +77,7 @@ protected: Q_INVOKABLE void transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage); - Q_INVOKABLE void replaceContentSet(const QString& id, const QString& url); + Q_INVOKABLE void replaceContentSet(const QString& itemHref); }; #endif // hifi_QmlCommerce_h diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index f24bd51bde..6d9a13decf 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -371,7 +371,7 @@ class AvatarData : public QObject, public SpatiallyNestable { // The result is unique among all avatars present at the time. Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName WRITE setSessionDisplayName NOTIFY sessionDisplayNameChanged) Q_PROPERTY(bool lookAtSnappingEnabled MEMBER _lookAtSnappingEnabled NOTIFY lookAtSnappingChanged) - Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript) + Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript NOTIFY skeletonModelURLChanged) Q_PROPERTY(QVector attachmentData READ getAttachmentData WRITE setAttachmentData) Q_PROPERTY(QStringList jointNames READ getJointNames) @@ -697,6 +697,7 @@ public: signals: void displayNameChanged(); void sessionDisplayNameChanged(); + void skeletonModelURLChanged(); void lookAtSnappingChanged(bool enabled); void sessionUUIDChanged(); diff --git a/libraries/avatars/src/ScriptAvatarData.cpp b/libraries/avatars/src/ScriptAvatarData.cpp index 1fd001e536..8491e5368b 100644 --- a/libraries/avatars/src/ScriptAvatarData.cpp +++ b/libraries/avatars/src/ScriptAvatarData.cpp @@ -16,6 +16,7 @@ ScriptAvatarData::ScriptAvatarData(AvatarSharedPointer avatarData) : { QObject::connect(avatarData.get(), &AvatarData::displayNameChanged, this, &ScriptAvatarData::displayNameChanged); QObject::connect(avatarData.get(), &AvatarData::sessionDisplayNameChanged, this, &ScriptAvatarData::sessionDisplayNameChanged); + QObject::connect(avatarData.get(), &AvatarData::skeletonModelURLChanged, this, &ScriptAvatarData::skeletonModelURLChanged); QObject::connect(avatarData.get(), &AvatarData::lookAtSnappingChanged, this, &ScriptAvatarData::lookAtSnappingChanged); } diff --git a/libraries/avatars/src/ScriptAvatarData.h b/libraries/avatars/src/ScriptAvatarData.h index 68074b838d..13713ff15f 100644 --- a/libraries/avatars/src/ScriptAvatarData.h +++ b/libraries/avatars/src/ScriptAvatarData.h @@ -51,7 +51,7 @@ class ScriptAvatarData : public QObject { // // ATTACHMENT AND JOINT PROPERTIES // - Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript) + Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript NOTIFY skeletonModelURLChanged) Q_PROPERTY(QVector attachmentData READ getAttachmentData) Q_PROPERTY(QStringList jointNames READ getJointNames) @@ -132,6 +132,7 @@ public: signals: void displayNameChanged(); void sessionDisplayNameChanged(); + void skeletonModelURLChanged(); void lookAtSnappingChanged(bool enabled); public slots: From 811ecf9db8341428ecbf5bba66c7e7a87aeb61ee Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 15 Feb 2018 14:11:01 -0800 Subject: [PATCH 11/25] Fix --- .../resources/qml/hifi/commerce/checkout/Checkout.qml | 6 +++--- .../qml/hifi/commerce/purchases/PurchasedItem.qml | 5 +++-- .../resources/qml/hifi/commerce/purchases/Purchases.qml | 2 +- scripts/system/marketplaces/marketplaces.js | 8 ++++---- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 08b100a64c..891c8142bf 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -146,7 +146,7 @@ Rectangle { } onItemTypeChanged: { - if (root.itemType === "entity" || root.itemType === "wearable" || root.itemType === "contentSet") { + if (root.itemType === "entity" || root.itemType === "wearable" || root.itemType === "contentSet" || root.itemType === "avatar") { root.isCertified = true; } else { root.isCertified = false; @@ -679,7 +679,7 @@ Rectangle { id: rezNowButton; enabled: (root.itemType === "entity" && root.canRezCertifiedItems) || (root.itemType === "contentSet" && Entities.canReplaceContent()) || - root.itemType === "wearable"; + root.itemType === "wearable" || root.itemType === "avatar"; buttonGlyph: (root.buttonGlyph)[itemTypesArray.indexOf(root.itemType)]; color: hifi.buttons.red; colorScheme: hifi.colorSchemes.light; @@ -710,7 +710,7 @@ Rectangle { lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = "root.visible = false;" lightboxPopup.button2text = "CONFIRM"; - lightboxPopup.button2method = "Avatar.skeletonModelURL('" + root.itemHref + "'); root.visible = false;"; + lightboxPopup.button2method = "MyAvatar.useFullAvatarURL('" + root.itemHref + "'); root.visible = false;"; lightboxPopup.visible = true; } else { sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index be460ea4ef..50ac34cd51 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -151,8 +151,9 @@ Item { anchors.topMargin: 4; anchors.left: itemPreviewImage.right; anchors.leftMargin: 8; - width: root.hasPermissionToRezThis ? (buttonContainer.x - itemPreviewImage.x - itemPreviewImage.width) : - Math.min(itemNameTextMetrics.tightBoundingRect.width + 2, buttonContainer.x - itemPreviewImage.x - itemPreviewImage.width - noPermissionGlyph.width + 2); + width: root.hasPermissionToRezThis ? (buttonContainer.x - itemPreviewImage.x - itemPreviewImage.width - anchors.leftMargin) : + Math.min(itemNameTextMetrics.tightBoundingRect.width + 2, + buttonContainer.x - itemPreviewImage.x - itemPreviewImage.width - anchors.leftMargin - noPermissionGlyph.width + 2); height: paintedHeight; // Text size size: 24; diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index df65a0a1ac..5593883e00 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -453,7 +453,7 @@ Rectangle { lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = "root.visible = false;" lightboxPopup.button2text = "CONFIRM"; - lightboxPopup.button2method = "MyAvatar.skeletonModelURL('" + msg.itemHref + "'); root.visible = false;"; + lightboxPopup.button2method = "MyAvatar.useFullAvatarURL('" + msg.itemHref + "'); root.visible = false;"; lightboxPopup.visible = true; } else if (msg.method === "showPermissionsExplanation") { if (msg.itemType === "entity") { diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 95d9294063..c61272119d 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -75,10 +75,10 @@ var selectionDisplay = null; // for gridTool.js to ignore tablet.pushOntoStack(MARKETPLACE_CHECKOUT_QML_PATH); tablet.sendToQml({ method: 'updateCheckoutQML', params: { - itemId: 'e197e3d7-eafc-4aa5-9341-acee57174fe9', - itemName: 'Oasis', - itemPrice: (debugError ? 10 : 11), - itemHref: 'http://mpassets-staging.highfidelity.com/e197e3d7-eafc-4aa5-9341-acee57174fe9-v1/oasis_Aug15.json.gz', + itemId: '424611a2-73d0-4c03-9087-26a6a279257b', + itemName: '2018-02-15 Finnegon', + itemPrice: (debugError ? 10 : 3), + itemHref: 'http://devmpassets.highfidelity.com/424611a2-73d0-4c03-9087-26a6a279257b-v1/finnigon.fst', categories: ["Miscellaneous"] } }); From c4e9c762582b027553d4113e72dcabafb9cb69f5 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 15 Feb 2018 15:14:54 -0800 Subject: [PATCH 12/25] Small UIT tweaks --- interface/resources/qml/controls-uit/TextField.qml | 5 ++--- .../qml/hifi/commerce/purchases/PurchasedItem.qml | 11 ++++++----- .../qml/hifi/commerce/purchases/Purchases.qml | 10 ++++++++++ interface/resources/qml/styles-uit/HifiConstants.qml | 2 +- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index a21d1f8efd..3fc5d83129 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -34,11 +34,10 @@ TextField { placeholderText: textField.placeholderText - FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } + FontLoader { id: firaSansRegular; source: "../../fonts/FiraSans-Regular.ttf"; } FontLoader { id: hifiGlyphs; source: "../../fonts/hifi-glyphs.ttf"; } - font.family: firaSansSemiBold.name + font.family: firaSansRegular.name font.pixelSize: hifi.fontSizes.textFieldInput - font.italic: textField.text == "" height: implicitHeight + 3 // Make surrounding box higher so that highlight is vertically centered. property alias textFieldLabel: textFieldLabel diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 50ac34cd51..272c542fde 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -46,6 +46,7 @@ Item { property var buttonGlyph: [hifi.glyphs.wand, hifi.glyphs.hat, hifi.glyphs.globe, hifi.glyphs.install, hifi.glyphs.avatar]; property bool showConfirmation: false; property bool hasPermissionToRezThis; + property bool permissionExplanationCardVisible; property string originalStatusText; property string originalStatusColor; @@ -151,7 +152,7 @@ Item { anchors.topMargin: 4; anchors.left: itemPreviewImage.right; anchors.leftMargin: 8; - width: root.hasPermissionToRezThis ? (buttonContainer.x - itemPreviewImage.x - itemPreviewImage.width - anchors.leftMargin) : + width: !noPermissionGlyph.visible ? (buttonContainer.x - itemPreviewImage.x - itemPreviewImage.width - anchors.leftMargin) : Math.min(itemNameTextMetrics.tightBoundingRect.width + 2, buttonContainer.x - itemPreviewImage.x - itemPreviewImage.width - anchors.leftMargin - noPermissionGlyph.width + 2); height: paintedHeight; @@ -181,7 +182,7 @@ Item { } HiFiGlyphs { id: noPermissionGlyph; - visible: !root.hasPermissionToRezThis; + visible: true// !root.hasPermissionToRezThis; anchors.verticalCenter: itemName.verticalCenter; anchors.left: itemName.right; anchors.leftMargin: itemName.truncated ? -14 : -2; @@ -203,14 +204,14 @@ Item { noPermissionGlyph.color = hifi.colors.redAccent; } onClicked: { - permissionExplanationCard.visible = true; + root.sendToPurchases({ method: 'openPermissionExplanationCard' }); } } } Rectangle { id: permissionExplanationCard; z: 995; - visible: false; + visible: root.permissionExplanationCardVisible; anchors.fill: parent; color: hifi.colors.white; @@ -261,7 +262,7 @@ Item { parent.text = hifi.glyphs.close; } onClicked: { - permissionExplanationCard.visible = false; + root.sendToPurchases({ method: 'openPermissionExplanationCard', closeAll: true }); } } } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 5593883e00..b01dfcfce3 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -393,6 +393,7 @@ Rectangle { numberSold: model.number_sold; limitedRun: model.limited_run; displayedItemCount: model.displayedItemCount; + permissionExplanationCardVisible: model.permissionExplanationCardVisible; itemType: { if (model.root_file_url.indexOf(".fst") > -1) { "avatar"; @@ -472,6 +473,14 @@ Rectangle { lightboxPopup.visible = true; } else if (msg.method === "setFilterText") { filterBar.text = msg.filterText; + } else if (msg.method === "openPermissionExplanationCard") { + for (var i = 0; i < filteredPurchasesModel.count; i++) { + if (i !== index || msg.closeAll) { + filteredPurchasesModel.setProperty(i, "permissionExplanationCardVisible", false); + } else { + filteredPurchasesModel.setProperty(i, "permissionExplanationCardVisible", true); + } + } } } } @@ -673,6 +682,7 @@ Rectangle { filteredPurchasesModel.clear(); for (var i = 0; i < tempPurchasesModel.count; i++) { filteredPurchasesModel.append(tempPurchasesModel.get(i)); + filteredPurchasesModel.setProperty(i, 'permissionExplanationCardVisible', false); } populateDisplayedItemCounts(); diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index 16b74f6b54..43de8333af 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -218,7 +218,7 @@ Item { readonly property var colorStart: [ colors.white, colors.primaryHighlight, "#d42043", "#343434", Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0) ] readonly property var colorFinish: [ colors.lightGrayText, colors.blueAccent, "#94132e", colors.black, Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0) ] readonly property var hoveredColor: [ colorStart[white], colorStart[blue], colorStart[red], colorFinish[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] - readonly property var pressedColor: [ colorFinish[white], colorFinish[blue], colorFinish[red], colorStart[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] + readonly property var pressedColor: [ colorFinish[white], colorFinish[blue], colorFinish[red], colorStart[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colors.lightGrayText ] readonly property var focusedColor: [ colors.lightGray50, colors.blueAccent, colors.redAccent, colors.darkGray, colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] readonly property var disabledColorStart: [ colorStart[white], colors.baseGrayHighlight] readonly property var disabledColorFinish: [ colorFinish[white], colors.baseGrayShadow] From 11f897beb098e0dd0464370ab34bb09adcc1fc01 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 20 Feb 2018 10:39:36 -0800 Subject: [PATCH 13/25] Initial app support --- interface/resources/qml/hifi/commerce/checkout/Checkout.qml | 2 +- scripts/system/marketplaces/marketplaces.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 891c8142bf..bf73decf9b 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -530,7 +530,7 @@ Rectangle { // "Buy" button HifiControlsUit.Button { id: buyButton; - visible: !(root.itemType === "avatar" && viewInMyPurchasesButton.visible) + visible: !((root.itemType === "avatar" || root.itemType === "app") && viewInMyPurchasesButton.visible) enabled: (root.balanceAfterPurchase >= 0 && ownershipStatusReceived && balanceReceived) || (!root.isCertified); color: viewInMyPurchasesButton.visible ? hifi.buttons.white : hifi.buttons.blue; colorScheme: hifi.colorSchemes.light; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index c61272119d..45085b2d45 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -550,7 +550,11 @@ var selectionDisplay = null; // for gridTool.js to ignore break; case 'checkout_rezClicked': case 'purchases_rezClicked': - rezEntity(message.itemHref, message.itemType); + if (message.itemType === "app") { + + } else { + rezEntity(message.itemHref, message.itemType); + } break; case 'header_marketplaceImageClicked': case 'purchases_backClicked': From f08c66e3ac9b8538a30054cc86838806afb9f19c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 20 Feb 2018 14:14:59 -0800 Subject: [PATCH 14/25] Remove debug code --- .../resources/qml/hifi/commerce/purchases/PurchasedItem.qml | 2 +- scripts/system/commerce/wallet.js | 2 +- scripts/system/marketplaces/marketplaces.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 272c542fde..3a414acbb5 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -182,7 +182,7 @@ Item { } HiFiGlyphs { id: noPermissionGlyph; - visible: true// !root.hasPermissionToRezThis; + visible: !root.hasPermissionToRezThis; anchors.verticalCenter: itemName.verticalCenter; anchors.left: itemName.right; anchors.leftMargin: itemName.truncated ? -14 : -2; diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 9ff7038c09..8cf5b72b9a 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -39,7 +39,7 @@ // for toolbar-mode: go back to home screen, this will close the window. tablet.gotoHomeScreen(); } else { - tablet.loadQMLSource(MARKETPLACE_PURCHASES_QML_PATH); + tablet.loadQMLSource(WALLET_QML_SOURCE); } } diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 45085b2d45..8c41906d77 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -65,7 +65,7 @@ var selectionDisplay = null; // for gridTool.js to ignore var onMarketplaceScreen = false; var onCommerceScreen = false; - var debugCheckout = true; + var debugCheckout = false; var debugError = false; function showMarketplace() { if (!debugCheckout) { From 16ffcc7fe2696c10ec5429a07de14f0dcc4b3bca Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 21 Feb 2018 09:48:15 -0800 Subject: [PATCH 15/25] Add #include of Forward.h to AudioMixer.h This makes the compile by Qt MOC less dependent on order of processing. See Manuscript case 12527. --- assignment-client/src/audio/AudioMixer.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index ed3a5b0541..12ae3422bc 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -18,6 +18,8 @@ #include #include +#include "Plugins/Forward.h" + #include "AudioMixerStats.h" #include "AudioMixerSlavePool.h" From 1fbc16aae224f5e9aa62a74690ec01b44e60c2b3 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 21 Feb 2018 10:52:17 -0800 Subject: [PATCH 16/25] Fix noPermissionGlyph margin --- .../resources/qml/hifi/commerce/purchases/PurchasedItem.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 3a414acbb5..5b70fdbde9 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -185,7 +185,7 @@ Item { visible: !root.hasPermissionToRezThis; anchors.verticalCenter: itemName.verticalCenter; anchors.left: itemName.right; - anchors.leftMargin: itemName.truncated ? -14 : -2; + anchors.leftMargin: itemName.truncated ? -10 : -2; text: hifi.glyphs.info; // Size size: 40; From 619901caa01d4ee3994f34563dc15a712590f58c Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 21 Feb 2018 11:12:09 -0800 Subject: [PATCH 17/25] Move up main #include for other AudioMixer* Having #include first will make more robust for MOC compile problems. Also fixes previous commit to use <> form and correct case. See Case 12527. --- assignment-client/src/audio/AudioMixer.cpp | 4 ++-- assignment-client/src/audio/AudioMixer.h | 2 +- assignment-client/src/audio/AudioMixerClientData.cpp | 4 ++-- assignment-client/src/audio/AudioMixerClientData.h | 1 + assignment-client/src/audio/AudioMixerSlave.cpp | 4 ++-- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 7f088d8183..8af4eec934 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "AudioMixer.h" + #include #include @@ -36,8 +38,6 @@ #include "AvatarAudioStream.h" #include "InjectedAudioStream.h" -#include "AudioMixer.h" - static const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.5f; // attenuation = -6dB * log2(distance) static const int DISABLE_STATIC_JITTER_FRAMES = -1; static const float DEFAULT_NOISE_MUTING_THRESHOLD = 1.0f; diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 12ae3422bc..8c47893aa3 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -18,7 +18,7 @@ #include #include -#include "Plugins/Forward.h" +#include #include "AudioMixerStats.h" #include "AudioMixerSlavePool.h" diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 2560f43337..d4d4f847ee 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "AudioMixerClientData.h" + #include #include @@ -22,8 +24,6 @@ #include "AudioLogging.h" #include "AudioHelpers.h" #include "AudioMixer.h" -#include "AudioMixerClientData.h" - AudioMixerClientData::AudioMixerClientData(const QUuid& nodeID) : NodeData(nodeID), diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index c3a31715ea..514e1c9756 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -21,6 +21,7 @@ #include #include +#include #include #include "PositionalAudioStream.h" diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index abc2f7220d..b975ba8bfe 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "AudioMixerSlave.h" + #include #include @@ -34,8 +36,6 @@ #include "InjectedAudioStream.h" #include "AudioHelpers.h" -#include "AudioMixerSlave.h" - using AudioStreamMap = AudioMixerClientData::AudioStreamMap; // packet helpers From b548567f5acf5170c7dc6f26a136d42552a28ce5 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 21 Feb 2018 11:17:47 -0800 Subject: [PATCH 18/25] Apps protection --- interface/resources/qml/hifi/commerce/checkout/Checkout.qml | 4 ++-- interface/resources/qml/hifi/commerce/purchases/Purchases.qml | 4 ++-- scripts/system/marketplaces/marketplaces.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index bf73decf9b..27acb121ca 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -135,10 +135,10 @@ Rectangle { root.itemType = "avatar"; } else if (root.itemHref.indexOf('.json.gz') > -1) { root.itemType = "contentSet"; + } else if (root.itemHref.indexOf('.app.json') > -1) { + root.itemType = "app"; } else if (root.itemHref.indexOf('.json') > -1) { root.itemType = "entity"; // "wearable" type handled later - } else if (root.itemHref.indexOf('.js') > -1) { - root.itemType = "app"; } else { console.log("WARNING - Item type is UNKNOWN!"); root.itemType = "entity"; diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index b01dfcfce3..9b333a60cd 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -401,10 +401,10 @@ Rectangle { "wearable"; } else if (model.root_file_url.endsWith('.json.gz')) { "contentSet"; + } else if (model.root_file_url.endsWith('.app.json')) { + "app"; } else if (model.root_file_url.endsWith('.json')) { "entity"; - } else if (model.root_file_url.endsWith('.js')) { - "app"; } else { "unknown"; } diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 8c41906d77..1eb16cf453 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -551,7 +551,7 @@ var selectionDisplay = null; // for gridTool.js to ignore case 'checkout_rezClicked': case 'purchases_rezClicked': if (message.itemType === "app") { - + console.log("How did you get here? You can't buy apps yet!"); } else { rezEntity(message.itemHref, message.itemType); } From 74655bde49b878f81e2707af08b871702911ff7a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 21 Feb 2018 12:12:20 -0800 Subject: [PATCH 19/25] Fix skeletonmodelURLchanged signal --- .../qml/hifi/commerce/purchases/PurchasedItem.qml | 9 ++------- interface/src/avatar/MyAvatar.cpp | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 5b70fdbde9..cc2bcd69aa 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -564,6 +564,7 @@ Item { : hifi.buttons.disabledTextColor[control.colorScheme] } RalewayBold { + id: rezIconLabel; anchors.top: rezIcon.bottom; anchors.topMargin: -4; anchors.right: parent.right; @@ -575,13 +576,7 @@ Item { size: 15; verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - text: { - if (MyAvatar.skeletonModelURL === root.itemHref) { - "CURRENT"; - } else { - (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)]; - } - } + text: MyAvatar.skeletonModelURL === root.itemHref ? "CURRENT" : (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)]; } } } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c25aaeeecd..50753724e8 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1479,7 +1479,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { }); saveAvatarUrl(); emit skeletonChanged(); - + emit skeletonModelURLChanged(); } void MyAvatar::removeAvatarEntities() { From 3d4e6de6abb4d638d844478f68e4adba3b686cbf Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 22 Feb 2018 10:49:11 -0800 Subject: [PATCH 20/25] Quick bugfix --- interface/resources/qml/hifi/commerce/checkout/Checkout.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 27acb121ca..ab47bb28ad 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -722,7 +722,7 @@ Rectangle { } RalewaySemiBold { id: noPermissionText; - visible: !root.canRezCertifiedItems && root.itemType !== "wearable"; + visible: !root.canRezCertifiedItems && root.itemType === "entity"; text: 'You do not have Certified Rez permissions in this domain.' // Text size size: 16; From 59bd05aa554ad6d433d50edae5b223a400a0765f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 23 Feb 2018 09:21:13 -0800 Subject: [PATCH 21/25] make shadow-related shader compile on osx --- libraries/render-utils/src/Shadow.slh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index abb04a4498..36eb35c757 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -89,7 +89,9 @@ float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDepth, vec3 worldNormal) { ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); - vec4 cascadeShadowCoords[2] = { vec4(0), vec4(0) }; + vec4 cascadeShadowCoords[2]; + cascadeShadowCoords[0] = vec4(0); + cascadeShadowCoords[1] = vec4(0); ivec2 cascadeIndices; float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices); From 277e1ebec4e7075caf7a8dae62da7f908c973a26 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 23 Feb 2018 10:50:57 -0800 Subject: [PATCH 22/25] fix /~/ expansion on osx --- libraries/shared/src/PathUtils.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index cac20434c6..f23d6387f6 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -139,10 +139,12 @@ QUrl PathUtils::expandToAppAbsolutePath(const QUrl& fileUrl) { if (path.startsWith("/~/")) { QString absolutePath = applicationAbsolutePath(); path.replace(0, 3, absolutePath); - url = QUrl("file:///" + path); + url = QUrl("file://" + path); + qDebug() << "QQQQ expandToAppAbsolutePath " << fileUrl << url; } return url; } + const QString& PathUtils::qmlBaseUrl() { static const QString staticResourcePath = resourcesUrl() + "qml/"; return staticResourcePath; From 9b8c925b721aa05f4570d82a48d614aabd7b3ea8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 23 Feb 2018 11:09:09 -0800 Subject: [PATCH 23/25] still trying to get /~/ in json files to work right --- libraries/shared/src/PathUtils.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index f23d6387f6..8b75f7c0fd 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -139,7 +139,11 @@ QUrl PathUtils::expandToAppAbsolutePath(const QUrl& fileUrl) { if (path.startsWith("/~/")) { QString absolutePath = applicationAbsolutePath(); path.replace(0, 3, absolutePath); +#if defined(Q_OS_OSX) url = QUrl("file://" + path); +#else + url = QUrl("file:///" + path); +#endif qDebug() << "QQQQ expandToAppAbsolutePath " << fileUrl << url; } return url; From 3ee979fc345ab2e5a54c899a5ec5da9eb68ec26a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 23 Feb 2018 12:09:35 -0800 Subject: [PATCH 24/25] don't send entity-edits to a server when in a serverless domain --- libraries/entities/src/EntityEditPacketSender.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 90740948ce..2ef7f1821b 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -91,6 +91,11 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, return; } + if (entityTree->isServerlessMode()) { + // if we are in a serverless domain, don't send edit packets + return; + } + QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0); if (type == PacketType::EntityAdd) { From 1e49dc5ca2e8cff5cb60483f464fd46f079853af Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 23 Feb 2018 13:58:23 -0800 Subject: [PATCH 25/25] try using app-local-data path for /~/ --- interface/src/avatar/MyAvatar.cpp | 1 + libraries/shared/src/PathUtils.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 50753724e8..cd86fed51a 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1164,6 +1164,7 @@ void MyAvatar::loadData() { updateAvatarEntity(entityID, properties); } settings.endArray(); + qDebug() << "QQQQ saved " << avatarEntityCount << " avatar entities"; if (avatarEntityCount == 0) { // HACK: manually remove empty 'avatarEntityData' else legacy data may persist in settings file settings.remove("avatarEntityData"); diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index a5841b2f6c..123ed4a5a9 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -81,7 +81,7 @@ const QString& PathUtils::resourcesPath() { #else staticResourcePath = ":/"; #endif - + #if !defined(Q_OS_ANDROID) && defined(DEV_BUILD) if (USE_SOURCE_TREE_RESOURCES()) { // For dev builds, optionally load content from the Git source tree @@ -124,8 +124,9 @@ QUrl PathUtils::expandToAppAbsolutePath(const QUrl& fileUrl) { QUrl url = fileUrl; QString path = fileUrl.path(); if (path.startsWith("/~/")) { - path.replace(0, 3, resourcesUrl()); + path.replace(0, 3, getAppLocalDataPath()); url = QUrl(path); + qDebug() << "QQQQ expandToAppAbsolutePath: " << fileUrl << url; } return url; }