diff --git a/harbour-tooter.pro b/harbour-tooter.pro index 94ba242..c5e9730 100644 --- a/harbour-tooter.pro +++ b/harbour-tooter.pro @@ -14,7 +14,8 @@ TARGET = harbour-tooter CONFIG += sailfishapp -SOURCES += src/harbour-tooter.cpp +SOURCES += src/harbour-tooter.cpp \ + src/imageuploader.cpp OTHER_FILES += qml/harbour-tooter.qml \ qml/cover/CoverPage.qml \ @@ -63,3 +64,6 @@ DISTFILES += \ qml/lib/Mastodon.js \ qml/lib/Worker.js \ qml/images/boosted.svg + +HEADERS += \ + src/imageuploader.h diff --git a/harbour-tooter.pro.user b/harbour-tooter.pro.user index 4bc4131..b34a024 100644 --- a/harbour-tooter.pro.user +++ b/harbour-tooter.pro.user @@ -1,14 +1,14 @@ - + EnvironmentId - {25497605-1bff-4134-a878-76c1475dd8e3} + {41ec03ca-9430-48f3-b421-990d428b2838} ProjectExplorer.Project.ActiveTarget - 0 + 1 ProjectExplorer.Project.EditorSettings @@ -61,12 +61,12 @@ MerSDK-SailfishOS-i486 MerSDK-SailfishOS-i486 - {f49c1b5a-d715-401a-9a10-0e5fe9e5b72a} - 1 + {fd18ca89-dfc9-4054-9c53-c67be7689951} + 0 2 0 - C:/Users/dysko/SF/build-harbour-tooter-MerSDK_SailfishOS_i486-Debug + /Users/dysko/GIT/build-harbour-tooter-MerSDK_SailfishOS_i486-Debug true @@ -138,7 +138,7 @@ true - C:/Users/dysko/SF/build-harbour-tooter-MerSDK_SailfishOS_i486-Release + /Users/dysko/GIT/build-harbour-tooter-MerSDK_SailfishOS_i486-Release true @@ -210,7 +210,7 @@ true - C:/Users/dysko/SF/build-harbour-tooter-MerSDK_SailfishOS_i486-Profile + /Users/dysko/GIT/build-harbour-tooter-MerSDK_SailfishOS_i486-Profile true @@ -396,7 +396,7 @@ 13 14 - C:/Users/dysko/SF/harbour-tooter + /Users/dysko/GIT/harbour-tooter false 10234 3 @@ -426,12 +426,12 @@ MerSDK-SailfishOS-armv7hl MerSDK-SailfishOS-armv7hl - {588087e2-ecc1-41aa-b652-86f16cba9351} - 1 + {f895389d-b51a-4d4f-8b03-5ec64bda6f66} + 0 2 0 - C:/Users/dysko/SF/build-harbour-tooter-MerSDK_SailfishOS_armv7hl-Debug + /Users/dysko/GIT/build-harbour-tooter-MerSDK_SailfishOS_armv7hl-Debug true @@ -503,7 +503,7 @@ true - C:/Users/dysko/SF/build-harbour-tooter-MerSDK_SailfishOS_armv7hl-Release + /Users/dysko/GIT/build-harbour-tooter-MerSDK_SailfishOS_armv7hl-Release true @@ -575,7 +575,7 @@ true - C:/Users/dysko/SF/build-harbour-tooter-MerSDK_SailfishOS_armv7hl-Profile + /Users/dysko/GIT/build-harbour-tooter-MerSDK_SailfishOS_armv7hl-Profile true @@ -761,7 +761,7 @@ 13 14 - C:/Users/dysko/SF/harbour-tooter + /Users/dysko/GIT/harbour-tooter false 10234 3 diff --git a/qml/harbour-tooter.qml b/qml/harbour-tooter.qml index 19ab2b5..1d0fe50 100644 --- a/qml/harbour-tooter.qml +++ b/qml/harbour-tooter.qml @@ -52,7 +52,8 @@ ApplicationWindow } if (Logic.conf['login']) { Logic.api.setConfig("api_user_token", Logic.conf['api_user_token']) - pageStack.push(Qt.resolvedUrl("./pages/MainPage.qml"), {}) + //pageStack.push(Qt.resolvedUrl("./pages/MainPage.qml"), {}) + pageStack.push(Qt.resolvedUrl("./pages/Conversation.qml"), {}) } else { pageStack.push(Qt.resolvedUrl("./pages/LoginPage.qml"), {}) } diff --git a/qml/pages/Conversation.qml b/qml/pages/Conversation.qml index a560c55..6b9461f 100644 --- a/qml/pages/Conversation.qml +++ b/qml/pages/Conversation.qml @@ -1,5 +1,6 @@ import QtQuick 2.0 import Sailfish.Silica 1.0 +import harbour.tooter.Uploader 1.0 import "../lib/API.js" as Logic import "./components/" @@ -99,7 +100,7 @@ Page { } autoScrollEnabled: true labelVisible: false -// focus: true + // focus: true text: description !== "" && (description.charAt(0) == '@' || description.charAt(0) == '#') ? description+' ' : '' height: implicitHeight horizontalAlignment: Text.AlignLeft @@ -119,12 +120,76 @@ Page { : (warningContent.visible ? Theme.secondaryHighlightColor : Theme.primaryColor)) onClicked: warningContent.visible = !warningContent.visible } + IconButton { + id: btnAddImage + anchors { + verticalCenter: privacy.verticalCenter + left: btnContentWarning.right + leftMargin: Theme.paddingSmall + } + icon.source: "image://theme/icon-s-attach?" + (pressed + ? Theme.highlightColor + : (warningContent.visible ? Theme.secondaryHighlightColor : Theme.primaryColor)) + onClicked: { + //receiver.receiveFromQml(42); + //imageUploader.run() + var once = true; + var imagePicker = pageStack.push("Sailfish.Pickers.MultiImagePickerDialog", { "allowedOrientations" : Orientation.All }); + imagePicker.selectedContentChanged.connect(function () { + if (once) { + for(var i = 0; i < imagePicker.selectedContent.count; i++){ + var file = imagePicker.selectedContent.get(i); + console.log(JSON.stringify(file)) + imageUploader.setFile(file.url); + imageUploader.setAuthorizationHeader(Logic.conf.api_user_token); + imageUploader.upload(); + } + once = false; + } + + + /*var file = imagePicker.selectedContent + ""; + //file = file.replace("file://", ""); + console.log(file) + imageUploader.setFile(file); + imageUploader.setAuthorizationHeader(Logic.conf.api_user_token); + imageUploader.upload();*/ + }); + } + } + ImageUploader { + id: imageUploader + + onProgressChanged: { + console.log("progress "+progress) + } + + onSuccess: { + console.log(replyData); + } + + onFailure: { + console.log(status) + console.log(statusText) + + } + + function run() { + imageUploader.setFile('file:///media/sdcard/686E-E026/Pictures/Camera/20170701_143819.jpg'); + imageUploader.setParameters("imageUploadData.imageAlbum", "imageUploadData.imageTitle", "imageUploadData.imageDesc"); + + imageUploader.setAuthorizationHeader(Logic.conf.api_user_token); + imageUploader.setUserAgent("constant.userAgent"); + + imageUploader.upload(); + } + } ComboBox { id: privacy anchors { top: toot.bottom topMargin: -Theme.paddingSmall*2 - left: btnContentWarning.right + left: btnAddImage.right right: btnSend.left } menu: ContextMenu { diff --git a/src/harbour-tooter.cpp b/src/harbour-tooter.cpp index 0e24cbb..6d6e4b6 100644 --- a/src/harbour-tooter.cpp +++ b/src/harbour-tooter.cpp @@ -1,50 +1,23 @@ -/* - Copyright (C) 2013 Jolla Ltd. - Contact: Thomas Perl - All rights reserved. - - You may use this file under the terms of BSD license as follows: - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Jolla Ltd nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - #ifdef QT_QML_DEBUG #include #endif #include +#include "imageuploader.h" int main(int argc, char *argv[]) { - // SailfishApp::main() will display "qml/template.qml", if you need more - // control over initialization, you can use: - // - // - SailfishApp::application(int, char *[]) to get the QGuiApplication * - // - SailfishApp::createView() to get a new QQuickView * instance - // - SailfishApp::pathTo(QString) to get a QUrl to a resource file - // - // To display the view, call "show()" (will show fullscreen on device). + QScopedPointer app(SailfishApp::application(argc, argv)); + QScopedPointer view(SailfishApp::createView()); + QQmlContext *context = view.data()->rootContext(); - return SailfishApp::main(argc, argv); + qmlRegisterType("harbour.tooter.Uploader", 1, 0, "ImageUploader"); + + QQmlEngine* engine = view->engine(); + QObject::connect(engine, SIGNAL(quit()), app.data(), SLOT(quit())); + + view->setSource(SailfishApp::pathTo("qml/harbour-tooter.qml")); + view->show(); + return app->exec(); } diff --git a/src/imageuploader.cpp b/src/imageuploader.cpp new file mode 100644 index 0000000..f8cc271 --- /dev/null +++ b/src/imageuploader.cpp @@ -0,0 +1,130 @@ +#include "imageuploader.h" + +#include +#include +#include +#include +#include +#include + +//static const QUrl IMGUR_UPLOAD_URL("https://httpbin.org/post"); +static const QUrl IMGUR_UPLOAD_URL("https://mastodon.social/api/v1/media"); + +ImageUploader::ImageUploader(QObject *parent) : QObject(parent), m_networkAccessManager(0), m_reply(0) { + m_networkAccessManager = new QNetworkAccessManager(this); +} + +ImageUploader::~ImageUploader() { + if (m_reply != 0) { + m_reply->disconnect(); + m_reply->deleteLater(); + m_reply = 0; + } +} + +void ImageUploader::setFile(const QString &fileName) { + m_fileName = fileName; +} + +void ImageUploader::setParameters(const QString &album, const QString &title, const QString &description) { + //if (!album.isEmpty()) { + postdata.append(QString("album=").toUtf8()); + postdata.append(QUrl::toPercentEncoding(album)); + //} + if (!title.isEmpty()) { + postdata.append(QString("&title=").toUtf8()); + postdata.append(QUrl::toPercentEncoding(title)); + } + if (!description.isEmpty()) { + postdata.append(QString("&description=").toUtf8()); + postdata.append(QUrl::toPercentEncoding(description)); + } +} + +void ImageUploader::setAuthorizationHeader(const QString &authorizationHeader) { + m_authorizationHeader = "Bearer "+authorizationHeader.toUtf8(); +} + +void ImageUploader::setUserAgent(const QString &userAgent) { + m_userAgent = userAgent.toUtf8(); +} + +void ImageUploader::upload() { + + if (!m_networkAccessManager) { + qWarning("ImageUploader::send(): networkAccessManager not set"); + return; + } + + if (m_reply != 0) { + m_reply->disconnect(); + m_reply->deleteLater(); + m_reply = 0; + } + + qDebug() << "TwitterApi::uploadImage"; + QUrl url = IMGUR_UPLOAD_URL; + QNetworkRequest request(url); + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + QHttpPart imagePart; + //imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"")); + QFile *file = new QFile(m_fileName); + file->open(QIODevice::ReadOnly); + QByteArray rawImage = file->readAll(); + imagePart.setBody(rawImage); + file->setParent(multiPart); + + multiPart->append(imagePart); + + + + + + //request.setUrl(IMGUR_UPLOAD_URL); + request.setRawHeader("Authorization", m_authorizationHeader); + request.setRawHeader("Content-Type", "application/x-www-form-urlencoded"); + + // request.setRawHeader("User-Agent", m_userAgent); + + + + m_reply = m_networkAccessManager->post(request, multiPart); + multiPart->setParent(m_reply); + m_reply->setObjectName("file"); + + + + connect(m_reply, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(uploadProgress(qint64,qint64))); + connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished())); +} + +qreal ImageUploader::progress() const { + return m_progress; +} + +void ImageUploader::uploadProgress(qint64 bytesSent, qint64 bytesTotal) { + qreal progress = qreal(bytesSent) / qreal(bytesTotal); + //qDebug("uploadProgress: %f , %f, %f", qreal(bytesSent), qreal(bytesTotal), qreal(progress)); + + if (m_progress != progress) { + m_progress = progress; + emit progressChanged(); + } +} + +void ImageUploader::replyFinished() { + if (!m_reply->error()) { + QByteArray replyData = m_reply->readAll(); + emit success(replyData); + } + else { + int status = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + QString statusText = m_reply->errorString(); + emit failure(status, statusText); + } + + m_reply->deleteLater(); + m_reply = 0; + postdata.clear(); +} diff --git a/src/imageuploader.h b/src/imageuploader.h new file mode 100644 index 0000000..2410b67 --- /dev/null +++ b/src/imageuploader.h @@ -0,0 +1,46 @@ +#ifndef IMAGEUPLOADER_H +#define IMAGEUPLOADER_H + +#include + +class QNetworkAccessManager; +class QNetworkReply; + +class ImageUploader : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + +public: + explicit ImageUploader(QObject *parent = 0); + ~ImageUploader(); + + Q_INVOKABLE void setFile(const QString &fileName); + Q_INVOKABLE void setAuthorizationHeader(const QString &authorizationHeader); + Q_INVOKABLE void setUserAgent(const QString &userAgent); + Q_INVOKABLE void setParameters(const QString &album, const QString &title, const QString &description); + Q_INVOKABLE void upload(); + + qreal progress() const; + +signals: + void success(const QString &replyData); + void failure(const int status, const QString &statusText); + void progressChanged(); + +private slots: + void uploadProgress(qint64 bytesSent, qint64 bytesTotal); + void replyFinished(); + +private: + qreal m_progress; + QNetworkAccessManager *m_networkAccessManager; + + QString m_fileName; + QByteArray m_authorizationHeader; + QByteArray m_userAgent; + QByteArray postdata; + QNetworkReply *m_reply; +}; + +#endif // IMAGEUPLOADER_H