Merge pull request #14261 from howard-stearns/oculus-store-commerce

Oculus store commerce
This commit is contained in:
Howard Stearns 2018-11-05 16:13:04 -08:00 committed by GitHub
commit a99facb793
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 702 additions and 987 deletions

View file

@ -96,7 +96,7 @@ Item {
topMargin: hifi.dimensions.contentSpacing.y topMargin: hifi.dimensions.contentSpacing.y
} }
text: qsTr("Sign in to High Fidelity to make friends, get HFC, and buy interesting things on the Marketplace!") text: qsTr("Sign in to High Fidelity to make friends, get HFC, and get interesting things on the Marketplace!")
width: parent.width width: parent.width
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
lineHeight: 1 lineHeight: 1

View file

@ -260,16 +260,21 @@ Item {
interactive: false; interactive: false;
anchors.fill: parent; anchors.fill: parent;
model: filterBarModel; model: filterBarModel;
delegate: Rectangle { delegate: Item {
width: parent.width;
height: 50;
Rectangle {
id: dropDownButton; id: dropDownButton;
color: hifi.colors.white; color: hifi.colors.white;
width: parent.width; width: parent.width;
height: 50; height: 50;
visible: true;
RalewaySemiBold { RalewaySemiBold {
id: dropDownButtonText; id: dropDownButtonText;
text: model.displayName; text: model.displayName;
anchors.fill: parent; anchors.fill: parent;
anchors.topMargin: 2;
anchors.leftMargin: 12; anchors.leftMargin: 12;
color: hifi.colors.baseGray; color: hifi.colors.baseGray;
horizontalAlignment: Text.AlignLeft; horizontalAlignment: Text.AlignLeft;
@ -294,6 +299,13 @@ Item {
} }
} }
} }
Rectangle {
height: 2;
width: parent.width;
color: hifi.colors.lightGray;
visible: model.separator
}
}
} }
} }

View file

