summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--include/MatrixClient.h6
-rw-r--r--include/TextInputWidget.h3
-rw-r--r--include/events/MessageEvent.h2
-rw-r--r--include/events/messages/Audio.h2
-rw-r--r--include/events/messages/File.h2
-rw-r--r--include/events/messages/Image.h2
-rw-r--r--include/events/messages/Video.h2
-rw-r--r--include/timeline/TimelineViewManager.h1
-rw-r--r--resources/styles/nheko-dark.qss6
-rw-r--r--resources/styles/nheko.qss6
-rw-r--r--resources/styles/system.qss6
-rw-r--r--src/ChatPage.cc11
-rw-r--r--src/MatrixClient.cc115
-rw-r--r--src/TextInputWidget.cc23
-rw-r--r--src/timeline/TimelineView.cc12
-rw-r--r--src/timeline/TimelineViewManager.cc16
-rw-r--r--src/timeline/widgets/AudioItem.cc3
-rw-r--r--src/timeline/widgets/FileItem.cc3
18 files changed, 165 insertions, 56 deletions
diff --git a/include/MatrixClient.h b/include/MatrixClient.h
index b0f6993d..722a8611 100644
--- a/include/MatrixClient.h
+++ b/include/MatrixClient.h
@@ -17,6 +17,7 @@
 
 #pragma once
 
+#include <QFileInfo>
 #include <QNetworkAccessManager>
 #include <QUrl>
 
@@ -43,6 +44,7 @@ public:
                              int txnId,
                              const QString &roomid,
                              const QString &msg,
