summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKonstantinos Sideris <sideris.konstantin@gmail.com>2017-11-29 23:39:35 +0200
committerKonstantinos Sideris <sideris.konstantin@gmail.com>2017-11-29 23:39:35 +0200
commitfdb76bb5c1bdce765479442a70ddca80b867caa6 (patch)
treeb418ea31dbe0f140fc2635584666db4ce36bf732
parentb21942a3e3db3e425155c58483a99bc2789de241 (diff)
Implement file uploads
fixes #24
-rw-r--r--include/FileItem.h1
-rw-r--r--include/MatrixClient.h2
-rw-r--r--include/TextInputWidget.h1
-rw-r--r--include/TimelineItem.h43
-rw-r--r--include/TimelineView.h33
-rw-r--r--include/TimelineViewManager.h1
-rw-r--r--src/ChatPage.cc11
-rw-r--r--src/FileItem.cc47
-rw-r--r--src/MatrixClient.cc60
-rw-r--r--src/TextInputWidget.cc35
-rw-r--r--src/TimelineItem.cc33
-rw-r--r--src/TimelineView.cc27
-rw-r--r--src/TimelineViewManager.cc19
13 files changed, 211 insertions, 102 deletions
diff --git a/include/FileItem.h b/include/FileItem.h
index 1c47689c..ebb18111 100644
--- a/include/FileItem.h
+++ b/include/FileItem.h
@@ -80,6 +80,7 @@ private slots:
private:
QString calculateFileSize(int nbytes) const;
void openUrl();
+ void init();
QUrl url_;
QString text_;
diff --git a/include/MatrixClient.h b/include/MatrixClient.h
index 80dc9df9..b0f6993d 100644
--- a/include/MatrixClient.h
+++ b/include/MatrixClient.h
@@ -56,6 +56,7 @@ public:
void downloadFile(const QString &event_id, const QUrl &url);
void messages(const QString &room_id, const QString &from_token, int limit = 30) noexcept;
void uploadImage(const QString &roomid, const QString &filename);
+ void uploadFile(const QString &roomid, const QString &filename);
void joinRoom(const QString &roomIdOrAlias);
void leaveRoom(const QString &roomId);
void sendTypingNotification(const QString &roomid, int timeoutInMillis = 20000);
@@ -92,6 +93,7 @@ signals:
const QString &token);
void versionSuccess();
void imageUploaded(const QString &roomid, const QString &filename, const QString &url);
+ void fileUploaded(const QString &roomid, const QString &filename, const QString &url);
void roomAvatarRetrieved(const QString &roomid, const QPixmap &img);
void userAvatarRetrieved(const QString &userId, const QImage &img);
diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h
index 88706e4a..80c16740 100644
--- a/include/TextInputWidget.h
+++ b/include/TextInputWidget.h
@@ -85,6 +85,7 @@ signals:
void sendTextMessage(QString msg);
void sendEmoteMessage(QString msg);
void uploadImage(QString filename);
+ void uploadFile(QString filename);
void sendJoinRoomRequest(const QString &room);
void startedTyping();
diff --git a/include/TimelineItem.h b/include/TimelineItem.h
index b94acbdb..9646405c 100644
--- a/include/TimelineItem.h
+++ b/include/TimelineItem.h
@@ -17,12 +17,14 @@
#pragma once
+#include <QDateTime>
#include <QHBoxLayout>
#include <QLabel>
#include <QPainter>
#include <QStyle>
#include <QStyleOption>
+#include "AvatarProvider.h"
#include "Emote.h"
#include "File.h"
#include "Image.h"
@@ -30,6 +32,7 @@
#include "Notice.h"
#include "RoomInfoListItem.h"
#include "Text.h"
+#include "TimelineViewManager.h"
class ImageItem;
class FileItem;
@@ -61,6 +64,7 @@ public:
QWidget *parent = 0);
// m.image
TimelineItem(ImageItem *item, const QString &userid, bool withSender, QWidget *parent = 0);
+ TimelineItem(FileItem *item, const QString &userid, bool withSender, QWidget *parent = 0);
TimelineItem(ImageItem *img,
const events::MessageEvent<msgs::Image> &e,
@@ -83,6 +87,12 @@ protected:
private:
void init();
+ template<class Widget>
+ void setupLocalWidgetLayout(Widget *widget,
+ const QString &userid,
+ const QString &msgDescription,
+ bool withSender);
+
void generateBody(const QString &body);
void generateBody(const QString &userid, const QString &body);
void generateTimestamp(const QDateTime &time);
@@ -110,3 +120,36 @@ private:
QLabel *userName_;
QLabel *body_;
};
+
+template<class Widget>
+void
+TimelineItem::setupLocalWidgetLayout(Widget *widget,
+ const QString &userid,
+ const QString &msgDescription,
+ bool withSender)
+{
+ auto displayName = TimelineViewManager::displayName(userid);
+ auto timestamp = QDateTime::currentDateTime();
+
+ descriptionMsg_ = {
+ "You", userid, QString(" %1").arg(msgDescription), descriptiveTime(timestamp)};
+
+ generateTimestamp(timestamp);
+
+ auto widgetLayout = new QHBoxLayout();
+ widgetLayout->setContentsMargins(0, 5, 0, 0);
+ widgetLayout->addWidget(widget);
+ widgetLayout->addStretch(1);
+
+ if (withSender) {
+ generateBody(displayName, "");
+ setupAvatarLayout(displayName);
+ mainLayout_->addLayout(headerLayout_);
+
+ AvatarProvider::resolve(userid, this);
+ } else {
+ setupSimpleLayout();
+ }
+
+ mainLayout_->addLayout(widgetLayout);
+}
diff --git a/include/TimelineView.h b/include/TimelineView.h
index e3bedff0..715d8a9a 100644
--- a/include/TimelineView.h
+++ b/include/TimelineView.h
@@ -17,26 +17,28 @@
#pragma once
+#include <QApplication>
#include <QLayout>
#include <QList>
#include <QQueue>
#include <QScrollArea>
+#include <QSettings>
#include <QStyle>
#include <QStyleOption>
#include "Emote.h"
#include "File.h"
#include "Image.h"
+#include "MatrixClient.h"
#include "MessageEvent.h"
#include "Notice.h"
#include "Text.h"
+#include "TimelineItem.h"
class FloatingButton;
-class MatrixClient;
class RoomMessages;
class ScrollBar;
class Timeline;
-class TimelineItem;
struct DescInfo;
namespace msgs = matrix::events::messages;
@@ -102,6 +104,8 @@ public:
// Add new events at the end of the timeline.
int addEvents(const Timeline &timeline);
void addUserMessage(matrix::events::MessageEventType ty, const QString &msg);
+
+ template<class Widget, events::MessageEventType MsgType>
void addUserMessage(const QString &url, const QString &filename);
void updatePendingMessage(int txn_id, QString event_id);
void scrollDown();
@@ -193,3 +197,28 @@ private:
QList<PendingMessage> pending_sent_msgs_;
QSharedPointer<MatrixClient> client_;
};
+
+template<class Widget, events::MessageEventType MsgType>
+void
+TimelineView::addUserMessage(const QString &url, const QString &filename)
+{
+ QSettings settings;
+ auto user_id = settings.value("auth/user_id").toString();
+ auto with_sender = lastSender_ != user_id;
+
+ auto widget = new Widget(client_, url, filename, this);
+
+ TimelineItem *view_item = new TimelineItem(widget, user_id, with_sender, scroll_widget_);
+ scroll_layout_->addWidget(view_item);
+
+ lastMessageDirection_ = TimelineDirection::Bottom;
+
+ QApplication::processEvents();
+
+ lastSender_ = user_id;
+
+ int txn_id = client_->incrementTransactionId();
+
+ PendingMessage message(MsgType, txn_id, url, filename, "", view_item);
+ handleNewUserMessage(message);
+}
diff --git a/include/TimelineViewManager.h b/include/TimelineViewManager.h
index d9fb730e..854c2550 100644
--- a/include/TimelineViewManager.h
+++ b/include/TimelineViewManager.h
@@ -67,6 +67,7 @@ public slots:
void queueTextMessage(const QString &msg);
void queueEmoteMessage(const QString &msg);
void queueImageMessage(const QString &roomid, const QString &filename, const QString &url);
+ void queueFileMessage(const QString &roomid, const QString &filename, const QString &url);
private slots:
void messageSent(const QString &eventid, const QString &roomid, int txnid);
diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index 82e694a1..5214d49a 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -187,6 +187,10 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
client_->uploadImage(current_room_, filename);
});
+ connect(text_input_, &TextInputWidget::uploadFile, this, [=](QString filename) {
+ client_->uploadFile(current_room_, filename);
+ });
+
connect(client_.data(), &MatrixClient::joinFailed, this, &ChatPage::showNotification);
connect(client_.data(),
&MatrixClient::imageUploaded,
@@ -195,6 +199,13 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
text_input_->hideUploadSpinner();
view_manager_->queueImageMessage(roomid, filename, url);
});
+ connect(client_.data(),
+ &MatrixClient::fileUploaded,
+ this,
+ [=](QString roomid, QString filename, QString url) {
+ text_input_->hideUploadSpinner();
+ view_manager_->queueFileMessage(roomid, filename, url);
+ });
connect(client_.data(),
SIGNAL(roomAvatarRetrieved(const QString &, const QPixmap &)),
diff --git a/src/FileItem.cc b/src/FileItem.cc
index cd934783..96fd9c07 100644
--- a/src/FileItem.cc
+++ b/src/FileItem.cc
@@ -30,25 +30,16 @@
namespace events = matrix::events;
namespace msgs = matrix::events::messages;
-FileItem::FileItem(QSharedPointer<MatrixClient> client,
- const events::MessageEvent<msgs::File> &event,
- QWidget *parent)
- : QWidget(parent)
- , event_{event}
- , client_{client}
+void
+FileItem::init()
{
setMouseTracking(true);
setCursor(Qt::PointingHandCursor);
setAttribute(Qt::WA_Hover, true);
- url_ = event.msgContent().url();
- text_ = event.content().body();
- readableFileSize_ = calculateFileSize(event.msgContent().info().size);
-
icon_.addFile(":/icons/icons/ui/arrow-pointing-down.png");
QList<QString> url_parts = url_.toString().split("mxc://");
-
if (url_parts.size() != 2) {
qDebug() << "Invalid format for image" << url_.toString();
return;
@@ -62,6 +53,20 @@ FileItem::FileItem(QSharedPointer<MatrixClient> client,
}
FileItem::FileItem(QSharedPointer<MatrixClient> client,
+ const events::MessageEvent<msgs::File> &event,
+ QWidget *parent)
+ : QWidget(parent)
+ , url_{event.msgContent().url()}
+ , text_{event.content().body()}
+ , event_{event}
+ , client_{client}
+{
+ readableFileSize_ = calculateFileSize(event.msgContent().info().size);
+
+ init();
+}
+
+FileItem::FileItem(QSharedPointer<MatrixClient> client,
const QString &url,
const QString &filename,
QWidget *parent)
@@ -70,25 +75,9 @@ FileItem::FileItem(QSharedPointer<MatrixClient> client,
, text_{QFileInfo(filename).fileName()}
, client_{client}
{
- setMouseTracking(true);
- setCursor(Qt::PointingHandCursor);
- setAttribute(Qt::WA_Hover, true);
-
- // TODO: calculateFileSize
- /* readableFileSize_ = calculateFileSize(event.msgContent().info().size); */
-
- QList<QString> url_parts = url_.toString().split("mxc://");
-
- icon_.addFile(":/icons/icons/ui/arrow-pointing-down.png");
+ readableFileSize_ = calculateFileSize(QFileInfo(filename).size());
- if (url_parts.size() != 2) {
- qDebug() << "Invalid format for image" << url_.toString();
- return;
- }
-
- QString media_params = url_parts[1];
- url_ = QString("%1/_matrix/media/r0/download/%2")
- .arg(client_.data()->getHomeServer().toString(), media_params);
+ init();
}
QString
diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc
index a171cd09..66630c80 100644
--- a/src/MatrixClient.cc
+++ b/src/MatrixClient.cc
@@ -21,6 +21,7 @@
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
+#include <QMimeDatabase>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QPixmap>
@@ -287,6 +288,9 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
case matrix::events::MessageEventType::Image:
body = {{"msgtype", "m.image"}, {"body", msg}, {"url", url}};
break;
+ case matrix::events::MessageEventType::File:
+ body = {{"msgtype", "m.file"}, {"body", msg}, {"url", url}};
+ break;
default:
qDebug() << "SendRoomMessage: Unknown message type for" << msg;
return;
@@ -756,6 +760,62 @@ MatrixClient::uploadImage(const QString &roomid, const QString &filename)
}
void
+MatrixClient::uploadFile(const QString &roomid, const QString &filename)
+{
+ QUrlQuery query;
+ query.addQueryItem("access_token", token_);
+
+ QUrl endpoint(server_);
+ endpoint.setPath(mediaApiUrl_ + "/upload");
+ endpoint.setQuery(query);
+
+ QFile file(filename);
+ if (!file.open(QIODevice::ReadWrite)) {
+ qDebug() << "Error while reading" << filename;
+ return;
+ }
+
+ QMimeDatabase db;
+ QMimeType mime = db.mimeTypeForFile(filename, QMimeDatabase::MatchContent);
+
+ QNetworkRequest request(QString(endpoint.toEncoded()));
+ request.setHeader(QNetworkRequest::ContentLengthHeader, file.size());
+ request.setHeader(QNetworkRequest::ContentTypeHeader, mime.name());
+
+ auto reply = post(request, file.readAll());
+ connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, filename]() {
+ reply->deleteLater();
+
+ int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ if (status == 0 || status >= 400) {
+ emit syncFailed(reply->errorString());
+ return;
+ }
+
+ auto data = reply->readAll();
+
+ if (data.isEmpty())
+ return;
+
+ auto json = QJsonDocument::fromJson(data);
+
+ if (!json.isObject()) {
+ qDebug() << "Media upload: Response is not a json object.";
+ return;
+ }
+
+ QJsonObject object = json.object();
+ if (!object.contains("content_uri")) {
+ qDebug() << "Media upload: Missing content_uri key";
+ qDebug() << object;
+ return;
+ }
+
+ emit fileUploaded(roomid, filename, object.value("content_uri").toString());
+ });
+}
+void
MatrixClient::joinRoom(const QString &roomIdOrAlias)
{
QUrlQuery query;
diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc
index 829784fd..c4d01357 100644
--- a/src/TextInputWidget.cc
+++ b/src/TextInputWidget.cc
@@ -276,30 +276,27 @@ TextInputWidget::command(QString command, QString args)
void
TextInputWidget::openFileSelection()
{
- QStringList supportedFiles;
- supportedFiles << "jpeg"
- << "gif"
- << "png"
- << "bmp"
- << "tiff"
- << "webp";
-
- auto fileName = QFileDialog::getOpenFileName(
- this,
- tr("Select an image"),
- "",
- tr("Image Files (*.bmp *.gif *.jpg *.jpeg *.png *.tiff *.webp)"));
+ QStringList imageExtensions;
+ imageExtensions << "jpeg"
+ << "gif"
+ << "png"
+ << "bmp"
+ << "tiff"
+ << "webp";
+
+ auto fileName =
+ QFileDialog::getOpenFileName(this, tr("Select an file"), "", tr("All Files (*)"));
if (fileName.isEmpty())
return;
- auto imageFormat = QString(QImageReader::imageFormat(fileName));
- if (!supportedFiles.contains(imageFormat)) {
- qDebug() << "Unsupported image format for" << fileName;
- return;
- }
+ auto format = QString(QImageReader::imageFormat(fileName));
+
+ if (imageExtensions.contains(format))
+ emit uploadImage(fileName);
+ else
+ emit uploadFile(fileName);
- emit uploadImage(fileName);
showUploadSpinner();
}
diff --git a/src/TimelineItem.cc b/src/TimelineItem.cc
index b57f5118..7297abc3 100644
--- a/src/TimelineItem.cc
+++ b/src/TimelineItem.cc
@@ -15,20 +15,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <QDateTime>
#include <QFontDatabase>
#include <QRegExp>
#include <QSettings>
#include <QTextEdit>
#include "Avatar.h"
-#include "AvatarProvider.h"
#include "Config.h"
#include "FileItem.h"
#include "ImageItem.h"
#include "Sync.h"
#include "TimelineItem.h"
-#include "TimelineViewManager.h"
static const QRegExp URL_REGEX("((?:https?|ftp)://\\S+)");
static const QString URL_HTML = "<a href=\"\\1\">\\1</a>";
@@ -119,29 +116,15 @@ TimelineItem::TimelineItem(ImageItem *image,
{
init();
- auto displayName = TimelineViewManager::displayName(userid);
- auto timestamp = QDateTime::currentDateTime();
-
- descriptionMsg_ = {"You", userid, " sent an image", descriptiveTime(timestamp)};
-
- generateTimestamp(timestamp);
-
- auto imageLayout = new QHBoxLayout();
- imageLayout->setMargin(0);
- imageLayout->addWidget(image);
- imageLayout->addStretch(1);
-
- if (withSender) {
- generateBody(displayName, "");
- setupAvatarLayout(displayName);
- mainLayout_->addLayout(headerLayout_);
+ setupLocalWidgetLayout<ImageItem>(image, userid, "sent an image", withSender);
+}
- AvatarProvider::resolve(userid, this);
- } else {
- setupSimpleLayout();
- }
+TimelineItem::TimelineItem(FileItem *file, const QString &userid, bool withSender, QWidget *parent)
+ : QWidget{parent}
+{
+ init();
- mainLayout_->addLayout(imageLayout);
+ setupLocalWidgetLayout<FileItem>(file, userid, "sent a file", withSender);
}
/*
@@ -169,7 +152,7 @@ TimelineItem::TimelineItem(ImageItem *image,
generateTimestamp(timestamp);
auto imageLayout = new QHBoxLayout();
- imageLayout->setMargin(0);
+ imageLayout->setContentsMargins(0, 5, 0, 0);
imageLayout->addWidget(image);
imageLayout->addStretch(1);
diff --git a/src/TimelineView.cc b/src/TimelineView.cc
index bdc59af3..346ecc52 100644
--- a/src/TimelineView.cc
+++ b/src/TimelineView.cc
@@ -18,7 +18,6 @@
#include <QApplication>
#include <QDebug>
#include <QFileInfo>
-#include <QSettings>
#include <QTimer>
#include "FileItem.h"
@@ -27,7 +26,6 @@
#include "RoomMessages.h"
#include "ScrollBar.h"
#include "Sync.h"
-#include "TimelineItem.h"
#include "TimelineView.h"
namespace events = matrix::events;
@@ -570,30 +568,6 @@ TimelineView::addUserMessage(matrix::events::MessageEventType ty, const QString
}
void
-TimelineView::addUserMessage(const QString &url, const QString &filename)
-{
- QSettings settings;
- auto user_id = settings.value("auth/user_id").toString();
- auto with_sender = lastSender_ != user_id;
-
- auto image = new ImageItem(client_, url, filename, this);
-
- TimelineItem *view_item = new TimelineItem(image, user_id, with_sender, scroll_widget_);
- scroll_layout_->addWidget(view_item);
-
- lastMessageDirection_ = TimelineDirection::Bottom;
-
- QApplication::processEvents();
-
- lastSender_ = user_id;
-
- int txn_id = client_->incrementTransactionId();
- PendingMessage message(
- matrix::events::MessageEventType::Image, txn_id, url, filename, "", view_item);
- handleNewUserMessage(message);
-}
-
-void
TimelineView::handleNewUserMessage(PendingMessage msg)
{
pending_msgs_.enqueue(msg);
@@ -610,6 +584,7 @@ TimelineView::sendNextPendingMessage()
PendingMessage &m = pending_msgs_.head();
switch (m.ty) {
case matrix::events::MessageEventType::Image:
+ case matrix::events::MessageEventType::File:
client_->sendRoomMessage(
m.ty, m.txn_id, room_id_, QFileInfo(m.filename).fileName(), m.body);
break;
diff --git a/src/TimelineViewManager.cc b/src/TimelineViewManager.cc
index 1f047d7c..daec481b 100644
--- a/src/TimelineViewManager.cc
+++ b/src/TimelineViewManager.cc
@@ -22,6 +22,8 @@
#include <QFileInfo>
#include <QSettings>
+#include "FileItem.h"
+#include "ImageItem.h"
#include "MatrixClient.h"
#include "Sync.h"
#include "TimelineView.h"
@@ -92,7 +94,22 @@ TimelineViewManager::queueImageMessage(const QString &roomid,
auto view = views_[roomid];
- view->addUserMessage(url, filename);
+ view->addUserMessage<ImageItem, matrix::events::MessageEventType::Image>(url, filename);
+}
+
+void
+TimelineViewManager::queueFileMessage(const QString &roomid,
+ const QString &filename,
+ const QString &url)
+{
+ if (!views_.contains(roomid)) {
+ qDebug() << "Cannot send m.file message to a non-managed view";
+ return;
+ }
+
+ auto view = views_[roomid];
+
+ view->addUserMessage<FileItem, matrix::events::MessageEventType::File>(url, filename);
}
void