diff --git a/interface/resources/qml/hifi/AvatarPackager.qml b/interface/resources/qml/hifi/AvatarPackager.qml
index 8eb765716e..4d27e6fce4 100644
--- a/interface/resources/qml/hifi/AvatarPackager.qml
+++ b/interface/resources/qml/hifi/AvatarPackager.qml
@@ -8,6 +8,7 @@ import "../stylesUit" 1.0
import "../windows" as Windows
import "../dialogs"
import "avatarPackager"
+import "avatarapp" 1.0 as AvatarApp
Windows.ScrollingWindow {
id: root
@@ -27,18 +28,14 @@ Windows.ScrollingWindow {
id: windowContent
height: pane.height
width: pane.width
- anchors.fill: parent
- // FIXME: modal overlay does not show
- Rectangle {
- id: modalOverlay
+ AvatarApp.MessageBox {
+ id: popup
anchors.fill: parent
- z: 20000
- color: "#aa031b33"
- clip: true
- visible: true
+ visible: false
}
+ // FIXME: modal overlay does not show
Column {
id: avatarPackager
anchors.fill: parent
@@ -61,6 +58,12 @@ Windows.ScrollingWindow {
PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name }
PropertyChanges { target: avatarProject; visible: true }
PropertyChanges { target: avatarPackagerFooter; content: avatarProject.footer }
+ },
+ State {
+ name: "project-upload"
+ PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name }
+ PropertyChanges { target: avatarUploader; visible: true }
+ PropertyChanges { target: avatarPackagerFooter; color: "blue"; visible: false }
}
]
@@ -86,6 +89,12 @@ Windows.ScrollingWindow {
anchors.fill: parent
}
+ AvatarProjectUpload {
+ id: avatarUploader
+ anchors.fill: parent
+ root: avatarProject
+ }
+
CreateAvatarProject {
id: createAvatarProject
colorScheme: root.colorScheme
diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerFooter.qml b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerFooter.qml
index 526a2047e3..8498d20858 100644
--- a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerFooter.qml
+++ b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerFooter.qml
@@ -7,10 +7,10 @@ Rectangle {
id: avatarPackagerFooter
color: "#404040"
- height: 74
+ height: content === defaultContent ? 0 : 74
width: parent.width
- property var content: Item { }
+ property var content: Item { id: defaultContent }
children: [background, content]
@@ -21,4 +21,4 @@ Rectangle {
border.width: 2;
}
-}
+}
\ No newline at end of file
diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml
index 0aca722352..286ebb23ba 100644
--- a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml
+++ b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml
@@ -25,6 +25,7 @@ Item {
anchors.rightMargin: 17
HifiControls.Button {
id: uploadButton
+ enabled: Account.loggedIn
//width: parent.width
//anchors.bottom: parent.bottom
anchors.verticalCenter: parent.verticalCenter
@@ -35,26 +36,82 @@ Item {
width: 133
height: 40
onClicked: function() {
- console.log("Uploading");
- root.uploader = AvatarPackagerCore.currentAvatarProject.upload();
- console.log("uploader: "+ root.uploader);
- root.uploader.uploadProgress.connect(function(uploaded, total) {
- console.log("Uploader progress: " + uploaded + " / " + total);
- });
- root.uploader.completed.connect(function() {
- try {
- var response = JSON.parse(root.uploader.responseData);
- console.log("Uploader complete! " + response);
- uploadStatus.text = response.status;
- } catch (e) {
- console.log("Error parsing JSON: " + root.uploader.reponseData);
- }
- });
- root.uploader.send();
+ if (AvatarPackagerCore.currentAvatarProject.fst.hasMarketplaceID()) {
+ showConfirmUploadPopup(uploadNew, uploadUpdate);
+ } else {
+ uploadNew();
+ }
}
}
}
+ function uploadNew() {
+ console.log("Uploading new");
+ upload(false);
+ }
+ function uploadUpdate() {
+ console.log("Uploading update");
+ upload(true);
+ }
+
+ function upload(updateExisting) {
+ root.uploader = AvatarPackagerCore.currentAvatarProject.upload(updateExisting);
+ console.log("uploader: "+ root.uploader);
+ root.uploader.uploadProgress.connect(function(uploaded, total) {
+ console.log("Uploader progress: " + uploaded + " / " + total);
+ });
+ root.uploader.completed.connect(function() {
+ try {
+ var response = JSON.parse(root.uploader.responseData);
+ console.log("Uploader complete! " + response);
+ uploadStatus.text = response.status;
+ } catch (e) {
+ console.log("Error parsing JSON: " + root.uploader.reponseData);
+ }
+ });
+ root.uploader.send();
+ avatarPackager.state = "project-upload";
+ }
+
+ function showConfirmUploadPopup() {
+ popup.titleText = 'Overwrite Avatar'
+ popup.bodyText = 'You have previously uploaded the avatar file from this project.' +
+ ' This will overwrite that avatar and you won’t be able to access the older version.'
+
+ popup.button1text = 'CREATE NEW';
+ popup.button2text = 'OVERWRITE';
+
+ popup.onButton2Clicked = function() {
+ popup.close();
+ uploadUpdate();
+ }
+ popup.onButton1Clicked = function() {
+ popup.close();
+ showConfirmCreateNewPopup();
+ };
+
+ popup.open();
+ //popup.forceActiveFocus();
+ }
+
+ function showConfirmCreateNewPopup(confirmCallback) {
+ popup.titleText = 'Create New'
+ popup.bodyText = 'This will upload your current files with the same avatar name.' +
+ ' You will lose the ability to update the previously uploaded avatar. Are you sure you want to continue?'
+
+ popup.button1text = 'CANCEL';
+ popup.button2text = 'CONFIRM';
+
+ popup.onButton1Clicked = function() { popup.close() };
+ popup.onButton2Clicked = function() {
+ popup.close();
+ uploadNew();
+ };
+
+ popup.open();
+ //popup.forceActiveFocus();
+ }
+
RalewaySemiBold {
id: avatarFBXNameLabel
size: 14
@@ -79,11 +136,15 @@ Item {
}
Rectangle {
+ id: fileList
+
+ visible: false
+
color: "white"
- visible: AvatarPackagerCore.currentAvatarProject !== null
anchors.top: openFolderButton.bottom
anchors.left: parent.left
anchors.right: parent.right
+ anchors.bottom: showFilesText.top
//anchors.bottom: uploadButton.top
anchors.topMargin: 10
anchors.bottomMargin: 10
@@ -92,96 +153,82 @@ Item {
ListView {
anchors.fill: parent
model: AvatarPackagerCore.currentAvatarProject === null ? [] : AvatarPackagerCore.currentAvatarProject.projectFiles
- delegate: Text { text: 'File: ' + modelData }
+ delegate: Rectangle {
+ width: parent.width
+ height: fileText.implicitHeight + 10
+ color: (index % 2 == 0) ? "white" : "grey"
+ Text {
+ id: fileText
+ anchors.top: parent.top + 5
+ anchors.left: parent.left + 5
+ text: modelData
+ }
+ }
}
}
+ Text {
+ id: showFilesText
+
+ visible: AvatarPackagerCore.currentAvatarProject !== null
+
+ anchors.bottom: loginRequiredMessage.top
+ anchors.bottomMargin: 10
+
+ font.pointSize: 12
+ text: AvatarPackagerCore.currentAvatarProject.projectFiles.length + " files in the project. " + (fileList.visible ? "Hide" : "Show") + " list"
+
+ onLinkActivated: fileList.visible = !fileList.visible
+ }
+
Rectangle {
- id: uploadingScreen
+ id: loginRequiredMessage
- visible: !!root.uploader
- anchors.fill: parent
+ visible: !Account.loggedIn
+ height: loginRequiredTextRow.height + 20
- color: "black"
-
- Item {
- visible: !!root.uploader && !root.uploader.complete
-
- anchors.fill: parent
-
- AnimatedImage {
- id: uploadSpinner
-
- anchors {
- horizontalCenter: parent.horizontalCenter
- verticalCenter: parent.verticalCenter
- }
-
- source: "../../../icons/loader-snake-64-w.gif"
- playing: true
- z: 10000
- }
+ anchors {
+ bottom: parent.bottom
+ left: parent.left
+ right: parent.right
}
- Item {
- visible: !!root.uploader && root.uploader.complete
+ color: "#FFD6AD"
+ border.color: "#F39622"
+ border.width: 2
+ radius: 2
+
+ Item {
+ id: loginRequiredTextRow
+
+ height: Math.max(loginWarningGlyph.implicitHeight, loginWarningText.implicitHeight)
anchors.fill: parent
+ anchors.margins: 10
HiFiGlyphs {
- id: successIcon
+ id: loginWarningGlyph
- anchors {
- horizontalCenter: parent.horizontalCenter
- verticalCenter: parent.verticalCenter
- }
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
- size: 128
- text: "\ue01a"
- color: "#1FC6A6"
- }
-
- Text {
- text: "Congratulations!"
-
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.top: successIcon.bottom
-
- color: "white"
- }
-
- HifiControls.Button {
width: implicitWidth
- height: implicitHeight
- anchors.bottom: parent.bottom
+ font.pointSize: 20
+ text: "+"
+ color: "black"
+ }
+ Text {
+ id: loginWarningText
+
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: loginWarningGlyph.right
anchors.right: parent.right
- text: "View in Inventory"
-
- color: hifi.buttons.blue
- colorScheme: root.colorScheme
- onClicked: function() {
- console.log("Opening in inventory");
-
- AvatarPackagerCore.currentAvatarProject.openInInventory();
- }
+ text: "Please login to upload your avatar to High Fidelity hosting."
+ font.pointSize: 12
+ wrapMode: Text.Wrap
}
}
-
- Column {
- Text {
- id: uploadStatus
-
- text: "Uploading"
- color: "white"
-
- }
- Text {
- text: "State: " + (!!root.uploader ? root.uploader.state : " NONE")
- color: "white"
- }
- }
-
}
}
diff --git a/interface/src/avatar/AvatarProject.cpp b/interface/src/avatar/AvatarProject.cpp
index 7ba9d395eb..4a14479cce 100644
--- a/interface/src/avatar/AvatarProject.cpp
+++ b/interface/src/avatar/AvatarProject.cpp
@@ -17,8 +17,9 @@
#include
#include
#include
-#include "FBXSerializer.h"
+#include
+#include "FBXSerializer.h"
#include
#include "scripting/HMDScriptingInterface.h"
@@ -57,20 +58,16 @@ AvatarProject* AvatarProject::createAvatarProject(const QString& avatarProjectNa
std::shared_ptr hfmModel;
-
try {
qDebug() << "Reading FBX file : " << fbxInfo.filePath();
const QByteArray fbxContents = fbx.readAll();
hfmModel = FBXSerializer().read(fbxContents, QVariantHash(), fbxInfo.filePath());
- }
- catch (const QString& error) {
+ } catch (const QString& error) {
qDebug() << "Error reading: " << error;
return nullptr;
}
//TODO: copy/fix textures here:
-
-
FST* fst = FST::createFSTFromModel(newFSTPath, newModelPath, *hfmModel);
fst->setName(avatarProjectName);
@@ -89,7 +86,6 @@ bool AvatarProject::isValidNewProjectName(const QString& projectName) {
AvatarProject::AvatarProject(const QString& fstPath, const QByteArray& data) :
AvatarProject::AvatarProject(new FST(fstPath, FSTReader::readMapping(data))) {
-
}
AvatarProject::AvatarProject(FST* fst) {
_fst = fst;
@@ -119,8 +115,22 @@ void AvatarProject::refreshProjectFiles() {
appendDirectory("", _directory);
}
-MarketplaceItemUploader* AvatarProject::upload() {
- return new MarketplaceItemUploader("test_avatar", "blank description", QFileInfo(getFSTPath()).fileName(), QUuid(), _projectFiles);
+MarketplaceItemUploader* AvatarProject::upload(bool updateExisting) {
+ QUuid itemID;
+ if (updateExisting) {
+ itemID = _fst->getMarketplaceID();
+ }
+ auto uploader = new MarketplaceItemUploader(getProjectName(), "Empty description", QFileInfo(getFSTPath()).fileName(), itemID,
+ _projectFiles);
+ connect(uploader, &MarketplaceItemUploader::completed, this, [this, uploader]() {
+ if (uploader->getError() == MarketplaceItemUploader::Error::None) {
+ _fst->setMarketplaceID(uploader->getMarketplaceID());
+ // TODO(thoys) uncomment this
+ //_fst->write();
+ }
+ });
+
+ return uploader;
}
void AvatarProject::openInInventory() {
@@ -128,7 +138,12 @@ void AvatarProject::openInInventory() {
DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"));
tablet->loadQMLSource("hifi/commerce/wallet/Wallet.qml");
DependencyManager::get()->openTablet();
- tablet->sendToQml(QVariantMap({
- { "method", "updatePurchases" },
- { "filterText", "filtertext" } }));
+ auto name = getProjectName();
+
+ // I'm not a fan of this, but it's the only current option.
+ QTimer::singleShot(1000, [name]() {
+ auto tablet = dynamic_cast(
+ DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"));
+ tablet->sendToQml(QVariantMap({ { "method", "updatePurchases" }, { "filterText", name } }));
+ });
}
diff --git a/interface/src/avatar/AvatarProject.h b/interface/src/avatar/AvatarProject.h
index 023cd7b079..b164e67e0d 100644
--- a/interface/src/avatar/AvatarProject.h
+++ b/interface/src/avatar/AvatarProject.h
@@ -41,7 +41,7 @@ public:
return false;
}
- Q_INVOKABLE MarketplaceItemUploader* upload();
+ Q_INVOKABLE MarketplaceItemUploader* upload(bool updateExisting);
Q_INVOKABLE void openInInventory();
/**
diff --git a/interface/src/avatar/MarketplaceItemUploader.cpp b/interface/src/avatar/MarketplaceItemUploader.cpp
index 7a5abacce4..1559f359a7 100644
--- a/interface/src/avatar/MarketplaceItemUploader.cpp
+++ b/interface/src/avatar/MarketplaceItemUploader.cpp
@@ -67,10 +67,9 @@ void MarketplaceItemUploader::doGetCategories() {
QNetworkReply* reply = networkAccessManager.get(request);
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
- auto doc = QJsonDocument::fromJson(reply->readAll());
-
auto error = reply->error();
if (error == QNetworkReply::NoError) {
+ auto doc = QJsonDocument::fromJson(reply->readAll());
auto extractCategoryID = [&doc]() -> std::pair {
auto items = doc.object()["data"].toObject()["items"];
if (!items.isArray()) {
@@ -119,116 +118,175 @@ void MarketplaceItemUploader::doGetCategories() {
}
void MarketplaceItemUploader::doUploadAvatar() {
- QBuffer buffer{ &_fileData };
- //buffer.open(QIODevice::WriteOnly);
- QuaZip zip{ &buffer };
- if (!zip.open(QuaZip::Mode::mdAdd)) {
- qWarning() << "Failed to open zip!!";
+ QBuffer buffer{ &_fileData };
+ //buffer.open(QIODevice::WriteOnly);
+ QuaZip zip{ &buffer };
+ if (!zip.open(QuaZip::Mode::mdAdd)) {
+ qWarning() << "Failed to open zip!!";
+ }
+
+ for (auto& filePath : _filePaths) {
+ qWarning() << "Zipping: " << filePath;
+ QFileInfo fileInfo{ filePath };
+
+ QuaZipFile zipFile{ &zip };
+ if (!zipFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileInfo.fileName()))) {
+ qWarning() << "Could not open zip file:" << zipFile.getZipError();
+ _error = Error::Unknown;
+ setState(State::Complete);
+ return;
}
-
- for (auto& filePath : _filePaths) {
- qWarning() << "Zipping: " << filePath;
- QFileInfo fileInfo{ filePath };
-
- QuaZipFile zipFile{ &zip };
- if (!zipFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileInfo.fileName()))) {
- qWarning() << "Could not open zip file:" << zipFile.getZipError();
- _error = Error::Unknown;
- setState(State::Complete);
- return;
- }
- QFile file{ filePath };
- if (file.open(QIODevice::ReadOnly)) {
- zipFile.write(file.readAll());
- } else {
- qWarning() << "Failed to open: " << filePath;
- }
- file.close();
- zipFile.close();
- if (zipFile.getZipError() != UNZ_OK) {
- qWarning() << "Could not close zip file: " << zipFile.getZipError();
- setState(State::Complete);
- return;
- }
- }
-
- zip.close();
-
- qDebug() << "Finished zipping, size: " << (buffer.size() / (1000.0f)) << "KB";
-
- QString path = "/api/v1/marketplace/items";
- bool creating = true;
- if (!_marketplaceID.isNull()) {
- creating = false;
- auto idWithBraces = _marketplaceID.toString();
- auto idWithoutBraces = idWithBraces.mid(1, idWithBraces.length() - 2);
- path += "/" + idWithoutBraces;
- }
- auto accountManager = DependencyManager::get();
- auto request = accountManager->createRequest(path, AccountManagerAuth::Required);
- qWarning() << "Request url is: " << request.url();
-
- QJsonObject root{ { "marketplace_item",
- QJsonObject{ { "title", _title },
- { "description", _description },
- { "root_file_key", _rootFilename },
- { "category_ids", QJsonArray({ 5 }) },
- //{ "attributions", QJsonArray({ QJsonObject{ { "name", "" }, { "link", "" } } }) },
- { "license", 0 },
- { "files", QString::fromLatin1(_fileData.toBase64()) } } } };
- request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
- QJsonDocument doc{ root };
-
- qWarning() << "data: " << doc.toJson();
-
- _fileData.toBase64();
- QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
-
- QNetworkReply* reply{ nullptr };
- if (creating) {
- reply = networkAccessManager.post(request, doc.toJson());
+ QFile file{ filePath };
+ if (file.open(QIODevice::ReadOnly)) {
+ zipFile.write(file.readAll());
} else {
- reply = networkAccessManager.put(request, doc.toJson());
+ qWarning() << "Failed to open: " << filePath;
}
+ file.close();
+ zipFile.close();
+ if (zipFile.getZipError() != UNZ_OK) {
+ qWarning() << "Could not close zip file: " << zipFile.getZipError();
+ setState(State::Complete);
+ return;
+ }
+ }
- connect(reply, &QNetworkReply::uploadProgress, this, &MarketplaceItemUploader::uploadProgress);
+ zip.close();
- connect(reply, &QNetworkReply::finished, this, [this, reply]() {
+ qDebug() << "Finished zipping, size: " << (buffer.size() / (1000.0f)) << "KB";
+
+ QString path = "/api/v1/marketplace/items";
+ bool creating = true;
+ if (!_marketplaceID.isNull()) {
+ creating = false;
+ auto idWithBraces = _marketplaceID.toString();
+ auto idWithoutBraces = idWithBraces.mid(1, idWithBraces.length() - 2);
+ path += "/" + idWithoutBraces;
+ }
+ auto accountManager = DependencyManager::get();
+ auto request = accountManager->createRequest(path, AccountManagerAuth::Required);
+ qWarning() << "Request url is: " << request.url();
+
+ QJsonObject root{ { "marketplace_item",
+ QJsonObject{ { "title", _title },
+ { "description", _description },
+ { "root_file_key", _rootFilename },
+ { "category_ids", QJsonArray({ 5 }) },
+ //{ "attributions", QJsonArray({ QJsonObject{ { "name", "" }, { "link", "" } } }) },
+ { "license", 0 },
+ { "files", QString::fromLatin1(_fileData.toBase64()) } } } };
+ request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
+ QJsonDocument doc{ root };
+
+ qWarning() << "data: " << doc.toJson();
+
+ _fileData.toBase64();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+
+ QNetworkReply* reply{ nullptr };
+ if (creating) {
+ reply = networkAccessManager.post(request, doc.toJson());
+ } else {
+ reply = networkAccessManager.put(request, doc.toJson());
+ }
+
+ connect(reply, &QNetworkReply::uploadProgress, this, &MarketplaceItemUploader::uploadProgress);
+
+ connect(reply, &QNetworkReply::finished, this, [this, reply]() {
+ //_responseData = reply->readAll();
+ auto error = reply->error();
+ if (error == QNetworkReply::NoError) {
_responseData = reply->readAll();
qWarning() << "Finished request " << _responseData;
- auto error = reply->error();
- if (error == QNetworkReply::NoError) {
+
+ auto doc = QJsonDocument::fromJson(_responseData.toLatin1());
+ auto status = doc.object()["status"].toString();
+ if (status == "success") {
+ _marketplaceID = QUuid::fromString(doc["data"].toObject()["marketplace_id"].toString());
+ _itemVersion = doc["data"].toObject()["version"].toDouble();
doWaitForInventory();
} else {
_error = Error::Unknown;
setState(State::Complete);
}
- });
+ } else {
+ _error = Error::Unknown;
+ setState(State::Complete);
+ }
+ });
- setState(State::UploadingAvatar);
+ setState(State::UploadingAvatar);
}
void MarketplaceItemUploader::doWaitForInventory() {
- static const QString path = "/api/v1/commerce/inventory";
+ static const QString path = "/api/v1/commerce/inventory";
- auto accountManager = DependencyManager::get();
- auto request = accountManager->createRequest(path, AccountManagerAuth::Required);
+ auto accountManager = DependencyManager::get();
+ auto request = accountManager->createRequest(path, AccountManagerAuth::Required);
- qWarning() << "Request url is: " << request.url();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
- QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkReply* reply = networkAccessManager.post(request, "");
- QNetworkReply* reply = networkAccessManager.post(request, "");
+ _numRequestsForInventory++;
- connect(reply, &QNetworkReply::finished, this, [this, reply]() {
- auto data = reply->readAll();
- qWarning() << "Finished inventory request " << data;
+ connect(reply, &QNetworkReply::finished, this, [this, reply]() {
+ auto data = reply->readAll();
- auto error = reply->error();
- if (error == QNetworkReply::NoError) {
- } else {
- _error = Error::Unknown;
- }
+ bool success = false;
+
+ auto error = reply->error();
+ if (error == QNetworkReply::NoError) {
+ // Parse response data
+ auto doc = QJsonDocument::fromJson(data);
+ auto isAssetAvailable = [this, &doc]() -> bool {
+ if (!doc.isObject()) {
+ return false;
+ }
+ auto root = doc.object();
+ auto status = root["status"].toString();
+ if (status != "success") {
+ return false;
+ }
+ auto data = root["data"];
+ if (!data.isObject()) {
+ return false;
+ }
+ auto assets = data.toObject()["assets"];
+ if (!assets.isArray()) {
+ return false;
+ }
+ for (auto asset : assets.toArray()) {
+ auto assetObject = asset.toObject();
+ auto id = QUuid::fromString(assetObject["id"].toString());
+ if (id.isNull()) {
+ continue;
+ }
+ if (id == _marketplaceID) {
+ auto version = assetObject["version"];
+ if (version.isDouble()) {
+ int versionInt = version.toDouble();
+ if (versionInt >= _itemVersion) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ };
+
+ success = isAssetAvailable();
+ }
+ if (success) {
setState(State::Complete);
- });
+ } else {
+ qDebug() << "Failed to find item in inventory";
+ if (_numRequestsForInventory > 8) {
+ _error = Error::Unknown;
+ setState(State::Complete);
+ } else {
+ QTimer::singleShot(5000, [this]() { doWaitForInventory(); });
+ }
+ }
+ });
}
diff --git a/interface/src/avatar/MarketplaceItemUploader.h b/interface/src/avatar/MarketplaceItemUploader.h
index a0ec3f6991..9cfd531aca 100644
--- a/interface/src/avatar/MarketplaceItemUploader.h
+++ b/interface/src/avatar/MarketplaceItemUploader.h
@@ -56,6 +56,8 @@ public:
State getState() const { return _state; }
bool getComplete() const { return _state == State::Complete; }
+ QUuid getMarketplaceID() const { return _marketplaceID; }
+
Error getError() const { return _error; }
signals:
@@ -77,9 +79,12 @@ private:
QString _description;
QString _rootFilename;
QUuid _marketplaceID;
+ int _itemVersion;
QString _responseData;
+ int _numRequestsForInventory{ 0 };
+
QStringList _filePaths;
QByteArray _fileData;
};
diff --git a/libraries/fbx/src/FST.h b/libraries/fbx/src/FST.h
index 524463b721..83bb1e1933 100644
--- a/libraries/fbx/src/FST.h
+++ b/libraries/fbx/src/FST.h
@@ -37,7 +37,9 @@ public:
QString getModelPath() const { return _modelPath; }
void setModelPath(const QString& modelPath);
+ Q_INVOKABLE bool hasMarketplaceID() const { return !_marketplaceID.isNull(); }
QUuid getMarketplaceID() const { return _marketplaceID; }
+ void setMarketplaceID(QUuid marketplaceID) { _marketplaceID = marketplaceID; }
QString getPath() { return _fstPath; }