+                             const QFileInfo &info,
                              const QString &url = "") noexcept;
         void login(const QString &username, const QString &password) noexcept;
         void registerUser(const QString &username,
@@ -57,6 +59,7 @@ public:
         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 uploadAudio(const QString &roomid, const QString &filename);
         void joinRoom(const QString &roomIdOrAlias);
         void leaveRoom(const QString &roomId);
         void sendTypingNotification(const QString &roomid, int timeoutInMillis = 20000);
@@ -94,6 +97,7 @@ signals:
         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 audioUploaded(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);
@@ -116,6 +120,8 @@ signals:
         void leftRoom(const QString &room_id);
 
 private:
+        QNetworkReply *makeUploadRequest(const QString &filename);
+
         // Client API prefix.
         QString clientApiUrl_;
 
diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h
index 3c338fe3..b208d3f4 100644
--- a/include/TextInputWidget.h
+++ b/include/TextInputWidget.h
@@ -85,8 +85,11 @@ private slots:
 signals:
         void sendTextMessage(QString msg);
         void sendEmoteMessage(QString msg);
+
         void uploadImage(QString filename);
         void uploadFile(QString filename);
+        void uploadAudio(QString filename);
+
         void sendJoinRoomRequest(const QString &room);
 
         void startedTyping();
diff --git a/include/events/MessageEvent.h b/include/events/MessageEvent.h
index 3c4f83e9..08cd926f 100644
--- a/include/events/MessageEvent.h
+++ b/include/events/MessageEvent.h
@@ -55,7 +55,7 @@ struct ThumbnailInfo
 {
         int h;
         int w;
-        int size;
+        int size = 0;
 
         QString mimetype;
 };
diff --git a/include/events/messages/Audio.h b/include/events/messages/Audio.h
index 8466e0e4..b5666d90 100644
--- a/include/events/messages/Audio.h
+++ b/include/events/messages/Audio.h
@@ -27,7 +27,7 @@ namespace messages {
 struct AudioInfo
 {
         uint64_t duration;
-        int size;
+        int size = 0;
 
         QString mimetype;
 };
diff --git a/include/events/messages/File.h b/include/events/messages/File.h
index 2fb2aa83..9064a556 100644
--- a/include/events/messages/File.h
+++ b/include/events/messages/File.h
@@ -27,7 +27,7 @@ namespace events {
 namespace messages {
 struct FileInfo
 {
-        int size;
+        int size = 0;
 
         QString mimetype;
         QString thumbnail_url;
diff --git a/include/events/messages/Image.h b/include/events/messages/Image.h
index 1e709579..03c7a368 100644
--- a/include/events/messages/Image.h
+++ b/include/events/messages/Image.h
@@ -29,7 +29,7 @@ struct ImageInfo
 {
         int h;
         int w;
-        int size;
+        int size = 0;
 
         QString mimetype;
         QString thumbnail_url;
diff --git a/include/events/messages/Video.h b/include/events/messages/Video.h
index 79713546..6aeaf4d5 100644
--- a/include/events/messages/Video.h
+++ b/include/events/messages/Video.h
@@ -29,7 +29,7 @@ struct VideoInfo
 {
         int h;
         int w;
-        int size;
+        int size = 0;
         int duration;
 
         QString mimetype;
diff --git a/include/timeline/TimelineViewManager.h b/include/timeline/TimelineViewManager.h
index 854c2550..edb44ecd 100644
--- a/include/timeline/TimelineViewManager.h
+++ b/include/timeline/TimelineViewManager.h
@@ -68,6 +68,7 @@ public slots:
         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);
+        void queueAudioMessage(const QString &roomid, const QString &filename, const QString &url);
 
 private slots:
         void messageSent(const QString &eventid, const QString &roomid, int txnid);
diff --git a/resources/styles/nheko-dark.qss b/resources/styles/nheko-dark.qss
index 0182d7a3..8610c445 100644
--- a/resources/styles/nheko-dark.qss
+++ b/resources/styles/nheko-dark.qss
@@ -28,6 +28,12 @@ FileItem {
     qproperty-iconColor: #caccd1;
 }
 
+AudioItem {
+    qproperty-textColor: #caccd1;
+    qproperty-backgroundColor: #414A59;
+    qproperty-iconColor: #caccd1;
+}
+
 RaisedButton {
     qproperty-foregroundColor: #caccd1;
     qproperty-backgroundColor: #333;
diff --git a/resources/styles/nheko.qss b/resources/styles/nheko.qss
index 4251b4a5..55f9a4f4 100644
--- a/resources/styles/nheko.qss
+++ b/resources/styles/nheko.qss
@@ -27,6 +27,12 @@ FileItem {
     qproperty-iconColor: white;
 }
 
+AudioItem {
+    qproperty-textColor: #333;
+    qproperty-backgroundColor: #f2f2f2;
+    qproperty-iconColor: white;
+}
+
 RaisedButton {
     qproperty-foregroundColor: white;
 }
diff --git a/resources/styles/system.qss b/resources/styles/system.qss
index 4cd1bbfe..bb24b7a6 100644
--- a/resources/styles/system.qss
+++ b/resources/styles/system.qss
@@ -25,6 +25,12 @@ FileItem {
     qproperty-iconColor: palette(window);
 }
 
+AudioItem {
+    qproperty-textColor: palette(text);
+    qproperty-backgroundColor: palette(base);
+    qproperty-iconColor: palette(window);
+}
+
 RaisedButton {
     qproperty-foregroundColor: palette(light);
 }
diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index 340e75c3..f773ff2c 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -192,6 +192,10 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
                 client_->uploadFile(current_room_, filename);
         });
 
+        connect(text_input_, &TextInputWidget::uploadAudio, this, [=](QString filename) {
+                client_->uploadAudio(current_room_, filename);
+        });
+
         connect(client_.data(), &MatrixClient::joinFailed, this, &ChatPage::showNotification);
         connect(client_.data(),
                 &MatrixClient::imageUploaded,
@@ -207,6 +211,13 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
                         text_input_->hideUploadSpinner();
                         view_manager_->queueFileMessage(roomid, filename, url);
                 });
+        connect(client_.data(),
+                &MatrixClient::audioUploaded,
+                this,
+                [=](QString roomid, QString filename, QString url) {
+                        text_input_->hideUploadSpinner();
+                        view_manager_->queueAudioMessage(roomid, filename, url);
+                });
 
         connect(client_.data(),
                 SIGNAL(roomAvatarRetrieved(const QString &, const QPixmap &)),
diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc
index 66630c80..3326a47f 100644
--- a/src/MatrixClient.cc
+++ b/src/MatrixClient.cc
@@ -265,6 +265,7 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
                               int txnId,
                               const QString &roomid,
                               const QString &msg,
+                              const QFileInfo &fileinfo,
                               const QString &url) noexcept
 {
         QUrlQuery query;
@@ -276,7 +277,13 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
         endpoint.setQuery(query);
 
         QString msgType("");
+
+        QMimeDatabase db;
+        QMimeType mime =
+          db.mimeTypeForFile(fileinfo.absoluteFilePath(), QMimeDatabase::MatchContent);
+
         QJsonObject body;
+        QJsonObject info = {{"size", fileinfo.size()}, {"mimetype", mime.name()}};
 
         switch (ty) {
         case matrix::events::MessageEventType::Text:
@@ -286,10 +293,13 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
                 body = {{"msgtype", "m.emote"}, {"body", msg}};
                 break;
         case matrix::events::MessageEventType::Image:
-                body = {{"msgtype", "m.image"}, {"body", msg}, {"url", url}};
+                body = {{"msgtype", "m.image"}, {"body", msg}, {"url", url}, {"info", info}};
                 break;
         case matrix::events::MessageEventType::File:
-                body = {{"msgtype", "m.file"}, {"body", msg}, {"url", url}};
+                body = {{"msgtype", "m.file"}, {"body", msg}, {"url", url}, {"info", info}};
+                break;
+        case matrix::events::MessageEventType::Audio:
+                body = {{"msgtype", "m.audio"}, {"body", msg}, {"url", url}, {"info", info}};
                 break;
         default:
                 qDebug() << "SendRoomMessage: Unknown message type for" << msg;
@@ -706,26 +716,11 @@ MatrixClient::messages(const QString &roomid, const QString &from_token, int lim
 void
 MatrixClient::uploadImage(const QString &roomid, const QString &filename)
 {
-        QUrlQuery query;
-        query.addQueryItem("access_token", token_);
+        auto reply = makeUploadRequest(filename);
 
-        QUrl endpoint(server_);
-        endpoint.setPath(mediaApiUrl_ + "/upload");
-        endpoint.setQuery(query);
-
-        QFile file(filename);
-        if (!file.open(QIODevice::ReadWrite)) {
-                qDebug() << "Error while reading" << filename;
+        if (reply == nullptr)
                 return;
-        }
-
-        auto imgFormat = QString(QImageReader::imageFormat(filename));
-
-        QNetworkRequest request(QString(endpoint.toEncoded()));
-        request.setHeader(QNetworkRequest::ContentLengthHeader, file.size());
-        request.setHeader(QNetworkRequest::ContentTypeHeader, QString("image/%1").arg(imgFormat));
 
-        auto reply = post(request, file.readAll());
         connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, filename]() {
                 reply->deleteLater();
 
@@ -762,27 +757,46 @@ MatrixClient::uploadImage(const QString &roomid, const QString &filename)
 void
 MatrixClient::uploadFile(const QString &roomid, const QString &filename)
 {
-        QUrlQuery query;
-        query.addQueryItem("access_token", token_);
+        auto reply = makeUploadRequest(filename);
 
-        QUrl endpoint(server_);
-        endpoint.setPath(mediaApiUrl_ + "/upload");
-        endpoint.setQuery(query);
+        connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, filename]() {
+                reply->deleteLater();
 
-        QFile file(filename);
-        if (!file.open(QIODevice::ReadWrite)) {
-                qDebug() << "Error while reading" << filename;
-                return;
-        }
+                int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
 
-        QMimeDatabase db;
-        QMimeType mime = db.mimeTypeForFile(filename, QMimeDatabase::MatchContent);
+                if (status == 0 || status >= 400) {
+                        emit syncFailed(reply->errorString());
+                        return;
+                }
 
-        QNetworkRequest request(QString(endpoint.toEncoded()));
-        request.setHeader(QNetworkRequest::ContentLengthHeader, file.size());
-        request.setHeader(QNetworkRequest::ContentTypeHeader, mime.name());
+                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::uploadAudio(const QString &roomid, const QString &filename)
+{
+        auto reply = makeUploadRequest(filename);
 
-        auto reply = post(request, file.readAll());
         connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, filename]() {
                 reply->deleteLater();
 
@@ -812,9 +826,10 @@ MatrixClient::uploadFile(const QString &roomid, const QString &filename)
                         return;
                 }
 
-                emit fileUploaded(roomid, filename, object.value("content_uri").toString());
+                emit audioUploaded(roomid, filename, object.value("content_uri").toString());
         });
 }
+
 void
 MatrixClient::joinRoom(const QString &roomIdOrAlias)
 {
@@ -961,3 +976,31 @@ MatrixClient::readEvent(const QString &room_id, const QString &event_id)
                 }
         });
 }
+
+QNetworkReply *
+MatrixClient::makeUploadRequest(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 nullptr;
+        }
+
+        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());
+
+        return reply;
+}
diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc
index 2f6c435a..dc2bebe7 100644
--- a/src/TextInputWidget.cc
+++ b/src/TextInputWidget.cc
@@ -20,6 +20,8 @@
 #include <QFile>
 #include <QFileDialog>
 #include <QImageReader>
+#include <QMimeDatabase>
+#include <QMimeType>
 #include <QPainter>
 #include <QStyleOption>
 
@@ -276,24 +278,21 @@ TextInputWidget::command(QString command, QString args)
 void
 TextInputWidget::openFileSelection()
 {
-        QStringList imageExtensions;
-        imageExtensions << "jpeg"
-                        << "gif"
-                        << "png"
-                        << "bmp"
-                        << "tiff"
-                        << "webp";
-
-        auto fileName =
-          QFileDialog::getOpenFileName(this, tr("Select an file"), "", tr("All Files (*)"));
+        const auto fileName =
+          QFileDialog::getOpenFileName(this, tr("Select a file"), "", tr("All Files (*)"));
 
         if (fileName.isEmpty())
                 return;
 
-        auto format = QString(QImageReader::imageFormat(fileName));
+        QMimeDatabase db;
+        QMimeType mime = db.mimeTypeForFile(fileName, QMimeDatabase::MatchContent);
+
+        const auto format = mime.name().split("/")[0];
 
-        if (imageExtensions.contains(format))
+        if (format == "image")
                 emit uploadImage(fileName);
+        else if (format == "audio")
+                emit uploadAudio(fileName);
         else
                 emit uploadFile(fileName);
 
diff --git a/src/timeline/TimelineView.cc b/src/timeline/TimelineView.cc
index e5fd7f88..af7c0f7f 100644
--- a/src/timeline/TimelineView.cc
+++ b/src/timeline/TimelineView.cc
@@ -436,13 +436,19 @@ TimelineView::sendNextPendingMessage()
 
         PendingMessage &m = pending_msgs_.head();
         switch (m.ty) {
+        case matrix::events::MessageEventType::Audio:
         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);
+                // FIXME: Improve the API
+                client_->sendRoomMessage(m.ty,
+                                         m.txn_id,
+                                         room_id_,
+                                         QFileInfo(m.filename).fileName(),
+                                         QFileInfo(m.filename),
+                                         m.body);
                 break;
         default:
-                client_->sendRoomMessage(m.ty, m.txn_id, room_id_, m.body);
+                client_->sendRoomMessage(m.ty, m.txn_id, room_id_, m.body, QFileInfo());
                 break;
         }
 }
