From f53aba2a3216d6bb5106dc5adda574134fe63b47 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 2 Mar 2018 15:53:06 -0800 Subject: [PATCH] Initial progress --- .../qml/hifi/commerce/checkout/Checkout.qml | 21 +- .../hifi/commerce/purchases/PurchasedItem.qml | 960 +++++++++--------- .../qml/hifi/commerce/purchases/Purchases.qml | 4 + scripts/system/html/js/marketplacesInject.js | 38 +- scripts/system/marketplaces/marketplaces.js | 6 +- 5 files changed, 566 insertions(+), 463 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 372fb3c774..328b662564 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -49,6 +49,7 @@ Rectangle { property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified(); property string referrer; property bool isInstalled; + property bool isUpdating; // Style color: hifi.colors.white; Connections { @@ -413,6 +414,7 @@ Rectangle { // "HFC" balance label HiFiGlyphs { id: itemPriceTextLabel; + visible: !root.isUpdating; text: hifi.glyphs.hfc; // Size size: 30; @@ -428,7 +430,7 @@ Rectangle { } FiraSansSemiBold { id: itemPriceText; - text: (root.itemPrice === -1) ? "--" : root.itemPrice; + text: root.isUpdating ? "FREE\nUPGRADE" : ((root.itemPrice === -1) ? "--" : root.itemPrice); // Text size size: 26; // Anchors @@ -549,8 +551,8 @@ Rectangle { height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: ((root.isCertified) ? ((ownershipStatusReceived && balanceReceived) ? - (viewInMyPurchasesButton.visible ? "Buy It Again" : "Confirm Purchase") : "--") : "Get Item"); + text: root.isUpdating ? "CONFIRM UPDATE" : (((root.isCertified) ? ((ownershipStatusReceived && balanceReceived) ? + (viewInMyPurchasesButton.visible ? "Buy It Again" : "Confirm Purchase") : "--") : "Get Item")); onClicked: { if (root.isCertified) { if (!root.shouldBuyWithControlledFailure) { @@ -1002,6 +1004,7 @@ Rectangle { function fromScript(message) { switch (message.method) { case 'updateCheckoutQML': + root.isUpdating = message.params.isUpdating; itemId = message.params.itemId; itemName = message.params.itemName; root.itemPrice = message.params.itemPrice; @@ -1019,7 +1022,7 @@ Rectangle { function refreshBuyUI() { if (root.isCertified) { if (root.ownershipStatusReceived && root.balanceReceived) { - if (root.balanceAfterPurchase < 0) { + if (root.balanceAfterPurchase < 0 && !root.isUpdating) { if (root.alreadyOwned) { buyText.text = "Your Wallet does not have sufficient funds to purchase this item again."; viewInMyPurchasesButton.visible = true; @@ -1031,13 +1034,19 @@ Rectangle { buyGlyph.text = hifi.glyphs.alert; buyGlyph.size = 54; } else { - if (root.alreadyOwned) { + if (root.alreadyOwned && !root.isUpdating) { viewInMyPurchasesButton.visible = true; } else { buyText.text = ""; } - if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { + if (root.isUpdating) { + buyText.text = "By agreeing to update, you agree to trade in your old item for the update that replaces it."; + buyTextContainer.color = "#FFC3CD"; + buyTextContainer.border.color = "#F3808F"; + buyGlyph.text = hifi.glyphs.alert; + buyGlyph.size = 54; + } else 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"; diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index fb8e509cde..d8e02b08e5 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -48,11 +48,13 @@ Item { property bool hasPermissionToRezThis; property bool permissionExplanationCardVisible; property bool isInstalled; + property string upgradeUrl; + property string upgradeTitle; property string originalStatusText; property string originalStatusColor; - height: 110; + height: root.upgradeUrl === "" ? 110 : 150; width: parent.width; Connections { @@ -137,500 +139,548 @@ Item { anchors.verticalCenter: parent.verticalCenter; height: root.height - 10; - Image { - id: itemPreviewImage; - source: root.itemPreviewImageUrl; + Item { + id: itemContainer; anchors.left: parent.left; + anchors.right: parent.right; anchors.top: parent.top; - anchors.bottom: parent.bottom; - width: height; - fillMode: Image.PreserveAspectCrop; + height: 100; - MouseArea { - anchors.fill: parent; - onClicked: { - sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId}); - } - } - } + Image { + id: itemPreviewImage; + source: root.itemPreviewImageUrl; + anchors.left: parent.left; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + width: height; + fillMode: Image.PreserveAspectCrop; - 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; - 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; - // Text size - size: 24; - // Style - color: hifi.colors.blueAccent; - text: root.itemName; - elide: Text.ElideRight; - // Alignment - horizontalAlignment: Text.AlignLeft; - verticalAlignment: Text.AlignVCenter; - - MouseArea { - anchors.fill: parent; - hoverEnabled: enabled; - onClicked: { - sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId}); - } - onEntered: { - itemName.color = hifi.colors.blueHighlight; - } - onExited: { - itemName.color = hifi.colors.blueAccent; - } - } - } - HiFiGlyphs { - id: noPermissionGlyph; - visible: !root.hasPermissionToRezThis; - anchors.verticalCenter: itemName.verticalCenter; - anchors.left: itemName.right; - anchors.leftMargin: itemName.truncated ? -10 : -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: { - root.sendToPurchases({ method: 'openPermissionExplanationCard' }); - } - } - } - Rectangle { - id: permissionExplanationCard; - z: 995; - visible: root.permissionExplanationCardVisible; - 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"; - } else { - "" + MouseArea { + anchors.fill: parent; + onClicked: { + sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId}); } } - 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; + } + + 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; + 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; + // Text size + size: 24; + // Style + color: hifi.colors.blueAccent; + text: root.itemName; + elide: Text.ElideRight; + // Alignment + horizontalAlignment: Text.AlignLeft; verticalAlignment: Text.AlignVCenter; - onLinkActivated: { - sendToPurchases({method: 'showPermissionsExplanation', itemType: root.itemType}); + MouseArea { + anchors.fill: parent; + hoverEnabled: enabled; + onClicked: { + sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId}); + } + onEntered: { + itemName.color = hifi.colors.blueHighlight; + } + onExited: { + itemName.color = hifi.colors.blueAccent; + } } } - // "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; + id: noPermissionGlyph; + visible: !root.hasPermissionToRezThis; + anchors.verticalCenter: itemName.verticalCenter; + anchors.left: itemName.right; + anchors.leftMargin: itemName.truncated ? -10 : -2; + text: hifi.glyphs.info; + // Size + size: 40; + width: 32; + // Style + color: hifi.colors.redAccent; + MouseArea { anchors.fill: parent; hoverEnabled: true; + onEntered: { - parent.text = hifi.glyphs.closeInverted; + noPermissionGlyph.color = hifi.colors.redHighlight; } onExited: { - parent.text = hifi.glyphs.close; + noPermissionGlyph.color = hifi.colors.redAccent; } onClicked: { - root.sendToPurchases({ method: 'openPermissionExplanationCard', closeAll: true }); + root.sendToPurchases({ method: 'openPermissionExplanationCard' }); + } + } + } + Rectangle { + id: permissionExplanationCard; + z: 995; + visible: root.permissionExplanationCardVisible; + 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"; + } else { + "" + } + } + 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: { + root.sendToPurchases({ method: 'openPermissionExplanationCard', closeAll: true }); + } + } + } + } + + Item { + id: certificateContainer; + anchors.top: itemName.bottom; + anchors.topMargin: 4; + anchors.left: itemName.left; + anchors.right: buttonContainer.left; + anchors.rightMargin: 2; + height: 24; + + HiFiGlyphs { + id: certificateIcon; + text: hifi.glyphs.scriptNew; + // Size + size: 30; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + width: 32; + // Style + color: hifi.colors.black; + } + + RalewayRegular { + id: viewCertificateText; + text: "VIEW CERTIFICATE"; + size: 13; + anchors.left: certificateIcon.right; + anchors.leftMargin: 4; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.right: parent.right; + color: hifi.colors.black; + } + + MouseArea { + anchors.fill: parent; + hoverEnabled: enabled; + onClicked: { + sendToPurchases({method: 'purchases_itemCertificateClicked', itemCertificateId: root.certificateId}); + } + onEntered: { + certificateIcon.color = hifi.colors.lightGray; + viewCertificateText.color = hifi.colors.lightGray; + } + onExited: { + certificateIcon.color = hifi.colors.black; + viewCertificateText.color = hifi.colors.black; + } + } + } + + Item { + id: editionContainer; + visible: root.displayedItemCount > 1 && !statusContainer.visible; + anchors.left: itemName.left; + anchors.top: certificateContainer.bottom; + anchors.topMargin: 8; + anchors.bottom: parent.bottom; + anchors.right: buttonContainer.left; + anchors.rightMargin: 2; + + RalewayRegular { + anchors.left: parent.left; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + width: paintedWidth; + text: "#" + root.itemEdition; + size: 13; + color: hifi.colors.black; + verticalAlignment: Text.AlignTop; + } + } + + Item { + id: statusContainer; + visible: root.purchaseStatus === "pending" || root.purchaseStatus === "invalidated" || root.purchaseStatusChanged; + anchors.left: itemName.left; + anchors.top: certificateContainer.bottom; + anchors.topMargin: 8; + anchors.bottom: parent.bottom; + anchors.right: buttonContainer.left; + anchors.rightMargin: 2; + + RalewaySemiBold { + id: statusText; + anchors.left: parent.left; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + width: paintedWidth; + text: { + if (root.purchaseStatus === "pending") { + "PENDING..." + } else if (root.purchaseStatus === "invalidated") { + "INVALIDATED" + } else if (root.numberSold !== -1) { + ("Sales: " + root.numberSold + "/" + (root.limitedRun === -1 ? "\u221e" : root.limitedRun)) + } else { + "" + } + } + size: 18; + color: { + if (root.purchaseStatus === "pending") { + hifi.colors.blueAccent + } else if (root.purchaseStatus === "invalidated") { + hifi.colors.redAccent + } else { + hifi.colors.baseGray + } + } + verticalAlignment: Text.AlignTop; + } + + HiFiGlyphs { + id: statusIcon; + text: { + if (root.purchaseStatus === "pending") { + hifi.glyphs.question + } else if (root.purchaseStatus === "invalidated") { + hifi.glyphs.question + } else { + "" + } + } + // Size + size: 36; + // Anchors + anchors.top: parent.top; + anchors.topMargin: -8; + anchors.left: statusText.right; + anchors.bottom: parent.bottom; + // Style + color: { + if (root.purchaseStatus === "pending") { + hifi.colors.blueAccent + } else if (root.purchaseStatus === "invalidated") { + hifi.colors.redAccent + } else { + hifi.colors.baseGray + } + } + verticalAlignment: Text.AlignTop; + } + + MouseArea { + anchors.fill: parent; + hoverEnabled: enabled; + onClicked: { + if (root.purchaseStatus === "pending") { + sendToPurchases({method: 'showPendingLightbox'}); + } else if (root.purchaseStatus === "invalidated") { + sendToPurchases({method: 'showInvalidatedLightbox'}); + } + } + onEntered: { + if (root.purchaseStatus === "pending") { + statusText.color = hifi.colors.blueHighlight; + statusIcon.color = hifi.colors.blueHighlight; + } else if (root.purchaseStatus === "invalidated") { + statusText.color = hifi.colors.redAccent; + statusIcon.color = hifi.colors.redAccent; + } + } + onExited: { + if (root.purchaseStatus === "pending") { + statusText.color = hifi.colors.blueAccent; + statusIcon.color = hifi.colors.blueAccent; + } else if (root.purchaseStatus === "invalidated") { + statusText.color = hifi.colors.redHighlight; + statusIcon.color = hifi.colors.redHighlight; + } + } + } + } + + Rectangle { + id: rezzedNotifContainer; + z: 998; + visible: false; + color: "#1FC6A6"; + anchors.fill: buttonContainer; + MouseArea { + anchors.fill: parent; + propagateComposedEvents: false; + hoverEnabled: true; + } + + RalewayBold { + anchors.fill: parent; + text: (root.buttonTextClicked)[itemTypesArray.indexOf(root.itemType)]; + size: 15; + color: hifi.colors.white; + verticalAlignment: Text.AlignVCenter; + horizontalAlignment: Text.AlignHCenter; + } + + Timer { + id: rezzedNotifContainerTimer; + interval: 2000; + onTriggered: rezzedNotifContainer.visible = false + } + } + + Rectangle { + id: appButtonContainer; + color: hifi.colors.white; + z: 994; + visible: root.isInstalled; + anchors.fill: buttonContainer; + + HifiControlsUit.Button { + id: openAppButton; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.light; + anchors.top: parent.top; + anchors.right: parent.right; + anchors.left: parent.left; + width: 92; + height: 44; + text: "OPEN" + onClicked: { + Commerce.openApp(root.itemHref); + } + } + + HifiControlsUit.Button { + id: uninstallAppButton; + color: hifi.buttons.noneBorderless; + colorScheme: hifi.colorSchemes.light; + anchors.bottom: parent.bottom; + anchors.right: parent.right; + anchors.left: parent.left; + height: 44; + text: "UNINSTALL" + onClicked: { + Commerce.uninstallApp(root.itemHref); + } + } + } + + Button { + id: buttonContainer; + 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.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', itemHref: root.itemHref}); + } else if (root.itemType === "avatar") { + sendToPurchases({method: 'showChangeAvatarLightbox', itemName: root.itemName, itemHref: root.itemHref}); + } else if (root.itemType === "app") { + // "Run" and "Uninstall" buttons are separate. + Commerce.installApp(root.itemHref); + } else { + sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); + root.showConfirmation = true; + } + } + + style: ButtonStyle { + background: Rectangle { + radius: 4; + gradient: Gradient { + GradientStop { + position: 0.2 + color: { + if (!control.enabled) { + hifi.buttons.disabledColorStart[control.colorScheme] + } else if (control.pressed) { + hifi.buttons.pressedColor[control.color] + } else if (control.hovered) { + hifi.buttons.hoveredColor[control.color] + } else { + hifi.buttons.colorStart[control.color] + } + } + } + GradientStop { + position: 1.0 + color: { + if (!control.enabled) { + hifi.buttons.disabledColorFinish[control.colorScheme] + } else if (control.pressed) { + hifi.buttons.pressedColor[control.color] + } else if (control.hovered) { + hifi.buttons.hoveredColor[control.color] + } else { + hifi.buttons.colorFinish[control.color] + } + } + } + } + } + + label: Item { + HiFiGlyphs { + id: rezIcon; + text: (root.buttonGlyph)[itemTypesArray.indexOf(root.itemType)]; + // Size + size: 60; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 0; + anchors.left: parent.left; + anchors.right: parent.right; + horizontalAlignment: Text.AlignHCenter; + // Style + color: enabled ? hifi.buttons.textColor[control.color] + : hifi.buttons.disabledTextColor[control.colorScheme] + } + RalewayBold { + id: rezIconLabel; + anchors.top: rezIcon.bottom; + anchors.topMargin: -4; + anchors.right: parent.right; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + font.capitalization: Font.AllUppercase + color: enabled ? hifi.buttons.textColor[control.color] + : hifi.buttons.disabledTextColor[control.colorScheme] + size: 15; + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: MyAvatar.skeletonModelURL === root.itemHref ? "CURRENT" : (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)]; + } } } } } - Item { - id: certificateContainer; - anchors.top: itemName.bottom; - anchors.topMargin: 4; - anchors.left: itemName.left; - anchors.right: buttonContainer.left; - anchors.rightMargin: 2; - height: 24; - - HiFiGlyphs { - id: certificateIcon; - text: hifi.glyphs.scriptNew; - // Size - size: 30; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - anchors.bottom: parent.bottom; - width: 32; - // Style - color: hifi.colors.black; - } - - RalewayRegular { - id: viewCertificateText; - text: "VIEW CERTIFICATE"; - size: 13; - anchors.left: certificateIcon.right; - anchors.leftMargin: 4; - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.right: parent.right; - color: hifi.colors.black; - } - - MouseArea { - anchors.fill: parent; - hoverEnabled: enabled; - onClicked: { - sendToPurchases({method: 'purchases_itemCertificateClicked', itemCertificateId: root.certificateId}); - } - onEntered: { - certificateIcon.color = hifi.colors.lightGray; - viewCertificateText.color = hifi.colors.lightGray; - } - onExited: { - certificateIcon.color = hifi.colors.black; - viewCertificateText.color = hifi.colors.black; - } - } - } - - Item { - id: editionContainer; - visible: root.displayedItemCount > 1 && !statusContainer.visible; - anchors.left: itemName.left; - anchors.top: certificateContainer.bottom; - anchors.topMargin: 8; + Rectangle { + id: upgradeAvailableContainer; + visible: root.upgradeUrl !== ""; + anchors.top: itemContainer.bottom; anchors.bottom: parent.bottom; - anchors.right: buttonContainer.left; - anchors.rightMargin: 2; + anchors.left: parent.left; + anchors.right: parent.right; + color: "#E7F8FB"; RalewayRegular { + id: updateAvailableText; + text: "UPDATE AVAILABLE"; + size: 13; anchors.left: parent.left; + anchors.leftMargin: 12; anchors.top: parent.top; anchors.bottom: parent.bottom; width: paintedWidth; - text: "#" + root.itemEdition; - size: 13; color: hifi.colors.black; - verticalAlignment: Text.AlignTop; + verticalAlignment: Text.AlignVCenter; } - } - - Item { - id: statusContainer; - visible: root.purchaseStatus === "pending" || root.purchaseStatus === "invalidated" || root.purchaseStatusChanged; - anchors.left: itemName.left; - anchors.top: certificateContainer.bottom; - anchors.topMargin: 8; - anchors.bottom: parent.bottom; - anchors.right: buttonContainer.left; - anchors.rightMargin: 2; RalewaySemiBold { - id: statusText; - anchors.left: parent.left; + id: updateNowText; + text: "Update this item now"; + size: 13; + anchors.left: updateAvailableText.right; + anchors.leftMargin: 16; anchors.top: parent.top; anchors.bottom: parent.bottom; width: paintedWidth; - text: { - if (root.purchaseStatus === "pending") { - "PENDING..." - } else if (root.purchaseStatus === "invalidated") { - "INVALIDATED" - } else if (root.numberSold !== -1) { - ("Sales: " + root.numberSold + "/" + (root.limitedRun === -1 ? "\u221e" : root.limitedRun)) - } else { - "" - } - } - size: 18; - color: { - if (root.purchaseStatus === "pending") { - hifi.colors.blueAccent - } else if (root.purchaseStatus === "invalidated") { - hifi.colors.redAccent - } else { - hifi.colors.baseGray - } - } - verticalAlignment: Text.AlignTop; - } - - HiFiGlyphs { - id: statusIcon; - text: { - if (root.purchaseStatus === "pending") { - hifi.glyphs.question - } else if (root.purchaseStatus === "invalidated") { - hifi.glyphs.question - } else { - "" - } - } - // Size - size: 36; - // Anchors - anchors.top: parent.top; - anchors.topMargin: -8; - anchors.left: statusText.right; - anchors.bottom: parent.bottom; - // Style - color: { - if (root.purchaseStatus === "pending") { - hifi.colors.blueAccent - } else if (root.purchaseStatus === "invalidated") { - hifi.colors.redAccent - } else { - hifi.colors.baseGray - } - } - verticalAlignment: Text.AlignTop; - } - - MouseArea { - anchors.fill: parent; - hoverEnabled: enabled; - onClicked: { - if (root.purchaseStatus === "pending") { - sendToPurchases({method: 'showPendingLightbox'}); - } else if (root.purchaseStatus === "invalidated") { - sendToPurchases({method: 'showInvalidatedLightbox'}); - } - } - onEntered: { - if (root.purchaseStatus === "pending") { - statusText.color = hifi.colors.blueHighlight; - statusIcon.color = hifi.colors.blueHighlight; - } else if (root.purchaseStatus === "invalidated") { - statusText.color = hifi.colors.redAccent; - statusIcon.color = hifi.colors.redAccent; - } - } - onExited: { - if (root.purchaseStatus === "pending") { - statusText.color = hifi.colors.blueAccent; - statusIcon.color = hifi.colors.blueAccent; - } else if (root.purchaseStatus === "invalidated") { - statusText.color = hifi.colors.redHighlight; - statusIcon.color = hifi.colors.redHighlight; - } - } - } - } - - Rectangle { - id: rezzedNotifContainer; - z: 998; - visible: false; - color: "#1FC6A6"; - anchors.fill: buttonContainer; - MouseArea { - anchors.fill: parent; - propagateComposedEvents: false; - hoverEnabled: true; - } - - RalewayBold { - anchors.fill: parent; - text: (root.buttonTextClicked)[itemTypesArray.indexOf(root.itemType)]; - size: 15; - color: hifi.colors.white; + color: hifi.colors.black; verticalAlignment: Text.AlignVCenter; - horizontalAlignment: Text.AlignHCenter; - } - Timer { - id: rezzedNotifContainerTimer; - interval: 2000; - onTriggered: rezzedNotifContainer.visible = false - } - } - - Rectangle { - id: appButtonContainer; - color: hifi.colors.white; - z: 994; - visible: root.isInstalled; - anchors.fill: buttonContainer; - - HifiControlsUit.Button { - id: openAppButton; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.light; - anchors.top: parent.top; - anchors.right: parent.right; - anchors.left: parent.left; - width: 92; - height: 44; - text: "OPEN" - onClicked: { - Commerce.openApp(root.itemHref); - } - } - - HifiControlsUit.Button { - id: uninstallAppButton; - color: hifi.buttons.noneBorderless; - colorScheme: hifi.colorSchemes.light; - anchors.bottom: parent.bottom; - anchors.right: parent.right; - anchors.left: parent.left; - height: 44; - text: "UNINSTALL" - onClicked: { - Commerce.uninstallApp(root.itemHref); - } - } - } - - Button { - id: buttonContainer; - 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.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', itemHref: root.itemHref}); - } else if (root.itemType === "avatar") { - sendToPurchases({method: 'showChangeAvatarLightbox', itemName: root.itemName, itemHref: root.itemHref}); - } else if (root.itemType === "app") { - // "Run" and "Uninstall" buttons are separate. - Commerce.installApp(root.itemHref); - } else { - sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); - root.showConfirmation = true; - } - } - - style: ButtonStyle { - background: Rectangle { - radius: 4; - gradient: Gradient { - GradientStop { - position: 0.2 - color: { - if (!control.enabled) { - hifi.buttons.disabledColorStart[control.colorScheme] - } else if (control.pressed) { - hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { - hifi.buttons.hoveredColor[control.color] - } else { - hifi.buttons.colorStart[control.color] - } - } - } - GradientStop { - position: 1.0 - color: { - if (!control.enabled) { - hifi.buttons.disabledColorFinish[control.colorScheme] - } else if (control.pressed) { - hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { - hifi.buttons.hoveredColor[control.color] - } else { - hifi.buttons.colorFinish[control.color] - } - } - } - } - } - - label: Item { - HiFiGlyphs { - id: rezIcon; - text: (root.buttonGlyph)[itemTypesArray.indexOf(root.itemType)]; - // Size - size: 60; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 0; - anchors.left: parent.left; - anchors.right: parent.right; - horizontalAlignment: Text.AlignHCenter; - // Style - color: enabled ? hifi.buttons.textColor[control.color] - : hifi.buttons.disabledTextColor[control.colorScheme] - } - RalewayBold { - id: rezIconLabel; - anchors.top: rezIcon.bottom; - anchors.topMargin: -4; - anchors.right: parent.right; - anchors.left: parent.left; - anchors.bottom: parent.bottom; - font.capitalization: Font.AllUppercase - color: enabled ? hifi.buttons.textColor[control.color] - : hifi.buttons.disabledTextColor[control.colorScheme] - size: 15; - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - text: MyAvatar.skeletonModelURL === root.itemHref ? "CURRENT" : (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)]; - } + onLinkActivated: { + sendToPurchases({method: 'updateItemClicked', itemId: root.itemId, itemEdition: root.itemEdition, upgradeUrl: root.upgradeUrl}); } } } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index c505baebf4..8952aad0cf 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -398,6 +398,8 @@ Rectangle { displayedItemCount: model.displayedItemCount; permissionExplanationCardVisible: model.permissionExplanationCardVisible; isInstalled: model.isInstalled; + upgradeUrl: model.upgrade_url; + upgradeTitle: model.upgrade_title; itemType: { if (model.root_file_url.indexOf(".fst") > -1) { "avatar"; @@ -485,6 +487,8 @@ Rectangle { filteredPurchasesModel.setProperty(i, "permissionExplanationCardVisible", true); } } + } else if (msg.method === "updateItemClicked") { + sendToScript(msg); } } } diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index fb49de1050..35f89a4a19 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -246,6 +246,7 @@ function buyButtonClicked(id, name, author, price, href, referrer) { EventBridge.emitWebEvent(JSON.stringify({ type: "CHECKOUT", + isUpdating: false, itemId: id, itemName: name, itemPrice: price ? parseInt(price, 10) : 0, @@ -255,6 +256,29 @@ })); } + function updateButtonClicked(id, name, author, href, referrer) { + EventBridge.emitWebEvent(JSON.stringify({ + type: "UPDATE", + isUpdating: true, + itemId: id, + itemName: name, + itemHref: href, + referrer: referrer, + itemAuthor: author + })); + } + + // From https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript + function getParameterByName(name, url) { + if (!url) url = window.location.href; + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), + results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, " ")); + } + function injectBuyButtonOnMainPage() { var cost; @@ -412,13 +436,25 @@ var cost = $('.item-cost').text(); if (availability !== 'available') { purchaseButton.html('UNAVAILABLE (' + availability + ')'); + } else if (url.indexOf('edition=' != -1)) { + purchaseButton.html('UPDATE FOR FREE'); } else if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { purchaseButton.html('PURCHASE ' + cost); } purchaseButton.on('click', function () { - if ('available' === availability) { + if (url.indexOf('edition=' != -1)) { + if (url.indexOf('upgradeUrl=' === -1)) { + console.log("ERROR! Item is an upgrade, but no upgradeUrl was specified."); + } else { + updateButtonClicked(window.location.pathname.split("/")[3], + $('#top-center').find('h1').text(), + $('#creator').find('.value').text(), + getParameterByName('upgradeUrl'), + "itemPage"); + } + } else if ('available' === availability) { buyButtonClicked(window.location.pathname.split("/")[3], $('#top-center').find('h1').text(), $('#creator').find('.value').text(), diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 8f51d88f2d..dafd3e525d 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -417,7 +417,7 @@ var selectionDisplay = null; // for gridTool.js to ignore isDownloadBeingCancelled = false; } else { var parsedJsonMessage = JSON.parse(message); - if (parsedJsonMessage.type === "CHECKOUT") { + if (parsedJsonMessage.type === "CHECKOUT" || parsedJsonMessage.type === "UPDATE") { wireEventBridge(true); tablet.pushOntoStack(MARKETPLACE_CHECKOUT_QML_PATH); tablet.sendToQml({ @@ -560,6 +560,10 @@ var selectionDisplay = null; // for gridTool.js to ignore case 'purchases_goToMarketplaceClicked': tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); break; + case 'updateItemClicked': + tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId + "?edition=" + message.itemEdition + "&upgradeUrl=" + message.upgradeUrl, + MARKETPLACES_INJECT_SCRIPT_URL); + break; case 'passphrasePopup_cancelClicked': case 'needsLogIn_cancelClicked': tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL);