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);