diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 3c0577532a..dbe4235cdd 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -96,7 +96,7 @@ Item { 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 wrapMode: Text.WordWrap lineHeight: 1 diff --git a/interface/resources/qml/controls-uit/FilterBar.qml b/interface/resources/qml/controls-uit/FilterBar.qml index ecae790b22..3d4e18ed48 100644 --- a/interface/resources/qml/controls-uit/FilterBar.qml +++ b/interface/resources/qml/controls-uit/FilterBar.qml @@ -260,38 +260,50 @@ Item { interactive: false; anchors.fill: parent; model: filterBarModel; - delegate: Rectangle { - id: dropDownButton; - color: hifi.colors.white; + delegate: Item { width: parent.width; height: 50; + Rectangle { + id: dropDownButton; + color: hifi.colors.white; + width: parent.width; + height: 50; + visible: true; - RalewaySemiBold { - id: dropDownButtonText; - text: model.displayName; - anchors.fill: parent; - anchors.leftMargin: 12; - color: hifi.colors.baseGray; - horizontalAlignment: Text.AlignLeft; - verticalAlignment: Text.AlignVCenter; - size: 18; + RalewaySemiBold { + id: dropDownButtonText; + text: model.displayName; + anchors.fill: parent; + anchors.topMargin: 2; + anchors.leftMargin: 12; + color: hifi.colors.baseGray; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + size: 18; + } + + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + propagateComposedEvents: false; + onEntered: { + dropDownButton.color = hifi.colors.blueHighlight; + } + onExited: { + dropDownButton.color = hifi.colors.white; + } + onClicked: { + textField.forceActiveFocus(); + root.primaryFilter_index = index; + dropdownContainer.visible = false; + } + } } - - MouseArea { - anchors.fill: parent; - hoverEnabled: true; - propagateComposedEvents: false; - onEntered: { - dropDownButton.color = hifi.colors.blueHighlight; - } - onExited: { - dropDownButton.color = hifi.colors.white; - } - onClicked: { - textField.forceActiveFocus(); - root.primaryFilter_index = index; - dropdownContainer.visible = false; - } + Rectangle { + height: 2; + width: parent.width; + color: hifi.colors.lightGray; + visible: model.separator } } } diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index b7782c697d..89a8eff025 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -63,8 +63,8 @@ MessageBox { popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase; popup.button1text = 'CANCEL' popup.titleText = 'Get Wearables' - popup.bodyText = 'Buy wearables from Marketplace.' + '
' + - 'Wear wearable from My Purchases.' + '
' + '
' + + popup.bodyText = 'Get wearables from Marketplace.' + '
' + + 'Wear wearable from Inventory.' + '
' + '
' + 'Visit “AvatarIsland” to get wearables' popup.imageSource = getWearablesUrl; @@ -89,7 +89,7 @@ MessageBox { function showDeleteFavorite(favoriteName, callback) { 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.button1text = 'CANCEL' popup.button2text = 'DELETE' @@ -128,8 +128,8 @@ MessageBox { popup.button1text = 'CANCEL' popup.titleText = 'Get Avatars' - popup.bodyText = 'Buy avatars from Marketplace.' + '
' + - 'Wear avatars in My Purchases.' + '
' + '
' + + popup.bodyText = 'Get avatars from Marketplace.' + '
' + + 'Wear avatars in Inventory.' + '
' + '
' + 'Visit “BodyMart” to get free avatars.' popup.imageSource = getAvatarsUrl; diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index b13f23f17d..271aab87d1 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -240,11 +240,6 @@ Rectangle { lightboxPopup.button1method = function() { lightboxPopup.visible = false; } - lightboxPopup.button2text = "GO TO WALLET"; - lightboxPopup.button2method = function() { - lightboxPopup.visible = false; - sendToScript({method: 'checkout_openWallet'}); - }; lightboxPopup.visible = true; } else { sendToScript(msg); @@ -383,7 +378,7 @@ Rectangle { anchors.leftMargin: 16; width: paintedWidth; height: paintedHeight; - text: "Review Purchase:"; + text: "Review:"; color: hifi.colors.black; size: 28; } @@ -448,7 +443,7 @@ Rectangle { // "HFC" balance label HiFiGlyphs { id: itemPriceTextLabel; - visible: !(root.isUpdating && root.itemEdition > 0); + visible: !(root.isUpdating && root.itemEdition > 0) && (root.itemPrice > 0); text: hifi.glyphs.hfc; // Size size: 30; @@ -464,7 +459,7 @@ Rectangle { } FiraSansSemiBold { 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 size: (root.isUpdating && root.itemEdition > 0) ? 20 : 26; // Anchors @@ -559,7 +554,7 @@ Rectangle { } } - // "View in My Purchases" button + // "View in Inventory" button HifiControlsUit.Button { id: viewInMyPurchasesButton; visible: false; @@ -570,7 +565,7 @@ Rectangle { height: 50; anchors.left: parent.left; 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: { if (root.isUpdating) { sendToScript({method: 'checkout_goToPurchases', filterText: root.baseItemName}); @@ -594,7 +589,7 @@ Rectangle { anchors.left: parent.left; anchors.right: parent.right; 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: { if (root.isUpdating && root.itemEdition > 0) { // If we're updating an app, the existing app needs to be uninstalled. @@ -608,9 +603,9 @@ Rectangle { } else if (root.isCertified) { if (!root.shouldBuyWithControlledFailure) { 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 " + root.itemName + - " until the server owner gives you 'Replace Content' permissions.

Are you sure you want to purchase this content set?"; + " until the server owner gives you 'Replace Content' permissions.

Are you sure you want to get this content set?"; lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = function() { lightboxPopup.visible = false; @@ -694,7 +689,7 @@ Rectangle { id: completeText2; text: "The " + (root.itemTypesText)[itemTypesArray.indexOf(root.itemType)] + ' ' + root.itemName + '' + - " has been added to your Purchases and a receipt will appear in your Wallet's transaction history."; + " has been added to your Inventory."; // Text size size: 18; // Anchors @@ -833,7 +828,7 @@ Rectangle { } lightboxPopup.button2text = "OPEN GOTO"; lightboxPopup.button2method = function() { - sendToScript({method: 'purchases_openGoTo'}); + sendToScript({method: 'checkout_openGoTo'}); lightboxPopup.visible = false; }; lightboxPopup.visible = true; @@ -864,7 +859,7 @@ Rectangle { RalewaySemiBold { id: myPurchasesLink; - text: 'View this item in My Purchases'; + text: 'View this item in your Inventory'; // Text size size: 18; // Anchors @@ -886,7 +881,8 @@ Rectangle { RalewaySemiBold { id: walletLink; - text: 'View receipt in Wallet'; + visible: !WalletScriptingInterface.limitedCommerce; + text: 'View receipt in Recent Activity'; // Text size size: 18; // Anchors @@ -902,18 +898,18 @@ Rectangle { horizontalAlignment: Text.AlignLeft; verticalAlignment: Text.AlignVCenter; onLinkActivated: { - sendToScript({method: 'purchases_openWallet'}); + sendToScript({method: 'checkout_openRecentActivity'}); } } RalewayRegular { 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. ' + 'Learn More'; // Text size size: 18; // Anchors - anchors.top: walletLink.bottom; + anchors.top: walletLink.visible ? walletLink.bottom : myPurchasesLink.bottom; anchors.topMargin: 32; height: paintedHeight; anchors.left: parent.left; @@ -925,8 +921,8 @@ Rectangle { horizontalAlignment: Text.AlignLeft; verticalAlignment: Text.AlignVCenter; onLinkActivated: { - lightboxPopup.titleText = "Purchase Confirmations"; - lightboxPopup.bodyText = 'Your item is marked "pending" while your purchase is being confirmed.

' + + lightboxPopup.titleText = "Confirmations"; + lightboxPopup.bodyText = 'Your item is marked "pending" while the transfer is being confirmed.

' + 'Confirmations usually take about 90 seconds.'; lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1method = function() { @@ -936,9 +932,9 @@ Rectangle { } } - // "Continue Shopping" button + // "Continue" button HifiControlsUit.Button { - id: continueShoppingButton; + id: continueButton; color: hifi.buttons.noneBorderlessGray; colorScheme: hifi.colorSchemes.light; anchors.bottom: parent.bottom; @@ -946,9 +942,9 @@ Rectangle { anchors.right: parent.right; width: 193; height: 44; - text: "Continue Shopping"; + text: "Continue"; onClicked: { - sendToScript({method: 'checkout_continueShopping', itemId: itemId}); + sendToScript({method: 'checkout_continue', itemId: itemId}); } } } @@ -971,7 +967,7 @@ Rectangle { RalewayRegular { id: failureHeaderText; - text: "Purchase Failed.
Your Purchases and HFC balance haven't changed."; + text: "Purchase Failed.
Your Inventory and HFC balance haven't changed."; // Text size size: 24; // Anchors @@ -1037,7 +1033,7 @@ Rectangle { width: parent.width/2 - anchors.leftMargin*2; text: "Back to Marketplace"; onClicked: { - sendToScript({method: 'checkout_continueShopping', itemId: itemId}); + sendToScript({method: 'checkout_continue', itemId: itemId}); } } } @@ -1122,10 +1118,10 @@ Rectangle { if (root.balanceAfterPurchase < 0) { // If you already own the item... if (!root.alreadyOwned) { - buyText.text = "Your Wallet does not have sufficient funds to purchase this item."; + buyText.text = "You do not have sufficient funds to purchase this item."; // Else if you don't already own the item... } else if (canBuyAgain()) { - buyText.text = "Your Wallet does not have sufficient funds to purchase this item again."; + buyText.text = "You do not have sufficient funds to purchase this item again."; } else { buyText.text = "While you do not have sufficient funds to buy this, you already have this item." } @@ -1171,7 +1167,7 @@ Rectangle { buyText.text = ""; } } else { - buyText.text = '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.'; + buyText.text = '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.'; buyTextContainer.color = hifi.colors.white; buyTextContainer.border.color = hifi.colors.white; buyGlyph.text = ""; diff --git a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml index 1b77dcd3e9..99c2d89da8 100644 --- a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml +++ b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml @@ -27,7 +27,6 @@ Item { property string referrerURL: (Account.metaverseServerURL + "/marketplace?"); readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin; property alias usernameDropdownVisible: usernameDropdown.visible; - property bool messagesWaiting: false; height: mainContainer.height + additionalDropdownHeight; @@ -38,7 +37,6 @@ Item { if (walletStatus === 0) { sendToParent({method: "needsLogIn"}); } else if (walletStatus === 5) { - Commerce.getAvailableUpdates(); Commerce.getSecurityImage(); } else if (walletStatus > 5) { console.log("ERROR in EmulatedMarketplaceHeader.qml: Unknown wallet status: " + walletStatus); @@ -59,14 +57,6 @@ Item { 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: { @@ -118,50 +108,6 @@ Item { anchors.right: securityImage.left; 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 { id: textMetrics; font.family: "Raleway" @@ -267,7 +213,7 @@ Item { anchors.topMargin: -buttonAndUsernameContainer.anchors.bottomMargin; anchors.right: buttonAndUsernameContainer.right; height: childrenRect.height; - width: 100; + width: 150; Rectangle { id: myItemsButton; @@ -279,7 +225,7 @@ Item { RalewaySemiBold { anchors.fill: parent; - text: "My Items" + text: "My Submissions" color: hifi.colors.baseGray; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; diff --git a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml index 5f874d3f04..0b982893f1 100644 --- a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml +++ b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml @@ -87,7 +87,7 @@ Rectangle { } RalewayRegular { id: introText2; - text: "My Purchases"; + text: "Inventory"; // Text size size: 22; // Anchors @@ -116,7 +116,7 @@ Rectangle { RalewayRegular { id: step1text; - text: "The 'REZ IT' button makes your purchase appear in front of you."; + text: "The 'REZ IT' button makes your item appear in front of you."; // Text size size: 20; // Anchors diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index 0a5c3e8053..bb4bb624bc 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -73,6 +73,10 @@ Item { } onTransferAssetToNodeResult: { + if (!root.visible) { + return; + } + root.isCurrentlySendingAsset = false; if (result.status === 'success') { @@ -92,6 +96,10 @@ Item { } onTransferAssetToUsernameResult: { + if (!root.visible) { + return; + } + root.isCurrentlySendingAsset = false; if (result.status === 'success') { @@ -1309,13 +1317,13 @@ Item { Rectangle { anchors.top: parent.top; - anchors.topMargin: root.assetName === "" ? 15 : 150; + anchors.topMargin: root.assetName === "" ? 15 : 125; anchors.left: parent.left; anchors.leftMargin: root.assetName === "" ? 15 : 50; anchors.right: parent.right; anchors.rightMargin: root.assetName === "" ? 15 : 50; anchors.bottom: parent.bottom; - anchors.bottomMargin: root.assetName === "" ? 15 : 240; + anchors.bottomMargin: root.assetName === "" ? 15 : 125; color: "#FFFFFF"; RalewaySemiBold { diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index d24344b40a..885838a26e 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -28,7 +28,7 @@ Rectangle { property string itemName: "--"; property string itemOwner: "--"; property string itemEdition: "--"; - property string dateOfPurchase: "--"; + property string dateAcquired: "--"; property string itemCost: "--"; property string certTitleTextColor: hifi.colors.darkGray; property string certTextColor: hifi.colors.white; @@ -64,7 +64,7 @@ Rectangle { root.itemName = ""; root.itemEdition = ""; root.itemOwner = ""; - root.dateOfPurchase = ""; + root.dateAcquired = ""; root.itemCost = ""; 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 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"; - root.dateOfPurchase = root.isMyCert ? getFormattedDate(result.data.transfer_created_at * 1000) : "Undisclosed"; - root.itemCost = (root.isMyCert && result.data.cost !== undefined) ? result.data.cost : "Undisclosed"; + root.dateAcquired = root.isMyCert ? getFormattedDate(result.data.transfer_created_at * 1000) : "Undisclosed"; + root.itemCost = (root.isMyCert && result.data.cost !== undefined) ? + (parseInt(result.data.cost) > 0 ? result.data.cost : "Free") : "Undisclosed"; } if (root.certInfoReplaceMode > 4) { 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.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 { errorText.text = "The person who placed this item doesn't own it."; } @@ -102,8 +103,8 @@ Rectangle { showInMarketplaceButton.visible = false; // "Edition" text previously set above in this function // "Owner" text previously set above in this function - // "Purchase Date" text previously set above in this function - // "Purchase Price" text previously set above in this function + // "Acquisition Date" text previously set above in this function + // "Acquisition Price" text previously set above in this function if (result.data.invalid_reason) { errorText.text = result.data.invalid_reason; } @@ -117,8 +118,8 @@ Rectangle { showInMarketplaceButton.visible = true; // "Edition" text previously set above in this function // "Owner" text previously set above in this function - // "Purchase Date" text previously set above in this function - // "Purchase Price" text previously set above in this function + // "Acquisition Date" 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, " + "this entity will be cleaned up by the domain."; } @@ -145,8 +146,8 @@ Rectangle { // "Item Name" text will be set in "onCertificateInfoResult()" // "Edition" text will be set in "onCertificateInfoResult()" // "Owner" text will be set in "onCertificateInfoResult()" - // "Purchase Date" text will be set in "onCertificateInfoResult()" - // "Purchase Price" text will be set in "onCertificateInfoResult()" + // "Acquisition Date" text will be set in "onCertificateInfoResult()" + // "Acquisition Price" text will be set in "onCertificateInfoResult()" errorText.text = ""; } else if (root.certificateStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT root.useGoldCert = false; @@ -160,7 +161,7 @@ Rectangle { root.itemName = ""; root.itemEdition = ""; root.itemOwner = ""; - root.dateOfPurchase = ""; + root.dateAcquired = ""; root.itemCost = ""; errorText.text = "Your request to inspect this item timed out. Please try again later."; } else if (root.certificateStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED @@ -175,8 +176,8 @@ Rectangle { // "Item Name" text will be set in "onCertificateInfoResult()" // "Edition" text will be set in "onCertificateInfoResult()" // "Owner" text will be set in "onCertificateInfoResult()" - // "Purchase Date" text will be set in "onCertificateInfoResult()" - // "Purchase Price" text will be set in "onCertificateInfoResult()" + // "Acquisition Date" 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."; } else if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED root.useGoldCert = false; @@ -190,8 +191,8 @@ Rectangle { // "Item Name" text will be set in "onCertificateInfoResult()" root.itemEdition = "Uncertified Copy" // "Owner" text will be set in "onCertificateInfoResult()" - // "Purchase Date" text will be set in "onCertificateInfoResult()" - // "Purchase Price" text will be set in "onCertificateInfoResult()" + // "Acquisition Date" text will be set in "onCertificateInfoResult()" + // "Acquisition Price" text will be set in "onCertificateInfoResult()" // "Error Text" text will be set in "onCertificateInfoResult()" } else { console.log("Unknown certificate status received from ledger signal!"); @@ -485,8 +486,8 @@ Rectangle { } RalewayRegular { - id: dateOfPurchaseHeader; - text: "PURCHASE DATE"; + id: dateAcquiredHeader; + text: "ACQUISITION DATE"; // Text size size: 16; // Anchors @@ -500,15 +501,15 @@ Rectangle { color: hifi.colors.darkGray; } AnonymousProRegular { - id: dateOfPurchase; - text: root.dateOfPurchase; + id: dateAcquired; + text: root.dateAcquired; // Text size size: 18; // Anchors - anchors.top: dateOfPurchaseHeader.bottom; + anchors.top: dateAcquiredHeader.bottom; anchors.topMargin: 8; - anchors.left: dateOfPurchaseHeader.left; - anchors.right: dateOfPurchaseHeader.right; + anchors.left: dateAcquiredHeader.left; + anchors.right: dateAcquiredHeader.right; height: paintedHeight; // Style color: root.infoTextColor; @@ -516,7 +517,7 @@ Rectangle { RalewayRegular { id: priceHeader; - text: "PURCHASE PRICE"; + text: "ACQUISITION PRICE"; // Text size size: 16; // Anchors @@ -530,7 +531,7 @@ Rectangle { } HiFiGlyphs { id: hfcGlyph; - visible: priceText.text !== "Undisclosed" && priceText.text !== ""; + visible: priceText.text !== "Undisclosed" && priceText.text !== "" && priceText.text !== "Free"; text: hifi.glyphs.hfc; // Size size: 24; @@ -618,7 +619,7 @@ Rectangle { root.itemName = "--"; root.itemOwner = "--"; root.itemEdition = "--"; - root.dateOfPurchase = "--"; + root.dateAcquired = "--"; root.marketplaceUrl = ""; root.itemCost = "--"; root.isMyCert = false; diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index eeb9ac3c54..0828d86eff 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -47,8 +47,7 @@ Item { property string wornEntityID; property string upgradeUrl; property string upgradeTitle; - property bool updateAvailable: root.upgradeUrl !== "" && !root.isShowingMyItems; - property bool isShowingMyItems; + property bool updateAvailable: root.upgradeUrl !== ""; property bool valid; property string originalStatusText; @@ -231,7 +230,7 @@ Item { Loader { id: giftButton; - visible: !root.isShowingMyItems; + visible: root.itemEdition > 0; sourceComponent: contextCardButton; anchors.right: parent.right; anchors.top: parent.top; @@ -345,6 +344,7 @@ Item { Rectangle { id: permissionExplanationCard; + visible: false; anchors.left: parent.left; anchors.leftMargin: 30; anchors.top: parent.top; diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 015ec3a172..df4d7f6175 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -19,7 +19,6 @@ import "../../../controls" as HifiControls import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. import "../wallet" as HifiWallet import "../common" as HifiCommerceCommon -import "../inspectionCertificate" as HifiInspectionCertificate import "../common/sendAsset" as HifiSendAsset import "../.." as HifiCommon @@ -34,7 +33,6 @@ Rectangle { property bool securityImageResultReceived: false; property bool purchasesReceived: false; property bool punctuationMode: false; - property bool isShowingMyItems: false; property bool isDebuggingFirstUseTutorial: false; property string installedApps; property bool keyboardRaised: false; @@ -92,7 +90,6 @@ Rectangle { if (result.status !== 'success') { console.log("Failed to get Available Updates", result.data.message); } else { - sendToScript({method: 'purchases_availableUpdatesReceived', numUpdates: result.data.updates.length }); root.numUpdatesAvailable = result.total_entries; } } @@ -106,10 +103,6 @@ Rectangle { } } - onIsShowingMyItemsChanged: { - getPurchases(); - } - Timer { id: notSetUpTimer; interval: 200; @@ -118,19 +111,6 @@ Rectangle { } } - HifiInspectionCertificate.InspectionCertificate { - id: inspectionCertificate; - z: 998; - visible: false; - anchors.fill: parent; - - Connections { - onSendToScript: { - sendToScript(message); - } - } - } - HifiCommerceCommon.CommerceLightbox { id: lightboxPopup; z: 999; @@ -180,7 +160,8 @@ Rectangle { HifiCommerceCommon.EmulatedMarketplaceHeader { id: titleBarContainer; z: 997; - visible: !needsLogIn.visible; + visible: false; + height: 100; // Size width: parent.width; // Anchors @@ -199,11 +180,6 @@ Rectangle { lightboxPopup.button1method = function() { lightboxPopup.visible = false; } - lightboxPopup.button2text = "GO TO WALLET"; - lightboxPopup.button2method = function() { - sendToScript({method: 'purchases_openWallet'}); - lightboxPopup.visible = false; - }; lightboxPopup.visible = true; } else { sendToScript(msg); @@ -475,7 +451,7 @@ Rectangle { anchors.left: parent.left; anchors.leftMargin: 16; width: paintedWidth; - text: isShowingMyItems ? "My Items" : "My Purchases"; + text: "Inventory"; color: hifi.colors.black; size: 22; } @@ -517,8 +493,13 @@ Rectangle { "filterName": "wearable" }, { + "separator" : true, "displayName": "Updatable", "filterName": "updated" + }, + { + "displayName": "My Submissions", + "filterName": "proofs" } ] filterBar.primaryFilterChoices.clear(); @@ -533,6 +514,7 @@ Rectangle { onTextChanged: { purchasesModel.searchFilter = filterBar.text; filterBar.previousText = filterBar.text; + } } } @@ -556,10 +538,18 @@ Rectangle { listModelName: 'purchases'; listView: purchasesContentsList; 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( - root.isShowingMyItems ? "proofs" : "purchased", - filterBar.primaryFilter_filterName, + editionFilter, + primaryFilter, filterBar.text, purchasesModel.currentPageToRetrieve, purchasesModel.itemsPerPage @@ -609,7 +599,6 @@ Rectangle { upgradeUrl: model.upgrade_url; upgradeTitle: model.upgrade_title; itemType: model.item_type; - isShowingMyItems: root.isShowingMyItems; valid: model.valid; anchors.topMargin: 10; anchors.bottomMargin: 10; @@ -626,8 +615,6 @@ Rectangle { sendToScript({ method: 'purchases_updateWearables' }); } } else if (msg.method === 'purchases_itemCertificateClicked') { - inspectionCertificate.visible = true; - inspectionCertificate.isLightbox = true; sendToScript(msg); } else if (msg.method === "showInvalidatedLightbox") { lightboxPopup.titleText = "Item Invalidated"; @@ -640,7 +627,7 @@ Rectangle { lightboxPopup.visible = true; } else if (msg.method === "showPendingLightbox") { 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."; lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1method = function() { @@ -822,7 +809,8 @@ Rectangle { Rectangle { id: updatesAvailableBanner; - visible: root.numUpdatesAvailable > 0 && !root.isShowingMyItems; + visible: root.numUpdatesAvailable > 0 && + filterBar.primaryFilter_filterName !== "proofs"; anchors.bottom: parent.bottom; anchors.left: parent.left; anchors.right: parent.right; @@ -883,9 +871,8 @@ Rectangle { id: noItemsAlertContainer; visible: !purchasesContentsList.visible && root.purchasesReceived && - root.isShowingMyItems && filterBar.text === "" && - filterBar.primaryFilter_displayName === ""; + filterBar.primaryFilter_filterName === "proofs"; anchors.top: filterBarContainer.bottom; anchors.topMargin: 12; anchors.left: parent.left; @@ -895,7 +882,7 @@ Rectangle { // Explanitory text RalewayRegular { id: noItemsYet; - text: "You haven't submitted anything to the Marketplace yet!

Submit an item to the Marketplace to add it to My Items."; + text: "You haven't submitted anything to the Marketplace yet!

Submit an item to the Marketplace to add it to My Submissions."; // Text size size: 22; // Anchors @@ -933,7 +920,6 @@ Rectangle { id: noPurchasesAlertContainer; visible: !purchasesContentsList.visible && root.purchasesReceived && - !root.isShowingMyItems && filterBar.text === "" && filterBar.primaryFilter_displayName === ""; anchors.top: filterBarContainer.bottom; @@ -945,7 +931,7 @@ Rectangle { // Explanitory text RalewayRegular { id: haventPurchasedYet; - text: "You haven't purchased anything yet!

Get an item from Marketplace to add it to My Purchases."; + text: "You haven't gotten anything yet!

Get an item from Marketplace to add it to your Inventory."; // Text size size: 22; // Anchors @@ -1065,11 +1051,10 @@ Rectangle { titleBarContainer.referrerURL = message.referrerURL || ""; filterBar.text = message.filterText ? message.filterText : ""; break; - case 'inspectionCertificate_setCertificateId': - inspectionCertificate.fromScript(message); - break; case 'purchases_showMyItems': - root.isShowingMyItems = true; + filterBar.primaryFilter_filterName = "proofs"; + filterBar.primaryFilter_displayName = "Proofs"; + filterBar.primaryFilter_index = 6; break; case 'updateConnections': sendAsset.updateConnections(message.connections); diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index 6cf00f78eb..575edfc34d 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -62,7 +62,7 @@ Item { isExpanded: false; question: "How can I get HFC?"; answer: "High Fidelity commerce is in open beta right now. Want more HFC? \ -Get it by going to

BankOfHighFidelity. and meeting with the banker!"; +Get it by going to BankOfHighFidelity and meeting with the banker!"; } ListElement { isExpanded: false; diff --git a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml index eadf1ca8a2..03af964830 100644 --- a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml +++ b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml @@ -93,7 +93,7 @@ Item { // Text below helper text RalewayRegular { id: loginDetailText; - text: "To buy/sell items on the Marketplace, or to use your Wallet, you must first log in to High Fidelity."; + text: "To get items on the Marketplace, or to use your Assets, you must first log in to High Fidelity."; // Text size size: 18; // Anchors diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index d032f060e2..76c2484f0b 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -20,6 +20,8 @@ import "../../../controls" as HifiControls import "../common" as HifiCommerceCommon import "../common/sendAsset" import "../.." as HifiCommon +import "../purchases" as HifiPurchases +import "../inspectionCertificate" as HifiInspectionCertificate Rectangle { HifiConstants { id: hifi; } @@ -27,6 +29,7 @@ Rectangle { id: root; property string activeView: "initialize"; + property string initialActiveViewAfterStatus5: "walletInventory"; property bool keyboardRaised: false; property bool isPassword: false; @@ -64,7 +67,8 @@ Rectangle { } } else if (walletStatus === 5) { if (root.activeView !== "walletSetup") { - root.activeView = "walletHome"; + root.activeView = root.initialActiveViewAfterStatus5; + Commerce.getAvailableUpdates(); Commerce.getSecurityImage(); } } else { @@ -86,6 +90,21 @@ Rectangle { 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 { @@ -124,7 +143,7 @@ Rectangle { // Title Bar text RalewaySemiBold { id: titleBarText; - text: "WALLET"; + text: "ASSETS"; // Text size size: hifi.fontSizes.overlayTitle; // 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 { id: http; } @@ -380,16 +432,17 @@ Rectangle { // "WALLET HOME" tab button Rectangle { id: walletHomeButtonContainer; + property bool messagesWaiting: false; visible: !walletSetup.visible; color: root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: parent.left; + anchors.left: exchangeMoneyButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; - + HiFiGlyphs { id: homeTabIcon; - text: hifi.glyphs.home2; + text: hifi.glyphs.leftRightArrows; // Size size: 50; // Anchors @@ -397,11 +450,24 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // 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 { - text: "WALLET HOME"; + text: "RECENT ACTIVITY"; // Text size size: 16; // Anchors @@ -412,7 +478,7 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // 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; // Alignment horizontalAlignment: Text.AlignHCenter; @@ -421,6 +487,7 @@ Rectangle { MouseArea { id: walletHomeTabMouseArea; anchors.fill: parent; + enabled: !WalletScriptingInterface.limitedCommerce; hoverEnabled: enabled; onClicked: { root.activeView = "walletHome"; @@ -434,16 +501,18 @@ Rectangle { // "EXCHANGE MONEY" tab button Rectangle { id: exchangeMoneyButtonContainer; + property bool messagesWaiting: false; + visible: !walletSetup.visible; - color: hifi.colors.black; + color: root.activeView === "walletInventory" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: walletHomeButtonContainer.right; + anchors.left: parent.left; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; - + HiFiGlyphs { id: exchangeMoneyTabIcon; - text: hifi.glyphs.leftRightArrows; + text: hifi.glyphs.home2; // Size size: 50; // Anchors @@ -451,11 +520,24 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // 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 { - text: "EXCHANGE MONEY"; + text: "INVENTORY"; // Text size size: 16; // Anchors @@ -466,12 +548,24 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: hifi.colors.lightGray50; + color: root.activeView === "walletInventory" || inventoryTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; 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,10 +575,10 @@ Rectangle { visible: !walletSetup.visible; color: root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: exchangeMoneyButtonContainer.right; + anchors.left: walletHomeButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; - + HiFiGlyphs { id: sendMoneyTabIcon; text: hifi.glyphs.paperPlane; @@ -495,7 +589,7 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // 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 { @@ -510,7 +604,7 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // 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; // Alignment horizontalAlignment: Text.AlignHCenter; @@ -520,6 +614,7 @@ Rectangle { MouseArea { id: sendMoneyTabMouseArea; anchors.fill: parent; + enabled: !WalletScriptingInterface.limitedCommerce; hoverEnabled: enabled; onClicked: { root.activeView = "sendMoney"; @@ -539,7 +634,7 @@ Rectangle { anchors.left: sendMoneyButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; - + HiFiGlyphs { id: helpTabIcon; text: hifi.glyphs.question; @@ -588,16 +683,16 @@ Rectangle { function resetTabButtonColors() { walletHomeButtonContainer.color = hifi.colors.black; sendMoneyButtonContainer.color = hifi.colors.black; - securityButtonContainer.color = hifi.colors.black; helpButtonContainer.color = hifi.colors.black; + exchangeMoneyButtonContainer.color = hifi.colors.black; if (root.activeView === "walletHome") { walletHomeButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView === "sendMoney") { sendMoneyButtonContainer.color = hifi.colors.blueAccent; - } else if (root.activeView === "security") { - securityButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView === "help") { helpButtonContainer.color = hifi.colors.blueAccent; + } else if (root.activeView == "walletInventory") { + exchangeMoneyButtonContainer.color = hifi.colors.blueAccent; } } } @@ -663,18 +758,40 @@ Rectangle { break; case 'updateConnections': sendMoney.updateConnections(message.connections); + walletInventory.fromScript(message); break; case 'selectRecipient': case 'updateSelectedRecipientUsername': sendMoney.fromScript(message); + walletInventory.fromScript(message); break; case 'http.response': http.handleHttpResponse(message); + // Duplicate handler is required because we don't track referrer for `http` + walletInventory.fromScript(message); break; case 'palIsStale': 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. 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: console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); } @@ -722,7 +839,8 @@ Rectangle { root.activeView = "initialize"; Commerce.getWalletStatus(); } else if (msg.referrer === 'purchases') { - sendToScript({method: 'goToPurchases'}); + root.activeView = "walletInventory"; + tabButtonsContainer.resetTabButtonColors(); } else if (msg.referrer === 'marketplace cta' || msg.referrer === 'mainPage') { sendToScript({method: 'goToMarketplaceMainPage', itemId: msg.referrer}); } else { diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index 627da1d43f..d32017189e 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -179,28 +179,6 @@ Item { color: hifi.colors.baseGrayHighlight; } - RalewaySemiBold { - id: myPurchasesLink; - text: 'My Purchases'; - // 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 { id: transactionHistoryModel; property int lastPendingCount: 0; diff --git a/interface/resources/qml/hifi/dialogs/security/Security.qml b/interface/resources/qml/hifi/dialogs/security/Security.qml index 8baff0ac13..96a554838f 100644 --- a/interface/resources/qml/hifi/dialogs/security/Security.qml +++ b/interface/resources/qml/hifi/dialogs/security/Security.qml @@ -328,7 +328,7 @@ Rectangle { HifiStylesUit.RalewayRegular { text: "Your wallet is not set up.\n" + - "Open the WALLET app to get started."; + "Open the ASSETS app to get started."; // Anchors anchors.fill: parent; // Text size diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 245e6c0017..c2faf8494a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -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 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()->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(); @@ -3152,7 +3154,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); surfaceContext->setContextProperty("Selection", DependencyManager::get().data()); surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get().data()); - surfaceContext->setContextProperty("Wallet", DependencyManager::get().data()); + surfaceContext->setContextProperty("WalletScriptingInterface", DependencyManager::get().data()); surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get().data()); @@ -6874,7 +6876,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance()); scriptEngine->registerGlobalObject("Selection", DependencyManager::get().data()); scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("Wallet", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("WalletScriptingInterface", DependencyManager::get().data()); scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get().data()); scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance()); scriptEngine->registerGlobalObject("ResourceRequestObserver", DependencyManager::get().data()); @@ -7879,6 +7881,7 @@ void Application::loadAvatarBrowser() const { auto tablet = dynamic_cast(DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system")); // construct the url to the marketplace item QString url = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace?category=avatars"; + QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js"; tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH); DependencyManager::get()->openTablet(); diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 60cd96c0ca..0c59fbc6d0 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -244,7 +244,6 @@ QString transactionString(const QJsonObject& valueObject) { return result; } -static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/"; void Ledger::historySuccess(QNetworkReply* reply) { // here we send a historyResult with some extra stuff in it // Namely, the styled text we'd like to show. The issue is the diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index e2158b9fd7..93ee60ba5b 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -10,13 +10,15 @@ // #include "WalletScriptingInterface.h" +#include CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qmlObject, parent) { Q_ASSERT(QThread::currentThread() == qApp->thread()); } WalletScriptingInterface::WalletScriptingInterface() { - + connect(DependencyManager::get().data(), + &AccountManager::limitedCommerceChanged, this, &WalletScriptingInterface::limitedCommerceChanged); } void WalletScriptingInterface::refreshWalletStatus() { @@ -42,4 +44,4 @@ void WalletScriptingInterface::proveAvatarEntityOwnershipVerification(const QUui } else { qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is not an avatar entity"; } -} \ No newline at end of file +} diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 25955ca7a3..36ee021b29 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -15,13 +15,13 @@ #include #include -#include "scripting/HMDScriptingInterface.h" #include #include #include #include "Application.h" #include "commerce/Wallet.h" #include "ui/overlays/ContextOverlayInterface.h" +#include class CheckoutProxy : public QmlWrapper { Q_OBJECT @@ -29,7 +29,6 @@ public: CheckoutProxy(QObject* qmlObject, QObject* parent = nullptr); }; - /**jsdoc * @namespace Wallet * @@ -37,28 +36,31 @@ public: * @hifi-client-entity * * @property {number} walletStatus + * @property {bool} limitedCommerce */ class WalletScriptingInterface : public QObject, public Dependency { Q_OBJECT - + SINGLETON_DEPENDENCY Q_PROPERTY(uint walletStatus READ getWalletStatus WRITE setWalletStatus NOTIFY walletStatusChanged) + Q_PROPERTY(bool limitedCommerce READ getLimitedCommerce WRITE setLimitedCommerce NOTIFY limitedCommerceChanged) public: + WalletScriptingInterface(); /**jsdoc - * @function Wallet.refreshWalletStatus + * @function WalletScriptingInterface.refreshWalletStatus */ Q_INVOKABLE void refreshWalletStatus(); /**jsdoc - * @function Wallet.getWalletStatus + * @function WalletScriptingInterface.getWalletStatus * @returns {number} */ Q_INVOKABLE uint getWalletStatus() { return _walletStatus; } /**jsdoc - * @function Wallet.proveAvatarEntityOwnershipVerification + * @function WalletScriptingInterface.proveAvatarEntityOwnershipVerification * @param {Uuid} entityID */ Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID); @@ -67,29 +69,38 @@ public: // scripts could cause the Wallet to incorrectly report its status. void setWalletStatus(const uint& status); + bool getLimitedCommerce() { return DependencyManager::get()->getLimitedCommerce(); } + void setLimitedCommerce(bool isLimited) { DependencyManager::get()->setLimitedCommerce(isLimited); }; + signals: /**jsdoc - * @function Wallet.walletStatusChanged + * @function WalletScriptingInterface.walletStatusChanged * @returns {Signal} */ void walletStatusChanged(); /**jsdoc - * @function Wallet.walletNotSetup + * @function WalletScriptingInterface.limitedCommerceChanged + * @returns {Signal} + */ + void limitedCommerceChanged(); + + /**jsdoc + * @function WalletScriptingInterface.walletNotSetup * @returns {Signal} */ void walletNotSetup(); /**jsdoc - * @function Wallet.ownershipVerificationSuccess + * @function WalletScriptingInterface.ownershipVerificationSuccess * @param {Uuid} entityID * @returns {Signal} */ void ownershipVerificationSuccess(const QUuid& entityID); /**jsdoc - * @function Wallet.ownershipVerificationFailed + * @function WalletScriptingInterface.ownershipVerificationFailed * @param {Uuid} entityID * @returns {Signal} */ diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 9f1018f6c7..11c268dd48 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -223,12 +223,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - Setting::Handle _settingSwitch{ "commerce", true }; - if (_settingSwitch.get()) { - return (entityProperties.getCertificateID().length() != 0); - } else { - return (entityProperties.getMarketplaceID().length() != 0); - } + return (entityProperties.getCertificateID().length() != 0); } bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 084615cae2..f13d25f22c 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -53,6 +53,7 @@ #include "ui/AvatarInputs.h" #include "avatar/AvatarManager.h" #include "scripting/AccountServicesScriptingInterface.h" +#include "scripting/WalletScriptingInterface.h" #include #include "ui/Snapshot.h" #include "SoundCacheScriptingInterface.h" @@ -270,6 +271,7 @@ void Web3DOverlay::setupQmlSurface(bool isTablet) { _webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface()); _webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("WalletScriptingInterface", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("ResourceRequestObserver", DependencyManager::get().data()); // Override min fps for tablet UI, for silky smooth scrolling diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index d99c0020da..2d1a0c0afb 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -837,3 +837,7 @@ void AccountManager::handleKeypairGenerationError() { // reset our waiting state for keypair response _isWaitingForKeypairResponse = false; } + +void AccountManager::setLimitedCommerce(bool isLimited) { + _limitedCommerce = isLimited; +} diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index f3b81cf1c9..093ac9a27c 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -98,6 +98,9 @@ public: void removeAccountFromFile(); + bool getLimitedCommerce() { return _limitedCommerce; } + void setLimitedCommerce(bool isLimited); + public slots: void requestAccessToken(const QString& login, const QString& password); void requestAccessTokenWithSteam(QByteArray authSessionTicket); @@ -121,6 +124,7 @@ signals: void loginFailed(); void logoutComplete(); void newKeypair(); + void limitedCommerceChanged(); private slots: void handleKeypairGenerationError(); @@ -150,6 +154,8 @@ private: QByteArray _pendingPrivateKey; QUuid _sessionID { QUuid::createUuid() }; + + bool _limitedCommerce { false }; }; #endif // hifi_AccountManager_h diff --git a/libraries/ui/src/ui/types/RequestFilters.cpp b/libraries/ui/src/ui/types/RequestFilters.cpp index 7f192d6e52..a3b3b7dc57 100644 --- a/libraries/ui/src/ui/types/RequestFilters.cpp +++ b/libraries/ui/src/ui/types/RequestFilters.cpp @@ -70,9 +70,9 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, // check if this is a request to a highfidelity URL bool isAuthable = isAuthableHighFidelityURL(info.requestUrl()); + auto accountManager = DependencyManager::get(); if (isAuthable) { // if we have an access token, add it to the right HTTP header for authorization - auto accountManager = DependencyManager::get(); if (accountManager->hasValidAccessToken()) { static const QString OAUTH_AUTHORIZATION_HEADER = "Authorization"; @@ -84,13 +84,9 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, 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 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 tokenStringCommerce{ "Chrome/48.0 (HighFidelityInterface WithHFC)" }; - Setting::Handle _settingSwitch{ "commerce", true }; - bool isMoney = _settingSwitch.get(); - - const QString tokenString = !isAuthable ? tokenStringMobile : (isMoney ? tokenStringCommerce : tokenStringMetaverse); + const QString tokenString = !isAuthable ? tokenStringMobile : (accountManager->getLimitedCommerce() ? tokenStringLimitedCommerce : tokenStringMetaverse); info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit()); } diff --git a/scripts/modules/appUi.js b/scripts/modules/appUi.js index efb842a9bb..e267293977 100644 --- a/scripts/modules/appUi.js +++ b/scripts/modules/appUi.js @@ -95,16 +95,16 @@ function AppUi(properties) { activeIcon: isWaiting ? that.activeMessagesButton : that.activeButton }); }; - that.notificationPollTimeout = false; - that.notificationPollTimeoutMs = 60000; - that.notificationPollEndpoint = false; - that.notificationPollStopPaginatingConditionMet = false; + that.notificationPollTimeout = [false]; + that.notificationPollTimeoutMs = [60000]; + that.notificationPollEndpoint = [false]; + that.notificationPollStopPaginatingConditionMet = [false]; that.notificationDataProcessPage = function (data) { return data; }; - that.notificationPollCallback = that.ignore; - that.notificationPollCaresAboutSince = false; - that.notificationInitialCallbackMade = false; + that.notificationPollCallback = [that.ignore]; + that.notificationPollCaresAboutSince = [false]; + that.notificationInitialCallbackMade = [false]; that.notificationDisplayBanner = function (message) { if (!that.isOpen) { Window.displayAnnouncement(message); @@ -129,7 +129,9 @@ function AppUi(properties) { } 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); if (that.isOpen) { that.buttonActive(false); @@ -139,83 +141,124 @@ function AppUi(properties) { 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: - Object.keys(properties).forEach(function (key) { that[key] = properties[key]; }); + Object.keys(properties).forEach(function (key) { + that[key] = properties[key]; + }); // // 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 currentDataPageToRetrieve = 1; - var concatenatedServerResponse = new Array(); - that.notificationPoll = function () { - if (!that.notificationPollEndpoint) { + var MS_IN_SEC = 1000; + that.notificationPoll = function (i) { + if (!that.notificationPollEndpoint[i]) { return; } - // User is "appearing offline" or is offline - if (GlobalServices.findableBy === "none" || Account.username === "") { - that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); + // User is "appearing offline" or is not logged in + if (GlobalServices.findableBy === "none" || Account.username === "Unknown user") { + // 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; } - 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 lastPollTimestamp = Settings.getValue(settingsKey, currentTimestamp); - if (that.notificationPollCaresAboutSince) { - url = url + "&since=" + lastPollTimestamp/1000; + if (that.notificationPollCaresAboutSince[i]) { + url = url + "&since=" + lastPollTimestamp / MS_IN_SEC; } Settings.setValue(settingsKey, currentTimestamp); console.debug(that.buttonName, 'polling for notifications at endpoint', url); - function requestCallback(error, response) { - if (error || (response.status !== 'success')) { - print("Error: unable to get", url, error || response.status); - that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); - return; - } - - if (!that.notificationPollStopPaginatingConditionMet || that.notificationPollStopPaginatingConditionMet(response)) { - 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); + request({ + json: true, + uri: url + }, + requestCallback, + { + indexOfRequest: i, + urlOfRequest: url + }); }; // 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() { - that.notificationInitialCallbackMade = false; - if (that.notificationPollTimeout) { - Script.clearTimeout(that.notificationPollTimeout); - that.notificationPollTimeout = false; + for (var j = 0; j < that.notificationPollEndpoint.length; j++) { + that.notificationInitialCallbackMade[j] = false; + if (that.notificationPollTimeout[j]) { + Script.clearTimeout(that.notificationPollTimeout[j]); + that.notificationPollTimeout[j] = false; + } + that.notificationPoll(j); } - that.notificationPoll(); } // // END Notification Handling @@ -322,9 +365,11 @@ function AppUi(properties) { } that.tablet.removeButton(that.button); } - if (that.notificationPollTimeout) { - Script.clearInterval(that.notificationPollTimeout); - that.notificationPollTimeout = false; + for (var i = 0; i < that.notificationPollTimeout.length; i++) { + if (that.notificationPollTimeout[i]) { + Script.clearInterval(that.notificationPollTimeout[i]); + that.notificationPollTimeout[i] = false; + } } }; // Set up the handlers. @@ -333,7 +378,7 @@ function AppUi(properties) { Script.scriptEnding.connect(that.onScriptEnding); GlobalServices.findableByChanged.connect(restartNotificationPoll); GlobalServices.myUsernameChanged.connect(restartNotificationPoll); - if (that.buttonName == Settings.getValue("startUpApp")) { + if (that.buttonName === Settings.getValue("startUpApp")) { Settings.setValue("startUpApp", ""); Script.setTimeout(function () { that.open(); diff --git a/scripts/modules/request.js b/scripts/modules/request.js index d0037f9b43..37f3ac0d7b 100644 --- a/scripts/modules/request.js +++ b/scripts/modules/request.js @@ -18,7 +18,8 @@ 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; // QT bug: apparently doesn't handle onload. Workaround using readyState. httpRequest.onreadystatechange = function () { @@ -38,7 +39,7 @@ module.exports = { if (error) { response = { statusCode: httpRequest.status }; } - callback(error, response); + callback(error, response, optionalCallbackParameter); } }; if (typeof options === 'string') { diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index ece35acce7..44f10396ca 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -159,7 +159,7 @@ var selectedAvatarEntityGrabbable = false; var selectedAvatarEntityID = 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 MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("html/js/marketplacesInject.js"); @@ -285,9 +285,9 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See case 'navigate': var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system") if(message.url.indexOf('app://') === 0) { - if(message.url === 'app://marketplace') { + if (message.url === 'app://marketplace') { tablet.gotoWebScreen(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); - } else if(message.url === 'app://purchases') { + } else if (message.url === 'app://purchases') { tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); } diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 2d44650d9c..033ca638e4 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -11,7 +11,7 @@ // 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 Script.include("/~/system/libraries/accountUtils.js"); @@ -20,7 +20,6 @@ var AppUi = Script.require('appUi'); var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; - // BEGIN AVATAR SELECTOR LOGIC var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 }; var SELECTED_COLOR = { red: 0xF3, green: 0x91, blue: 0x29 }; @@ -48,7 +47,6 @@ ExtendedOverlay.prototype.editOverlay = function (properties) { // change displa function color(selected, hovering) { var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR; function scale(component) { - var delta = 0xFF - component; return component; } 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. // 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. + // Depends on nearer coverOverlays to extend closer to us than farther ones. + var pickedOverlay = Overlays.findRayIntersection(pickRay); if (!pickedOverlay.intersects) { if (noHit) { return noHit(); @@ -131,6 +130,7 @@ function addAvatarNode(id) { } var pingPong = true; +var OVERLAY_SCALE = 0.032; function updateOverlays() { var eye = Camera.position; AvatarList.getAvatarIdentifiers().forEach(function (id) { @@ -148,7 +148,8 @@ function updateOverlays() { 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) + // 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 if (headIndex > 0) { offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; @@ -164,7 +165,7 @@ function updateOverlays() { overlay.editOverlay({ color: color(ExtendedOverlay.isSelected(id), overlay.hovering), position: target, - dimensions: 0.032 * distance + dimensions: OVERLAY_SCALE * distance }); }); pingPong = !pingPong; @@ -380,6 +381,31 @@ function onUsernameChanged() { Settings.setValue("keepMeLoggedIn", false); Settings.setValue("keepMeLoggedIn/savedUsername", ""); } +} + +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() @@ -387,8 +413,6 @@ function onUsernameChanged() { // Description: // -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(). -var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; -var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); function fromQml(message) { switch (message.method) { case 'passphrasePopup_cancelClicked': @@ -413,6 +437,7 @@ function fromQml(message) { } break; case 'needsLogIn_loginClicked': + ui.close(); openLoginWindow(); break; case 'disableHmdPreview': @@ -422,10 +447,6 @@ function fromQml(message) { case 'transactionHistory_linkClicked': ui.open(message.marketplaceLink, MARKETPLACES_INJECT_SCRIPT_URL); break; - case 'goToPurchases_fromWalletHome': - case 'goToPurchases': - ui.open(MARKETPLACE_PURCHASES_QML_PATH); - break; case 'goToMarketplaceMainPage': ui.open(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); break; @@ -450,22 +471,20 @@ function fromQml(message) { removeOverlays(); break; case 'sendAsset_sendPublicly': - if (message.assetName === "") { - deleteSendMoneyParticleEffect(); - sendMoneyRecipient = message.recipient; - var amount = message.amount; - var props = SEND_MONEY_PARTICLE_PROPERTIES; - props.parentID = MyAvatar.sessionUUID; - props.position = MyAvatar.position; - props.position.y += 0.2; - if (message.effectImage) { - props.textures = message.effectImage; - } - sendMoneyParticleEffect = Entities.addEntity(props, true); - particleEffectTimestamp = Date.now(); - updateSendMoneyParticleEffect(); - sendMoneyParticleEffectUpdateTimer = Script.setInterval(updateSendMoneyParticleEffect, SEND_MONEY_PARTICLE_TIMER_UPDATE); + deleteSendMoneyParticleEffect(); + sendMoneyRecipient = message.recipient; + var props = SEND_MONEY_PARTICLE_PROPERTIES; + props.parentID = MyAvatar.sessionUUID; + props.position = MyAvatar.position; + props.position.y += 0.2; + if (message.effectImage) { + props.textures = message.effectImage; } + sendMoneyParticleEffect = Entities.addEntity(props, true); + particleEffectTimestamp = Date.now(); + updateSendMoneyParticleEffect(); + sendMoneyParticleEffectUpdateTimer = + Script.setInterval(updateSendMoneyParticleEffect, SEND_MONEY_PARTICLE_TIMER_UPDATE); break; case 'transactionHistory_goToBank': if (Account.metaverseServerURL.indexOf("staging") >= 0) { @@ -474,6 +493,49 @@ function fromQml(message) { Window.location = "hifi://BankOfHighFidelity"; } 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': // Handled elsewhere, don't log. break; @@ -482,41 +544,76 @@ function fromQml(message) { } } +var isWired = false; function walletOpened() { Users.usernameFromIDReply.connect(usernameFromIDReply); Controller.mousePressEvent.connect(handleMouseEvent); Controller.mouseMoveEvent.connect(handleMouseMoveEvent); triggerMapping.enable(); triggerPressMapping.enable(); - shouldShowDot = false; - ui.messagesWaiting(shouldShowDot); + isWired = true; + + if (shouldShowDotHistory) { + ui.sendMessage({ + method: 'updateRecentActivityMessageLight', + messagesWaiting: shouldShowDotHistory + }); + } } function walletClosed() { off(); } -function notificationDataProcessPage(data) { +function notificationDataProcessPageUpdates(data) { + return data.data.updates; +} + +function notificationDataProcessPageHistory(data) { return data.data.history; } -var shouldShowDot = false; -function notificationPollCallback(historyArray) { +var shouldShowDotUpdates = false; +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) { var notificationCount = historyArray.length; - shouldShowDot = shouldShowDot || notificationCount > 0; - ui.messagesWaiting(shouldShowDot); + shouldShowDotHistory = shouldShowDotHistory || notificationCount > 0; + ui.messagesWaiting(shouldShowDotUpdates || shouldShowDotHistory); if (notificationCount > 0) { var message; - if (!ui.notificationInitialCallbackMade) { - message = "You have " + notificationCount + " unread wallet " + - "transaction" + (notificationCount === 1 ? "" : "s") + ". Open WALLET to see all activity."; + if (!ui.notificationInitialCallbackMade[1]) { + message = "You have " + notificationCount + " unread recent " + + "transaction" + (notificationCount === 1 ? "" : "s") + ". Open ASSETS to see all activity."; ui.notificationDisplayBanner(message); } else { for (var i = 0; i < notificationCount; i++) { message = '"' + (historyArray[i].message) + '" ' + - "Open WALLET to see all activity."; + "Open ASSETS to see all activity."; 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; 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 NOTIFICATION_POLL_TIMEOUT = 300000; var ui; 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({ buttonName: BUTTON_NAME, + buttonPrefix: "wallet-", sortOrder: 10, home: WALLET_QML_SOURCE, onOpened: walletOpened, onClosed: walletClosed, onMessage: fromQml, - notificationPollEndpoint: "/api/v1/commerce/history?per_page=10", - notificationPollTimeoutMs: 300000, - notificationDataProcessPage: notificationDataProcessPage, - notificationPollCallback: notificationPollCallback, - notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, - notificationPollCaresAboutSince: true + notificationPollEndpoint: notificationPollEndpointArray, + notificationPollTimeoutMs: notificationPollTimeoutMsArray, + notificationDataProcessPage: notificationDataProcessPageArray, + notificationPollCallback: notificationPollCallbackArray, + notificationPollStopPaginatingConditionMet: notificationPollStopPaginatingConditionMetArray, + notificationPollCaresAboutSince: notificationPollCaresAboutSinceArray }); GlobalServices.myUsernameChanged.connect(onUsernameChanged); installMarketplaceItemTester(); @@ -583,11 +703,14 @@ function startup() { var isUpdateOverlaysWired = false; function off() { - Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Controller.mousePressEvent.disconnect(handleMouseEvent); - Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); - triggerMapping.disable(); - triggerPressMapping.disable(); + 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); diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 24a96023da..c4a4adebdf 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -27,6 +27,7 @@ var xmlHttpRequest = null; var isPreparing = false; // Explicitly track download request status. + var limitedCommerce = false; var commerceMode = false; var userIsLoggedIn = false; var walletNeedsSetup = false; @@ -59,7 +60,7 @@ ); // Footer. - var isInitialHiFiPage = location.href === marketplaceBaseURL + "/marketplace?"; + var isInitialHiFiPage = location.href === (marketplaceBaseURL + "/marketplace?"); $("body").append( '
' + (!isInitialHiFiPage ? '' : '') + @@ -92,7 +93,7 @@ window.location = "https://clara.io/library?gameCheck=true&public=true"; }); $('#exploreHifiMarketplace').on('click', function () { - window.location = marketplaceBaseURL + "/marketplace"; + window.location = marketplaceBaseURL + "/marketplace?"; }); } @@ -169,7 +170,7 @@ var span = document.createElement('span'); 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'); 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 += " "; - } - 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() { var logInOrOutButton = document.createElement('a'); logInOrOutButton.id = "logInOrOutButton"; @@ -283,6 +250,7 @@ $(this).attr('href', '#'); } 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').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, // since that doesn't trigger another AJAX request. injectBuyButtonOnMainPage(); - maybeAddPurchasesButton(); } } @@ -419,7 +386,12 @@ var href = purchaseButton.attr('href'); purchaseButton.attr('href', '#'); + var cost = $('.item-cost').text(); + var costInt = parseInt(cost, 10); var availability = $.trim($('.item-availability').text()); + if (limitedCommerce && (costInt > 0)) { + availability = ''; + } if (availability === 'available') { purchaseButton.css({ "background": "linear-gradient(#00b4ef, #0093C5)", @@ -436,14 +408,13 @@ }); } - var cost = $('.item-cost').text(); var type = $('.item-type').text(); var isUpdating = window.location.href.indexOf('edition=') > -1; var urlParams = new URLSearchParams(window.location.search); if (isUpdating) { purchaseButton.html('UPDATE FOR FREE'); } else if (availability !== 'available') { - purchaseButton.html('UNAVAILABLE (' + availability + ')'); + purchaseButton.html('UNAVAILABLE ' + (availability ? ('(' + availability + ')') : '')); } else if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { purchaseButton.html('PURCHASE ' + cost); @@ -461,7 +432,6 @@ type); } }); - maybeAddPurchasesButton(); } } @@ -742,6 +712,7 @@ cancelClaraDownload(); } else if (message.type === "marketplaces") { if (message.action === "commerceSetting") { + limitedCommerce = !!message.data.limitedCommerce; commerceMode = !!message.data.commerceMode; userIsLoggedIn = !!message.data.userIsLoggedIn; walletNeedsSetup = !!message.data.walletNeedsSetup; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 3085145176..8bfd776971 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -10,7 +10,7 @@ /* global Tablet, Script, HMD, UserActivityLogger, Entities, Account, Wallet, ContextOverlay, Settings, Camera, Vec3, Quat, MyAvatar, Clipboard, Menu, Grid, Uuid, GlobalServices, openLoginWindow, getConnectionData, Overlays, SoundCache, - DesktopPreviewProvider */ + DesktopPreviewProvider, ResourceRequestObserver */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ 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_INSPECTIONCERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.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 MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); @@ -115,10 +115,10 @@ function setTabletVisibleInSecondaryCamera(visibleInSecondaryCam) { tabletShouldBeVisibleInSecondaryCamera = Overlays.getProperty(HMD.tabletID, "isVisibleInSecondaryCamera"); } - Overlays.editOverlay(HMD.tabletID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - Overlays.editOverlay(HMD.homeButtonID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - Overlays.editOverlay(HMD.homeButtonHighlightID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - Overlays.editOverlay(HMD.tabletScreenID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); + Overlays.editOverlay(HMD.tabletID, { isVisibleInSecondaryCamera: visibleInSecondaryCam }); + Overlays.editOverlay(HMD.homeButtonID, { isVisibleInSecondaryCamera: visibleInSecondaryCam }); + Overlays.editOverlay(HMD.homeButtonHighlightID, { isVisibleInSecondaryCamera: visibleInSecondaryCam }); + Overlays.editOverlay(HMD.tabletScreenID, { isVisibleInSecondaryCamera: visibleInSecondaryCam }); } function openWallet() { @@ -137,7 +137,7 @@ function setupWallet(referrer) { } function onMarketplaceOpen(referrer) { - var cta = referrer, match; + var match; if (Account.loggedIn && walletNeedsSetup()) { if (referrer === MARKETPLACE_URL_INITIAL) { setupWallet('marketplace cta'); @@ -218,7 +218,7 @@ function onUsernameChanged() { } function walletNeedsSetup() { - return Wallet.walletStatus === 1; + return WalletScriptingInterface.walletStatus === 1; } function sendCommerceSettings() { @@ -230,289 +230,11 @@ function sendCommerceSettings() { userIsLoggedIn: Account.loggedIn, walletNeedsSetup: walletNeedsSetup(), 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(); function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) { // 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; } +var CERT_ID_URLPARAM_LENGTH = 15; // length of "certificate_id=" function rezEntity(itemHref, itemType, marketplaceItemTesterId) { var isWearable = itemType === "wearable"; 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? if (certPos >= 0) { - certPos += 15; // length of "certificate_id=" + certPos += CERT_ID_URLPARAM_LENGTH; var certURLEncoded = itemHref.substring(certPos); var certB64Encoded = decodeURIComponent(certURLEncoded); for (var key in wearableTransforms) { @@ -593,7 +316,7 @@ function rezEntity(itemHref, itemType, marketplaceItemTesterId) { if (certificateTransforms) { for (var certID in certificateTransforms) { if (certificateTransforms.hasOwnProperty(certID) && - certID == certB64Encoded) { + certID === certB64Encoded) { var certificateTransform = certificateTransforms[certID]; wearableLocalPosition = certificateTransform.localPosition; wearableLocalRotation = certificateTransform.localRotation; @@ -636,8 +359,10 @@ function rezEntity(itemHref, itemType, marketplaceItemTesterId) { targetDirection = Vec3.multiplyQbyV(targetDirection, Vec3.UNIT_Z); var targetPosition = getPositionToCreateEntity(); - var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection. - var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection. + // Distance to move entities parallel 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++) { var curLoopEntityProps = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions", "registrationPoint", "rotation", "parentID"]); @@ -665,7 +390,8 @@ function rezEntity(itemHref, itemType, marketplaceItemTesterId) { } 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])) { Entities.editEntity(pastedEntityIDs[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 UI_FADE_TIMEOUT_MS = 150; @@ -867,24 +520,21 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { } switch (message.method) { case 'gotoBank': - ui.close(); + ui.close(); if (Account.metaverseServerURL.indexOf("staging") >= 0) { Window.location = "hifi://hifiqa-master-metaverse-staging"; // So that we can test in staging. } else { Window.location = "hifi://BankOfHighFidelity"; } - break; - case 'purchases_openWallet': - case 'checkout_openWallet': - case 'checkout_setUpClicked': - openWallet(); break; - case 'purchases_walletNotSetUp': + case 'checkout_openRecentActivity': + ui.open(MARKETPLACE_WALLET_QML_PATH); wireQmlEventBridge(true); ui.tablet.sendToQml({ - method: 'updateWalletReferrer', - referrer: "purchases" + method: 'checkout_openRecentActivity' }); + break; + case 'checkout_setUpClicked': openWallet(); break; case 'checkout_walletNotSetUp': @@ -907,15 +557,9 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { case 'checkout_itemLinkClicked': openMarketplace(message.itemId); break; - case 'checkout_continueShopping': + case 'checkout_continue': openMarketplace(); break; - case 'purchases_itemInfoClicked': - var itemId = message.itemId; - if (itemId && itemId !== "") { - openMarketplace(itemId); - } - break; case 'checkout_rezClicked': case 'purchases_rezClicked': case 'tester_rezClicked': @@ -944,7 +588,6 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { } break; case 'header_marketplaceImageClicked': - case 'purchases_backClicked': openMarketplace(message.referrerURL); break; case 'purchases_goToMarketplaceClicked': @@ -952,9 +595,6 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { break; case 'updateItemClicked': openMarketplace(message.upgradeUrl + "?edition=" + message.itemEdition); - break; - case 'giftAsset': - break; case 'passphrasePopup_cancelClicked': case 'needsLogIn_cancelClicked': @@ -974,13 +614,9 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { case 'maybeEnableHmdPreview': maybeEnableHMDPreview(); break; - case 'purchases_openGoTo': + case 'checkout_openGoTo': ui.open("hifi/tablet/TabletAddressDialog.qml"); break; - case 'purchases_itemCertificateClicked': - contextOverlayEntity = ""; - setCertificateInfo(contextOverlayEntity, message.itemCertificateId); - break; case 'inspectionCertificate_closeClicked': ui.close(); break; @@ -999,85 +635,25 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { method: 'purchases_showMyItems' }); 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': // Handled elsewhere, don't log. 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; 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, filterText: filterText }); + referrerURL = ""; + filterText = ""; } ui.isOpen = (onMarketplaceScreen || onCommerceScreen) && !onWalletScreen; @@ -1162,15 +740,7 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { } if (onCommerceScreen) { - if (!isWired) { - Users.usernameFromIDReply.connect(usernameFromIDReply); - Controller.mousePressEvent.connect(handleMouseEvent); - Controller.mouseMoveEvent.connect(handleMouseMoveEvent); - triggerMapping.enable(); - triggerPressMapping.enable(); - } - isWired = true; - Wallet.refreshWalletStatus(); + WalletScriptingInterface.refreshWalletStatus(); } else { if (onMarketplaceScreen) { onMarketplaceOpen('marketplace cta'); @@ -1192,44 +762,11 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { "\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 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; function startup() { ui = new AppUi({ @@ -1238,50 +775,26 @@ function startup() { inject: MARKETPLACES_INJECT_SCRIPT_URL, home: MARKETPLACE_URL_INITIAL, onScreenChanged: onTabletScreenChanged, - 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 + onMessage: onQmlMessageReceived }); ContextOverlay.contextOverlayClicked.connect(openInspectionCertificateQML); Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); GlobalServices.myUsernameChanged.connect(onUsernameChanged); ui.tablet.webEventReceived.connect(onWebEventReceived); - Wallet.walletStatusChanged.connect(sendCommerceSettings); + WalletScriptingInterface.walletStatusChanged.connect(sendCommerceSettings); Window.messageBoxClosed.connect(onMessageBoxClosed); ResourceRequestObserver.resourceRequestEvent.connect(onResourceRequestEvent); - Wallet.refreshWalletStatus(); + WalletScriptingInterface.refreshWalletStatus(); } -var isWired = false; -var isUpdateOverlaysWired = false; 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() { maybeEnableHMDPreview(); - deleteSendAssetParticleEffect(); Window.messageBoxClosed.disconnect(onMessageBoxClosed); - Wallet.walletStatusChanged.disconnect(sendCommerceSettings); + WalletScriptingInterface.walletStatusChanged.disconnect(sendCommerceSettings); ui.tablet.webEventReceived.disconnect(onWebEventReceived); GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 36fe264274..9558b99310 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -634,7 +634,7 @@ Window.notifyEditError = onEditError; Window.notify = onNotify; Tablet.tabletNotification.connect(tabletNotification); - Wallet.walletNotSetup.connect(walletNotSetup); + WalletScriptingInterface.walletNotSetup.connect(walletNotSetup); Messages.subscribe(NOTIFICATIONS_MESSAGE_CHANNEL); Messages.messageReceived.connect(onMessageReceived); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index a2ebae1a33..341ce9ebc8 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -844,7 +844,7 @@ function notificationPollCallback(connectionsArray) { newOnlineUsers++; storedOnlineUsers[user.username] = user; - if (!ui.isOpen && ui.notificationInitialCallbackMade) { + if (!ui.isOpen && ui.notificationInitialCallbackMade[0]) { message = user.username + " is available in " + user.location.root.name + ". Open PEOPLE to join them."; ui.notificationDisplayBanner(message); @@ -868,7 +868,7 @@ function notificationPollCallback(connectionsArray) { shouldShowDot: shouldShowDot }); - if (newOnlineUsers > 0 && !ui.notificationInitialCallbackMade) { + if (newOnlineUsers > 0 && !ui.notificationInitialCallbackMade[0]) { message = newOnlineUsers + " of your connections " + (newOnlineUsers === 1 ? "is" : "are") + " available online. Open PEOPLE to join them."; ui.notificationDisplayBanner(message); @@ -889,12 +889,12 @@ function startup() { onOpened: palOpened, onClosed: off, onMessage: fromQml, - notificationPollEndpoint: "/api/v1/users?filter=connections&status=online&per_page=10", - notificationPollTimeoutMs: 60000, - notificationDataProcessPage: notificationDataProcessPage, - notificationPollCallback: notificationPollCallback, - notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, - notificationPollCaresAboutSince: false + notificationPollEndpoint: ["/api/v1/users?filter=connections&status=online&per_page=10"], + notificationPollTimeoutMs: [60000], + notificationDataProcessPage: [notificationDataProcessPage], + notificationPollCallback: [notificationPollCallback], + notificationPollStopPaginatingConditionMet: [isReturnedDataEmpty], + notificationPollCaresAboutSince: [false] }); Window.domainChanged.connect(clearLocalQMLDataAndClosePAL); Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL); diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index 6d8ba3a927..94117fd9ea 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -37,7 +37,7 @@ function notificationPollCallback(userStoriesArray) { // pingPong = !pingPong; var totalNewStories = 0; - var shouldNotifyIndividually = !ui.isOpen && ui.notificationInitialCallbackMade; + var shouldNotifyIndividually = !ui.isOpen && ui.notificationInitialCallbackMade[0]; userStoriesArray.forEach(function (story) { if (story.audience !== "for_connections" && story.audience !== "for_feed") { @@ -91,7 +91,7 @@ function notificationPollCallback(userStoriesArray) { shouldShowDot = totalNewStories > 0 || (totalStories > 0 && shouldShowDot); 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" + (totalStories === 1 ? "" : "s") + " to know about. " + "Open GOTO to see " + (totalStories === 1 ? "it" : "them") + "."; @@ -122,12 +122,12 @@ function startup() { sortOrder: 8, onOpened: gotoOpened, home: GOTO_QML_SOURCE, - notificationPollEndpoint: endpoint, - notificationPollTimeoutMs: 60000, - notificationDataProcessPage: notificationDataProcessPage, - notificationPollCallback: notificationPollCallback, - notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, - notificationPollCaresAboutSince: false + notificationPollEndpoint: [endpoint], + notificationPollTimeoutMs: [60000], + notificationDataProcessPage: [notificationDataProcessPage], + notificationPollCallback: [notificationPollCallback], + notificationPollStopPaginatingConditionMet: [isReturnedDataEmpty], + notificationPollCaresAboutSince: [false] }); }