Update avatar packager styling

This commit is contained in:
Ryan Huffman 2018-12-27 00:13:45 -08:00
parent 8c56e35f69
commit 1da179dc04
12 changed files with 368 additions and 61 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

View file

@ -48,6 +48,7 @@ Windows.ScrollingWindow {
id: popup id: popup
anchors.fill: parent anchors.fill: parent
visible: false visible: false
closeOnClickOutside: true
} }
Column { Column {

View file

@ -19,18 +19,27 @@ Item {
property int colorScheme; property int colorScheme;
property var uploader: undefined; property var uploader: undefined;
property bool hasSuccessfullyUploaded: true;
visible: false visible: false
anchors.fill: parent anchors.fill: parent
anchors.margins: 10 anchors.margins: 10
property var footer: Item { property var footer: Item {
anchors.fill: parent
Item {
id: uploadFooter id: uploadFooter
visible: !root.uploader || root.finished || root.uploader.state !== 4
anchors.fill: parent anchors.fill: parent
anchors.rightMargin: 17 anchors.rightMargin: 17
HifiControls.Button { HifiControls.Button {
id: uploadButton id: uploadButton
visible: !AvatarPackagerCore.currentAvatarProject.fst.hasMarketplaceID && !root.hasSuccessfullyUploaded
enabled: Account.loggedIn enabled: Account.loggedIn
//width: parent.width //width: parent.width
//anchors.bottom: parent.bottom //anchors.bottom: parent.bottom
@ -42,12 +51,98 @@ Item {
width: 133 width: 133
height: 40 height: 40
onClicked: function() { onClicked: function() {
if (AvatarPackagerCore.currentAvatarProject.fst.hasMarketplaceID()) {
showConfirmUploadPopup(uploadNew, uploadUpdate);
} else {
uploadNew(); uploadNew();
} }
} }
HifiControls.Button {
id: updateButton
visible: AvatarPackagerCore.currentAvatarProject.fst.hasMarketplaceID && !root.hasSuccessfullyUploaded
enabled: Account.loggedIn
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
text: qsTr("Update")
color: hifi.buttons.blue
colorScheme: root.colorScheme
width: 134
height: 40
onClicked: function() {
showConfirmUploadPopup(uploadNew, uploadUpdate);
}
}
Item {
anchors.fill: parent
visible: root.hasSuccessfullyUploaded
HifiControls.Button {
enabled: Account.loggedIn
anchors.verticalCenter: parent.verticalCenter
anchors.right: viewInInventoryButton.left
anchors.rightMargin: 16
text: qsTr("Update")
color: hifi.buttons.white
colorScheme: root.colorScheme
width: 134
height: 40
onClicked: function() {
showConfirmUploadPopup(uploadNew, uploadUpdate);
}
}
HifiControls.Button {
id: viewInInventoryButton
enabled: Account.loggedIn
width: 168
height: 40
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
text: qsTr("View in Inventory")
color: hifi.buttons.blue
colorScheme: root.colorScheme
onClicked: AvatarPackagerCore.currentAvatarProject.openInInventory()
}
}
}
Rectangle {
id: uploadingItemFooter
anchors.fill: parent
anchors.topMargin: 1
visible: !!root.uploader && !root.finished && root.uploader.state === 4
color: "#00B4EF"
LoadingCircle {
id: runningImage
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 16
width: 28
height: 28
}
RalewayRegular {
id: stepText
size: 20
anchors.verticalCenter: parent.verticalCenter
anchors.left: runningImage.right
anchors.leftMargin: 16
text: "Adding item to Inventory"
color: "white"
}
} }
} }
@ -66,7 +161,10 @@ Item {
root.uploader.uploadProgress.connect(function(uploaded, total) { root.uploader.uploadProgress.connect(function(uploaded, total) {
console.log("Uploader progress: " + uploaded + " / " + total); console.log("Uploader progress: " + uploaded + " / " + total);
}); });
root.uploader.finished.connect(function() { root.uploader.completed.connect(function() {
root.hasSuccessfullyUploaded = true;
});
root.uploader.finishedChanged.connect(function() {
try { try {
var response = JSON.parse(root.uploader.responseData); var response = JSON.parse(root.uploader.responseData);
console.log("Uploader complete! " + response); console.log("Uploader complete! " + response);
@ -121,6 +219,25 @@ Item {
RalewayRegular { RalewayRegular {
id: infoMessage id: infoMessage
states: [
State {
when: root.hasSuccessfullyUploaded
name: "upload-success"
PropertyChanges {
target: infoMessage
text: "Your avatar has been uploaded to our servers. You can modify the project files and update it again to make changes on the uploaded avatar."
}
},
State {
name: "has-previous-success"
when: !!AvatarPackagerCore.currentAvatarProject && AvatarPackagerCore.currentAvatarProject.fst.hasMarketplaceID
PropertyChanges {
target: infoMessage
text: "Click \"Update\" to overwrite the hosted files and update the avatar in your inventory. You will have to “Wear” the avatar again to see changes."
}
}
]
color: 'white' color: 'white'
size: 20 size: 20
@ -132,7 +249,7 @@ Item {
wrapMode: Text.Wrap wrapMode: Text.Wrap
text: "Click \"Update\" to overwrite the hosted files and update the avatar in your inventory. You will have to “Wear” the avatar again to see changes." text: "You can upload your files to our servers to always access them, and to make your avatar visible to other users."
} }
HifiControls.Button { HifiControls.Button {

View file

@ -8,28 +8,35 @@ import QtQuick.Controls 2.2 as Original
import "../../controlsUit" 1.0 as HifiControls import "../../controlsUit" 1.0 as HifiControls
import "../../stylesUit" 1.0 import "../../stylesUit" 1.0
Item { Item {
id: uploadingScreen id: uploadingScreen
property var root: undefined property var root: undefined
//visible: !!root.uploader
visible: false visible: false
anchors.fill: parent anchors.fill: parent
Timer { Timer {
id: backToProjectTimer id: backToProjectTimer
interval: 2000 interval: 5000
running: false running: false
repeat: false repeat: false
onTriggered: avatarPackager.state = "project" onTriggered: {
if (avatarPackager.state =="project-upload") {
avatarPackager.state = "project"
}
}
} }
function stateChangedCallback(newState) {
if (newState >= 4) {
root.uploader.stateChanged.disconnect(stateChangedCallback)
backToProjectTimer.start();
}
}
onVisibleChanged: { onVisibleChanged: {
console.log("Visibility changed");
if (visible) { if (visible) {
root.uploader.finished.connect(function() { root.uploader.stateChanged.connect(stateChangedCallback);
console.log("Did complete"); root.uploader.finishedChanged.connect(function() {
backToProjectTimer.start(); backToProjectTimer.start();
}); });
} }
@ -46,48 +53,62 @@ Item {
id: statusItem id: statusItem
width: parent.width width: parent.width
height: 128 height: 192
AnimatedImage { states: [
State {
name: "success"
when: !!root.uploader && root.uploader.state >= 4 && root.uploader.error === 0
PropertyChanges { target: uploadSpinner; visible: false }
PropertyChanges { target: errorIcon; visible: false }
PropertyChanges { target: successIcon; visible: true }
},
State {
name: "error"
when: !!root.uploader && root.uploader.finished && root.uploader.error !== 0
PropertyChanges { target: uploadSpinner; visible: false }
PropertyChanges { target: errorIcon; visible: true }
PropertyChanges { target: successIcon; visible: false }
}
]
LoadingCircle {
id: uploadSpinner id: uploadSpinner
visible: !!root.uploader && !root.uploader.complete visible: true
anchors { anchors {
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
source: "../../../icons/loader-snake-64-w.gif"
playing: true
z: 10000
} }
HiFiGlyphs { HiFiGlyphs {
id: errorIcon id: errorIcon
visible: !!root.uploader && root.uploader.complete && root.uploader.error !== 0
visible: false
anchors { anchors {
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
size: 128 size: 164
text: "w" text: "w"
color: "red" color: "#EA4C5F"
} }
HiFiGlyphs { HiFiGlyphs {
id: successIcon id: successIcon
visible: !!root.uploader && root.uploader.complete && root.uploader.error === 0 visible: false
anchors { anchors {
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
size: 128 size: 164
text: "\ue01a" text: "\ue01a"
color: "#1FC6A6" color: "#1FC6A6"
} }
@ -96,34 +117,49 @@ Item {
id: statusRows id: statusRows
anchors.top: statusItem.bottom anchors.top: statusItem.bottom
anchors.left: parent.left
anchors.leftMargin: 12
AvatarUploadStatusItem { AvatarUploadStatusItem {
id: statusCategories id: statusCategories
uploader: root.uploader
text: "Retreiving categories" text: "Retreiving categories"
state: root.uploader.state == 1 ? "running" : (root.uploader.state > 1 ? "success" : (root.uploader.error ? "fail" : "")) uploaderState: 1
} }
AvatarUploadStatusItem { AvatarUploadStatusItem {
id: statusUploading id: statusUploading
uploader: root.uploader
anchors.top: statusCategories.bottom anchors.top: statusCategories.bottom
text: "Uploading data" text: "Uploading data"
state: root.uploader.state == 2 ? "running" : (root.uploader.state > 2 ? "success" : (root.uploader.error ? "fail" : "")) uploaderState: 2
} }
// TODO add waiting for response
//AvatarUploadStatusItem {
//id: statusResponse
//text: "Waiting for completion"
//}
AvatarUploadStatusItem { AvatarUploadStatusItem {
id: statusInventory id: statusResponse
uploader: root.uploader
anchors.top: statusUploading.bottom anchors.top: statusUploading.bottom
text: "Waiting for inventory" text: "Waiting for response"
state: root.uploader.state == 3 ? "running" : (root.uploader.state > 3 ? "success" : (root.uploader.error ? "fail" : "")) uploaderState: 3
} }
} }
RalewayRegular {
visible: root.uploader.error
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: 16
anchors.rightMargin: 16
anchors.bottomMargin: 16
size: 28
wrapMode: Text.Wrap
color: "white"
text: "We couldn't upload your avatar at this time. Please try again later."
}
} }
Column { Column {

View file

@ -0,0 +1,96 @@
import QtQuick 2.6
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import "../../controlsUit" 1.0 as HifiControls
import "../../stylesUit" 1.0
Item {
id: root
height: 48
property string text: "NO STEP TEXT"
property int uploaderState;
property var uploader;
state: root.uploader.state > uploaderState
? "success"
: (root.uploader.error !== 0 ? "fail" : (root.uploader.state === uploaderState ? "running" : ""))
states: [
State {
name: "running"
PropertyChanges { target: stepText; color: "white" }
PropertyChanges { target: runningImage; visible: true; playing: true }
},
State {
name: "fail"
PropertyChanges { target: stepText; color: "#EA4C5F" }
PropertyChanges { target: failGlyph; visible: true }
},
State {
name: "success"
PropertyChanges { target: stepText; color: "white" }
PropertyChanges { target: successGlyph; visible: true }
}
]
Item {
id: statusItem
width: 48
height: parent.height
AnimatedImage {
id: runningImage
visible: false
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
width: 32
height: 32
source: "../../../icons/loader-snake-64-w.gif"
playing: false
}
HiFiGlyphs {
id: successGlyph
visible: false
width: implicitWidth
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
size: 48
text: "\ue01a"
color: "#1FC6A6"
}
HiFiGlyphs {
id: failGlyph
visible: false
width: implicitWidth
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
size: 48
text: "+"
color: "#EA4C5F"
}
}
RalewayRegular {
id: stepText
anchors.left: statusItem.right
anchors.verticalCenter: parent.verticalCenter
text: root.text
size: 28
color: "#777777"
}
}

View file

@ -0,0 +1,20 @@
import QtQuick 2.6
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
Image {
id: root
width: 128
height: 128
source: "../../../images/loader-snake-128.png"
RotationAnimation on rotation {
duration: 2000
loops: Animation.Infinite
from: 0
to: 360
}
}

View file

@ -23,6 +23,8 @@ Rectangle {
property string button2color: hifi.buttons.blue; property string button2color: hifi.buttons.blue;
property string button2text: '' property string button2text: ''
property bool closeOnClickOutside: false;
property var onButton2Clicked; property var onButton2Clicked;
property var onButton1Clicked; property var onButton1Clicked;
property var onLinkClicked; property var onLinkClicked;
@ -56,6 +58,11 @@ Rectangle {
anchors.fill: parent; anchors.fill: parent;
propagateComposedEvents: false; propagateComposedEvents: false;
hoverEnabled: true; hoverEnabled: true;
onClicked: {
if (closeOnClickOutside) {
root.close()
}
}
} }
Rectangle { Rectangle {
@ -68,6 +75,15 @@ Rectangle {
console.debug('mainContainer: height = ', height) console.debug('mainContainer: height = ', height)
} }
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
hoverEnabled: true;
onClicked: function(ev) {
ev.accepted = true;
}
}
anchors.centerIn: parent anchors.centerIn: parent
color: "white" color: "white"

View file

@ -34,7 +34,7 @@ class AvatarProject : public QObject {
Q_PROPERTY(QString projectFolderPath READ getProjectPath) Q_PROPERTY(QString projectFolderPath READ getProjectPath)
Q_PROPERTY(QString projectFSTPath READ getFSTPath) Q_PROPERTY(QString projectFSTPath READ getFSTPath)
Q_PROPERTY(QString projectFBXPath READ getFBXPath) Q_PROPERTY(QString projectFBXPath READ getFBXPath)
Q_PROPERTY(QString name READ getProjectName) Q_PROPERTY(QString name READ getProjectName NOTIFY nameChanged)
public: public:
Q_INVOKABLE MarketplaceItemUploader* upload(bool updateExisting); Q_INVOKABLE MarketplaceItemUploader* upload(bool updateExisting);
@ -57,6 +57,7 @@ public:
} }
signals: signals:
void nameChanged();
void projectFilesChanged(); void projectFilesChanged();
private: private:

View file

@ -39,6 +39,8 @@ MarketplaceItemUploader::MarketplaceItemUploader(QString title,
} }
void MarketplaceItemUploader::setState(State newState) { void MarketplaceItemUploader::setState(State newState) {
Q_ASSERT(_state != State::Complete);
Q_ASSERT(_error == Error::None);
Q_ASSERT(newState != _state); Q_ASSERT(newState != _state);
qDebug() << "Setting uploader state to: " << newState; qDebug() << "Setting uploader state to: " << newState;
@ -46,16 +48,17 @@ void MarketplaceItemUploader::setState(State newState) {
emit stateChanged(newState); emit stateChanged(newState);
if (newState == State::Complete) { if (newState == State::Complete) {
emit completed(); emit completed();
emit finished(); emit finishedChanged();
} }
} }
void MarketplaceItemUploader::setError(Error error) { void MarketplaceItemUploader::setError(Error error) {
Q_ASSERT(_state != State::Complete);
Q_ASSERT(_error == Error::None); Q_ASSERT(_error == Error::None);
_error = error; _error = error;
emit errorChanged(error); emit errorChanged(error);
emit finished(); emit finishedChanged();
} }
void MarketplaceItemUploader::send() { void MarketplaceItemUploader::send() {
@ -179,7 +182,6 @@ void MarketplaceItemUploader::doUploadAvatar() {
{ "description", _description }, { "description", _description },
{ "root_file_key", _rootFilename }, { "root_file_key", _rootFilename },
{ "category_ids", QJsonArray({ 5 }) }, { "category_ids", QJsonArray({ 5 }) },
//{ "attributions", QJsonArray({ QJsonObject{ { "name", "" }, { "link", "" } } }) },
{ "license", 0 }, { "license", 0 },
{ "files", QString::fromLatin1(_fileData.toBase64()) } } } }; { "files", QString::fromLatin1(_fileData.toBase64()) } } } };
request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
@ -197,15 +199,22 @@ void MarketplaceItemUploader::doUploadAvatar() {
reply = networkAccessManager.put(request, doc.toJson()); reply = networkAccessManager.put(request, doc.toJson());
} }
connect(reply, &QNetworkReply::uploadProgress, this, &MarketplaceItemUploader::uploadProgress); connect(reply, &QNetworkReply::uploadProgress, this, [this](float bytesSent, float bytesTotal) {
if (_state == State::UploadingAvatar) {
emit uploadProgress(bytesSent, bytesTotal);
if (bytesSent >= bytesTotal) {
setState(State::WaitingForUploadResponse);
}
}
});
connect(reply, &QNetworkReply::finished, this, [this, reply]() { connect(reply, &QNetworkReply::finished, this, [this, reply]() {
//_responseData = reply->readAll();
auto error = reply->error();
if (error == QNetworkReply::NoError) {
_responseData = reply->readAll(); _responseData = reply->readAll();
qWarning() << "Finished request " << _responseData; qWarning() << "Finished request " << _responseData;
auto error = reply->error();
if (error == QNetworkReply::NoError) {
auto doc = QJsonDocument::fromJson(_responseData.toLatin1()); auto doc = QJsonDocument::fromJson(_responseData.toLatin1());
auto status = doc.object()["status"].toString(); auto status = doc.object()["status"].toString();
if (status == "success") { if (status == "success") {

View file

@ -21,6 +21,8 @@ class QNetworkReply;
class MarketplaceItemUploader : public QObject { class MarketplaceItemUploader : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool finished READ getFinished NOTIFY finishedChanged)
Q_PROPERTY(bool complete READ getComplete NOTIFY stateChanged) Q_PROPERTY(bool complete READ getComplete NOTIFY stateChanged)
Q_PROPERTY(State state READ getState NOTIFY stateChanged) Q_PROPERTY(State state READ getState NOTIFY stateChanged)
Q_PROPERTY(Error error READ getError NOTIFY errorChanged) Q_PROPERTY(Error error READ getError NOTIFY errorChanged)
@ -38,6 +40,7 @@ public:
Idle, Idle,
GettingCategories, GettingCategories,
UploadingAvatar, UploadingAvatar,
WaitingForUploadResponse,
WaitingForInventory, WaitingForInventory,
Complete Complete
}; };
@ -62,6 +65,7 @@ public:
QUuid getMarketplaceID() const { return _marketplaceID; } QUuid getMarketplaceID() const { return _marketplaceID; }
Error getError() const { return _error; } Error getError() const { return _error; }
bool getFinished() const { return _state == State::Complete || _error != Error::None; }
signals: signals:
void uploadProgress(qint64 bytesSent, qint64 bytesTotal); void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
@ -71,7 +75,7 @@ signals:
void errorChanged(Error error); void errorChanged(Error error);
// Triggered when the upload has finished, either succesfully completing, or stopping with an error // Triggered when the upload has finished, either succesfully completing, or stopping with an error
void finished(); void finishedChanged();
private: private:
void doGetCategories(); void doGetCategories();

View file

@ -175,3 +175,8 @@ bool FST::write() {
fst.write(FSTReader::writeMapping(getMapping())); fst.write(FSTReader::writeMapping(getMapping()));
return true; return true;
} }
void FST::setMarketplaceID(QUuid marketplaceID) {
_marketplaceID = marketplaceID;
emit marketplaceIDChanged();
}

View file

@ -24,6 +24,7 @@ class FST : public QObject {
Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged) Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString modelPath READ getModelPath WRITE setModelPath NOTIFY modelPathChanged) Q_PROPERTY(QString modelPath READ getModelPath WRITE setModelPath NOTIFY modelPathChanged)
Q_PROPERTY(QUuid marketplaceID READ getMarketplaceID) Q_PROPERTY(QUuid marketplaceID READ getMarketplaceID)
Q_PROPERTY(bool hasMarketplaceID READ getHasMarketplaceID NOTIFY marketplaceIDChanged)
public: public:
FST(const QString& fstPath, QVariantHash data); FST(const QString& fstPath, QVariantHash data);
@ -37,9 +38,9 @@ public:
QString getModelPath() const { return _modelPath; } QString getModelPath() const { return _modelPath; }
void setModelPath(const QString& modelPath); void setModelPath(const QString& modelPath);
Q_INVOKABLE bool hasMarketplaceID() const { return !_marketplaceID.isNull(); } Q_INVOKABLE bool getHasMarketplaceID() const { return !_marketplaceID.isNull(); }
QUuid getMarketplaceID() const { return _marketplaceID; } QUuid getMarketplaceID() const { return _marketplaceID; }
void setMarketplaceID(QUuid marketplaceID) { _marketplaceID = marketplaceID; } void setMarketplaceID(QUuid marketplaceID);
QStringList getScriptPaths() const { return _scriptPaths; } QStringList getScriptPaths() const { return _scriptPaths; }
void setScriptPaths(QStringList scriptPaths) { _scriptPaths = scriptPaths; } void setScriptPaths(QStringList scriptPaths) { _scriptPaths = scriptPaths; }
@ -53,6 +54,7 @@ public:
signals: signals:
void nameChanged(const QString& name); void nameChanged(const QString& name);
void modelPathChanged(const QString& modelPath); void modelPathChanged(const QString& modelPath);
void marketplaceIDChanged();
private: private:
QString _fstPath; QString _fstPath;