diff --git a/src/timeline/TimelineViewManager.cc b/src/timeline/TimelineViewManager.cc
index 39f07639..281cafcd 100644
--- a/src/timeline/TimelineViewManager.cc
+++ b/src/timeline/TimelineViewManager.cc
@@ -27,6 +27,7 @@
 
 #include "timeline/TimelineView.h"
 #include "timeline/TimelineViewManager.h"
+#include "timeline/widgets/AudioItem.h"
 #include "timeline/widgets/FileItem.h"
 #include "timeline/widgets/ImageItem.h"
 
@@ -114,6 +115,21 @@ TimelineViewManager::queueFileMessage(const QString &roomid,
 }
 
 void
+TimelineViewManager::queueAudioMessage(const QString &roomid,
+                                       const QString &filename,
+                                       const QString &url)
+{
+        if (!views_.contains(roomid)) {
+                qDebug() << "Cannot send m.audio message to a non-managed view";
+                return;
+        }
+
+        auto view = views_[roomid];
+
+        view->addUserMessage<AudioItem, matrix::events::MessageEventType::Audio>(url, filename);
+}
+
+void
 TimelineViewManager::clearAll()
 {
         for (auto view : views_)
diff --git a/src/timeline/widgets/AudioItem.cc b/src/timeline/widgets/AudioItem.cc
index 7c4b2d48..2a417b3e 100644
--- a/src/timeline/widgets/AudioItem.cc
+++ b/src/timeline/widgets/AudioItem.cc
@@ -107,6 +107,9 @@ AudioItem::AudioItem(QSharedPointer<MatrixClient> client,
 QString
 AudioItem::calculateFileSize(int nbytes) const
 {
+        if (nbytes == 0)
+                return QString("");
+
         if (nbytes < 1024)
                 return QString("%1 B").arg(nbytes);
 
diff --git a/src/timeline/widgets/FileItem.cc b/src/timeline/widgets/FileItem.cc
index e70be9da..e4cc02b2 100644
--- a/src/timeline/widgets/FileItem.cc
+++ b/src/timeline/widgets/FileItem.cc
@@ -94,6 +94,9 @@ FileItem::FileItem(QSharedPointer<MatrixClient> client,
 QString
 FileItem::calculateFileSize(int nbytes) const
 {
+        if (nbytes == 0)
+                return QString("");
+
         if (nbytes < 1024)
                 return QString("%1 B").arg(nbytes);