2019-08-30 19:29:25 +02:00
|
|
|
#include "TimelineViewManager.h"
|
|
|
|
|
2019-09-29 10:45:35 +02:00
|
|
|
#include <QFileDialog>
|
2019-08-30 23:20:53 +02:00
|
|
|
#include <QMetaType>
|
2019-09-29 10:45:35 +02:00
|
|
|
#include <QMimeDatabase>
|
2019-08-30 23:20:53 +02:00
|
|
|
#include <QQmlContext>
|
|
|
|
|
|
|
|
#include "Logging.h"
|
2019-09-07 22:22:07 +02:00
|
|
|
#include "MxcImageProvider.h"
|
2019-09-28 11:07:58 +02:00
|
|
|
#include "dialogs/ImageOverlay.h"
|
2019-08-30 23:20:53 +02:00
|
|
|
|
2019-08-30 19:29:25 +02:00
|
|
|
TimelineViewManager::TimelineViewManager(QWidget *parent)
|
2019-09-28 11:07:58 +02:00
|
|
|
: imgProvider(new MxcImageProvider())
|
2019-08-30 19:29:25 +02:00
|
|
|
{
|
2019-09-02 23:28:05 +02:00
|
|
|
qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject,
|
|
|
|
"com.github.nheko",
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
"MtxEvent",
|
|
|
|
"Can't instantiate enum!");
|
2019-08-30 19:29:25 +02:00
|
|
|
view = new QQuickView();
|
|
|
|
container = QWidget::createWindowContainer(view, parent);
|
|
|
|
container->setMinimumSize(200, 200);
|
2019-08-31 22:43:31 +02:00
|
|
|
view->rootContext()->setContextProperty("timelineManager", this);
|
2019-09-28 11:07:58 +02:00
|
|
|
view->engine()->addImageProvider("MxcImage", imgProvider);
|
2019-08-30 19:29:25 +02:00
|
|
|
view->setSource(QUrl("qrc:///qml/TimelineView.qml"));
|
2019-08-30 23:20:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::initialize(const mtx::responses::Rooms &rooms)
|
|
|
|
{
|
|
|
|
for (auto it = rooms.join.cbegin(); it != rooms.join.cend(); ++it) {
|
|
|
|
addRoom(QString::fromStdString(it->first));
|
2019-08-31 22:43:31 +02:00
|
|
|
models.value(QString::fromStdString(it->first))->addEvents(it->second.timeline);
|
2019-08-30 23:20:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::addRoom(const QString &room_id)
|
|
|
|
{
|
|
|
|
if (!models.contains(room_id))
|
2019-08-31 23:44:17 +02:00
|
|
|
models.insert(room_id, QSharedPointer<TimelineModel>(new TimelineModel(room_id)));
|
2019-08-30 23:20:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::setHistoryView(const QString &room_id)
|
|
|
|
{
|
|
|
|
nhlog::ui()->info("Trying to activate room {}", room_id.toStdString());
|
|
|
|
|
|
|
|
auto room = models.find(room_id);
|
|
|
|
if (room != models.end()) {
|
2019-09-03 08:23:07 +02:00
|
|
|
timeline_ = room.value().data();
|
2019-08-31 23:44:17 +02:00
|
|
|
timeline_->fetchHistory();
|
2019-08-31 22:43:31 +02:00
|
|
|
emit activeTimelineChanged(timeline_);
|
2019-08-30 23:20:53 +02:00
|
|
|
nhlog::ui()->info("Activated room {}", room_id.toStdString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-28 11:07:58 +02:00
|
|
|
void
|
2019-09-29 10:45:35 +02:00
|
|
|
TimelineViewManager::openImageOverlay(QString mxcUrl,
|
|
|
|
QString originalFilename,
|
|
|
|
QString mimeType,
|
|
|
|
qml_mtx_events::EventType eventType) const
|
2019-09-28 11:07:58 +02:00
|
|
|
{
|
|
|
|
QQuickImageResponse *imgResponse =
|
2019-09-29 10:45:35 +02:00
|
|
|
imgProvider->requestImageResponse(mxcUrl.remove("mxc://"), QSize());
|
|
|
|
connect(imgResponse,
|
|
|
|
&QQuickImageResponse::finished,
|
|
|
|
this,
|
|
|
|
[this, mxcUrl, originalFilename, mimeType, eventType, imgResponse]() {
|
|
|
|
if (!imgResponse->errorString().isEmpty()) {
|
|
|
|
nhlog::ui()->error("Error when retrieving image for overlay: {}",
|
|
|
|
imgResponse->errorString().toStdString());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto pixmap = QPixmap::fromImage(imgResponse->textureFactory()->image());
|
|
|
|
|
|
|
|
auto imgDialog = new dialogs::ImageOverlay(pixmap);
|
|
|
|
imgDialog->show();
|
|
|
|
connect(imgDialog,
|
|
|
|
&dialogs::ImageOverlay::saving,
|
|
|
|
this,
|
|
|
|
[this, mxcUrl, originalFilename, mimeType, eventType]() {
|
|
|
|
saveMedia(mxcUrl, originalFilename, mimeType, eventType);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::saveMedia(QString mxcUrl,
|
|
|
|
QString originalFilename,
|
|
|
|
QString mimeType,
|
|
|
|
qml_mtx_events::EventType eventType) const
|
|
|
|
{
|
|
|
|
QString dialogTitle;
|
|
|
|
if (eventType == qml_mtx_events::EventType::ImageMessage) {
|
|
|
|
dialogTitle = tr("Save image");
|
|
|
|
} else if (eventType == qml_mtx_events::EventType::VideoMessage) {
|
|
|
|
dialogTitle = tr("Save video");
|
|
|
|
} else if (eventType == qml_mtx_events::EventType::AudioMessage) {
|
|
|
|
dialogTitle = tr("Save audio");
|
|
|
|
} else {
|
|
|
|
dialogTitle = tr("Save file");
|
|
|
|
}
|
|
|
|
|
|
|
|
QString filterString = QMimeDatabase().mimeTypeForName(mimeType).filterString();
|
|
|
|
|
|
|
|
auto filename =
|
|
|
|
QFileDialog::getSaveFileName(container, dialogTitle, originalFilename, filterString);
|
|
|
|
|
|
|
|
if (filename.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
const auto url = mxcUrl.toStdString();
|
|
|
|
|
|
|
|
http::client()->download(
|
|
|
|
url,
|
|
|
|
[filename, url](const std::string &data,
|
|
|
|
const std::string &,
|
|
|
|
const std::string &,
|
|
|
|
mtx::http::RequestErr err) {
|
|
|
|
if (err) {
|
|
|
|
nhlog::net()->warn("failed to retrieve image {}: {} {}",
|
|
|
|
url,
|
|
|
|
err->matrix_error.error,
|
|
|
|
static_cast<int>(err->status_code));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
QFile file(filename);
|
|
|
|
|
|
|
|
if (!file.open(QIODevice::WriteOnly))
|
|
|
|
return;
|
|
|
|
|
|
|
|
file.write(QByteArray(data.data(), data.size()));
|
|
|
|
file.close();
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
nhlog::ui()->warn("Error while saving file to: {}", e.what());
|
|
|
|
}
|
|
|
|
});
|
2019-09-28 11:07:58 +02:00
|
|
|
}
|
|
|
|
|
2019-09-18 23:37:30 +02:00
|
|
|
void
|
|
|
|
TimelineViewManager::updateReadReceipts(const QString &room_id,
|
|
|
|
const std::vector<QString> &event_ids)
|
|
|
|
{
|
|
|
|
auto room = models.find(room_id);
|
|
|
|
if (room != models.end()) {
|
|
|
|
room.value()->markEventsAsRead(event_ids);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-30 23:20:53 +02:00
|
|
|
void
|
|
|
|
TimelineViewManager::initWithMessages(const std::map<QString, mtx::responses::Timeline> &msgs)
|
|
|
|
{
|
|
|
|
for (const auto &e : msgs) {
|
|
|
|
addRoom(e.first);
|
2019-08-31 22:43:31 +02:00
|
|
|
|
|
|
|
models.value(e.first)->addEvents(e.second);
|
2019-08-30 23:20:53 +02:00
|
|
|
}
|
2019-08-30 19:29:25 +02:00
|
|
|
}
|
2019-09-11 00:00:04 +02:00
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::queueTextMessage(const QString &msg)
|
|
|
|
{
|
|
|
|
mtx::events::msg::Text text = {};
|
|
|
|
text.body = msg.trimmed().toStdString();
|
|
|
|
text.format = "org.matrix.custom.html";
|
|
|
|
text.formatted_body = utils::markdownToHtml(msg).toStdString();
|
|
|
|
|
|
|
|
if (timeline_)
|
|
|
|
timeline_->sendMessage(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::queueReplyMessage(const QString &reply, const RelatedInfo &related)
|
|
|
|
{
|
|
|
|
mtx::events::msg::Text text = {};
|
|
|
|
|
|
|
|
QString body;
|
|
|
|
bool firstLine = true;
|
2019-09-11 00:59:04 +02:00
|
|
|
for (const auto &line : related.quoted_body.split("\n")) {
|
2019-09-11 00:00:04 +02:00
|
|
|
if (firstLine) {
|
|
|
|
firstLine = false;
|
|
|
|
body = QString("> <%1> %2\n").arg(related.quoted_user).arg(line);
|
|
|
|
} else {
|
|
|
|
body = QString("%1\n> %2\n").arg(body).arg(line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
text.body = QString("%1\n%2").arg(body).arg(reply).toStdString();
|
|
|
|
text.format = "org.matrix.custom.html";
|
|
|
|
text.formatted_body =
|
|
|
|
utils::getFormattedQuoteBody(related, utils::markdownToHtml(reply)).toStdString();
|
|
|
|
text.relates_to.in_reply_to.event_id = related.related_event;
|
|
|
|
|
|
|
|
if (timeline_)
|
|
|
|
timeline_->sendMessage(text);
|
|
|
|
}
|
2019-09-11 00:17:45 +02:00
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::queueEmoteMessage(const QString &msg)
|
|
|
|
{
|
|
|
|
auto html = utils::markdownToHtml(msg);
|
|
|
|
|
|
|
|
mtx::events::msg::Emote emote;
|
|
|
|
emote.body = msg.trimmed().toStdString();
|
|
|
|
|
|
|
|
if (html != msg.trimmed().toHtmlEscaped())
|
|
|
|
emote.formatted_body = html.toStdString();
|
|
|
|
|
|
|
|
if (timeline_)
|
|
|
|
timeline_->sendMessage(emote);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::queueImageMessage(const QString &roomid,
|
|
|
|
const QString &filename,
|
|
|
|
const QString &url,
|
|
|
|
const QString &mime,
|
|
|
|
uint64_t dsize,
|
|
|
|
const QSize &dimensions)
|
|
|
|
{
|
|
|
|
mtx::events::msg::Image image;
|
|
|
|
image.info.mimetype = mime.toStdString();
|
|
|
|
image.info.size = dsize;
|
|
|
|
image.body = filename.toStdString();
|
|
|
|
image.url = url.toStdString();
|
|
|
|
image.info.h = dimensions.height();
|
|
|
|
image.info.w = dimensions.width();
|
|
|
|
models.value(roomid)->sendMessage(image);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::queueFileMessage(const QString &roomid,
|
|
|
|
const QString &filename,
|
|
|
|
const QString &url,
|
|
|
|
const QString &mime,
|
|
|
|
uint64_t dsize)
|
|
|
|
{
|
|
|
|
mtx::events::msg::File file;
|
|
|
|
file.info.mimetype = mime.toStdString();
|
|
|
|
file.info.size = dsize;
|
|
|
|
file.body = filename.toStdString();
|
|
|
|
file.url = url.toStdString();
|
|
|
|
models.value(roomid)->sendMessage(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::queueAudioMessage(const QString &roomid,
|
|
|
|
const QString &filename,
|
|
|
|
const QString &url,
|
|
|
|
const QString &mime,
|
|
|
|
uint64_t dsize)
|
|
|
|
{
|
|
|
|
mtx::events::msg::Audio audio;
|
|
|
|
audio.info.mimetype = mime.toStdString();
|
|
|
|
audio.info.size = dsize;
|
|
|
|
audio.body = filename.toStdString();
|
|
|
|
audio.url = url.toStdString();
|
|
|
|
models.value(roomid)->sendMessage(audio);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::queueVideoMessage(const QString &roomid,
|
|
|
|
const QString &filename,
|
|
|
|
const QString &url,
|
|
|
|
const QString &mime,
|
|
|
|
uint64_t dsize)
|
|
|
|
{
|
|
|
|
mtx::events::msg::Video video;
|
|
|
|
video.info.mimetype = mime.toStdString();
|
|
|
|
video.info.size = dsize;
|
|
|
|
video.body = filename.toStdString();
|
|
|
|
video.url = url.toStdString();
|
|
|
|
models.value(roomid)->sendMessage(video);
|
|
|
|
}
|