diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index baddc26301..5be03a5e4f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5082,6 +5082,11 @@ void Application::takeSnapshot(bool notify) { emit DependencyManager::get()->snapshotTaken(path, notify); } +void Application::shareSnapshot(const QString& path) { + // not much to do here, everything is done in snapshot code... + Snapshot::uploadSnapshot(path); +} + float Application::getRenderResolutionScale() const { if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) { return 1.0f; diff --git a/interface/src/Application.h b/interface/src/Application.h index ea6a117cfa..aa3244c53a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -249,6 +249,7 @@ public: float getAverageSimsPerSecond() const { return _simCounter.rate(); } void takeSnapshot(bool notify); + void shareSnapshot(const QString& filename); signals: void svoImportRequested(const QString& url); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 026818ec82..9ce0b5bca5 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -202,7 +202,13 @@ void WindowScriptingInterface::copyToClipboard(const QString& text) { void WindowScriptingInterface::takeSnapshot(bool notify) { // only evil-doers call takeSnapshot from a random thread - qApp->postLambdaEvent([&] { + qApp->postLambdaEvent([notify] { qApp->takeSnapshot(notify); }); } + +void WindowScriptingInterface::shareSnapshot(const QString& path) { + qApp->postLambdaEvent([path] { + qApp->shareSnapshot(path); + }); +} diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 1abcb9db35..24f2a619ce 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -55,12 +55,14 @@ public slots: QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); void copyToClipboard(const QString& text); void takeSnapshot(bool notify); + void shareSnapshot(const QString& path); signals: void domainChanged(const QString& domainHostname); void svoImportRequested(const QString& url); void domainConnectionRefused(const QString& reasonMessage, int reasonCode); void snapshotTaken(const QString& path, bool notify); + void snapshotShared(bool success); private slots: WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height); diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index aaf11d14a4..f6bd8e2db9 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -32,6 +32,7 @@ #include "Application.h" #include "Snapshot.h" +#include "scripting/WindowScriptingInterface.h" // filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg // %1 <= username, %2 <= date and time, %3 <= current location @@ -141,3 +142,87 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) { return imageTempFile; } } + +void Snapshot::uploadSnapshot(const QString& filename) { + + const QString SNAPSHOT_UPLOAD_URL = "/api/v1/snapshots"; + static SnapshotUploader uploader; + + QFile* file = new QFile(filename); + Q_ASSERT(file->exists()); + file->open(QIODevice::ReadOnly); + + QHttpPart imagePart; + imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"image\"; filename=\"" + file->fileName() + "\"")); + imagePart.setBodyDevice(file); + + QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart + multiPart->append(imagePart); + + auto accountManager = DependencyManager::get(); + JSONCallbackParameters callbackParams(&uploader, "uploadSuccess", &uploader, "uploadFailure"); + + accountManager->sendRequest(SNAPSHOT_UPLOAD_URL, + AccountManagerAuth::Required, + QNetworkAccessManager::PostOperation, + callbackParams, + nullptr, + multiPart); +} + +void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { + const QString STORY_UPLOAD_URL = "/api/v1/user_stories"; + static SnapshotUploader uploader; + + // parse the reply for the thumbnail_url + QByteArray contents = reply.readAll(); + QJsonParseError jsonError; + auto doc = QJsonDocument::fromJson(contents, &jsonError); + if (jsonError.error == QJsonParseError::NoError) { + QString thumbnailUrl = doc.object().value("thumbnail_url").toString(); + auto addressManager = DependencyManager::get(); + QString placeName = addressManager->getPlaceName(); + if (placeName.isEmpty()) { + placeName = addressManager->getHost(); + } + QString currentPath = addressManager->currentPath(true); + + // create json post data + QJsonObject rootObject; + QJsonObject userStoryObject; + userStoryObject.insert("thumbnail_url", thumbnailUrl); + userStoryObject.insert("place_name", placeName); + userStoryObject.insert("path", currentPath); + userStoryObject.insert("action", "snapshot"); + rootObject.insert("user_story", userStoryObject); + + auto accountManager = DependencyManager::get(); + JSONCallbackParameters callbackParams(&uploader, "createStorySuccess", &uploader, "createStoryFailure"); + + accountManager->sendRequest(STORY_UPLOAD_URL, + AccountManagerAuth::Required, + QNetworkAccessManager::PostOperation, + callbackParams, + QJsonDocument(rootObject).toJson()); + + } else { + emit DependencyManager::get()->snapshotShared(false); + } +} + +void SnapshotUploader::uploadFailure(QNetworkReply& reply) { + // TODO: parse response, potentially helpful for logging (?) + emit DependencyManager::get()->snapshotShared(false); +} + +void SnapshotUploader::createStorySuccess(QNetworkReply& reply) { + emit DependencyManager::get()->snapshotShared(true); +} + +void SnapshotUploader::createStoryFailure(QNetworkReply& reply) { + // TODO: parse response, potentially helpful for logging (?) + emit DependencyManager::get()->snapshotShared(false); +} diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 2e7986a5c0..e780537b76 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -32,6 +32,16 @@ private: QUrl _URL; }; + +class SnapshotUploader: public QObject { + Q_OBJECT +public slots: + void uploadSuccess(QNetworkReply& reply); + void uploadFailure(QNetworkReply& reply); + void createStorySuccess(QNetworkReply& reply); + void createStoryFailure(QNetworkReply& reply); +}; + class Snapshot { public: static QString saveSnapshot(QImage image); @@ -40,6 +50,7 @@ public: static Setting::Handle snapshotsLocation; static Setting::Handle hasSetSnapshotsLocation; + static void uploadSnapshot(const QString& filename); private: static QFile* savedFileForSnapshot(QImage & image, bool isTemporary); }; diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 3df9761e77..61345254be 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -24,7 +24,18 @@ function confirmShare(data) { if (!Window.confirm("Share snapshot " + data.localPath + "?")) { // This dialog will be more elaborate... return; } - Window.alert("Pretending to upload. That code will go here."); + Window.snapshotShared.connect(snapshotShared); + Window.shareSnapshot(data.localPath); +} + +function snapshotShared(success) { + if (success) { + // for now just print status + print('snapshot uploaded and shared'); + } else { + // for now just print an error. + print('snapshot upload/share failed'); + } } function onClicked() {