@ -63,8 +63,8 @@ MessageBox {
popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase; popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase;
popup.button1text = 'CANCEL' popup.button1text = 'CANCEL'
popup.titleText = 'Get Wearables' popup.titleText = 'Get Wearables'
popup.bodyText = 'Buy wearables from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' + popup.bodyText = 'Get wearables from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' +
'Wear wearable from <b><a href="app://purchases">My Purchases.</a></b>' + '<br/>' + '<br/>' + 'Wear wearable from <b><a href="app://purchases">Inventory.</a></b>' + '<br/>' + '<br/>' +
'Visit “AvatarIsland” to get wearables' 'Visit “AvatarIsland” to get wearables'
popup.imageSource = getWearablesUrl; popup.imageSource = getWearablesUrl;
@ -89,7 +89,7 @@ MessageBox {
function showDeleteFavorite(favoriteName, callback) { function showDeleteFavorite(favoriteName, callback) {
popup.titleText = 'Delete Favorite: {AvatarName}'.replace('{AvatarName}', favoriteName) popup.titleText = 'Delete Favorite: {AvatarName}'.replace('{AvatarName}', favoriteName)
popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from My Purchases.' popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from Inventory.'
popup.imageSource = null; popup.imageSource = null;
popup.button1text = 'CANCEL' popup.button1text = 'CANCEL'
popup.button2text = 'DELETE' popup.button2text = 'DELETE'
@ -128,8 +128,8 @@ MessageBox {
popup.button1text = 'CANCEL' popup.button1text = 'CANCEL'
popup.titleText = 'Get Avatars' popup.titleText = 'Get Avatars'
popup.bodyText = 'Buy avatars from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' + popup.bodyText = 'Get avatars from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' +
'Wear avatars in <b><a href="app://purchases">My Purchases.</a></b>' + '<br/>' + '<br/>' + 'Wear avatars in <b><a href="app://purchases">Inventory.</a></b>' + '<br/>' + '<br/>' +
'Visit “BodyMart” to get free avatars.' 'Visit “BodyMart” to get free avatars.'
popup.imageSource = getAvatarsUrl; popup.imageSource = getAvatarsUrl;

View file

@ -240,11 +240,6 @@ Rectangle {
lightboxPopup.button1method = function() { lightboxPopup.button1method = function() {
lightboxPopup.visible = false; lightboxPopup.visible = false;
} }
lightboxPopup.button2text = "GO TO WALLET";
lightboxPopup.button2method = function() {
lightboxPopup.visible = false;
sendToScript({method: 'checkout_openWallet'});
};
lightboxPopup.visible = true; lightboxPopup.visible = true;
} else { } else {
sendToScript(msg); sendToScript(msg);
@ -383,7 +378,7 @@ Rectangle {
anchors.leftMargin: 16; anchors.leftMargin: 16;
width: paintedWidth; width: paintedWidth;
height: paintedHeight; height: paintedHeight;
text: "Review Purchase:"; text: "Review:";
color: hifi.colors.black; color: hifi.colors.black;
size: 28; size: 28;
} }
@ -448,7 +443,7 @@ Rectangle {
// "HFC" balance label // "HFC" balance label
HiFiGlyphs { HiFiGlyphs {
id: itemPriceTextLabel; id: itemPriceTextLabel;
visible: !(root.isUpdating && root.itemEdition > 0); visible: !(root.isUpdating && root.itemEdition > 0) && (root.itemPrice > 0);
text: hifi.glyphs.hfc; text: hifi.glyphs.hfc;
// Size // Size
size: 30; size: 30;
@ -464,7 +459,7 @@ Rectangle {
} }
FiraSansSemiBold { FiraSansSemiBold {
id: itemPriceText; id: itemPriceText;
text: (root.isUpdating && root.itemEdition > 0) ? "FREE\nUPDATE" : ((root.itemPrice === -1) ? "--" : root.itemPrice); text: (root.isUpdating && root.itemEdition > 0) ? "FREE\nUPDATE" : ((root.itemPrice === -1) ? "--" : ((root.itemPrice > 0) ? root.itemPrice : "FREE"));
// Text size // Text size
size: (root.isUpdating && root.itemEdition > 0) ? 20 : 26; size: (root.isUpdating && root.itemEdition > 0) ? 20 : 26;
// Anchors // Anchors
@ -559,7 +554,7 @@ Rectangle {
} }
} }
// "View in My Purchases" button // "View in Inventory" button
HifiControlsUit.Button { HifiControlsUit.Button {
id: viewInMyPurchasesButton; id: viewInMyPurchasesButton;
visible: false; visible: false;
@ -570,7 +565,7 @@ Rectangle {
height: 50; height: 50;
anchors.left: parent.left; anchors.left: parent.left;
anchors.right: parent.right; anchors.right: parent.right;
text: root.isUpdating ? "UPDATE TO THIS ITEM FOR FREE" : "VIEW THIS ITEM IN MY PURCHASES"; text: root.isUpdating ? "UPDATE TO THIS ITEM FOR FREE" : "VIEW THIS ITEM IN YOUR INVENTORY";
onClicked: { onClicked: {
if (root.isUpdating) { if (root.isUpdating) {
sendToScript({method: 'checkout_goToPurchases', filterText: root.baseItemName}); sendToScript({method: 'checkout_goToPurchases', filterText: root.baseItemName});
@ -594,7 +589,7 @@ Rectangle {
anchors.left: parent.left; anchors.left: parent.left;
anchors.right: parent.right; anchors.right: parent.right;
text: (root.isUpdating && root.itemEdition > 0) ? "CONFIRM UPDATE" : (((root.isCertified) ? ((ownershipStatusReceived && balanceReceived && availableUpdatesReceived) ? text: (root.isUpdating && root.itemEdition > 0) ? "CONFIRM UPDATE" : (((root.isCertified) ? ((ownershipStatusReceived && balanceReceived && availableUpdatesReceived) ?
((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Buy It Again" : "Confirm Purchase") : "--") : "Get Item")); ((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Get It Again" : "Confirm") : "--") : "Get Item"));
onClicked: { onClicked: {
if (root.isUpdating && root.itemEdition > 0) { if (root.isUpdating && root.itemEdition > 0) {
// If we're updating an app, the existing app needs to be uninstalled. // If we're updating an app, the existing app needs to be uninstalled.
@ -608,9 +603,9 @@ Rectangle {
} else if (root.isCertified) { } else if (root.isCertified) {
if (!root.shouldBuyWithControlledFailure) { if (!root.shouldBuyWithControlledFailure) {
if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { if (root.itemType === "contentSet" && !Entities.canReplaceContent()) {
lightboxPopup.titleText = "Purchase Content Set"; lightboxPopup.titleText = "Get Content Set";
lightboxPopup.bodyText = "You will not be able to replace this domain's content with <b>" + root.itemName + lightboxPopup.bodyText = "You will not be able to replace this domain's content with <b>" + root.itemName +
" </b>until the server owner gives you 'Replace Content' permissions.<br><br>Are you sure you want to purchase this content set?"; " </b>until the server owner gives you 'Replace Content' permissions.<br><br>Are you sure you want to get this content set?";
lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1text = "CANCEL";
lightboxPopup.button1method = function() { lightboxPopup.button1method = function() {
lightboxPopup.visible = false; lightboxPopup.visible = false;
@ -694,7 +689,7 @@ Rectangle {
id: completeText2; id: completeText2;
text: "The " + (root.itemTypesText)[itemTypesArray.indexOf(root.itemType)] + text: "The " + (root.itemTypesText)[itemTypesArray.indexOf(root.itemType)] +
' <font color="' + hifi.colors.blueAccent + '"><a href="#">' + root.itemName + '</a></font>' + ' <font color="' + hifi.colors.blueAccent + '"><a href="#">' + root.itemName + '</a></font>' +
" has been added to your Purchases and a receipt will appear in your Wallet's transaction history."; " has been added to your Inventory.";
// Text size // Text size
size: 18; size: 18;
// Anchors // Anchors
@ -833,7 +828,7 @@ Rectangle {
} }
lightboxPopup.button2text = "OPEN GOTO"; lightboxPopup.button2text = "OPEN GOTO";
lightboxPopup.button2method = function() { lightboxPopup.button2method = function() {
sendToScript({method: 'purchases_openGoTo'}); sendToScript({method: 'checkout_openGoTo'});
lightboxPopup.visible = false; lightboxPopup.visible = false;
}; };
lightboxPopup.visible = true; lightboxPopup.visible = true;
@ -864,7 +859,7 @@ Rectangle {
RalewaySemiBold { RalewaySemiBold {
id: myPurchasesLink; id: myPurchasesLink;
text: '<font color="' + hifi.colors.primaryHighlight + '"><a href="#">View this item in My Purchases</a></font>'; text: '<font color="' + hifi.colors.primaryHighlight + '"><a href="#">View this item in your Inventory</a></font>';
// Text size // Text size
size: 18; size: 18;
// Anchors // Anchors
@ -886,7 +881,8 @@ Rectangle {
RalewaySemiBold { RalewaySemiBold {
id: walletLink; id: walletLink;
text: '<font color="' + hifi.colors.primaryHighlight + '"><a href="#">View receipt in Wallet</a></font>'; visible: !WalletScriptingInterface.limitedCommerce;
text: '<font color="' + hifi.colors.primaryHighlight + '"><a href="#">View receipt in Recent Activity</a></font>';
// Text size // Text size
size: 18; size: 18;
// Anchors // Anchors
@ -902,18 +898,18 @@ Rectangle {
horizontalAlignment: Text.AlignLeft; horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
onLinkActivated: { onLinkActivated: {
sendToScript({method: 'purchases_openWallet'}); sendToScript({method: 'checkout_openRecentActivity'});
} }
} }
RalewayRegular { RalewayRegular {
id: pendingText; id: pendingText;
text: 'Your item is marked "pending" while your purchase is being confirmed. ' + text: 'Your item is marked "pending" while the transfer is being confirmed. ' +
'<b><font color="' + hifi.colors.primaryHighlight + '"><a href="#">Learn More</a></font></b>'; '<b><font color="' + hifi.colors.primaryHighlight + '"><a href="#">Learn More</a></font></b>';
// Text size // Text size
size: 18; size: 18;
// Anchors // Anchors
anchors.top: walletLink.bottom; anchors.top: walletLink.visible ? walletLink.bottom : myPurchasesLink.bottom;
anchors.topMargin: 32; anchors.topMargin: 32;
height: paintedHeight; height: paintedHeight;
anchors.left: parent.left; anchors.left: parent.left;
@ -925,8 +921,8 @@ Rectangle {
horizontalAlignment: Text.AlignLeft; horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
onLinkActivated: { onLinkActivated: {
lightboxPopup.titleText = "Purchase Confirmations"; lightboxPopup.titleText = "Confirmations";
lightboxPopup.bodyText = 'Your item is marked "pending" while your purchase is being confirmed.<br><br>' + lightboxPopup.bodyText = 'Your item is marked "pending" while the transfer is being confirmed.<br><br>' +
'Confirmations usually take about 90 seconds.'; 'Confirmations usually take about 90 seconds.';
lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = function() { lightboxPopup.button1method = function() {
@ -936,9 +932,9 @@ Rectangle {
} }
} }
// "Continue Shopping" button // "Continue" button
HifiControlsUit.Button { HifiControlsUit.Button {
id: continueShoppingButton; id: continueButton;
color: hifi.buttons.noneBorderlessGray; color: hifi.buttons.noneBorderlessGray;
colorScheme: hifi.colorSchemes.light; colorScheme: hifi.colorSchemes.light;
anchors.bottom: parent.bottom; anchors.bottom: parent.bottom;
@ -946,9 +942,9 @@ Rectangle {
anchors.right: parent.right; anchors.right: parent.right;
width: 193; width: 193;
height: 44; height: 44;
text: "Continue Shopping"; text: "Continue";
onClicked: { onClicked: {
sendToScript({method: 'checkout_continueShopping', itemId: itemId}); sendToScript({method: 'checkout_continue', itemId: itemId});
} }
} }
} }
@ -971,7 +967,7 @@ Rectangle {
RalewayRegular { RalewayRegular {
id: failureHeaderText; id: failureHeaderText;
text: "<b>Purchase Failed.</b><br>Your Purchases and HFC balance haven't changed."; text: "<b>Purchase Failed.</b><br>Your Inventory and HFC balance haven't changed.";
// Text size // Text size
size: 24; size: 24;
// Anchors // Anchors
@ -1037,7 +1033,7 @@ Rectangle {
width: parent.width/2 - anchors.leftMargin*2; width: parent.width/2 - anchors.leftMargin*2;
text: "Back to Marketplace"; text: "Back to Marketplace";
onClicked: { onClicked: {
sendToScript({method: 'checkout_continueShopping', itemId: itemId}); sendToScript({method: 'checkout_continue', itemId: itemId});
} }
} }
} }
@ -1122,10 +1118,10 @@ Rectangle {
if (root.balanceAfterPurchase < 0) { if (root.balanceAfterPurchase < 0) {
// If you already own the item... // If you already own the item...
if (!root.alreadyOwned) { if (!root.alreadyOwned) {
buyText.text = "<b>Your Wallet does not have sufficient funds to purchase this item.</b>"; buyText.text = "<b>You do not have sufficient funds to purchase this item.</b>";
// Else if you don't already own the item... // Else if you don't already own the item...
} else if (canBuyAgain()) { } else if (canBuyAgain()) {
buyText.text = "<b>Your Wallet does not have sufficient funds to purchase this item again.</b>"; buyText.text = "<b>You do not have sufficient funds to purchase this item again.</b>";
} else { } else {
buyText.text = "<b>While you do not have sufficient funds to buy this, you already have this item.</b>" buyText.text = "<b>While you do not have sufficient funds to buy this, you already have this item.</b>"
} }
@ -1171,7 +1167,7 @@ Rectangle {
buyText.text = ""; buyText.text = "";
} }
} else { } else {
buyText.text = '<i>This type of item cannot currently be certified, so it will not show up in "My Purchases". You can access it again for free from the Marketplace.</i>'; buyText.text = '<i>This type of item cannot currently be certified, so it will not show up in "Inventory". You can access it again for free from the Marketplace.</i>';
buyTextContainer.color = hifi.colors.white; buyTextContainer.color = hifi.colors.white;
buyTextContainer.border.color = hifi.colors.white; buyTextContainer.border.color = hifi.colors.white;
buyGlyph.text = ""; buyGlyph.text = "";

View file

@ -27,7 +27,6 @@ Item {
property string referrerURL: (Account.metaverseServerURL + "/marketplace?"); property string referrerURL: (Account.metaverseServerURL + "/marketplace?");
readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin; readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin;
property alias usernameDropdownVisible: usernameDropdown.visible; property alias usernameDropdownVisible: usernameDropdown.visible;
property bool messagesWaiting: false;
height: mainContainer.height + additionalDropdownHeight; height: mainContainer.height + additionalDropdownHeight;
@ -38,7 +37,6 @@ Item {
if (walletStatus === 0) { if (walletStatus === 0) {
sendToParent({method: "needsLogIn"}); sendToParent({method: "needsLogIn"});
} else if (walletStatus === 5) { } else if (walletStatus === 5) {
Commerce.getAvailableUpdates();
Commerce.getSecurityImage(); Commerce.getSecurityImage();
} else if (walletStatus > 5) { } else if (walletStatus > 5) {
console.log("ERROR in EmulatedMarketplaceHeader.qml: Unknown wallet status: " + walletStatus); console.log("ERROR in EmulatedMarketplaceHeader.qml: Unknown wallet status: " + walletStatus);
@ -59,14 +57,6 @@ Item {
securityImage.source = "image://security/securityImage"; securityImage.source = "image://security/securityImage";
} }
} }
onAvailableUpdatesResult: {
if (result.status !== 'success') {
console.log("Failed to get Available Updates", result.data.message);
} else {
root.messagesWaiting = result.data.updates.length > 0;
}
}
} }
Component.onCompleted: { Component.onCompleted: {
@ -118,50 +108,6 @@ Item {
anchors.right: securityImage.left; anchors.right: securityImage.left;
anchors.rightMargin: 6; anchors.rightMargin: 6;
Rectangle {
id: myPurchasesLink;
anchors.right: myUsernameButton.left;
anchors.rightMargin: 8;
anchors.verticalCenter: parent.verticalCenter;
height: 40;
width: myPurchasesText.paintedWidth + 10;
RalewaySemiBold {
id: myPurchasesText;
text: "My Purchases";
// Text size
size: 18;
// Style
color: hifi.colors.blueAccent;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
// Anchors
anchors.centerIn: parent;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToParent({ method: 'header_goToPurchases', hasUpdates: root.messagesWaiting });
}
onEntered: myPurchasesText.color = hifi.colors.blueHighlight;
onExited: myPurchasesText.color = hifi.colors.blueAccent;
}
}
Rectangle {
id: messagesWaitingLight;
visible: root.messagesWaiting;
anchors.right: myPurchasesLink.left;
anchors.rightMargin: -2;
anchors.verticalCenter: parent.verticalCenter;
height: 10;
width: height;
radius: height/2;
color: "red";
}
TextMetrics { TextMetrics {
id: textMetrics; id: textMetrics;
font.family: "Raleway" font.family: "Raleway"
@ -267,7 +213,7 @@ Item {
anchors.topMargin: -buttonAndUsernameContainer.anchors.bottomMargin; anchors.topMargin: -buttonAndUsernameContainer.anchors.bottomMargin;
anchors.right: buttonAndUsernameContainer.right; anchors.right: buttonAndUsernameContainer.right;
height: childrenRect.height; height: childrenRect.height;
width: 100; width: 150;
Rectangle { Rectangle {
id: myItemsButton; id: myItemsButton;
@ -279,7 +225,7 @@ Item {
RalewaySemiBold { RalewaySemiBold {
anchors.fill: parent; anchors.fill: parent;
text: "My Items" text: "My Submissions"
color: hifi.colors.baseGray; color: hifi.colors.baseGray;
horizontalAlignment: Text.AlignHCenter; horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;

View file

@ -87,7 +87,7 @@ Rectangle {
} }
RalewayRegular { RalewayRegular {
id: introText2; id: introText2;
text: "My Purchases"; text: "Inventory";
// Text size // Text size
size: 22; size: 22;
// Anchors // Anchors
@ -116,7 +116,7 @@ Rectangle {
RalewayRegular { RalewayRegular {
id: step1text; id: step1text;
text: "The <b>'REZ IT'</b> button makes your purchase appear in front of you."; text: "The <b>'REZ IT'</b> button makes your item appear in front of you.";
// Text size // Text size
size: 20; size: 20;
// Anchors // Anchors

View file

@ -73,6 +73,10 @@ Item {
} }
onTransferAssetToNodeResult: { onTransferAssetToNodeResult: {
if (!root.visible) {
return;
}
root.isCurrentlySendingAsset = false; root.isCurrentlySendingAsset = false;
if (result.status === 'success') { if (result.status === 'success') {
@ -92,6 +96,10 @@ Item {
} }
onTransferAssetToUsernameResult: { onTransferAssetToUsernameResult: {
if (!root.visible) {
return;
}
root.isCurrentlySendingAsset = false; root.isCurrentlySendingAsset = false;
if (result.status === 'success') { if (result.status === 'success') {
@ -1309,13 +1317,13 @@ Item {
Rectangle { Rectangle {
anchors.top: parent.top; anchors.top: parent.top;
anchors.topMargin: root.assetName === "" ? 15 : 150; anchors.topMargin: root.assetName === "" ? 15 : 125;
anchors.left: parent.left; anchors.left: parent.left;
anchors.leftMargin: root.assetName === "" ? 15 : 50; anchors.leftMargin: root.assetName === "" ? 15 : 50;
anchors.right: parent.right; anchors.right: parent.right;
anchors.rightMargin: root.assetName === "" ? 15 : 50; anchors.rightMargin: root.assetName === "" ? 15 : 50;
anchors.bottom: parent.bottom; anchors.bottom: parent.bottom;
anchors.bottomMargin: root.assetName === "" ? 15 : 240; anchors.bottomMargin: root.assetName === "" ? 15 : 125;
color: "#FFFFFF"; color: "#FFFFFF";
RalewaySemiBold { RalewaySemiBold {

View file

@ -28,7 +28,7 @@ Rectangle {
property string itemName: "--"; property string itemName: "--";
property string itemOwner: "--"; property string itemOwner: "--";
property string itemEdition: "--"; property string itemEdition: "--";
property string dateOfPurchase: "--"; property string dateAcquired: "--";
property string itemCost: "--"; property string itemCost: "--";
property string certTitleTextColor: hifi.colors.darkGray; property string certTitleTextColor: hifi.colors.darkGray;
property string certTextColor: hifi.colors.white; property string certTextColor: hifi.colors.white;
@ -64,7 +64,7 @@ Rectangle {
root.itemName = ""; root.itemName = "";
root.itemEdition = ""; root.itemEdition = "";
root.itemOwner = ""; root.itemOwner = "";
root.dateOfPurchase = ""; root.dateAcquired = "";
root.itemCost = ""; root.itemCost = "";
errorText.text = "Information about this certificate is currently unavailable. Please try again later."; errorText.text = "Information about this certificate is currently unavailable. Please try again later.";
} }
@ -77,8 +77,9 @@ Rectangle {
// "\u2022" is the Unicode character 'BULLET' - it's what's used in password fields on the web, etc // "\u2022" is the Unicode character 'BULLET' - it's what's used in password fields on the web, etc
root.itemOwner = root.isMyCert ? Account.username : root.itemOwner = root.isMyCert ? Account.username :
"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"; "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
root.dateOfPurchase = root.isMyCert ? getFormattedDate(result.data.transfer_created_at * 1000) : "Undisclosed"; root.dateAcquired = root.isMyCert ? getFormattedDate(result.data.transfer_created_at * 1000) : "Undisclosed";
root.itemCost = (root.isMyCert && result.data.cost !== undefined) ? result.data.cost : "Undisclosed"; root.itemCost = (root.isMyCert && result.data.cost !== undefined) ?
(parseInt(result.data.cost) > 0 ? result.data.cost : "Free") : "Undisclosed";
} }
if (root.certInfoReplaceMode > 4) { if (root.certInfoReplaceMode > 4) {
root.itemEdition = result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run); root.itemEdition = result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run);
@ -86,7 +87,7 @@ Rectangle {
if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED
if (root.isMyCert) { if (root.isMyCert) {
errorText.text = "This item is an uncertified copy of an item you purchased."; errorText.text = "This item is an uncertified copy of an item you acquired.";
} else { } else {
errorText.text = "The person who placed this item doesn't own it."; errorText.text = "The person who placed this item doesn't own it.";
} }
@ -102,8 +103,8 @@ Rectangle {
showInMarketplaceButton.visible = false; showInMarketplaceButton.visible = false;
// "Edition" text previously set above in this function // "Edition" text previously set above in this function
// "Owner" text previously set above in this function // "Owner" text previously set above in this function
// "Purchase Date" text previously set above in this function // "Acquisition Date" text previously set above in this function
// "Purchase Price" text previously set above in this function // "Acquisition Price" text previously set above in this function
if (result.data.invalid_reason) { if (result.data.invalid_reason) {
errorText.text = result.data.invalid_reason; errorText.text = result.data.invalid_reason;
} }
@ -117,8 +118,8 @@ Rectangle {
showInMarketplaceButton.visible = true; showInMarketplaceButton.visible = true;
// "Edition" text previously set above in this function // "Edition" text previously set above in this function
// "Owner" text previously set above in this function // "Owner" text previously set above in this function
// "Purchase Date" text previously set above in this function // "Acquisition Date" text previously set above in this function
// "Purchase Price" text previously set above in this function // "Acquisition Price" text previously set above in this function
errorText.text = "The status of this item is still pending confirmation. If the purchase is not confirmed, " + errorText.text = "The status of this item is still pending confirmation. If the purchase is not confirmed, " +
"this entity will be cleaned up by the domain."; "this entity will be cleaned up by the domain.";
} }
@ -145,8 +146,8 @@ Rectangle {
// "Item Name" text will be set in "onCertificateInfoResult()" // "Item Name" text will be set in "onCertificateInfoResult()"
// "Edition" text will be set in "onCertificateInfoResult()" // "Edition" text will be set in "onCertificateInfoResult()"
// "Owner" text will be set in "onCertificateInfoResult()" // "Owner" text will be set in "onCertificateInfoResult()"
// "Purchase Date" text will be set in "onCertificateInfoResult()" // "Acquisition Date" text will be set in "onCertificateInfoResult()"
// "Purchase Price" text will be set in "onCertificateInfoResult()" // "Acquisition Price" text will be set in "onCertificateInfoResult()"
errorText.text = ""; errorText.text = "";
} else if (root.certificateStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT } else if (root.certificateStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT
root.useGoldCert = false; root.useGoldCert = false;
@ -160,7 +161,7 @@ Rectangle {
root.itemName = ""; root.itemName = "";
root.itemEdition = ""; root.itemEdition = "";
root.itemOwner = ""; root.itemOwner = "";
root.dateOfPurchase = ""; root.dateAcquired = "";
root.itemCost = ""; root.itemCost = "";
errorText.text = "Your request to inspect this item timed out. Please try again later."; errorText.text = "Your request to inspect this item timed out. Please try again later.";
} else if (root.certificateStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED } else if (root.certificateStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED
@ -175,8 +176,8 @@ Rectangle {
// "Item Name" text will be set in "onCertificateInfoResult()" // "Item Name" text will be set in "onCertificateInfoResult()"
// "Edition" text will be set in "onCertificateInfoResult()" // "Edition" text will be set in "onCertificateInfoResult()"
// "Owner" text will be set in "onCertificateInfoResult()" // "Owner" text will be set in "onCertificateInfoResult()"
// "Purchase Date" text will be set in "onCertificateInfoResult()" // "Acquisition Date" text will be set in "onCertificateInfoResult()"
// "Purchase Price" text will be set in "onCertificateInfoResult()" // "Acquisition Price" text will be set in "onCertificateInfoResult()"
errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item."; errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item.";
} else if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED } else if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED
root.useGoldCert = false; root.useGoldCert = false;
@ -190,8 +191,8 @@ Rectangle {
// "Item Name" text will be set in "onCertificateInfoResult()" // "Item Name" text will be set in "onCertificateInfoResult()"
root.itemEdition = "Uncertified Copy" root.itemEdition = "Uncertified Copy"
// "Owner" text will be set in "onCertificateInfoResult()" // "Owner" text will be set in "onCertificateInfoResult()"
// "Purchase Date" text will be set in "onCertificateInfoResult()" // "Acquisition Date" text will be set in "onCertificateInfoResult()"
// "Purchase Price" text will be set in "onCertificateInfoResult()" // "Acquisition Price" text will be set in "onCertificateInfoResult()"
// "Error Text" text will be set in "onCertificateInfoResult()" // "Error Text" text will be set in "onCertificateInfoResult()"
} else { } else {
console.log("Unknown certificate status received from ledger signal!"); console.log("Unknown certificate status received from ledger signal!");
@ -485,8 +486,8 @@ Rectangle {
} }
RalewayRegular { RalewayRegular {
id: dateOfPurchaseHeader; id: dateAcquiredHeader;
text: "PURCHASE DATE"; text: "ACQUISITION DATE";
// Text size // Text size
size: 16; size: 16;
// Anchors // Anchors
@ -500,15 +501,15 @@ Rectangle {
color: hifi.colors.darkGray; color: hifi.colors.darkGray;
} }
AnonymousProRegular { AnonymousProRegular {
id: dateOfPurchase; id: dateAcquired;
text: root.dateOfPurchase; text: root.dateAcquired;
// Text size // Text size
size: 18; size: 18;
// Anchors // Anchors
anchors.top: dateOfPurchaseHeader.bottom; anchors.top: dateAcquiredHeader.bottom;
anchors.topMargin: 8; anchors.topMargin: 8;
anchors.left: dateOfPurchaseHeader.left; anchors.left: dateAcquiredHeader.left;
anchors.right: dateOfPurchaseHeader.right; anchors.right: dateAcquiredHeader.right;
height: paintedHeight; height: paintedHeight;
// Style // Style
color: root.infoTextColor; color: root.infoTextColor;
@ -516,7 +517,7 @@ Rectangle {
RalewayRegular { RalewayRegular {
id: priceHeader; id: priceHeader;
text: "PURCHASE PRICE"; text: "ACQUISITION PRICE";
// Text size // Text size
size: 16; size: 16;
// Anchors // Anchors
@ -530,7 +531,7 @@ Rectangle {
} }
HiFiGlyphs { HiFiGlyphs {
id: hfcGlyph; id: hfcGlyph;
visible: priceText.text !== "Undisclosed" && priceText.text !== ""; visible: priceText.text !== "Undisclosed" && priceText.text !== "" && priceText.text !== "Free";
text: hifi.glyphs.hfc; text: hifi.glyphs.hfc;
// Size // Size
size: 24; size: 24;
@ -618,7 +619,7 @@ Rectangle {
root.itemName = "--"; root.itemName = "--";
root.itemOwner = "--"; root.itemOwner = "--";
root.itemEdition = "--"; root.itemEdition = "--";
root.dateOfPurchase = "--"; root.dateAcquired = "--";
root.marketplaceUrl = ""; root.marketplaceUrl = "";
root.itemCost = "--"; root.itemCost = "--";
root.isMyCert = false; root.isMyCert = false;

View file

@ -47,8 +47,7 @@ Item {
property string wornEntityID; property string wornEntityID;
property string upgradeUrl; property string upgradeUrl;
property string upgradeTitle; property string upgradeTitle;
property bool updateAvailable: root.upgradeUrl !== "" && !root.isShowingMyItems; property bool updateAvailable: root.upgradeUrl !== "";
property bool isShowingMyItems;
property bool valid; property bool valid;
property string originalStatusText; property string originalStatusText;
@ -231,7 +230,7 @@ Item {
Loader { Loader {
id: giftButton; id: giftButton;
visible: !root.isShowingMyItems; visible: root.itemEdition > 0;
sourceComponent: contextCardButton; sourceComponent: contextCardButton;
anchors.right: parent.right; anchors.right: parent.right;
anchors.top: parent.top; anchors.top: parent.top;
@ -345,6 +344,7 @@ Item {
Rectangle { Rectangle {
id: permissionExplanationCard; id: permissionExplanationCard;
visible: false;
anchors.left: parent.left; anchors.left: parent.left;
anchors.leftMargin: 30; anchors.leftMargin: 30;
anchors.top: parent.top; anchors.top: parent.top;

View file

@ -19,7 +19,6 @@ import "../../../controls" as HifiControls
import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
import "../wallet" as HifiWallet import "../wallet" as HifiWallet
import "../common" as HifiCommerceCommon import "../common" as HifiCommerceCommon
import "../inspectionCertificate" as HifiInspectionCertificate
import "../common/sendAsset" as HifiSendAsset import "../common/sendAsset" as HifiSendAsset
import "../.." as HifiCommon import "../.." as HifiCommon
@ -34,7 +33,6 @@ Rectangle {
property bool securityImageResultReceived: false; property bool securityImageResultReceived: false;
property bool purchasesReceived: false; property bool purchasesReceived: false;
property bool punctuationMode: false; property bool punctuationMode: false;
property bool isShowingMyItems: false;
property bool isDebuggingFirstUseTutorial: false; property bool isDebuggingFirstUseTutorial: false;
property string installedApps; property string installedApps;
property bool keyboardRaised: false; property bool keyboardRaised: false;
@ -92,7 +90,6 @@ Rectangle {
if (result.status !== 'success') { if (result.status !== 'success') {
console.log("Failed to get Available Updates", result.data.message); console.log("Failed to get Available Updates", result.data.message);
} else { } else {
sendToScript({method: 'purchases_availableUpdatesReceived', numUpdates: result.data.updates.length });
root.numUpdatesAvailable = result.total_entries; root.numUpdatesAvailable = result.total_entries;
} }
} }
@ -106,10 +103,6 @@ Rectangle {
} }
} }
onIsShowingMyItemsChanged: {
getPurchases();
}
Timer { Timer {
id: notSetUpTimer; id: notSetUpTimer;
interval: 200; interval: 200;
@ -118,19 +111,6 @@ Rectangle {
} }
} }
HifiInspectionCertificate.InspectionCertificate {
id: inspectionCertificate;
z: 998;
visible: false;
anchors.fill: parent;
Connections {
onSendToScript: {
sendToScript(message);
}
}
}
HifiCommerceCommon.CommerceLightbox { HifiCommerceCommon.CommerceLightbox {
id: lightboxPopup; id: lightboxPopup;
z: 999; z: 999;
@ -180,7 +160,8 @@ Rectangle {
HifiCommerceCommon.EmulatedMarketplaceHeader { HifiCommerceCommon.EmulatedMarketplaceHeader {
id: titleBarContainer; id: titleBarContainer;
z: 997; z: 997;
visible: !needsLogIn.visible; visible: false;
height: 100;
// Size // Size
width: parent.width; width: parent.width;
// Anchors // Anchors
@ -199,11 +180,6 @@ Rectangle {
lightboxPopup.button1method = function() { lightboxPopup.button1method = function() {
lightboxPopup.visible = false; lightboxPopup.visible = false;
} }
lightboxPopup.button2text = "GO TO WALLET";
lightboxPopup.button2method = function() {
sendToScript({method: 'purchases_openWallet'});
lightboxPopup.visible = false;
};
lightboxPopup.visible = true; lightboxPopup.visible = true;
} else { } else {
sendToScript(msg); sendToScript(msg);
@ -475,7 +451,7 @@ Rectangle {
anchors.left: parent.left; anchors.left: parent.left;
anchors.leftMargin: 16; anchors.leftMargin: 16;
width: paintedWidth; width: paintedWidth;
text: isShowingMyItems ? "My Items" : "My Purchases"; text: "Inventory";
color: hifi.colors.black; color: hifi.colors.black;
size: 22; size: 22;
} }
@ -517,8 +493,13 @@ Rectangle {
"filterName": "wearable" "filterName": "wearable"
}, },
{ {
"separator" : true,
"displayName": "Updatable", "displayName": "Updatable",
"filterName": "updated" "filterName": "updated"
},
{
"displayName": "My Submissions",
"filterName": "proofs"
} }
] ]
filterBar.primaryFilterChoices.clear(); filterBar.primaryFilterChoices.clear();
@ -533,6 +514,7 @@ Rectangle {
onTextChanged: { onTextChanged: {
purchasesModel.searchFilter = filterBar.text; purchasesModel.searchFilter = filterBar.text;
filterBar.previousText = filterBar.text; filterBar.previousText = filterBar.text;
} }
} }
} }
@ -556,10 +538,18 @@ Rectangle {
listModelName: 'purchases'; listModelName: 'purchases';
listView: purchasesContentsList; listView: purchasesContentsList;
getPage: function () { getPage: function () {
console.debug('getPage', purchasesModel.listModelName, root.isShowingMyItems, filterBar.primaryFilter_filterName, purchasesModel.currentPageToRetrieve, purchasesModel.itemsPerPage); console.debug('getPage', purchasesModel.listModelName, filterBar.primaryFilter_filterName, purchasesModel.currentPageToRetrieve, purchasesModel.itemsPerPage);
var editionFilter = "";
var primaryFilter = "";
if (filterBar.primaryFilter_filterName === "proofs") {
editionFilter = "proofs";
} else {
primaryFilter = filterBar.primaryFilter_filterName;
}
Commerce.inventory( Commerce.inventory(
root.isShowingMyItems ? "proofs" : "purchased", editionFilter,
filterBar.primaryFilter_filterName, primaryFilter,
filterBar.text, filterBar.text,
purchasesModel.currentPageToRetrieve, purchasesModel.currentPageToRetrieve,
purchasesModel.itemsPerPage purchasesModel.itemsPerPage
@ -609,7 +599,6 @@ Rectangle {
upgradeUrl: model.upgrade_url; upgradeUrl: model.upgrade_url;
upgradeTitle: model.upgrade_title; upgradeTitle: model.upgrade_title;
itemType: model.item_type; itemType: model.item_type;
isShowingMyItems: root.isShowingMyItems;
valid: model.valid; valid: model.valid;
anchors.topMargin: 10; anchors.topMargin: 10;
anchors.bottomMargin: 10; anchors.bottomMargin: 10;
@ -626,8 +615,6 @@ Rectangle {
sendToScript({ method: 'purchases_updateWearables' }); sendToScript({ method: 'purchases_updateWearables' });
} }
} else if (msg.method === 'purchases_itemCertificateClicked') { } else if (msg.method === 'purchases_itemCertificateClicked') {
inspectionCertificate.visible = true;
inspectionCertificate.isLightbox = true;
sendToScript(msg); sendToScript(msg);
} else if (msg.method === "showInvalidatedLightbox") { } else if (msg.method === "showInvalidatedLightbox") {
lightboxPopup.titleText = "Item Invalidated"; lightboxPopup.titleText = "Item Invalidated";
@ -640,7 +627,7 @@ Rectangle {
lightboxPopup.visible = true; lightboxPopup.visible = true;
} else if (msg.method === "showPendingLightbox") { } else if (msg.method === "showPendingLightbox") {
lightboxPopup.titleText = "Item Pending"; lightboxPopup.titleText = "Item Pending";
lightboxPopup.bodyText = 'Your item is marked "pending" while your purchase is being confirmed. ' + lightboxPopup.bodyText = 'Your item is marked "pending" while the transfer is being confirmed. ' +
"Usually, purchases take about 90 seconds to confirm."; "Usually, purchases take about 90 seconds to confirm.";
lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = function() { lightboxPopup.button1method = function() {
@ -822,7 +809,8 @@ Rectangle {
Rectangle { Rectangle {
id: updatesAvailableBanner; id: updatesAvailableBanner;
visible: root.numUpdatesAvailable > 0 && !root.isShowingMyItems; visible: root.numUpdatesAvailable > 0 &&
filterBar.primaryFilter_filterName !== "proofs";
anchors.bottom: parent.bottom; anchors.bottom: parent.bottom;
anchors.left: parent.left; anchors.left: parent.left;
anchors.right: parent.right; anchors.right: parent.right;
@ -883,9 +871,8 @@ Rectangle {
id: noItemsAlertContainer; id: noItemsAlertContainer;
visible: !purchasesContentsList.visible && visible: !purchasesContentsList.visible &&
root.purchasesReceived && root.purchasesReceived &&
root.isShowingMyItems &&
filterBar.text === "" && filterBar.text === "" &&
filterBar.primaryFilter_displayName === ""; filterBar.primaryFilter_filterName === "proofs";
anchors.top: filterBarContainer.bottom; anchors.top: filterBarContainer.bottom;
anchors.topMargin: 12; anchors.topMargin: 12;
anchors.left: parent.left; anchors.left: parent.left;
@ -895,7 +882,7 @@ Rectangle {
// Explanitory text // Explanitory text
RalewayRegular { RalewayRegular {
id: noItemsYet; id: noItemsYet;
text: "<b>You haven't submitted anything to the Marketplace yet!</b><br><br>Submit an item to the Marketplace to add it to My Items."; text: "<b>You haven't submitted anything to the Marketplace yet!</b><br><br>Submit an item to the Marketplace to add it to My Submissions.";
// Text size // Text size
size: 22; size: 22;
// Anchors // Anchors
@ -933,7 +920,6 @@ Rectangle {
id: noPurchasesAlertContainer; id: noPurchasesAlertContainer;
visible: !purchasesContentsList.visible && visible: !purchasesContentsList.visible &&
root.purchasesReceived && root.purchasesReceived &&
!root.isShowingMyItems &&
filterBar.text === "" && filterBar.text === "" &&
filterBar.primaryFilter_displayName === ""; filterBar.primaryFilter_displayName === "";
anchors.top: filterBarContainer.bottom; anchors.top: filterBarContainer.bottom;
@ -945,7 +931,7 @@ Rectangle {
// Explanitory text // Explanitory text
RalewayRegular { RalewayRegular {
id: haventPurchasedYet; id: haventPurchasedYet;
text: "<b>You haven't purchased anything yet!</b><br><br>Get an item from <b>Marketplace</b> to add it to My Purchases."; text: "<b>You haven't gotten anything yet!</b><br><br>Get an item from <b>Marketplace</b> to add it to your Inventory.";
// Text size // Text size
size: 22; size: 22;
// Anchors // Anchors
@ -1065,11 +1051,10 @@ Rectangle {
titleBarContainer.referrerURL = message.referrerURL || ""; titleBarContainer.referrerURL = message.referrerURL || "";
filterBar.text = message.filterText ? message.filterText : ""; filterBar.text = message.filterText ? message.filterText : "";
break; break;
case 'inspectionCertificate_setCertificateId':
inspectionCertificate.fromScript(message);
break;
case 'purchases_showMyItems': case 'purchases_showMyItems':
root.isShowingMyItems = true; filterBar.primaryFilter_filterName = "proofs";
filterBar.primaryFilter_displayName = "Proofs";
filterBar.primaryFilter_index = 6;
break; break;
case 'updateConnections': case 'updateConnections':
sendAsset.updateConnections(message.connections); sendAsset.updateConnections(message.connections);

View file

@ -62,7 +62,7 @@ Item {
isExpanded: false; isExpanded: false;
question: "How can I get HFC?"; question: "How can I get HFC?";
answer: "High Fidelity commerce is in open beta right now. Want more HFC? \ answer: "High Fidelity commerce is in open beta right now. Want more HFC? \
Get it by going to <br><br><b><font color='#0093C5'><a href='#bank'>BankOfHighFidelity.</a></font></b> and meeting with the banker!"; Get it by going to <b><font color='#0093C5'><a href='#bank'>BankOfHighFidelity</a></font></b> and meeting with the banker!";
} }
ListElement { ListElement {
isExpanded: false; isExpanded: false;

View file

@ -93,7 +93,7 @@ Item {
// Text below helper text // Text below helper text
RalewayRegular { RalewayRegular {
id: loginDetailText; id: loginDetailText;
text: "To buy/sell items on the <b>Marketplace</b>, or to use your <b>Wallet</b>, you must first log in to High Fidelity."; text: "To get items on the <b>Marketplace</b>, or to use your <b>Assets</b>, you must first log in to High Fidelity.";
// Text size // Text size
size: 18; size: 18;
// Anchors // Anchors

View file

@ -20,6 +20,8 @@ import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon import "../common" as HifiCommerceCommon
import "../common/sendAsset" import "../common/sendAsset"
import "../.." as HifiCommon import "../.." as HifiCommon
import "../purchases" as HifiPurchases
import "../inspectionCertificate" as HifiInspectionCertificate
Rectangle { Rectangle {
HifiConstants { id: hifi; } HifiConstants { id: hifi; }
@ -27,6 +29,7 @@ Rectangle {
id: root; id: root;
property string activeView: "initialize"; property string activeView: "initialize";
property string initialActiveViewAfterStatus5: "walletInventory";
property bool keyboardRaised: false; property bool keyboardRaised: false;
property bool isPassword: false; property bool isPassword: false;
@ -64,7 +67,8 @@ Rectangle {
} }
} else if (walletStatus === 5) { } else if (walletStatus === 5) {
if (root.activeView !== "walletSetup") { if (root.activeView !== "walletSetup") {
root.activeView = "walletHome"; root.activeView = root.initialActiveViewAfterStatus5;
Commerce.getAvailableUpdates();
Commerce.getSecurityImage(); Commerce.getSecurityImage();
} }
} else { } else {
@ -86,6 +90,21 @@ Rectangle {
titleBarSecurityImage.source = "image://security/securityImage"; titleBarSecurityImage.source = "image://security/securityImage";
} }
} }
onAvailableUpdatesResult: {
if (result.status !== 'success') {
console.log("Failed to get Available Updates", result.data.message);
} else {
exchangeMoneyButtonContainer.messagesWaiting = result.data.updates.length > 0;
}
}
}
onActiveViewChanged: {
if (activeView === "walletHome") {
walletHomeButtonContainer.messagesWaiting = false;
sendToScript({method: 'clearShouldShowDotHistory'});
}
} }
HifiCommerceCommon.CommerceLightbox { HifiCommerceCommon.CommerceLightbox {
@ -124,7 +143,7 @@ Rectangle {
// Title Bar text // Title Bar text
RalewaySemiBold { RalewaySemiBold {
id: titleBarText; id: titleBarText;
text: "WALLET"; text: "ASSETS";
// Text size // Text size
size: hifi.fontSizes.overlayTitle; size: hifi.fontSizes.overlayTitle;
// Anchors // Anchors
@ -319,6 +338,39 @@ Rectangle {
} }
} }
HifiInspectionCertificate.InspectionCertificate {
id: inspectionCertificate;
z: 998;
visible: false;
anchors.fill: parent;
Connections {
onSendToScript: {
sendToScript(message);
}
}
}
HifiPurchases.Purchases {
id: walletInventory;
visible: root.activeView === "walletInventory";
anchors.top: titleBarContainer.bottom;
anchors.bottom: tabButtonsContainer.top;
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendToScript: {
if (message.method === 'purchases_itemCertificateClicked') {
inspectionCertificate.visible = true;
inspectionCertificate.isLightbox = true;
sendToScript(message);
} else {
sendToScript(message);
}
}
}
}
HifiCommon.RootHttpRequest { HifiCommon.RootHttpRequest {
id: http; id: http;
} }
@ -380,16 +432,17 @@ Rectangle {
// "WALLET HOME" tab button // "WALLET HOME" tab button
Rectangle { Rectangle {
id: walletHomeButtonContainer; id: walletHomeButtonContainer;
property bool messagesWaiting: false;
visible: !walletSetup.visible; visible: !walletSetup.visible;
color: root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black; color: root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top; anchors.top: parent.top;
anchors.left: parent.left; anchors.left: exchangeMoneyButtonContainer.right;
anchors.bottom: parent.bottom; anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs; width: parent.width / tabButtonsContainer.numTabs;
HiFiGlyphs { HiFiGlyphs {
id: homeTabIcon; id: homeTabIcon;
text: hifi.glyphs.home2; text: hifi.glyphs.leftRightArrows;
// Size // Size
size: 50; size: 50;
// Anchors // Anchors
@ -397,11 +450,24 @@ Rectangle {
anchors.top: parent.top; anchors.top: parent.top;
anchors.topMargin: -2; anchors.topMargin: -2;
// Style // Style
color: root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight);
}
Rectangle {
id: recentActivityMessagesWaitingLight;
visible: parent.messagesWaiting;
anchors.right: homeTabIcon.left;
anchors.rightMargin: -4;
anchors.top: homeTabIcon.top;
anchors.topMargin: 16;
height: 10;
width: height;
radius: height/2;
color: "red";
} }
RalewaySemiBold { RalewaySemiBold {
text: "WALLET HOME"; text: "RECENT ACTIVITY";
// Text size // Text size
size: 16; size: 16;
// Anchors // Anchors
@ -412,7 +478,7 @@ Rectangle {
anchors.right: parent.right; anchors.right: parent.right;
anchors.rightMargin: 4; anchors.rightMargin: 4;
// Style // Style
color: root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight);
wrapMode: Text.WordWrap; wrapMode: Text.WordWrap;
// Alignment // Alignment
horizontalAlignment: Text.AlignHCenter; horizontalAlignment: Text.AlignHCenter;
@ -421,6 +487,7 @@ Rectangle {
MouseArea { MouseArea {
id: walletHomeTabMouseArea; id: walletHomeTabMouseArea;
anchors.fill: parent; anchors.fill: parent;
enabled: !WalletScriptingInterface.limitedCommerce;
hoverEnabled: enabled; hoverEnabled: enabled;
onClicked: { onClicked: {
root.activeView = "walletHome"; root.activeView = "walletHome";
@ -434,16 +501,18 @@ Rectangle {
// "EXCHANGE MONEY" tab button // "EXCHANGE MONEY" tab button
Rectangle { Rectangle {
id: exchangeMoneyButtonContainer; id: exchangeMoneyButtonContainer;
property bool messagesWaiting: false;
visible: !walletSetup.visible; visible: !walletSetup.visible;
color: hifi.colors.black; color: root.activeView === "walletInventory" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top; anchors.top: parent.top;
anchors.left: walletHomeButtonContainer.right; anchors.left: parent.left;
anchors.bottom: parent.bottom; anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs; width: parent.width / tabButtonsContainer.numTabs;
HiFiGlyphs { HiFiGlyphs {
id: exchangeMoneyTabIcon; id: exchangeMoneyTabIcon;
text: hifi.glyphs.leftRightArrows; text: hifi.glyphs.home2;
// Size // Size
size: 50; size: 50;
// Anchors // Anchors
@ -451,11 +520,24 @@ Rectangle {
anchors.top: parent.top; anchors.top: parent.top;
anchors.topMargin: -2; anchors.topMargin: -2;
// Style // Style
color: hifi.colors.lightGray50; color: root.activeView === "walletInventory" || inventoryTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
}
Rectangle {
id: exchangeMoneyMessagesWaitingLight;
visible: parent.messagesWaiting;
anchors.right: exchangeMoneyTabIcon.left;
anchors.rightMargin: 9;
anchors.top: exchangeMoneyTabIcon.top;
anchors.topMargin: 16;
height: 10;
width: height;
radius: height/2;
color: "red";
} }
RalewaySemiBold { RalewaySemiBold {
text: "EXCHANGE MONEY"; text: "INVENTORY";
// Text size // Text size
size: 16; size: 16;
// Anchors // Anchors
@ -466,12 +548,24 @@ Rectangle {
anchors.right: parent.right; anchors.right: parent.right;
anchors.rightMargin: 4; anchors.rightMargin: 4;
// Style // Style
color: hifi.colors.lightGray50; color: root.activeView === "walletInventory" || inventoryTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
wrapMode: Text.WordWrap; wrapMode: Text.WordWrap;
// Alignment // Alignment
horizontalAlignment: Text.AlignHCenter; horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignTop; verticalAlignment: Text.AlignTop;
} }
MouseArea {
id: inventoryTabMouseArea;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
root.activeView = "walletInventory";
tabButtonsContainer.resetTabButtonColors();
}
onEntered: parent.color = hifi.colors.blueHighlight;
onExited: parent.color = root.activeView === "walletInventory" ? hifi.colors.blueAccent : hifi.colors.black;
}
} }
@ -481,7 +575,7 @@ Rectangle {
visible: !walletSetup.visible; visible: !walletSetup.visible;
color: root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black; color: root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top; anchors.top: parent.top;
anchors.left: exchangeMoneyButtonContainer.right; anchors.left: walletHomeButtonContainer.right;
anchors.bottom: parent.bottom; anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs; width: parent.width / tabButtonsContainer.numTabs;
@ -495,7 +589,7 @@ Rectangle {
anchors.top: parent.top; anchors.top: parent.top;
anchors.topMargin: -2; anchors.topMargin: -2;
// Style // Style
color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight);
} }
RalewaySemiBold { RalewaySemiBold {
@ -510,7 +604,7 @@ Rectangle {
anchors.right: parent.right; anchors.right: parent.right;
anchors.rightMargin: 4; anchors.rightMargin: 4;
// Style // Style
color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight);
wrapMode: Text.WordWrap; wrapMode: Text.WordWrap;
// Alignment // Alignment
horizontalAlignment: Text.AlignHCenter; horizontalAlignment: Text.AlignHCenter;
@ -520,6 +614,7 @@ Rectangle {
MouseArea { MouseArea {
id: sendMoneyTabMouseArea; id: sendMoneyTabMouseArea;
anchors.fill: parent; anchors.fill: parent;
enabled: !WalletScriptingInterface.limitedCommerce;
hoverEnabled: enabled; hoverEnabled: enabled;
onClicked: { onClicked: {
root.activeView = "sendMoney"; root.activeView = "sendMoney";
@ -588,16 +683,16 @@ Rectangle {
function resetTabButtonColors() { function resetTabButtonColors() {
walletHomeButtonContainer.color = hifi.colors.black; walletHomeButtonContainer.color = hifi.colors.black;
sendMoneyButtonContainer.color = hifi.colors.black; sendMoneyButtonContainer.color = hifi.colors.black;
securityButtonContainer.color = hifi.colors.black;
helpButtonContainer.color = hifi.colors.black; helpButtonContainer.color = hifi.colors.black;
exchangeMoneyButtonContainer.color = hifi.colors.black;
if (root.activeView === "walletHome") { if (root.activeView === "walletHome") {
walletHomeButtonContainer.color = hifi.colors.blueAccent; walletHomeButtonContainer.color = hifi.colors.blueAccent;
} else if (root.activeView === "sendMoney") { } else if (root.activeView === "sendMoney") {
sendMoneyButtonContainer.color = hifi.colors.blueAccent; sendMoneyButtonContainer.color = hifi.colors.blueAccent;
} else if (root.activeView === "security") {
securityButtonContainer.color = hifi.colors.blueAccent;
} else if (root.activeView === "help") { } else if (root.activeView === "help") {
helpButtonContainer.color = hifi.colors.blueAccent; helpButtonContainer.color = hifi.colors.blueAccent;
} else if (root.activeView == "walletInventory") {
exchangeMoneyButtonContainer.color = hifi.colors.blueAccent;
} }
} }
} }
@ -663,18 +758,40 @@ Rectangle {
break; break;
case 'updateConnections': case 'updateConnections':
sendMoney.updateConnections(message.connections); sendMoney.updateConnections(message.connections);
walletInventory.fromScript(message);
break; break;
case 'selectRecipient': case 'selectRecipient':
case 'updateSelectedRecipientUsername': case 'updateSelectedRecipientUsername':
sendMoney.fromScript(message); sendMoney.fromScript(message);
walletInventory.fromScript(message);
break; break;
case 'http.response': case 'http.response':
http.handleHttpResponse(message); http.handleHttpResponse(message);
// Duplicate handler is required because we don't track referrer for `http`
walletInventory.fromScript(message);
break; break;
case 'palIsStale': case 'palIsStale':
case 'avatarDisconnected': case 'avatarDisconnected':
// Because we don't have "channels" for sending messages to a specific QML object, the messages are broadcast to all QML Items. If an Item of yours happens to be visible when some script sends a message with a method you don't expect, you'll get "Unrecognized message..." logs. // Because we don't have "channels" for sending messages to a specific QML object, the messages are broadcast to all QML Items. If an Item of yours happens to be visible when some script sends a message with a method you don't expect, you'll get "Unrecognized message..." logs.
break; break;
case 'inspectionCertificate_setCertificateId':
inspectionCertificate.fromScript(message);
break;
case 'updatePurchases':
case 'purchases_showMyItems':
case 'updateWearables':
walletInventory.fromScript(message);
break;
case 'updateRecentActivityMessageLight':
walletHomeButtonContainer.messagesWaiting = message.messagesWaiting;
break;
case 'checkout_openRecentActivity':
if (root.activeView === "initialize") {
root.initialActiveViewAfterStatus5 = "walletHome";
} else {
root.activeView = "walletHome";
}
break;
default: default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
} }
@ -722,7 +839,8 @@ Rectangle {
root.activeView = "initialize"; root.activeView = "initialize";
Commerce.getWalletStatus(); Commerce.getWalletStatus();
} else if (msg.referrer === 'purchases') { } else if (msg.referrer === 'purchases') {
sendToScript({method: 'goToPurchases'}); root.activeView = "walletInventory";
tabButtonsContainer.resetTabButtonColors();
} else if (msg.referrer === 'marketplace cta' || msg.referrer === 'mainPage') { } else if (msg.referrer === 'marketplace cta' || msg.referrer === 'mainPage') {
sendToScript({method: 'goToMarketplaceMainPage', itemId: msg.referrer}); sendToScript({method: 'goToMarketplaceMainPage', itemId: msg.referrer});
} else { } else {

View file

@ -179,28 +179,6 @@ Item {
color: hifi.colors.baseGrayHighlight; color: hifi.colors.baseGrayHighlight;
} }
RalewaySemiBold {
id: myPurchasesLink;
text: '<font color="#0093C5"><a href="#myPurchases">My Purchases</a></font>';
// Anchors
anchors.top: parent.top;
anchors.topMargin: 26;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: paintedWidth;
height: 30;
y: 4;
// Text size
size: 18;
// Style
color: hifi.colors.baseGrayHighlight;
horizontalAlignment: Text.AlignRight;
onLinkActivated: {
sendSignalToWallet({method: 'goToPurchases_fromWalletHome'});
}
}
HifiModels.PSFListModel { HifiModels.PSFListModel {
id: transactionHistoryModel; id: transactionHistoryModel;
property int lastPendingCount: 0; property int lastPendingCount: 0;

View file

@ -328,7 +328,7 @@ Rectangle {
HifiStylesUit.RalewayRegular { HifiStylesUit.RalewayRegular {
text: "Your wallet is not set up.\n" + text: "Your wallet is not set up.\n" +
"Open the WALLET app to get started."; "Open the ASSETS app to get started.";
// Anchors // Anchors
anchors.fill: parent; anchors.fill: parent;
// Text size // Text size

View file

@ -1148,7 +1148,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store // set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store
static const QString OCULUS_STORE_ARG = "--oculus-store"; static const QString OCULUS_STORE_ARG = "--oculus-store";
setProperty(hifi::properties::OCULUS_STORE, arguments().indexOf(OCULUS_STORE_ARG) != -1); bool isStore = arguments().indexOf(OCULUS_STORE_ARG) != -1;
setProperty(hifi::properties::OCULUS_STORE, isStore);
DependencyManager::get<WalletScriptingInterface>()->setLimitedCommerce(isStore); // Or we could make it a separate arg, or if either arg is set, etc. And should this instead by a hifi::properties?
updateHeartbeat(); updateHeartbeat();
@ -3152,7 +3154,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
surfaceContext->setContextProperty("Selection", DependencyManager::get<SelectionScriptingInterface>().data()); surfaceContext->setContextProperty("Selection", DependencyManager::get<SelectionScriptingInterface>().data());
surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data()); surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
surfaceContext->setContextProperty("Wallet", DependencyManager::get<WalletScriptingInterface>().data()); surfaceContext->setContextProperty("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance());
surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data()); surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
@ -6874,7 +6876,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance()); scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance());
scriptEngine->registerGlobalObject("Selection", DependencyManager::get<SelectionScriptingInterface>().data()); scriptEngine->registerGlobalObject("Selection", DependencyManager::get<SelectionScriptingInterface>().data());
scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data()); scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
scriptEngine->registerGlobalObject("Wallet", DependencyManager::get<WalletScriptingInterface>().data()); scriptEngine->registerGlobalObject("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get<AddressManager>().data()); scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get<AddressManager>().data());
scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance()); scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance());
scriptEngine->registerGlobalObject("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data()); scriptEngine->registerGlobalObject("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
@ -7879,6 +7881,7 @@ void Application::loadAvatarBrowser() const {
auto tablet = dynamic_cast<TabletProxy*>(DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system")); auto tablet = dynamic_cast<TabletProxy*>(DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system"));
// construct the url to the marketplace item // construct the url to the marketplace item
QString url = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace?category=avatars"; QString url = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace?category=avatars";
QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js"; QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js";
tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH); tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH);
DependencyManager::get<HMDScriptingInterface>()->openTablet(); DependencyManager::get<HMDScriptingInterface>()->openTablet();

View file

@ -244,7 +244,6 @@ QString transactionString(const QJsonObject& valueObject) {
return result; return result;
} }
static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/";
void Ledger::historySuccess(QNetworkReply* reply) { void Ledger::historySuccess(QNetworkReply* reply) {
// here we send a historyResult with some extra stuff in it // here we send a historyResult with some extra stuff in it
// Namely, the styled text we'd like to show. The issue is the // Namely, the styled text we'd like to show. The issue is the

View file

@ -10,13 +10,15 @@
// //
#include "WalletScriptingInterface.h" #include "WalletScriptingInterface.h"
#include <SettingHandle.h>
CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qmlObject, parent) { CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qmlObject, parent) {
Q_ASSERT(QThread::currentThread() == qApp->thread()); Q_ASSERT(QThread::currentThread() == qApp->thread());
} }
WalletScriptingInterface::WalletScriptingInterface() { WalletScriptingInterface::WalletScriptingInterface() {
connect(DependencyManager::get<AccountManager>().data(),
&AccountManager::limitedCommerceChanged, this, &WalletScriptingInterface::limitedCommerceChanged);
} }
void WalletScriptingInterface::refreshWalletStatus() { void WalletScriptingInterface::refreshWalletStatus() {

View file

@ -15,13 +15,13 @@
#include <QtCore/QObject> #include <QtCore/QObject>
#include <DependencyManager.h> #include <DependencyManager.h>
#include "scripting/HMDScriptingInterface.h"
#include <ui/TabletScriptingInterface.h> #include <ui/TabletScriptingInterface.h>
#include <ui/QmlWrapper.h> #include <ui/QmlWrapper.h>
#include <OffscreenUi.h> #include <OffscreenUi.h>
#include "Application.h" #include "Application.h"
#include "commerce/Wallet.h" #include "commerce/Wallet.h"
#include "ui/overlays/ContextOverlayInterface.h" #include "ui/overlays/ContextOverlayInterface.h"
#include <AccountManager.h>
class CheckoutProxy : public QmlWrapper { class CheckoutProxy : public QmlWrapper {
Q_OBJECT Q_OBJECT
@ -29,7 +29,6 @@ public:
CheckoutProxy(QObject* qmlObject, QObject* parent = nullptr); CheckoutProxy(QObject* qmlObject, QObject* parent = nullptr);
}; };
/**jsdoc /**jsdoc
* @namespace Wallet * @namespace Wallet
* *
@ -37,28 +36,31 @@ public:
* @hifi-client-entity * @hifi-client-entity
* *
* @property {number} walletStatus * @property {number} walletStatus
* @property {bool} limitedCommerce
*/ */
class WalletScriptingInterface : public QObject, public Dependency { class WalletScriptingInterface : public QObject, public Dependency {
Q_OBJECT Q_OBJECT
SINGLETON_DEPENDENCY
Q_PROPERTY(uint walletStatus READ getWalletStatus WRITE setWalletStatus NOTIFY walletStatusChanged) Q_PROPERTY(uint walletStatus READ getWalletStatus WRITE setWalletStatus NOTIFY walletStatusChanged)
Q_PROPERTY(bool limitedCommerce READ getLimitedCommerce WRITE setLimitedCommerce NOTIFY limitedCommerceChanged)
public: public:
WalletScriptingInterface(); WalletScriptingInterface();
/**jsdoc /**jsdoc
* @function Wallet.refreshWalletStatus * @function WalletScriptingInterface.refreshWalletStatus
*/ */
Q_INVOKABLE void refreshWalletStatus(); Q_INVOKABLE void refreshWalletStatus();
/**jsdoc /**jsdoc
* @function Wallet.getWalletStatus * @function WalletScriptingInterface.getWalletStatus
* @returns {number} * @returns {number}
*/ */
Q_INVOKABLE uint getWalletStatus() { return _walletStatus; } Q_INVOKABLE uint getWalletStatus() { return _walletStatus; }
/**jsdoc /**jsdoc
* @function Wallet.proveAvatarEntityOwnershipVerification * @function WalletScriptingInterface.proveAvatarEntityOwnershipVerification
* @param {Uuid} entityID * @param {Uuid} entityID
*/ */
Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID); Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID);
@ -67,29 +69,38 @@ public:
// scripts could cause the Wallet to incorrectly report its status. // scripts could cause the Wallet to incorrectly report its status.
void setWalletStatus(const uint& status); void setWalletStatus(const uint& status);
bool getLimitedCommerce() { return DependencyManager::get<AccountManager>()->getLimitedCommerce(); }
void setLimitedCommerce(bool isLimited) { DependencyManager::get<AccountManager>()->setLimitedCommerce(isLimited); };
signals: signals:
/**jsdoc /**jsdoc
* @function Wallet.walletStatusChanged * @function WalletScriptingInterface.walletStatusChanged
* @returns {Signal} * @returns {Signal}
*/ */
void walletStatusChanged(); void walletStatusChanged();
/**jsdoc /**jsdoc
* @function Wallet.walletNotSetup * @function WalletScriptingInterface.limitedCommerceChanged
* @returns {Signal}
*/
void limitedCommerceChanged();
/**jsdoc
* @function WalletScriptingInterface.walletNotSetup
* @returns {Signal} * @returns {Signal}
*/ */
void walletNotSetup(); void walletNotSetup();
/**jsdoc /**jsdoc
* @function Wallet.ownershipVerificationSuccess * @function WalletScriptingInterface.ownershipVerificationSuccess
* @param {Uuid} entityID * @param {Uuid} entityID
* @returns {Signal} * @returns {Signal}
*/ */
void ownershipVerificationSuccess(const QUuid& entityID); void ownershipVerificationSuccess(const QUuid& entityID);
/**jsdoc /**jsdoc
* @function Wallet.ownershipVerificationFailed * @function WalletScriptingInterface.ownershipVerificationFailed
* @param {Uuid} entityID * @param {Uuid} entityID
* @returns {Signal} * @returns {Signal}
*/ */

View file

@ -223,12 +223,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID&
bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) { bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) {
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags);
Setting::Handle<bool> _settingSwitch{ "commerce", true };
if (_settingSwitch.get()) {
return (entityProperties.getCertificateID().length() != 0); return (entityProperties.getCertificateID().length() != 0);
} else {
return (entityProperties.getMarketplaceID().length() != 0);
}
} }
bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) {

View file

@ -53,6 +53,7 @@
#include "ui/AvatarInputs.h" #include "ui/AvatarInputs.h"
#include "avatar/AvatarManager.h" #include "avatar/AvatarManager.h"
#include "scripting/AccountServicesScriptingInterface.h" #include "scripting/AccountServicesScriptingInterface.h"
#include "scripting/WalletScriptingInterface.h"
#include <plugins/InputConfiguration.h> #include <plugins/InputConfiguration.h>
#include "ui/Snapshot.h" #include "ui/Snapshot.h"
#include "SoundCacheScriptingInterface.h" #include "SoundCacheScriptingInterface.h"
@ -270,6 +271,7 @@ void Web3DOverlay::setupQmlSurface(bool isTablet) {
_webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data()); _webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface()); _webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface());
_webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data()); _webSurface->getSurfaceContext()->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
// Override min fps for tablet UI, for silky smooth scrolling // Override min fps for tablet UI, for silky smooth scrolling

View file

@ -837,3 +837,7 @@ void AccountManager::handleKeypairGenerationError() {
// reset our waiting state for keypair response // reset our waiting state for keypair response
_isWaitingForKeypairResponse = false; _isWaitingForKeypairResponse = false;
} }
void AccountManager::setLimitedCommerce(bool isLimited) {
_limitedCommerce = isLimited;
}

View file

@ -98,6 +98,9 @@ public:
void removeAccountFromFile(); void removeAccountFromFile();
bool getLimitedCommerce() { return _limitedCommerce; }
void setLimitedCommerce(bool isLimited);
public slots: public slots:
void requestAccessToken(const QString& login, const QString& password); void requestAccessToken(const QString& login, const QString& password);
void requestAccessTokenWithSteam(QByteArray authSessionTicket); void requestAccessTokenWithSteam(QByteArray authSessionTicket);
@ -121,6 +124,7 @@ signals:
void loginFailed(); void loginFailed();
void logoutComplete(); void logoutComplete();
void newKeypair(); void newKeypair();
void limitedCommerceChanged();
private slots: private slots:
void handleKeypairGenerationError(); void handleKeypairGenerationError();
@ -150,6 +154,8 @@ private:
QByteArray _pendingPrivateKey; QByteArray _pendingPrivateKey;
QUuid _sessionID { QUuid::createUuid() }; QUuid _sessionID { QUuid::createUuid() };
bool _limitedCommerce { false };
}; };
#endif // hifi_AccountManager_h #endif // hifi_AccountManager_h

View file

@ -70,9 +70,9 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info,
// check if this is a request to a highfidelity URL // check if this is a request to a highfidelity URL
bool isAuthable = isAuthableHighFidelityURL(info.requestUrl()); bool isAuthable = isAuthableHighFidelityURL(info.requestUrl());
auto accountManager = DependencyManager::get<AccountManager>();
if (isAuthable) { if (isAuthable) {
// if we have an access token, add it to the right HTTP header for authorization // if we have an access token, add it to the right HTTP header for authorization
auto accountManager = DependencyManager::get<AccountManager>();
if (accountManager->hasValidAccessToken()) { if (accountManager->hasValidAccessToken()) {
static const QString OAUTH_AUTHORIZATION_HEADER = "Authorization"; static const QString OAUTH_AUTHORIZATION_HEADER = "Authorization";
@ -84,13 +84,9 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info,
static const QString USER_AGENT = "User-Agent"; static const QString USER_AGENT = "User-Agent";
const QString tokenStringMobile{ "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36" }; const QString tokenStringMobile{ "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36" };
const QString tokenStringMetaverse{ "Chrome/48.0 (HighFidelityInterface)" }; const QString tokenStringMetaverse{ "Chrome/48.0 (HighFidelityInterface)" };
const QString tokenStringLimitedCommerce{ "Chrome/48.0 (HighFidelityInterface limitedCommerce)" };
// During the period in which we have HFC commerce in the system, but not applied everywhere: const QString tokenString = !isAuthable ? tokenStringMobile : (accountManager->getLimitedCommerce() ? tokenStringLimitedCommerce : tokenStringMetaverse);
const QString tokenStringCommerce{ "Chrome/48.0 (HighFidelityInterface WithHFC)" };
Setting::Handle<bool> _settingSwitch{ "commerce", true };
bool isMoney = _settingSwitch.get();
const QString tokenString = !isAuthable ? tokenStringMobile : (isMoney ? tokenStringCommerce : tokenStringMetaverse);
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit()); info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
} }

View file

@ -95,16 +95,16 @@ function AppUi(properties) {
activeIcon: isWaiting ? that.activeMessagesButton : that.activeButton activeIcon: isWaiting ? that.activeMessagesButton : that.activeButton
}); });
}; };
that.notificationPollTimeout = false; that.notificationPollTimeout = [false];
that.notificationPollTimeoutMs = 60000; that.notificationPollTimeoutMs = [60000];
that.notificationPollEndpoint = false; that.notificationPollEndpoint = [false];
that.notificationPollStopPaginatingConditionMet = false; that.notificationPollStopPaginatingConditionMet = [false];
that.notificationDataProcessPage = function (data) { that.notificationDataProcessPage = function (data) {
return data; return data;
}; };
that.notificationPollCallback = that.ignore; that.notificationPollCallback = [that.ignore];
that.notificationPollCaresAboutSince = false; that.notificationPollCaresAboutSince = [false];
that.notificationInitialCallbackMade = false; that.notificationInitialCallbackMade = [false];
that.notificationDisplayBanner = function (message) { that.notificationDisplayBanner = function (message) {
if (!that.isOpen) { if (!that.isOpen) {
Window.displayAnnouncement(message); Window.displayAnnouncement(message);
@ -129,7 +129,9 @@ function AppUi(properties) {
} }
that.isOpen = true; that.isOpen = true;
} }
} else { // Not us. Should we do something for type Home, Menu, and particularly Closed (meaning tablet hidden? } else {
// A different screen is now visible, or the tablet has been closed.
// Tablet visibility is controlled separately by `tabletShownChanged()`
that.wireEventBridge(false); that.wireEventBridge(false);
if (that.isOpen) { if (that.isOpen) {
that.buttonActive(false); that.buttonActive(false);
@ -139,83 +141,124 @@ function AppUi(properties) {
that.isOpen = false; that.isOpen = false;
} }
} }
// console.debug(that.buttonName + " app reports: Tablet screen changed.\nNew screen type: " + type +
// "\nNew screen URL: " + url + "\nCurrent app open status: " + that.isOpen + "\n");
}; };
// Overwrite with the given properties: // Overwrite with the given properties:
Object.keys(properties).forEach(function (key) { that[key] = properties[key]; }); Object.keys(properties).forEach(function (key) {
that[key] = properties[key];
});
// //
// START Notification Handling // START Notification Handling
// //
var currentDataPageToRetrieve = [];
var concatenatedServerResponse = [];
for (var i = 0; i < that.notificationPollEndpoint.length; i++) {
currentDataPageToRetrieve[i] = 1;
concatenatedServerResponse[i] = new Array();
}
var MAX_LOG_LENGTH_CHARACTERS = 300;
function requestCallback(error, response, optionalParams) {
var indexOfRequest = optionalParams.indexOfRequest;
var urlOfRequest = optionalParams.urlOfRequest;
if (error || (response.status !== 'success')) {
print("Error: unable to get", urlOfRequest, error || response.status);
startNotificationTimer(indexOfRequest);
return;
}
if (!that.notificationPollStopPaginatingConditionMet[indexOfRequest] ||
that.notificationPollStopPaginatingConditionMet[indexOfRequest](response)) {
startNotificationTimer(indexOfRequest);
var notificationData;
if (concatenatedServerResponse[indexOfRequest].length) {
notificationData = concatenatedServerResponse[indexOfRequest];
} else {
notificationData = that.notificationDataProcessPage[indexOfRequest](response);
}
console.debug(that.buttonName, that.notificationPollEndpoint[indexOfRequest],
'truncated notification data for processing:',
JSON.stringify(notificationData).substring(0, MAX_LOG_LENGTH_CHARACTERS));
that.notificationPollCallback[indexOfRequest](notificationData);
that.notificationInitialCallbackMade[indexOfRequest] = true;
currentDataPageToRetrieve[indexOfRequest] = 1;
concatenatedServerResponse[indexOfRequest] = new Array();
} else {
concatenatedServerResponse[indexOfRequest] =
concatenatedServerResponse[indexOfRequest].concat(that.notificationDataProcessPage[indexOfRequest](response));
currentDataPageToRetrieve[indexOfRequest]++;
request({
json: true,
uri: (urlOfRequest + "&page=" + currentDataPageToRetrieve[indexOfRequest])
}, requestCallback, optionalParams);
}
}
var METAVERSE_BASE = Account.metaverseServerURL; var METAVERSE_BASE = Account.metaverseServerURL;
var currentDataPageToRetrieve = 1; var MS_IN_SEC = 1000;
var concatenatedServerResponse = new Array(); that.notificationPoll = function (i) {
that.notificationPoll = function () { if (!that.notificationPollEndpoint[i]) {
if (!that.notificationPollEndpoint) {
return; return;
} }
// User is "appearing offline" or is offline // User is "appearing offline" or is not logged in
if (GlobalServices.findableBy === "none" || Account.username === "") { if (GlobalServices.findableBy === "none" || Account.username === "Unknown user") {
that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); // The notification polling will restart when the user changes their availability
// or when they log in, so it's not necessary to restart a timer here.
console.debug(that.buttonName + " Notifications: User is appearing offline or not logged in. " +
that.buttonName + " will poll for notifications when user logs in and has their availability " +
"set to not appear offline.");
return; return;
} }
var url = METAVERSE_BASE + that.notificationPollEndpoint; var url = METAVERSE_BASE + that.notificationPollEndpoint[i];
var settingsKey = "notifications/" + that.buttonName + "/lastPoll"; var settingsKey = "notifications/" + that.notificationPollEndpoint[i] + "/lastPoll";
var currentTimestamp = new Date().getTime(); var currentTimestamp = new Date().getTime();
var lastPollTimestamp = Settings.getValue(settingsKey, currentTimestamp); var lastPollTimestamp = Settings.getValue(settingsKey, currentTimestamp);
if (that.notificationPollCaresAboutSince) { if (that.notificationPollCaresAboutSince[i]) {
url = url + "&since=" + lastPollTimestamp/1000; url = url + "&since=" + lastPollTimestamp / MS_IN_SEC;
} }
Settings.setValue(settingsKey, currentTimestamp); Settings.setValue(settingsKey, currentTimestamp);
console.debug(that.buttonName, 'polling for notifications at endpoint', url); console.debug(that.buttonName, 'polling for notifications at endpoint', url);
function requestCallback(error, response) { request({
if (error || (response.status !== 'success')) { json: true,
print("Error: unable to get", url, error || response.status); uri: url
that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); },
return; requestCallback,
} {
indexOfRequest: i,
if (!that.notificationPollStopPaginatingConditionMet || that.notificationPollStopPaginatingConditionMet(response)) { urlOfRequest: url
that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); });
var notificationData;
if (concatenatedServerResponse.length) {
notificationData = concatenatedServerResponse;
} else {
notificationData = that.notificationDataProcessPage(response);
}
console.debug(that.buttonName, 'notification data for processing:', JSON.stringify(notificationData));
that.notificationPollCallback(notificationData);
that.notificationInitialCallbackMade = true;
currentDataPageToRetrieve = 1;
concatenatedServerResponse = new Array();
} else {
concatenatedServerResponse = concatenatedServerResponse.concat(that.notificationDataProcessPage(response));
currentDataPageToRetrieve++;
request({ json: true, uri: (url + "&page=" + currentDataPageToRetrieve) }, requestCallback);
}
}
request({ json: true, uri: url }, requestCallback);
}; };
// This won't do anything if there isn't a notification endpoint set // This won't do anything if there isn't a notification endpoint set
that.notificationPoll(); for (i = 0; i < that.notificationPollEndpoint.length; i++) {
that.notificationPoll(i);
}
function startNotificationTimer(indexOfRequest) {
that.notificationPollTimeout[indexOfRequest] = Script.setTimeout(function () {
that.notificationPoll(indexOfRequest);
}, that.notificationPollTimeoutMs[indexOfRequest]);
}
function restartNotificationPoll() { function restartNotificationPoll() {
that.notificationInitialCallbackMade = false; for (var j = 0; j < that.notificationPollEndpoint.length; j++) {
if (that.notificationPollTimeout) { that.notificationInitialCallbackMade[j] = false;
Script.clearTimeout(that.notificationPollTimeout); if (that.notificationPollTimeout[j]) {
that.notificationPollTimeout = false; Script.clearTimeout(that.notificationPollTimeout[j]);
that.notificationPollTimeout[j] = false;
}
that.notificationPoll(j);
} }
that.notificationPoll();
} }
// //
// END Notification Handling // END Notification Handling
@ -322,9 +365,11 @@ function AppUi(properties) {
} }
that.tablet.removeButton(that.button); that.tablet.removeButton(that.button);
} }
if (that.notificationPollTimeout) { for (var i = 0; i < that.notificationPollTimeout.length; i++) {
Script.clearInterval(that.notificationPollTimeout); if (that.notificationPollTimeout[i]) {
that.notificationPollTimeout = false; Script.clearInterval(that.notificationPollTimeout[i]);
that.notificationPollTimeout[i] = false;
}
} }
}; };
// Set up the handlers. // Set up the handlers.
@ -333,7 +378,7 @@ function AppUi(properties) {
Script.scriptEnding.connect(that.onScriptEnding); Script.scriptEnding.connect(that.onScriptEnding);
GlobalServices.findableByChanged.connect(restartNotificationPoll); GlobalServices.findableByChanged.connect(restartNotificationPoll);
GlobalServices.myUsernameChanged.connect(restartNotificationPoll); GlobalServices.myUsernameChanged.connect(restartNotificationPoll);
if (that.buttonName == Settings.getValue("startUpApp")) { if (that.buttonName === Settings.getValue("startUpApp")) {
Settings.setValue("startUpApp", ""); Settings.setValue("startUpApp", "");
Script.setTimeout(function () { Script.setTimeout(function () {
that.open(); that.open();

View file

@ -18,7 +18,8 @@
module.exports = { module.exports = {
// ------------------------------------------------------------------ // ------------------------------------------------------------------
request: function (options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request. // cb(error, responseOfCorrectContentType, optionalCallbackParameter) of url. A subset of npm request.
request: function (options, callback, optionalCallbackParameter) {
var httpRequest = new XMLHttpRequest(), key; var httpRequest = new XMLHttpRequest(), key;
// QT bug: apparently doesn't handle onload. Workaround using readyState. // QT bug: apparently doesn't handle onload. Workaround using readyState.
httpRequest.onreadystatechange = function () { httpRequest.onreadystatechange = function () {
@ -38,7 +39,7 @@ module.exports = {
if (error) { if (error) {
response = { statusCode: httpRequest.status }; response = { statusCode: httpRequest.status };
} }
callback(error, response); callback(error, response, optionalCallbackParameter);
} }
}; };
if (typeof options === 'string') { if (typeof options === 'string') {

View file

@ -159,7 +159,7 @@ var selectedAvatarEntityGrabbable = false;
var selectedAvatarEntityID = null; var selectedAvatarEntityID = null;
var grabbedAvatarEntityChangeNotifier = null; var grabbedAvatarEntityChangeNotifier = null;
var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/wallet/Wallet.qml";
var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace";
var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("html/js/marketplacesInject.js"); var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("html/js/marketplacesInject.js");

View file

@ -11,7 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
/* global getConnectionData */ /* global getConnectionData getControllerWorldLocation openLoginWindow WalletScriptingInterface */
(function () { // BEGIN LOCAL_SCOPE (function () { // BEGIN LOCAL_SCOPE
Script.include("/~/system/libraries/accountUtils.js"); Script.include("/~/system/libraries/accountUtils.js");
@ -20,7 +20,6 @@ var AppUi = Script.require('appUi');
var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace";
// BEGIN AVATAR SELECTOR LOGIC // BEGIN AVATAR SELECTOR LOGIC
var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 }; var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 };
var SELECTED_COLOR = { red: 0xF3, green: 0x91, blue: 0x29 }; var SELECTED_COLOR = { red: 0xF3, green: 0x91, blue: 0x29 };
@ -48,7 +47,6 @@ ExtendedOverlay.prototype.editOverlay = function (properties) { // change displa
function color(selected, hovering) { function color(selected, hovering) {
var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR; var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR;
function scale(component) { function scale(component) {
var delta = 0xFF - component;
return component; return component;
} }
return { red: scale(base.red), green: scale(base.green), blue: scale(base.blue) }; return { red: scale(base.red), green: scale(base.green), blue: scale(base.blue) };
@ -105,7 +103,8 @@ ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId
// hit(overlay) on the one overlay intersected by pickRay, if any. // hit(overlay) on the one overlay intersected by pickRay, if any.
// noHit() if no ExtendedOverlay was intersected (helps with hover) // noHit() if no ExtendedOverlay was intersected (helps with hover)
ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) { ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) {
var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones. // Depends on nearer coverOverlays to extend closer to us than farther ones.
var pickedOverlay = Overlays.findRayIntersection(pickRay);
if (!pickedOverlay.intersects) { if (!pickedOverlay.intersects) {
if (noHit) { if (noHit) {
return noHit(); return noHit();
@ -131,6 +130,7 @@ function addAvatarNode(id) {
} }
var pingPong = true; var pingPong = true;
var OVERLAY_SCALE = 0.032;
function updateOverlays() { function updateOverlays() {
var eye = Camera.position; var eye = Camera.position;
AvatarList.getAvatarIdentifiers().forEach(function (id) { AvatarList.getAvatarIdentifiers().forEach(function (id) {
@ -148,7 +148,8 @@ function updateOverlays() {
var target = avatar.position; var target = avatar.position;
var distance = Vec3.distance(target, eye); var distance = Vec3.distance(target, eye);
var offset = 0.2; var offset = 0.2;
var diff = Vec3.subtract(target, eye); // get diff between target and eye (a vector pointing to the eye from avatar position) // get diff between target and eye (a vector pointing to the eye from avatar position)
var diff = Vec3.subtract(target, eye);
var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can
if (headIndex > 0) { if (headIndex > 0) {
offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2;
@ -164,7 +165,7 @@ function updateOverlays() {
overlay.editOverlay({ overlay.editOverlay({
color: color(ExtendedOverlay.isSelected(id), overlay.hovering), color: color(ExtendedOverlay.isSelected(id), overlay.hovering),
position: target, position: target,
dimensions: 0.032 * distance dimensions: OVERLAY_SCALE * distance
}); });
}); });
pingPong = !pingPong; pingPong = !pingPong;
@ -382,13 +383,36 @@ function onUsernameChanged() {
} }
} }
var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js");
var METAVERSE_SERVER_URL = Account.metaverseServerURL;
var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page.
function openMarketplace(optionalItemOrUrl) {
// This is a bit of a kluge, but so is the whole file.
// If given a whole path, use it with no cta.
// If given an id, build the appropriate url and use the id as the cta.
// Otherwise, use home and 'marketplace cta'.
// AND... if call onMarketplaceOpen to setupWallet if we need to.
var url = optionalItemOrUrl || MARKETPLACE_URL_INITIAL;
// If optionalItemOrUrl contains the metaverse base, then it's a url, not an item id.
if (optionalItemOrUrl && optionalItemOrUrl.indexOf(METAVERSE_SERVER_URL) === -1) {
url = MARKETPLACE_URL + '/items/' + optionalItemOrUrl;
}
ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL);
}
function setCertificateInfo(itemCertificateId) {
ui.tablet.sendToQml({
method: 'inspectionCertificate_setCertificateId',
entityId: "",
certificateId: itemCertificateId
});
}
// Function Name: fromQml() // Function Name: fromQml()
// //
// Description: // Description:
// -Called when a message is received from SpectatorCamera.qml. The "message" argument is what is sent from the QML // -Called when a message is received from SpectatorCamera.qml. The "message" argument is what is sent from the QML
// in the format "{method, params}", like json-rpc. See also sendToQml(). // in the format "{method, params}", like json-rpc. See also sendToQml().
var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml";
var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js");
function fromQml(message) { function fromQml(message) {
switch (message.method) { switch (message.method) {
case 'passphrasePopup_cancelClicked': case 'passphrasePopup_cancelClicked':
@ -413,6 +437,7 @@ function fromQml(message) {
} }
break; break;
case 'needsLogIn_loginClicked': case 'needsLogIn_loginClicked':
ui.close();
openLoginWindow(); openLoginWindow();
break; break;
case 'disableHmdPreview': case 'disableHmdPreview':
@ -422,10 +447,6 @@ function fromQml(message) {
case 'transactionHistory_linkClicked': case 'transactionHistory_linkClicked':
ui.open(message.marketplaceLink, MARKETPLACES_INJECT_SCRIPT_URL); ui.open(message.marketplaceLink, MARKETPLACES_INJECT_SCRIPT_URL);
break; break;
case 'goToPurchases_fromWalletHome':
case 'goToPurchases':
ui.open(MARKETPLACE_PURCHASES_QML_PATH);
break;
case 'goToMarketplaceMainPage': case 'goToMarketplaceMainPage':
ui.open(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); ui.open(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL);
break; break;
@ -450,10 +471,8 @@ function fromQml(message) {
removeOverlays(); removeOverlays();
break; break;
case 'sendAsset_sendPublicly': case 'sendAsset_sendPublicly':
if (message.assetName === "") {
deleteSendMoneyParticleEffect(); deleteSendMoneyParticleEffect();
sendMoneyRecipient = message.recipient; sendMoneyRecipient = message.recipient;
var amount = message.amount;
var props = SEND_MONEY_PARTICLE_PROPERTIES; var props = SEND_MONEY_PARTICLE_PROPERTIES;
props.parentID = MyAvatar.sessionUUID; props.parentID = MyAvatar.sessionUUID;
props.position = MyAvatar.position; props.position = MyAvatar.position;
@ -464,8 +483,8 @@ function fromQml(message) {
sendMoneyParticleEffect = Entities.addEntity(props, true); sendMoneyParticleEffect = Entities.addEntity(props, true);
particleEffectTimestamp = Date.now(); particleEffectTimestamp = Date.now();
updateSendMoneyParticleEffect(); updateSendMoneyParticleEffect();
sendMoneyParticleEffectUpdateTimer = Script.setInterval(updateSendMoneyParticleEffect, SEND_MONEY_PARTICLE_TIMER_UPDATE); sendMoneyParticleEffectUpdateTimer =
} Script.setInterval(updateSendMoneyParticleEffect, SEND_MONEY_PARTICLE_TIMER_UPDATE);
break; break;
case 'transactionHistory_goToBank': case 'transactionHistory_goToBank':
if (Account.metaverseServerURL.indexOf("staging") >= 0) { if (Account.metaverseServerURL.indexOf("staging") >= 0) {
@ -474,6 +493,49 @@ function fromQml(message) {
Window.location = "hifi://BankOfHighFidelity"; Window.location = "hifi://BankOfHighFidelity";
} }
break; break;
case 'purchases_updateWearables':
var currentlyWornWearables = [];
var ATTACHMENT_SEARCH_RADIUS = 100; // meters (just in case)
var nearbyEntities = Entities.findEntitiesByType('Model', MyAvatar.position, ATTACHMENT_SEARCH_RADIUS);
for (var i = 0; i < nearbyEntities.length; i++) {
var currentProperties = Entities.getEntityProperties(
nearbyEntities[i], ['certificateID', 'editionNumber', 'parentID']
);
if (currentProperties.parentID === MyAvatar.sessionUUID) {
currentlyWornWearables.push({
entityID: nearbyEntities[i],
entityCertID: currentProperties.certificateID,
entityEdition: currentProperties.editionNumber
});
}
}
ui.tablet.sendToQml({ method: 'updateWearables', wornWearables: currentlyWornWearables });
break;
case 'purchases_walletNotSetUp':
ui.tablet.sendToQml({
method: 'updateWalletReferrer',
referrer: "purchases"
});
break;
case 'purchases_openGoTo':
ui.open("hifi/tablet/TabletAddressDialog.qml");
break;
case 'purchases_itemInfoClicked':
var itemId = message.itemId;
if (itemId && itemId !== "") {
openMarketplace(itemId);
}
break;
case 'purchases_itemCertificateClicked':
setCertificateInfo(message.itemCertificateId);
break;
case 'clearShouldShowDotHistory':
shouldShowDotHistory = false;
ui.messagesWaiting(shouldShowDotUpdates || shouldShowDotHistory);
break;
case 'http.request': case 'http.request':
// Handled elsewhere, don't log. // Handled elsewhere, don't log.
break; break;
@ -482,41 +544,76 @@ function fromQml(message) {
} }
} }
var isWired = false;
function walletOpened() { function walletOpened() {
Users.usernameFromIDReply.connect(usernameFromIDReply); Users.usernameFromIDReply.connect(usernameFromIDReply);
Controller.mousePressEvent.connect(handleMouseEvent); Controller.mousePressEvent.connect(handleMouseEvent);
Controller.mouseMoveEvent.connect(handleMouseMoveEvent); Controller.mouseMoveEvent.connect(handleMouseMoveEvent);
triggerMapping.enable(); triggerMapping.enable();
triggerPressMapping.enable(); triggerPressMapping.enable();
shouldShowDot = false; isWired = true;
ui.messagesWaiting(shouldShowDot);
if (shouldShowDotHistory) {
ui.sendMessage({
method: 'updateRecentActivityMessageLight',
messagesWaiting: shouldShowDotHistory
});
}
} }
function walletClosed() { function walletClosed() {
off(); off();
} }
function notificationDataProcessPage(data) { function notificationDataProcessPageUpdates(data) {
return data.data.updates;
}
function notificationDataProcessPageHistory(data) {
return data.data.history; return data.data.history;
} }
var shouldShowDot = false; var shouldShowDotUpdates = false;
function notificationPollCallback(historyArray) { function notificationPollCallbackUpdates(updatesArray) {
shouldShowDotUpdates = shouldShowDotUpdates || updatesArray.length > 0;
ui.messagesWaiting(shouldShowDotUpdates || shouldShowDotHistory);
if (updatesArray.length > 0) {
var message;
if (!ui.notificationInitialCallbackMade[0]) {
message = updatesArray.length + " of your purchased items " +
(updatesArray.length === 1 ? "has an update " : "have updates ") +
"available. Open ASSETS to update.";
ui.notificationDisplayBanner(message);
ui.notificationPollCaresAboutSince[0] = true;
} else {
for (var i = 0; i < updatesArray.length; i++) {
message = "Update available for \"" +
updatesArray[i].base_item_title + "\"." +
"Open ASSETS to update.";
ui.notificationDisplayBanner(message);
}
}
}
}
var shouldShowDotHistory = false;
function notificationPollCallbackHistory(historyArray) {
if (!ui.isOpen) { if (!ui.isOpen) {
var notificationCount = historyArray.length; var notificationCount = historyArray.length;
shouldShowDot = shouldShowDot || notificationCount > 0; shouldShowDotHistory = shouldShowDotHistory || notificationCount > 0;
ui.messagesWaiting(shouldShowDot); ui.messagesWaiting(shouldShowDotUpdates || shouldShowDotHistory);
if (notificationCount > 0) { if (notificationCount > 0) {
var message; var message;
if (!ui.notificationInitialCallbackMade) { if (!ui.notificationInitialCallbackMade[1]) {
message = "You have " + notificationCount + " unread wallet " + message = "You have " + notificationCount + " unread recent " +
"transaction" + (notificationCount === 1 ? "" : "s") + ". Open WALLET to see all activity."; "transaction" + (notificationCount === 1 ? "" : "s") + ". Open ASSETS to see all activity.";
ui.notificationDisplayBanner(message); ui.notificationDisplayBanner(message);
} else { } else {
for (var i = 0; i < notificationCount; i++) { for (var i = 0; i < notificationCount; i++) {
message = '"' + (historyArray[i].message) + '" ' + message = '"' + (historyArray[i].message) + '" ' +
"Open WALLET to see all activity."; "Open ASSETS to see all activity.";
ui.notificationDisplayBanner(message); ui.notificationDisplayBanner(message);
} }
} }
@ -524,7 +621,12 @@ function notificationPollCallback(historyArray) {
} }
} }
function isReturnedDataEmpty(data) { function isReturnedDataEmptyUpdates(data) {
var updatesArray = data.data.updates;
return updatesArray.length === 0;
}
function isReturnedDataEmptyHistory(data) {
var historyArray = data.data.history; var historyArray = data.data.history;
return historyArray.length === 0; return historyArray.length === 0;
} }
@ -559,23 +661,41 @@ function uninstallMarketplaceItemTester() {
} }
} }
var BUTTON_NAME = "WALLET"; var BUTTON_NAME = "ASSETS";
var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml"; var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml";
var NOTIFICATION_POLL_TIMEOUT = 300000;
var ui; var ui;
function startup() { function startup() {
var notificationPollEndpointArray = ["/api/v1/commerce/available_updates?per_page=10"];
var notificationPollTimeoutMsArray = [NOTIFICATION_POLL_TIMEOUT];
var notificationDataProcessPageArray = [notificationDataProcessPageUpdates];
var notificationPollCallbackArray = [notificationPollCallbackUpdates];
var notificationPollStopPaginatingConditionMetArray = [isReturnedDataEmptyUpdates];
var notificationPollCaresAboutSinceArray = [false];
if (!WalletScriptingInterface.limitedCommerce) {
notificationPollEndpointArray[1] = "/api/v1/commerce/history?per_page=10";
notificationPollTimeoutMsArray[1] = NOTIFICATION_POLL_TIMEOUT;
notificationDataProcessPageArray[1] = notificationDataProcessPageHistory;
notificationPollCallbackArray[1] = notificationPollCallbackHistory;
notificationPollStopPaginatingConditionMetArray[1] = isReturnedDataEmptyHistory;
notificationPollCaresAboutSinceArray[1] = true;
}
ui = new AppUi({ ui = new AppUi({
buttonName: BUTTON_NAME, buttonName: BUTTON_NAME,
buttonPrefix: "wallet-",
sortOrder: 10, sortOrder: 10,
home: WALLET_QML_SOURCE, home: WALLET_QML_SOURCE,
onOpened: walletOpened, onOpened: walletOpened,
onClosed: walletClosed, onClosed: walletClosed,
onMessage: fromQml, onMessage: fromQml,
notificationPollEndpoint: "/api/v1/commerce/history?per_page=10", notificationPollEndpoint: notificationPollEndpointArray,
notificationPollTimeoutMs: 300000, notificationPollTimeoutMs: notificationPollTimeoutMsArray,
notificationDataProcessPage: notificationDataProcessPage, notificationDataProcessPage: notificationDataProcessPageArray,
notificationPollCallback: notificationPollCallback, notificationPollCallback: notificationPollCallbackArray,
notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, notificationPollStopPaginatingConditionMet: notificationPollStopPaginatingConditionMetArray,
notificationPollCaresAboutSince: true notificationPollCaresAboutSince: notificationPollCaresAboutSinceArray
}); });
GlobalServices.myUsernameChanged.connect(onUsernameChanged); GlobalServices.myUsernameChanged.connect(onUsernameChanged);
installMarketplaceItemTester(); installMarketplaceItemTester();
@ -583,11 +703,14 @@ function startup() {
var isUpdateOverlaysWired = false; var isUpdateOverlaysWired = false;
function off() { function off() {
if (isWired) {
Users.usernameFromIDReply.disconnect(usernameFromIDReply); Users.usernameFromIDReply.disconnect(usernameFromIDReply);
Controller.mousePressEvent.disconnect(handleMouseEvent); Controller.mousePressEvent.disconnect(handleMouseEvent);
Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent);
triggerMapping.disable(); triggerMapping.disable();
triggerPressMapping.disable(); triggerPressMapping.disable();
isWired = false;
}
if (isUpdateOverlaysWired) { if (isUpdateOverlaysWired) {
Script.update.disconnect(updateOverlays); Script.update.disconnect(updateOverlays);

View file

@ -27,6 +27,7 @@
var xmlHttpRequest = null; var xmlHttpRequest = null;
var isPreparing = false; // Explicitly track download request status. var isPreparing = false; // Explicitly track download request status.
var limitedCommerce = false;
var commerceMode = false; var commerceMode = false;
var userIsLoggedIn = false; var userIsLoggedIn = false;
var walletNeedsSetup = false; var walletNeedsSetup = false;
@ -59,7 +60,7 @@
); );
// Footer. // Footer.
var isInitialHiFiPage = location.href === marketplaceBaseURL + "/marketplace?"; var isInitialHiFiPage = location.href === (marketplaceBaseURL + "/marketplace?");
$("body").append( $("body").append(
'<div id="marketplace-navigation">' + '<div id="marketplace-navigation">' +
(!isInitialHiFiPage ? '<input id="back-button" type="button" class="white" value="&lt; Back" />' : '') + (!isInitialHiFiPage ? '<input id="back-button" type="button" class="white" value="&lt; Back" />' : '') +
@ -92,7 +93,7 @@
window.location = "https://clara.io/library?gameCheck=true&public=true"; window.location = "https://clara.io/library?gameCheck=true&public=true";
}); });
$('#exploreHifiMarketplace').on('click', function () { $('#exploreHifiMarketplace').on('click', function () {
window.location = marketplaceBaseURL + "/marketplace"; window.location = marketplaceBaseURL + "/marketplace?";
}); });
} }
@ -169,7 +170,7 @@
var span = document.createElement('span'); var span = document.createElement('span');
span.style = "margin:10px;color:#1b6420;font-size:15px;"; span.style = "margin:10px;color:#1b6420;font-size:15px;";
span.innerHTML = "to purchase items from the Marketplace."; span.innerHTML = "to get items from the Marketplace.";
var xButton = document.createElement('a'); var xButton = document.createElement('a');
xButton.id = "xButton"; xButton.id = "xButton";
@ -195,40 +196,6 @@
} }
} }
function maybeAddPurchasesButton() {
if (userIsLoggedIn) {
// Why isn't this an id?! This really shouldn't be a class on the website, but it is.
var navbarBrandElement = document.getElementsByClassName('navbar-brand')[0];
var purchasesElement = document.createElement('a');
var dropDownElement = document.getElementById('user-dropdown');
$('#user-dropdown').find('.username')[0].style = "max-width:80px;white-space:nowrap;overflow:hidden;" +
"text-overflow:ellipsis;display:inline-block;position:relative;top:4px;";
$('#user-dropdown').find('.caret')[0].style = "position:relative;top:-3px;";
purchasesElement.id = "purchasesButton";
purchasesElement.setAttribute('href', "#");
purchasesElement.innerHTML = "";
if (messagesWaiting) {
purchasesElement.innerHTML += "<span style='width:10px;height:10px;background-color:red;border-radius:50%;display:inline-block;'></span> ";
}
purchasesElement.innerHTML += "My Purchases";
// FRONTEND WEBDEV RANT: The username dropdown should REALLY not be programmed to be on the same
// line as the search bar, overlaid on top of the search bar, floated right, and then relatively bumped up using "top:-50px".
$('.navbar-brand').css('margin-right', '10px');
purchasesElement.style = "height:100%;margin-top:18px;font-weight:bold;float:right;margin-right:" + (dropDownElement.offsetWidth + 30) +
"px;position:relative;z-index:999;";
navbarBrandElement.parentNode.insertAdjacentElement('beforeend', purchasesElement);
$('#purchasesButton').on('click', function () {
EventBridge.emitWebEvent(JSON.stringify({
type: "PURCHASES",
referrerURL: window.location.href,
hasUpdates: messagesWaiting
}));
});
}
}
function changeDropdownMenu() { function changeDropdownMenu() {
var logInOrOutButton = document.createElement('a'); var logInOrOutButton = document.createElement('a');
logInOrOutButton.id = "logInOrOutButton"; logInOrOutButton.id = "logInOrOutButton";
@ -283,6 +250,7 @@
$(this).attr('href', '#'); $(this).attr('href', '#');
} }
cost = $(this).closest('.col-xs-3').find('.item-cost').text(); cost = $(this).closest('.col-xs-3').find('.item-cost').text();
var costInt = parseInt(cost, 10);
$(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6'); $(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6');
$(this).closest('.col-xs-3').attr("class", 'col-xs-6'); $(this).closest('.col-xs-3').attr("class", 'col-xs-6');
@ -398,7 +366,6 @@
// Try this here in case it works (it will if the user just pressed the "back" button, // Try this here in case it works (it will if the user just pressed the "back" button,
// since that doesn't trigger another AJAX request. // since that doesn't trigger another AJAX request.
injectBuyButtonOnMainPage(); injectBuyButtonOnMainPage();
maybeAddPurchasesButton();
} }
} }
@ -419,7 +386,12 @@
var href = purchaseButton.attr('href'); var href = purchaseButton.attr('href');
purchaseButton.attr('href', '#'); purchaseButton.attr('href', '#');
var cost = $('.item-cost').text();
var costInt = parseInt(cost, 10);
var availability = $.trim($('.item-availability').text()); var availability = $.trim($('.item-availability').text());
if (limitedCommerce && (costInt > 0)) {
availability = '';
}
if (availability === 'available') { if (availability === 'available') {
purchaseButton.css({ purchaseButton.css({
"background": "linear-gradient(#00b4ef, #0093C5)", "background": "linear-gradient(#00b4ef, #0093C5)",
@ -436,14 +408,13 @@
}); });
} }
var cost = $('.item-cost').text();
var type = $('.item-type').text(); var type = $('.item-type').text();
var isUpdating = window.location.href.indexOf('edition=') > -1; var isUpdating = window.location.href.indexOf('edition=') > -1;
var urlParams = new URLSearchParams(window.location.search); var urlParams = new URLSearchParams(window.location.search);
if (isUpdating) { if (isUpdating) {
purchaseButton.html('UPDATE FOR FREE'); purchaseButton.html('UPDATE FOR FREE');
} else if (availability !== 'available') { } else if (availability !== 'available') {
purchaseButton.html('UNAVAILABLE (' + availability + ')'); purchaseButton.html('UNAVAILABLE ' + (availability ? ('(' + availability + ')') : ''));
} else if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { } else if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) {
purchaseButton.html('PURCHASE <span class="hifi-glyph hifi-glyph-hfc" style="filter:invert(1);background-size:20px;' + purchaseButton.html('PURCHASE <span class="hifi-glyph hifi-glyph-hfc" style="filter:invert(1);background-size:20px;' +
'width:20px;height:20px;position:relative;top:5px;"></span> ' + cost); 'width:20px;height:20px;position:relative;top:5px;"></span> ' + cost);
@ -461,7 +432,6 @@
type); type);
} }
}); });
maybeAddPurchasesButton();
} }
} }
@ -742,6 +712,7 @@
cancelClaraDownload(); cancelClaraDownload();
} else if (message.type === "marketplaces") { } else if (message.type === "marketplaces") {
if (message.action === "commerceSetting") { if (message.action === "commerceSetting") {
limitedCommerce = !!message.data.limitedCommerce;
commerceMode = !!message.data.commerceMode; commerceMode = !!message.data.commerceMode;
userIsLoggedIn = !!message.data.userIsLoggedIn; userIsLoggedIn = !!message.data.userIsLoggedIn;
walletNeedsSetup = !!message.data.walletNeedsSetup; walletNeedsSetup = !!message.data.walletNeedsSetup;

View file

@ -10,7 +10,7 @@
/* global Tablet, Script, HMD, UserActivityLogger, Entities, Account, Wallet, ContextOverlay, Settings, Camera, Vec3, /* global Tablet, Script, HMD, UserActivityLogger, Entities, Account, Wallet, ContextOverlay, Settings, Camera, Vec3,
Quat, MyAvatar, Clipboard, Menu, Grid, Uuid, GlobalServices, openLoginWindow, getConnectionData, Overlays, SoundCache, Quat, MyAvatar, Clipboard, Menu, Grid, Uuid, GlobalServices, openLoginWindow, getConnectionData, Overlays, SoundCache,
DesktopPreviewProvider */ DesktopPreviewProvider, ResourceRequestObserver */
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
var selectionDisplay = null; // for gridTool.js to ignore var selectionDisplay = null; // for gridTool.js to ignore
@ -23,7 +23,7 @@ Script.include("/~/system/libraries/connectionUtils.js");
var MARKETPLACE_CHECKOUT_QML_PATH = "hifi/commerce/checkout/Checkout.qml"; var MARKETPLACE_CHECKOUT_QML_PATH = "hifi/commerce/checkout/Checkout.qml";
var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml"; var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml";
var MARKETPLACE_ITEM_TESTER_QML_PATH = "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml"; var MARKETPLACE_ITEM_TESTER_QML_PATH = "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml";
var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; // HRS FIXME "hifi/commerce/purchases/Purchases.qml";
var MARKETPLACE_WALLET_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; var MARKETPLACE_WALLET_QML_PATH = "hifi/commerce/wallet/Wallet.qml";
var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js");
var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html");
@ -137,7 +137,7 @@ function setupWallet(referrer) {
} }
function onMarketplaceOpen(referrer) { function onMarketplaceOpen(referrer) {
var cta = referrer, match; var match;
if (Account.loggedIn && walletNeedsSetup()) { if (Account.loggedIn && walletNeedsSetup()) {
if (referrer === MARKETPLACE_URL_INITIAL) { if (referrer === MARKETPLACE_URL_INITIAL) {
setupWallet('marketplace cta'); setupWallet('marketplace cta');
@ -218,7 +218,7 @@ function onUsernameChanged() {
} }
function walletNeedsSetup() { function walletNeedsSetup() {
return Wallet.walletStatus === 1; return WalletScriptingInterface.walletStatus === 1;
} }
function sendCommerceSettings() { function sendCommerceSettings() {
@ -230,289 +230,11 @@ function sendCommerceSettings() {
userIsLoggedIn: Account.loggedIn, userIsLoggedIn: Account.loggedIn,
walletNeedsSetup: walletNeedsSetup(), walletNeedsSetup: walletNeedsSetup(),
metaverseServerURL: Account.metaverseServerURL, metaverseServerURL: Account.metaverseServerURL,
messagesWaiting: shouldShowDot limitedCommerce: WalletScriptingInterface.limitedCommerce
} }
}); });
} }
// BEGIN AVATAR SELECTOR LOGIC
var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 };
var SELECTED_COLOR = { red: 0xF3, green: 0x91, blue: 0x29 };
var HOVER_COLOR = { red: 0xD0, green: 0xD0, blue: 0xD0 };
var overlays = {}; // Keeps track of all our extended overlay data objects, keyed by target identifier.
function ExtendedOverlay(key, type, properties) { // A wrapper around overlays to store the key it is associated with.
overlays[key] = this;
this.key = key;
this.selected = false;
this.hovering = false;
this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected...
}
// Instance methods:
ExtendedOverlay.prototype.deleteOverlay = function () { // remove display and data of this overlay
Overlays.deleteOverlay(this.activeOverlay);
delete overlays[this.key];
};
ExtendedOverlay.prototype.editOverlay = function (properties) { // change display of this overlay
Overlays.editOverlay(this.activeOverlay, properties);
};
function color(selected, hovering) {
var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR;
function scale(component) {
return component;
}
return { red: scale(base.red), green: scale(base.green), blue: scale(base.blue) };
}
// so we don't have to traverse the overlays to get the last one
var lastHoveringId = 0;
ExtendedOverlay.prototype.hover = function (hovering) {
this.hovering = hovering;
if (this.key === lastHoveringId) {
if (hovering) {
return;
}
lastHoveringId = 0;
}
this.editOverlay({ color: color(this.selected, hovering) });
if (hovering) {
// un-hover the last hovering overlay
if (lastHoveringId && lastHoveringId !== this.key) {
ExtendedOverlay.get(lastHoveringId).hover(false);
}
lastHoveringId = this.key;
}
};
ExtendedOverlay.prototype.select = function (selected) {
if (this.selected === selected) {
return;
}
this.editOverlay({ color: color(selected, this.hovering) });
this.selected = selected;
};
// Class methods:
var selectedId = false;
ExtendedOverlay.isSelected = function (id) {
return selectedId === id;
};
ExtendedOverlay.get = function (key) { // answer the extended overlay data object associated with the given avatar identifier
return overlays[key];
};
ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator returns truthy.
var key;
for (key in overlays) {
if (iterator(ExtendedOverlay.get(key))) {
return;
}
}
};
ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId (if any)
if (lastHoveringId) {
ExtendedOverlay.get(lastHoveringId).hover(false);
}
};
// hit(overlay) on the one overlay intersected by pickRay, if any.
// noHit() if no ExtendedOverlay was intersected (helps with hover)
ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) {
var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones.
if (!pickedOverlay.intersects) {
if (noHit) {
return noHit();
}
return;
}
ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours.
if ((overlay.activeOverlay) === pickedOverlay.overlayID) {
hit(overlay);
return true;
}
});
};
function addAvatarNode(id) {
return new ExtendedOverlay(id, "sphere", {
drawInFront: true,
solid: true,
alpha: 0.8,
color: color(false, false),
ignoreRayIntersection: false
});
}
var pingPong = true;
function updateOverlays() {
var eye = Camera.position;
AvatarList.getAvatarIdentifiers().forEach(function (id) {
if (!id) {
return; // don't update ourself, or avatars we're not interested in
}
var avatar = AvatarList.getAvatar(id);
if (!avatar) {
return; // will be deleted below if there had been an overlay.
}
var overlay = ExtendedOverlay.get(id);
if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back.
overlay = addAvatarNode(id);
}
var target = avatar.position;
var distance = Vec3.distance(target, eye);
var offset = 0.2;
var diff = Vec3.subtract(target, eye); // get diff between target and eye (a vector pointing to the eye from avatar position)
var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can
if (headIndex > 0) {
offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2;
}
// move a bit in front, towards the camera
target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset));
// now bump it up a bit
target.y = target.y + offset;
overlay.ping = pingPong;
overlay.editOverlay({
color: color(ExtendedOverlay.isSelected(id), overlay.hovering),
position: target,
dimensions: 0.032 * distance
});
});
pingPong = !pingPong;
ExtendedOverlay.some(function (overlay) { // Remove any that weren't updated. (User is gone.)
if (overlay.ping === pingPong) {
overlay.deleteOverlay();
}
});
}
function removeOverlays() {
selectedId = false;
lastHoveringId = 0;
ExtendedOverlay.some(function (overlay) {
overlay.deleteOverlay();
});
}
//
// Clicks.
//
function usernameFromIDReply(id, username, machineFingerprint, isAdmin) {
if (selectedId === id) {
var message = {
method: 'updateSelectedRecipientUsername',
userName: username === "" ? "unknown username" : username
};
ui.tablet.sendToQml(message);
}
}
function handleClick(pickRay) {
ExtendedOverlay.applyPickRay(pickRay, function (overlay) {
var nextSelectedStatus = !overlay.selected;
var avatarId = overlay.key;
selectedId = nextSelectedStatus ? avatarId : false;
if (nextSelectedStatus) {
Users.requestUsernameFromID(avatarId);
}
var message = {
method: 'selectRecipient',
id: avatarId,
isSelected: nextSelectedStatus,
displayName: '"' + AvatarList.getAvatar(avatarId).sessionDisplayName + '"',
userName: ''
};
ui.tablet.sendToQml(message);
ExtendedOverlay.some(function (overlay) {
var id = overlay.key;
var selected = ExtendedOverlay.isSelected(id);
overlay.select(selected);
});
return true;
});
}
function handleMouseEvent(mousePressEvent) { // handleClick if we get one.
if (!mousePressEvent.isLeftButton) {
return;
}
handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y));
}
function handleMouseMove(pickRay) { // given the pickRay, just do the hover logic
ExtendedOverlay.applyPickRay(pickRay, function (overlay) {
overlay.hover(true);
}, function () {
ExtendedOverlay.unHover();
});
}
// handy global to keep track of which hand is the mouse (if any)
var currentHandPressed = 0;
var TRIGGER_CLICK_THRESHOLD = 0.85;
var TRIGGER_PRESS_THRESHOLD = 0.05;
function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position
var pickRay;
if (HMD.active) {
if (currentHandPressed !== 0) {
pickRay = controllerComputePickRay(currentHandPressed);
} else {
// nothing should hover, so
ExtendedOverlay.unHover();
return;
}
} else {
pickRay = Camera.computePickRay(event.x, event.y);
}
handleMouseMove(pickRay);
}
function handleTriggerPressed(hand, value) {
// The idea is if you press one trigger, it is the one
// we will consider the mouse. Even if the other is pressed,
// we ignore it until this one is no longer pressed.
var isPressed = value > TRIGGER_PRESS_THRESHOLD;
if (currentHandPressed === 0) {
currentHandPressed = isPressed ? hand : 0;
return;
}
if (currentHandPressed === hand) {
currentHandPressed = isPressed ? hand : 0;
return;
}
// otherwise, the other hand is still triggered
// so do nothing.
}
// We get mouseMoveEvents from the handControllers, via handControllerPointer.
// But we don't get mousePressEvents.
var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click');
var triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press');
function controllerComputePickRay(hand) {
var controllerPose = getControllerWorldLocation(hand, true);
if (controllerPose.valid) {
return { origin: controllerPose.position, direction: Quat.getUp(controllerPose.orientation) };
}
}
function makeClickHandler(hand) {
return function (clicked) {
if (clicked > TRIGGER_CLICK_THRESHOLD) {
var pickRay = controllerComputePickRay(hand);
handleClick(pickRay);
}
};
}
function makePressHandler(hand) {
return function (value) {
handleTriggerPressed(hand, value);
};
}
triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand));
triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand));
triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand));
triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand));
// END AVATAR SELECTOR LOGIC
var grid = new Grid(); var grid = new Grid();
function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) { function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) {
// Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original // Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original
@ -562,6 +284,7 @@ function defaultFor(arg, val) {
return typeof arg !== 'undefined' ? arg : val; return typeof arg !== 'undefined' ? arg : val;
} }
var CERT_ID_URLPARAM_LENGTH = 15; // length of "certificate_id="
function rezEntity(itemHref, itemType, marketplaceItemTesterId) { function rezEntity(itemHref, itemType, marketplaceItemTesterId) {
var isWearable = itemType === "wearable"; var isWearable = itemType === "wearable";
var success = Clipboard.importEntities(itemHref, true, marketplaceItemTesterId); var success = Clipboard.importEntities(itemHref, true, marketplaceItemTesterId);
@ -584,7 +307,7 @@ function rezEntity(itemHref, itemType, marketplaceItemTesterId) {
} }
var certPos = itemHref.search("certificate_id="); // TODO how do I parse a URL from here? var certPos = itemHref.search("certificate_id="); // TODO how do I parse a URL from here?
if (certPos >= 0) { if (certPos >= 0) {
certPos += 15; // length of "certificate_id=" certPos += CERT_ID_URLPARAM_LENGTH;
var certURLEncoded = itemHref.substring(certPos); var certURLEncoded = itemHref.substring(certPos);
var certB64Encoded = decodeURIComponent(certURLEncoded); var certB64Encoded = decodeURIComponent(certURLEncoded);
for (var key in wearableTransforms) { for (var key in wearableTransforms) {
@ -593,7 +316,7 @@ function rezEntity(itemHref, itemType, marketplaceItemTesterId) {
if (certificateTransforms) { if (certificateTransforms) {
for (var certID in certificateTransforms) { for (var certID in certificateTransforms) {
if (certificateTransforms.hasOwnProperty(certID) && if (certificateTransforms.hasOwnProperty(certID) &&
certID == certB64Encoded) { certID === certB64Encoded) {
var certificateTransform = certificateTransforms[certID]; var certificateTransform = certificateTransforms[certID];
wearableLocalPosition = certificateTransform.localPosition; wearableLocalPosition = certificateTransform.localPosition;
wearableLocalRotation = certificateTransform.localRotation; wearableLocalRotation = certificateTransform.localRotation;
@ -636,8 +359,10 @@ function rezEntity(itemHref, itemType, marketplaceItemTesterId) {
targetDirection = Vec3.multiplyQbyV(targetDirection, Vec3.UNIT_Z); targetDirection = Vec3.multiplyQbyV(targetDirection, Vec3.UNIT_Z);
var targetPosition = getPositionToCreateEntity(); var targetPosition = getPositionToCreateEntity();
var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection. // Distance to move entities parallel to targetDirection.
var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection. var deltaParallel = HALF_TREE_SCALE;
// Distance to move entities perpendicular to targetDirection.
var deltaPerpendicular = Vec3.ZERO;
for (var i = 0, length = pastedEntityIDs.length; i < length; i++) { for (var i = 0, length = pastedEntityIDs.length; i < length; i++) {
var curLoopEntityProps = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions", var curLoopEntityProps = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions",
"registrationPoint", "rotation", "parentID"]); "registrationPoint", "rotation", "parentID"]);
@ -665,7 +390,8 @@ function rezEntity(itemHref, itemType, marketplaceItemTesterId) {
} }
if (!Vec3.equal(deltaPosition, Vec3.ZERO)) { if (!Vec3.equal(deltaPosition, Vec3.ZERO)) {
for (var editEntityIndex = 0, numEntities = pastedEntityIDs.length; editEntityIndex < numEntities; editEntityIndex++) { for (var editEntityIndex = 0,
numEntities = pastedEntityIDs.length; editEntityIndex < numEntities; editEntityIndex++) {
if (Uuid.isNull(entityParentIDs[editEntityIndex])) { if (Uuid.isNull(entityParentIDs[editEntityIndex])) {
Entities.editEntity(pastedEntityIDs[editEntityIndex], { Entities.editEntity(pastedEntityIDs[editEntityIndex], {
position: Vec3.sum(deltaPosition, entityPositions[editEntityIndex]) position: Vec3.sum(deltaPosition, entityPositions[editEntityIndex])
@ -768,79 +494,6 @@ function onWebEventReceived(message) {
}); });
} }
} }
var sendAssetRecipient;
var sendAssetParticleEffectUpdateTimer;
var particleEffectTimestamp;
var sendAssetParticleEffect;
var SEND_ASSET_PARTICLE_TIMER_UPDATE = 250;
var SEND_ASSET_PARTICLE_EMITTING_DURATION = 3000;
var SEND_ASSET_PARTICLE_LIFETIME_SECONDS = 8;
var SEND_ASSET_PARTICLE_PROPERTIES = {
accelerationSpread: { x: 0, y: 0, z: 0 },
alpha: 1,
alphaFinish: 1,
alphaSpread: 0,
alphaStart: 1,
azimuthFinish: 0,
azimuthStart: -6,
color: { red: 255, green: 222, blue: 255 },
colorFinish: { red: 255, green: 229, blue: 225 },
colorSpread: { red: 0, green: 0, blue: 0 },
colorStart: { red: 243, green: 255, blue: 255 },
emitAcceleration: { x: 0, y: 0, z: 0 }, // Immediately gets updated to be accurate
emitDimensions: { x: 0, y: 0, z: 0 },
emitOrientation: { x: 0, y: 0, z: 0 },
emitRate: 4,
emitSpeed: 2.1,
emitterShouldTrail: true,
isEmitting: 1,
lifespan: SEND_ASSET_PARTICLE_LIFETIME_SECONDS + 1, // Immediately gets updated to be accurate
lifetime: SEND_ASSET_PARTICLE_LIFETIME_SECONDS + 1,
maxParticles: 20,
name: 'asset-particles',
particleRadius: 0.2,
polarFinish: 0,
polarStart: 0,
radiusFinish: 0.05,
radiusSpread: 0,
radiusStart: 0.2,
speedSpread: 0,
textures: "http://hifi-content.s3.amazonaws.com/alan/dev/Particles/Bokeh-Particle-HFC.png",
type: 'ParticleEffect'
};
function updateSendAssetParticleEffect() {
var timestampNow = Date.now();
if ((timestampNow - particleEffectTimestamp) > (SEND_ASSET_PARTICLE_LIFETIME_SECONDS * 1000)) {
deleteSendAssetParticleEffect();
return;
} else if ((timestampNow - particleEffectTimestamp) > SEND_ASSET_PARTICLE_EMITTING_DURATION) {
Entities.editEntity(sendAssetParticleEffect, {
isEmitting: 0
});
} else if (sendAssetParticleEffect) {
var recipientPosition = AvatarList.getAvatar(sendAssetRecipient).position;
var distance = Vec3.distance(recipientPosition, MyAvatar.position);
var accel = Vec3.subtract(recipientPosition, MyAvatar.position);
accel.y -= 3.0;
var life = Math.sqrt(2 * distance / Vec3.length(accel));
Entities.editEntity(sendAssetParticleEffect, {
emitAcceleration: accel,
lifespan: life
});
}
}
function deleteSendAssetParticleEffect() {
if (sendAssetParticleEffectUpdateTimer) {
Script.clearInterval(sendAssetParticleEffectUpdateTimer);
sendAssetParticleEffectUpdateTimer = null;
}
if (sendAssetParticleEffect) {
sendAssetParticleEffect = Entities.deleteEntity(sendAssetParticleEffect);
}
sendAssetRecipient = null;
}
var savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview"); var savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview");
var UI_FADE_TIMEOUT_MS = 150; var UI_FADE_TIMEOUT_MS = 150;
@ -874,17 +527,14 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
Window.location = "hifi://BankOfHighFidelity"; Window.location = "hifi://BankOfHighFidelity";
} }
break; break;
case 'purchases_openWallet': case 'checkout_openRecentActivity':
case 'checkout_openWallet': ui.open(MARKETPLACE_WALLET_QML_PATH);
case 'checkout_setUpClicked':
openWallet();
break;
case 'purchases_walletNotSetUp':
wireQmlEventBridge(true); wireQmlEventBridge(true);
ui.tablet.sendToQml({ ui.tablet.sendToQml({
method: 'updateWalletReferrer', method: 'checkout_openRecentActivity'
referrer: "purchases"
}); });
break;
case 'checkout_setUpClicked':
openWallet(); openWallet();
break; break;
case 'checkout_walletNotSetUp': case 'checkout_walletNotSetUp':
@ -907,15 +557,9 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
case 'checkout_itemLinkClicked': case 'checkout_itemLinkClicked':
openMarketplace(message.itemId); openMarketplace(message.itemId);
break; break;
case 'checkout_continueShopping': case 'checkout_continue':
openMarketplace(); openMarketplace();
break; break;
case 'purchases_itemInfoClicked':
var itemId = message.itemId;
if (itemId && itemId !== "") {
openMarketplace(itemId);
}
break;
case 'checkout_rezClicked': case 'checkout_rezClicked':
case 'purchases_rezClicked': case 'purchases_rezClicked':
case 'tester_rezClicked': case 'tester_rezClicked':
@ -944,7 +588,6 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
} }
break; break;
case 'header_marketplaceImageClicked': case 'header_marketplaceImageClicked':
case 'purchases_backClicked':
openMarketplace(message.referrerURL); openMarketplace(message.referrerURL);
break; break;
case 'purchases_goToMarketplaceClicked': case 'purchases_goToMarketplaceClicked':
@ -952,9 +595,6 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
break; break;
case 'updateItemClicked': case 'updateItemClicked':
openMarketplace(message.upgradeUrl + "?edition=" + message.itemEdition); openMarketplace(message.upgradeUrl + "?edition=" + message.itemEdition);
break;
case 'giftAsset':
break; break;
case 'passphrasePopup_cancelClicked': case 'passphrasePopup_cancelClicked':
case 'needsLogIn_cancelClicked': case 'needsLogIn_cancelClicked':
@ -974,13 +614,9 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
case 'maybeEnableHmdPreview': case 'maybeEnableHmdPreview':
maybeEnableHMDPreview(); maybeEnableHMDPreview();
break; break;
case 'purchases_openGoTo': case 'checkout_openGoTo':
ui.open("hifi/tablet/TabletAddressDialog.qml"); ui.open("hifi/tablet/TabletAddressDialog.qml");
break; break;
case 'purchases_itemCertificateClicked':
contextOverlayEntity = "";
setCertificateInfo(contextOverlayEntity, message.itemCertificateId);
break;
case 'inspectionCertificate_closeClicked': case 'inspectionCertificate_closeClicked':
ui.close(); ui.close();
break; break;
@ -999,85 +635,25 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
method: 'purchases_showMyItems' method: 'purchases_showMyItems'
}); });
break; break;
case 'refreshConnections':
// Guard to prevent this code from being executed while sending money --
// we only want to execute this while sending non-HFC gifts
if (!onWalletScreen) {
print('Refreshing Connections...');
getConnectionData(false);
}
break;
case 'enable_ChooseRecipientNearbyMode':
// Guard to prevent this code from being executed while sending money --
// we only want to execute this while sending non-HFC gifts
if (!onWalletScreen) {
if (!isUpdateOverlaysWired) {
Script.update.connect(updateOverlays);
isUpdateOverlaysWired = true;
}
}
break;
case 'disable_ChooseRecipientNearbyMode':
// Guard to prevent this code from being executed while sending money --
// we only want to execute this while sending non-HFC gifts
if (!onWalletScreen) {
if (isUpdateOverlaysWired) {
Script.update.disconnect(updateOverlays);
isUpdateOverlaysWired = false;
}
removeOverlays();
}
break;
case 'purchases_availableUpdatesReceived':
shouldShowDot = message.numUpdates > 0;
ui.messagesWaiting(shouldShowDot && !ui.isOpen);
break;
case 'purchases_updateWearables':
var currentlyWornWearables = [];
var ATTACHMENT_SEARCH_RADIUS = 100; // meters (just in case)
var nearbyEntities = Entities.findEntitiesByType('Model', MyAvatar.position, ATTACHMENT_SEARCH_RADIUS);
for (var i = 0; i < nearbyEntities.length; i++) {
var currentProperties = Entities.getEntityProperties(
nearbyEntities[i], ['certificateID', 'editionNumber', 'parentID']
);
if (currentProperties.parentID === MyAvatar.sessionUUID) {
currentlyWornWearables.push({
entityID: nearbyEntities[i],
entityCertID: currentProperties.certificateID,
entityEdition: currentProperties.editionNumber
});
}
}
ui.tablet.sendToQml({ method: 'updateWearables', wornWearables: currentlyWornWearables });
break;
case 'sendAsset_sendPublicly':
if (message.assetName !== "") {
deleteSendAssetParticleEffect();
sendAssetRecipient = message.recipient;
var props = SEND_ASSET_PARTICLE_PROPERTIES;
props.parentID = MyAvatar.sessionUUID;
props.position = MyAvatar.position;
props.position.y += 0.2;
if (message.effectImage) {
props.textures = message.effectImage;
}
sendAssetParticleEffect = Entities.addEntity(props, true);
particleEffectTimestamp = Date.now();
updateSendAssetParticleEffect();
sendAssetParticleEffectUpdateTimer = Script.setInterval(updateSendAssetParticleEffect,
SEND_ASSET_PARTICLE_TIMER_UPDATE);
}
break;
case 'http.request': case 'http.request':
// Handled elsewhere, don't log. // Handled elsewhere, don't log.
break; break;
case 'goToPurchases_fromWalletHome': // HRS FIXME What's this about? // All of these are handled by wallet.js
case 'purchases_updateWearables':
case 'enable_ChooseRecipientNearbyMode':
case 'disable_ChooseRecipientNearbyMode':
case 'sendAsset_sendPublicly':
case 'refreshConnections':
case 'transactionHistory_goToBank':
case 'purchases_walletNotSetUp':
case 'purchases_openGoTo':
case 'purchases_itemInfoClicked':
case 'purchases_itemCertificateClicked':
case 'clearShouldShowDotHistory':
case 'giftAsset':
break; break;
default: default:
print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message)); print('Unrecognized message from Checkout.qml: ' + JSON.stringify(message));
} }
}; };
@ -1146,6 +722,8 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) {
referrerURL: referrerURL, referrerURL: referrerURL,
filterText: filterText filterText: filterText
}); });
referrerURL = "";
filterText = "";
} }
ui.isOpen = (onMarketplaceScreen || onCommerceScreen) && !onWalletScreen; ui.isOpen = (onMarketplaceScreen || onCommerceScreen) && !onWalletScreen;
@ -1162,15 +740,7 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) {
} }
if (onCommerceScreen) { if (onCommerceScreen) {
if (!isWired) { WalletScriptingInterface.refreshWalletStatus();
Users.usernameFromIDReply.connect(usernameFromIDReply);
Controller.mousePressEvent.connect(handleMouseEvent);
Controller.mouseMoveEvent.connect(handleMouseMoveEvent);
triggerMapping.enable();
triggerPressMapping.enable();
}
isWired = true;
Wallet.refreshWalletStatus();
} else { } else {
if (onMarketplaceScreen) { if (onMarketplaceScreen) {
onMarketplaceOpen('marketplace cta'); onMarketplaceOpen('marketplace cta');
@ -1192,44 +762,11 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) {
"\nNew screen URL: " + url + "\nCurrent app open status: " + ui.isOpen + "\n"); "\nNew screen URL: " + url + "\nCurrent app open status: " + ui.isOpen + "\n");
}; };
function notificationDataProcessPage(data) {
return data.data.updates;
}
var shouldShowDot = false;
function notificationPollCallback(updatesArray) {
shouldShowDot = shouldShowDot || updatesArray.length > 0;
ui.messagesWaiting(shouldShowDot && !ui.isOpen);
if (updatesArray.length > 0) {
var message;
if (!ui.notificationInitialCallbackMade) {
message = updatesArray.length + " of your purchased items " +
(updatesArray.length === 1 ? "has an update " : "have updates ") +
"available. Open MARKET to update.";
ui.notificationDisplayBanner(message);
ui.notificationPollCaresAboutSince = true;
} else {
for (var i = 0; i < updatesArray.length; i++) {
message = "Update available for \"" +
updatesArray[i].base_item_title + "\"." +
"Open MARKET to update.";
ui.notificationDisplayBanner(message);
}
}
}
}
function isReturnedDataEmpty(data) {
var historyArray = data.data.updates;
return historyArray.length === 0;
}
var BUTTON_NAME = "MARKET"; var BUTTON_NAME = "MARKET";
var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace"; var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace";
var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. // Append "?" if necessary to signal injected script that it's the initial page.
var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + (MARKETPLACE_URL.indexOf("?") > -1 ? "" : "?");
var ui; var ui;
function startup() { function startup() {
ui = new AppUi({ ui = new AppUi({
@ -1238,50 +775,26 @@ function startup() {
inject: MARKETPLACES_INJECT_SCRIPT_URL, inject: MARKETPLACES_INJECT_SCRIPT_URL,
home: MARKETPLACE_URL_INITIAL, home: MARKETPLACE_URL_INITIAL,
onScreenChanged: onTabletScreenChanged, onScreenChanged: onTabletScreenChanged,
onMessage: onQmlMessageReceived, onMessage: onQmlMessageReceived
notificationPollEndpoint: "/api/v1/commerce/available_updates?per_page=10",
notificationPollTimeoutMs: 300000,
notificationDataProcessPage: notificationDataProcessPage,
notificationPollCallback: notificationPollCallback,
notificationPollStopPaginatingConditionMet: isReturnedDataEmpty,
notificationPollCaresAboutSince: false // Changes to true after first poll
}); });
ContextOverlay.contextOverlayClicked.connect(openInspectionCertificateQML); ContextOverlay.contextOverlayClicked.connect(openInspectionCertificateQML);
Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged);
GlobalServices.myUsernameChanged.connect(onUsernameChanged); GlobalServices.myUsernameChanged.connect(onUsernameChanged);
ui.tablet.webEventReceived.connect(onWebEventReceived); ui.tablet.webEventReceived.connect(onWebEventReceived);
Wallet.walletStatusChanged.connect(sendCommerceSettings); WalletScriptingInterface.walletStatusChanged.connect(sendCommerceSettings);
Window.messageBoxClosed.connect(onMessageBoxClosed); Window.messageBoxClosed.connect(onMessageBoxClosed);
ResourceRequestObserver.resourceRequestEvent.connect(onResourceRequestEvent); ResourceRequestObserver.resourceRequestEvent.connect(onResourceRequestEvent);
Wallet.refreshWalletStatus(); WalletScriptingInterface.refreshWalletStatus();
} }
var isWired = false;
var isUpdateOverlaysWired = false;
function off() { function off() {
if (isWired) {
Users.usernameFromIDReply.disconnect(usernameFromIDReply);
Controller.mousePressEvent.disconnect(handleMouseEvent);
Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent);
triggerMapping.disable();
triggerPressMapping.disable();
isWired = false;
}
if (isUpdateOverlaysWired) {
Script.update.disconnect(updateOverlays);
isUpdateOverlaysWired = false;
}
removeOverlays();
} }
function shutdown() { function shutdown() {
maybeEnableHMDPreview(); maybeEnableHMDPreview();
deleteSendAssetParticleEffect();
Window.messageBoxClosed.disconnect(onMessageBoxClosed); Window.messageBoxClosed.disconnect(onMessageBoxClosed);
Wallet.walletStatusChanged.disconnect(sendCommerceSettings); WalletScriptingInterface.walletStatusChanged.disconnect(sendCommerceSettings);
ui.tablet.webEventReceived.disconnect(onWebEventReceived); ui.tablet.webEventReceived.disconnect(onWebEventReceived);
GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); GlobalServices.myUsernameChanged.disconnect(onUsernameChanged);
Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged);

View file

@ -634,7 +634,7 @@
Window.notifyEditError = onEditError; Window.notifyEditError = onEditError;
Window.notify = onNotify; Window.notify = onNotify;
Tablet.tabletNotification.connect(tabletNotification); Tablet.tabletNotification.connect(tabletNotification);
Wallet.walletNotSetup.connect(walletNotSetup); WalletScriptingInterface.walletNotSetup.connect(walletNotSetup);
Messages.subscribe(NOTIFICATIONS_MESSAGE_CHANNEL); Messages.subscribe(NOTIFICATIONS_MESSAGE_CHANNEL);
Messages.messageReceived.connect(onMessageReceived); Messages.messageReceived.connect(onMessageReceived);

View file

@ -844,7 +844,7 @@ function notificationPollCallback(connectionsArray) {
newOnlineUsers++; newOnlineUsers++;
storedOnlineUsers[user.username] = user; storedOnlineUsers[user.username] = user;
if (!ui.isOpen && ui.notificationInitialCallbackMade) { if (!ui.isOpen && ui.notificationInitialCallbackMade[0]) {
message = user.username + " is available in " + message = user.username + " is available in " +
user.location.root.name + ". Open PEOPLE to join them."; user.location.root.name + ". Open PEOPLE to join them.";
ui.notificationDisplayBanner(message); ui.notificationDisplayBanner(message);
@ -868,7 +868,7 @@ function notificationPollCallback(connectionsArray) {
shouldShowDot: shouldShowDot shouldShowDot: shouldShowDot
}); });
if (newOnlineUsers > 0 && !ui.notificationInitialCallbackMade) { if (newOnlineUsers > 0 && !ui.notificationInitialCallbackMade[0]) {
message = newOnlineUsers + " of your connections " + message = newOnlineUsers + " of your connections " +
(newOnlineUsers === 1 ? "is" : "are") + " available online. Open PEOPLE to join them."; (newOnlineUsers === 1 ? "is" : "are") + " available online. Open PEOPLE to join them.";
ui.notificationDisplayBanner(message); ui.notificationDisplayBanner(message);
@ -889,12 +889,12 @@ function startup() {
onOpened: palOpened, onOpened: palOpened,
onClosed: off, onClosed: off,
onMessage: fromQml, onMessage: fromQml,
notificationPollEndpoint: "/api/v1/users?filter=connections&status=online&per_page=10", notificationPollEndpoint: ["/api/v1/users?filter=connections&status=online&per_page=10"],
notificationPollTimeoutMs: 60000, notificationPollTimeoutMs: [60000],
notificationDataProcessPage: notificationDataProcessPage, notificationDataProcessPage: [notificationDataProcessPage],
notificationPollCallback: notificationPollCallback, notificationPollCallback: [notificationPollCallback],
notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, notificationPollStopPaginatingConditionMet: [isReturnedDataEmpty],
notificationPollCaresAboutSince: false notificationPollCaresAboutSince: [false]
}); });
Window.domainChanged.connect(clearLocalQMLDataAndClosePAL); Window.domainChanged.connect(clearLocalQMLDataAndClosePAL);
Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL); Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL);

View file

@ -37,7 +37,7 @@ function notificationPollCallback(userStoriesArray) {
// //
pingPong = !pingPong; pingPong = !pingPong;
var totalNewStories = 0; var totalNewStories = 0;
var shouldNotifyIndividually = !ui.isOpen && ui.notificationInitialCallbackMade; var shouldNotifyIndividually = !ui.isOpen && ui.notificationInitialCallbackMade[0];
userStoriesArray.forEach(function (story) { userStoriesArray.forEach(function (story) {
if (story.audience !== "for_connections" && if (story.audience !== "for_connections" &&
story.audience !== "for_feed") { story.audience !== "for_feed") {
@ -91,7 +91,7 @@ function notificationPollCallback(userStoriesArray) {
shouldShowDot = totalNewStories > 0 || (totalStories > 0 && shouldShowDot); shouldShowDot = totalNewStories > 0 || (totalStories > 0 && shouldShowDot);
ui.messagesWaiting(shouldShowDot && !ui.isOpen); ui.messagesWaiting(shouldShowDot && !ui.isOpen);
if (totalStories > 0 && !ui.isOpen && !ui.notificationInitialCallbackMade) { if (totalStories > 0 && !ui.isOpen && !ui.notificationInitialCallbackMade[0]) {
message = "There " + (totalStories === 1 ? "is " : "are ") + totalStories + " event" + message = "There " + (totalStories === 1 ? "is " : "are ") + totalStories + " event" +
(totalStories === 1 ? "" : "s") + " to know about. " + (totalStories === 1 ? "" : "s") + " to know about. " +
"Open GOTO to see " + (totalStories === 1 ? "it" : "them") + "."; "Open GOTO to see " + (totalStories === 1 ? "it" : "them") + ".";
@ -122,12 +122,12 @@ function startup() {
sortOrder: 8, sortOrder: 8,
onOpened: gotoOpened, onOpened: gotoOpened,
home: GOTO_QML_SOURCE, home: GOTO_QML_SOURCE,
notificationPollEndpoint: endpoint, notificationPollEndpoint: [endpoint],
notificationPollTimeoutMs: 60000, notificationPollTimeoutMs: [60000],
notificationDataProcessPage: notificationDataProcessPage, notificationDataProcessPage: [notificationDataProcessPage],
notificationPollCallback: notificationPollCallback, notificationPollCallback: [notificationPollCallback],
notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, notificationPollStopPaginatingConditionMet: [isReturnedDataEmpty],
notificationPollCaresAboutSince: false notificationPollCaresAboutSince: [false]
}); });
} }