mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-29 06:08:48 +03:00
parent
b21942a3e3
commit
fdb76bb5c1
13 changed files with 210 additions and 101 deletions
|
@ -80,6 +80,7 @@ private slots:
|
|||
private:
|
||||
QString calculateFileSize(int nbytes) const;
|
||||
void openUrl();
|
||||
void init();
|
||||
|
||||
QUrl url_;
|
||||
QString text_;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 &)),
|
||||
|
|
|
@ -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;
|
||||
|
@ -61,6 +52,20 @@ FileItem::FileItem(QSharedPointer<MatrixClient> client,
|
|||
connect(client_.data(), &MatrixClient::fileDownloaded, this, &FileItem::fileDownloaded);
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -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);
|
||||
readableFileSize_ = calculateFileSize(QFileInfo(filename).size());
|
||||
|
||||
// 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");
|
||||
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
@ -755,6 +759,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)
|
||||
{
|
||||
|
|
|
@ -276,30 +276,27 @@ TextInputWidget::command(QString command, QString args)
|
|||
void
|
||||
TextInputWidget::openFileSelection()
|
||||
{
|
||||
QStringList supportedFiles;
|
||||
supportedFiles << "jpeg"
|
||||
<< "gif"
|
||||
<< "png"
|
||||
<< "bmp"
|
||||
<< "tiff"
|
||||
<< "webp";
|
||||
QStringList imageExtensions;
|
||||
imageExtensions << "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)"));
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
setupLocalWidgetLayout<ImageItem>(image, userid, "sent an image", withSender);
|
||||
}
|
||||
|
||||
descriptionMsg_ = {"You", userid, " sent an image", descriptiveTime(timestamp)};
|
||||
TimelineItem::TimelineItem(FileItem *file, const QString &userid, bool withSender, QWidget *parent)
|
||||
: QWidget{parent}
|
||||
{
|
||||
init();
|
||||
|
||||
generateTimestamp(timestamp);
|
||||
|
||||
auto imageLayout = new QHBoxLayout();
|
||||
imageLayout->setMargin(0);
|
||||
imageLayout->addWidget(image);
|
||||
imageLayout->addStretch(1);
|
||||
|
||||
if (withSender) {
|
||||
generateBody(displayName, "");
|
||||
setupAvatarLayout(displayName);
|
||||
mainLayout_->addLayout(headerLayout_);
|
||||
|
||||
AvatarProvider::resolve(userid, this);
|
||||
} else {
|
||||
setupSimpleLayout();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -569,30 +567,6 @@ TimelineView::addUserMessage(matrix::events::MessageEventType ty, const QString
|
|||
handleNewUserMessage(message);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue