Allow audio clip uploads

This commit is contained in:
Konstantinos Sideris 2017-12-01 17:33:49 +02:00
parent 78353a29bc
commit 5573548fb1
18 changed files with 168 additions and 59 deletions

View file

@ -17,6 +17,7 @@
#pragma once #pragma once
#include <QFileInfo>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QUrl> #include <QUrl>
@ -43,6 +44,7 @@ public:
int txnId, int txnId,
const QString &roomid, const QString &roomid,
const QString &msg, const QString &msg,
const QFileInfo &info,
const QString &url = "") noexcept; const QString &url = "") noexcept;
void login(const QString &username, const QString &password) noexcept; void login(const QString &username, const QString &password) noexcept;
void registerUser(const QString &username, 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 messages(const QString &room_id, const QString &from_token, int limit = 30) noexcept;
void uploadImage(const QString &roomid, const QString &filename); void uploadImage(const QString &roomid, const QString &filename);
void uploadFile(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 joinRoom(const QString &roomIdOrAlias);
void leaveRoom(const QString &roomId); void leaveRoom(const QString &roomId);
void sendTypingNotification(const QString &roomid, int timeoutInMillis = 20000); void sendTypingNotification(const QString &roomid, int timeoutInMillis = 20000);
@ -94,6 +97,7 @@ signals:
void versionSuccess(); void versionSuccess();
void imageUploaded(const QString &roomid, const QString &filename, const QString &url); void imageUploaded(const QString &roomid, const QString &filename, const QString &url);
void fileUploaded(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 roomAvatarRetrieved(const QString &roomid, const QPixmap &img);
void userAvatarRetrieved(const QString &userId, const QImage &img); void userAvatarRetrieved(const QString &userId, const QImage &img);
@ -116,6 +120,8 @@ signals:
void leftRoom(const QString &room_id); void leftRoom(const QString &room_id);
private: private:
QNetworkReply *makeUploadRequest(const QString &filename);
// Client API prefix. // Client API prefix.
QString clientApiUrl_; QString clientApiUrl_;

View file

@ -85,8 +85,11 @@ private slots:
signals: signals:
void sendTextMessage(QString msg); void sendTextMessage(QString msg);
void sendEmoteMessage(QString msg); void sendEmoteMessage(QString msg);
void uploadImage(QString filename); void uploadImage(QString filename);
void uploadFile(QString filename); void uploadFile(QString filename);
void uploadAudio(QString filename);
void sendJoinRoomRequest(const QString &room); void sendJoinRoomRequest(const QString &room);
void startedTyping(); void startedTyping();

View file

@ -55,7 +55,7 @@ struct ThumbnailInfo
{ {
int h; int h;
int w; int w;
int size; int size = 0;
QString mimetype; QString mimetype;
}; };

View file

@ -27,7 +27,7 @@ namespace messages {
struct AudioInfo struct AudioInfo
{ {
uint64_t duration; uint64_t duration;
int size; int size = 0;
QString mimetype; QString mimetype;
}; };

View file

@ -27,7 +27,7 @@ namespace events {
namespace messages { namespace messages {
struct FileInfo struct FileInfo
{ {
int size; int size = 0;
QString mimetype; QString mimetype;
QString thumbnail_url; QString thumbnail_url;

View file

@ -29,7 +29,7 @@ struct ImageInfo
{ {
int h; int h;
int w; int w;
int size; int size = 0;
QString mimetype; QString mimetype;
QString thumbnail_url; QString thumbnail_url;

View file

@ -29,7 +29,7 @@ struct VideoInfo
{ {
int h; int h;
int w; int w;
int size; int size = 0;
int duration; int duration;
QString mimetype; QString mimetype;

View file

@ -68,6 +68,7 @@ public slots:
void queueEmoteMessage(const QString &msg); void queueEmoteMessage(const QString &msg);
void queueImageMessage(const QString &roomid, const QString &filename, const QString &url); void queueImageMessage(const QString &roomid, const QString &filename, const QString &url);
void queueFileMessage(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: private slots:
void messageSent(const QString &eventid, const QString &roomid, int txnid); void messageSent(const QString &eventid, const QString &roomid, int txnid);

View file

@ -28,6 +28,12 @@ FileItem {
qproperty-iconColor: #caccd1; qproperty-iconColor: #caccd1;
} }
AudioItem {
qproperty-textColor: #caccd1;
qproperty-backgroundColor: #414A59;
qproperty-iconColor: #caccd1;
}
RaisedButton { RaisedButton {
qproperty-foregroundColor: #caccd1; qproperty-foregroundColor: #caccd1;
qproperty-backgroundColor: #333; qproperty-backgroundColor: #333;

View file

@ -27,6 +27,12 @@ FileItem {
qproperty-iconColor: white; qproperty-iconColor: white;
} }
AudioItem {
qproperty-textColor: #333;
qproperty-backgroundColor: #f2f2f2;
qproperty-iconColor: white;
}
RaisedButton { RaisedButton {
qproperty-foregroundColor: white; qproperty-foregroundColor: white;
} }

View file

@ -25,6 +25,12 @@ FileItem {
qproperty-iconColor: palette(window); qproperty-iconColor: palette(window);
} }
AudioItem {
qproperty-textColor: palette(text);
qproperty-backgroundColor: palette(base);
qproperty-iconColor: palette(window);
}
RaisedButton { RaisedButton {
qproperty-foregroundColor: palette(light); qproperty-foregroundColor: palette(light);
} }

View file

@ -192,6 +192,10 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
client_->uploadFile(current_room_, filename); 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::joinFailed, this, &ChatPage::showNotification);
connect(client_.data(), connect(client_.data(),
&MatrixClient::imageUploaded, &MatrixClient::imageUploaded,
@ -207,6 +211,13 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
text_input_->hideUploadSpinner(); text_input_->hideUploadSpinner();
view_manager_->queueFileMessage(roomid, filename, url); 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(), connect(client_.data(),
SIGNAL(roomAvatarRetrieved(const QString &, const QPixmap &)), SIGNAL(roomAvatarRetrieved(const QString &, const QPixmap &)),

View file

@ -265,6 +265,7 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
int txnId, int txnId,
const QString &roomid, const QString &roomid,
const QString &msg, const QString &msg,
const QFileInfo &fileinfo,
const QString &url) noexcept const QString &url) noexcept
{ {
QUrlQuery query; QUrlQuery query;
@ -276,7 +277,13 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
endpoint.setQuery(query); endpoint.setQuery(query);
QString msgType(""); QString msgType("");
QMimeDatabase db;
QMimeType mime =
db.mimeTypeForFile(fileinfo.absoluteFilePath(), QMimeDatabase::MatchContent);
QJsonObject body; QJsonObject body;
QJsonObject info = {{"size", fileinfo.size()}, {"mimetype", mime.name()}};
switch (ty) { switch (ty) {
case matrix::events::MessageEventType::Text: case matrix::events::MessageEventType::Text:
@ -286,10 +293,13 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
body = {{"msgtype", "m.emote"}, {"body", msg}}; body = {{"msgtype", "m.emote"}, {"body", msg}};
break; break;
case matrix::events::MessageEventType::Image: case matrix::events::MessageEventType::Image:
body = {{"msgtype", "m.image"}, {"body", msg}, {"url", url}}; body = {{"msgtype", "m.image"}, {"body", msg}, {"url", url}, {"info", info}};
break; break;
case matrix::events::MessageEventType::File: 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; break;
default: default:
qDebug() << "SendRoomMessage: Unknown message type for" << msg; qDebug() << "SendRoomMessage: Unknown message type for" << msg;
@ -706,26 +716,11 @@ MatrixClient::messages(const QString &roomid, const QString &from_token, int lim
void void
MatrixClient::uploadImage(const QString &roomid, const QString &filename) MatrixClient::uploadImage(const QString &roomid, const QString &filename)
{ {
QUrlQuery query; auto reply = makeUploadRequest(filename);
query.addQueryItem("access_token", token_);
QUrl endpoint(server_); if (reply == nullptr)
endpoint.setPath(mediaApiUrl_ + "/upload");
endpoint.setQuery(query);
QFile file(filename);
if (!file.open(QIODevice::ReadWrite)) {
qDebug() << "Error while reading" << filename;
return; 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]() { connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, filename]() {
reply->deleteLater(); reply->deleteLater();
@ -762,27 +757,8 @@ MatrixClient::uploadImage(const QString &roomid, const QString &filename)
void void
MatrixClient::uploadFile(const QString &roomid, const QString &filename) MatrixClient::uploadFile(const QString &roomid, const QString &filename)
{ {
QUrlQuery query; auto reply = makeUploadRequest(filename);
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]() { connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, filename]() {
reply->deleteLater(); reply->deleteLater();
@ -815,6 +791,45 @@ MatrixClient::uploadFile(const QString &roomid, const QString &filename)
emit fileUploaded(roomid, filename, object.value("content_uri").toString()); emit fileUploaded(roomid, filename, object.value("content_uri").toString());
}); });
} }
void
MatrixClient::uploadAudio(const QString &roomid, const QString &filename)
{
auto reply = makeUploadRequest(filename);
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 audioUploaded(roomid, filename, object.value("content_uri").toString());
});
}
void void
MatrixClient::joinRoom(const QString &roomIdOrAlias) 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;
}

View file

@ -20,6 +20,8 @@
#include <QFile> #include <QFile>
#include <QFileDialog> #include <QFileDialog>
#include <QImageReader> #include <QImageReader>
#include <QMimeDatabase>
#include <QMimeType>
#include <QPainter> #include <QPainter>
#include <QStyleOption> #include <QStyleOption>
@ -276,24 +278,21 @@ TextInputWidget::command(QString command, QString args)
void void
TextInputWidget::openFileSelection() TextInputWidget::openFileSelection()
{ {
QStringList imageExtensions; const auto fileName =
imageExtensions << "jpeg" QFileDialog::getOpenFileName(this, tr("Select a file"), "", tr("All Files (*)"));
<< "gif"
<< "png"
<< "bmp"
<< "tiff"
<< "webp";
auto fileName =
QFileDialog::getOpenFileName(this, tr("Select an file"), "", tr("All Files (*)"));
if (fileName.isEmpty()) if (fileName.isEmpty())
return; return;
auto format = QString(QImageReader::imageFormat(fileName)); QMimeDatabase db;
QMimeType mime = db.mimeTypeForFile(fileName, QMimeDatabase::MatchContent);
if (imageExtensions.contains(format)) const auto format = mime.name().split("/")[0];
if (format == "image")
emit uploadImage(fileName); emit uploadImage(fileName);
else if (format == "audio")
emit uploadAudio(fileName);
else else
emit uploadFile(fileName); emit uploadFile(fileName);

View file

@ -436,13 +436,19 @@ TimelineView::sendNextPendingMessage()
PendingMessage &m = pending_msgs_.head(); PendingMessage &m = pending_msgs_.head();
switch (m.ty) { switch (m.ty) {
case matrix::events::MessageEventType::Audio:
case matrix::events::MessageEventType::Image: case matrix::events::MessageEventType::Image:
case matrix::events::MessageEventType::File: case matrix::events::MessageEventType::File:
client_->sendRoomMessage( // FIXME: Improve the API
m.ty, m.txn_id, room_id_, QFileInfo(m.filename).fileName(), m.body); client_->sendRoomMessage(m.ty,
m.txn_id,
room_id_,
QFileInfo(m.filename).fileName(),
QFileInfo(m.filename),
m.body);
break; break;
default: 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; break;
} }
} }

View file

@ -27,6 +27,7 @@
#include "timeline/TimelineView.h" #include "timeline/TimelineView.h"
#include "timeline/TimelineViewManager.h" #include "timeline/TimelineViewManager.h"
#include "timeline/widgets/AudioItem.h"
#include "timeline/widgets/FileItem.h" #include "timeline/widgets/FileItem.h"
#include "timeline/widgets/ImageItem.h" #include "timeline/widgets/ImageItem.h"
@ -113,6 +114,21 @@ TimelineViewManager::queueFileMessage(const QString &roomid,
view->addUserMessage<FileItem, matrix::events::MessageEventType::File>(url, filename); view->addUserMessage<FileItem, matrix::events::MessageEventType::File>(url, filename);
} }
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 void
TimelineViewManager::clearAll() TimelineViewManager::clearAll()
{ {

View file

@ -107,6 +107,9 @@ AudioItem::AudioItem(QSharedPointer<MatrixClient> client,
QString QString
AudioItem::calculateFileSize(int nbytes) const AudioItem::calculateFileSize(int nbytes) const
{ {
if (nbytes == 0)
return QString("");
if (nbytes < 1024) if (nbytes < 1024)
return QString("%1 B").arg(nbytes); return QString("%1 B").arg(nbytes);

View file

@ -94,6 +94,9 @@ FileItem::FileItem(QSharedPointer<MatrixClient> client,
QString QString
FileItem::calculateFileSize(int nbytes) const FileItem::calculateFileSize(int nbytes) const
{ {
if (nbytes == 0)
return QString("");
if (nbytes < 1024) if (nbytes < 1024)
return QString("%1 B").arg(nbytes); return QString("%1 B").arg(nbytes);