matrixion/src/timeline/TimelineViewManager.cpp

773 lines
30 KiB
C++
Raw Normal View History

2021-03-05 02:35:15 +03:00
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
2019-11-09 05:06:10 +03:00
#include "TimelineViewManager.h"
2020-11-25 19:02:23 +03:00
#include <QDropEvent>
2019-11-09 05:06:10 +03:00
#include <QMetaType>
#include <QPalette>
#include <QQmlContext>
2020-05-27 11:49:26 +03:00
#include <QQmlEngine>
2020-07-11 02:19:48 +03:00
#include <QString>
2019-11-09 05:06:10 +03:00
#include "BlurhashProvider.h"
2019-11-09 05:06:10 +03:00
#include "ChatPage.h"
2021-04-29 22:46:49 +03:00
#include "Clipboard.h"
2019-11-09 05:06:10 +03:00
#include "ColorImageProvider.h"
2021-07-21 02:03:38 +03:00
#include "CombinedImagePackModel.h"
#include "CompletionProxyModel.h"
2019-11-09 05:06:10 +03:00
#include "DelegateChooser.h"
2020-10-27 19:45:28 +03:00
#include "DeviceVerificationFlow.h"
#include "EventAccessors.h"
#include "ImagePackListModel.h"
2021-06-11 03:13:12 +03:00
#include "InviteesModel.h"
2018-07-17 16:37:25 +03:00
#include "Logging.h"
#include "MainWindow.h"
2020-01-17 03:25:14 +03:00
#include "MatrixClient.h"
2019-11-09 05:06:10 +03:00
#include "MxcImageProvider.h"
#include "ReadReceiptsModel.h"
#include "RoomDirectoryModel.h"
#include "RoomsModel.h"
#include "SingleImagePackModel.h"
2019-11-09 05:06:10 +03:00
#include "UserSettingsPage.h"
#include "UsersModel.h"
2019-11-09 05:06:10 +03:00
#include "dialogs/ImageOverlay.h"
#include "emoji/EmojiModel.h"
#include "emoji/Provider.h"
#include "ui/MxcMediaProxy.h"
2021-02-14 03:28:28 +03:00
#include "ui/NhekoCursorShape.h"
2020-11-25 19:02:23 +03:00
#include "ui/NhekoDropArea.h"
2021-05-13 09:23:56 +03:00
#include "ui/NhekoGlobalObject.h"
2017-04-06 02:06:42 +03:00
2020-01-11 20:53:32 +03:00
Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents)
2020-07-04 05:24:28 +03:00
Q_DECLARE_METATYPE(std::vector<DeviceInfo>)
Q_DECLARE_METATYPE(std::vector<mtx::responses::PublicRoomsChunk>)
2020-01-11 20:53:32 +03:00
namespace msgs = mtx::events::msg;
namespace {
template<template<class...> class Op, class... Args>
using is_detected = typename nheko::detail::detector<nheko::nonesuch, void, Op, Args...>::value_t;
template<class Content>
using file_t = decltype(Content::file);
template<class Content>
using url_t = decltype(Content::url);
template<class Content>
using body_t = decltype(Content::body);
template<class Content>
using formatted_body_t = decltype(Content::formatted_body);
template<typename T>
static constexpr bool
messageWithFileAndUrl(const mtx::events::Event<T> &)
{
return is_detected<file_t, T>::value && is_detected<url_t, T>::value;
}
template<typename T>
static constexpr void
removeReplyFallback(mtx::events::Event<T> &e)
{
if constexpr (is_detected<body_t, T>::value) {
if constexpr (std::is_same_v<std::optional<std::string>,
std::remove_cv_t<decltype(e.content.body)>>) {
if (e.content.body) {
e.content.body = utils::stripReplyFromBody(e.content.body);
}
} else if constexpr (std::is_same_v<std::string,
std::remove_cv_t<decltype(e.content.body)>>) {
e.content.body = utils::stripReplyFromBody(e.content.body);
}
}
if constexpr (is_detected<formatted_body_t, T>::value) {
if (e.content.format == "org.matrix.custom.html") {
e.content.formatted_body =
utils::stripReplyFromFormattedBody(e.content.formatted_body);
}
}
}
}
void
2019-11-09 05:06:10 +03:00
TimelineViewManager::updateColorPalette()
{
userColors.clear();
2020-10-17 01:57:29 +03:00
if (ChatPage::instance()->userSettings()->theme() == "light") {
2020-03-30 22:48:28 +03:00
view->rootContext()->setContextProperty("currentActivePalette", QPalette());
view->rootContext()->setContextProperty("currentInactivePalette", QPalette());
2020-10-17 01:57:29 +03:00
} else if (ChatPage::instance()->userSettings()->theme() == "dark") {
2020-03-30 22:48:28 +03:00
view->rootContext()->setContextProperty("currentActivePalette", QPalette());
view->rootContext()->setContextProperty("currentInactivePalette", QPalette());
2019-11-09 05:06:10 +03:00
} else {
view->rootContext()->setContextProperty("currentActivePalette", QPalette());
view->rootContext()->setContextProperty("currentInactivePalette", nullptr);
}
}
QColor
TimelineViewManager::userColor(QString id, QColor background)
{
if (!userColors.contains(id))
userColors.insert(id, QColor(utils::generateContrastingHexColor(id, background)));
return userColors.value(id);
}
2020-06-24 17:24:22 +03:00
QString
TimelineViewManager::userPresence(QString id) const
{
if (id.isEmpty())
return "";
else
return QString::fromStdString(
mtx::presence::to_string(cache::presenceState(id.toStdString())));
}
QString
TimelineViewManager::userStatus(QString id) const
{
return QString::fromStdString(cache::statusMessage(id.toStdString()));
}
2020-10-17 01:57:29 +03:00
TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *parent)
2021-05-30 01:23:57 +03:00
: QObject(parent)
, imgProvider(new MxcImageProvider())
2019-11-09 05:06:10 +03:00
, colorImgProvider(new ColorImageProvider())
, blurhashProvider(new BlurhashProvider())
2020-07-11 02:19:48 +03:00
, callManager_(callManager)
, rooms_(new RoomlistModel(this))
2021-06-10 00:52:28 +03:00
, communities_(new CommunitiesModel(this))
{
2020-07-17 23:16:30 +03:00
qRegisterMetaType<mtx::events::msg::KeyVerificationAccept>();
qRegisterMetaType<mtx::events::msg::KeyVerificationCancel>();
qRegisterMetaType<mtx::events::msg::KeyVerificationDone>();
qRegisterMetaType<mtx::events::msg::KeyVerificationKey>();
qRegisterMetaType<mtx::events::msg::KeyVerificationMac>();
qRegisterMetaType<mtx::events::msg::KeyVerificationReady>();
qRegisterMetaType<mtx::events::msg::KeyVerificationRequest>();
qRegisterMetaType<mtx::events::msg::KeyVerificationStart>();
2021-07-21 02:03:38 +03:00
qRegisterMetaType<CombinedImagePackModel *>();
2020-07-17 23:16:30 +03:00
2021-07-29 05:33:23 +03:00
qRegisterMetaType<std::vector<mtx::responses::PublicRoomsChunk>>();
2019-11-09 05:06:10 +03:00
qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject,
"im.nheko",
2019-11-09 05:06:10 +03:00
1,
0,
"MtxEvent",
"Can't instantiate enum!");
qmlRegisterUncreatableMetaObject(
olm::staticMetaObject, "im.nheko", 1, 0, "Olm", "Can't instantiate enum!");
qmlRegisterUncreatableMetaObject(
crypto::staticMetaObject, "im.nheko", 1, 0, "Crypto", "Can't instantiate enum!");
2020-07-04 05:24:28 +03:00
qmlRegisterUncreatableMetaObject(verification::staticMetaObject,
"im.nheko",
1,
0,
"VerificationStatus",
"Can't instantiate enum!");
qmlRegisterType<DelegateChoice>("im.nheko", 1, 0, "DelegateChoice");
qmlRegisterType<DelegateChooser>("im.nheko", 1, 0, "DelegateChooser");
2020-11-25 19:02:23 +03:00
qmlRegisterType<NhekoDropArea>("im.nheko", 1, 0, "NhekoDropArea");
2021-02-14 03:28:28 +03:00
qmlRegisterType<NhekoCursorShape>("im.nheko", 1, 0, "CursorShape");
qmlRegisterType<MxcMediaProxy>("im.nheko", 1, 0, "MxcMedia");
qmlRegisterUncreatableType<DeviceVerificationFlow>(
"im.nheko", 1, 0, "DeviceVerificationFlow", "Can't create verification flow from QML!");
2020-07-04 05:24:28 +03:00
qmlRegisterUncreatableType<UserProfile>(
"im.nheko",
1,
0,
"UserProfileModel",
"UserProfile needs to be instantiated on the C++ side");
2021-05-30 04:09:21 +03:00
qmlRegisterUncreatableType<MemberList>(
"im.nheko", 1, 0, "MemberList", "MemberList needs to be instantiated on the C++ side");
qmlRegisterUncreatableType<RoomSettings>(
"im.nheko",
1,
0,
"RoomSettingsModel",
"Room Settings needs to be instantiated on the C++ side");
2021-07-12 23:28:01 +03:00
qmlRegisterUncreatableType<TimelineModel>(
"im.nheko", 1, 0, "Room", "Room needs to be instantiated on the C++ side");
qmlRegisterUncreatableType<ImagePackListModel>(
"im.nheko",
1,
0,
"ImagePackListModel",
"ImagePackListModel needs to be instantiated on the C++ side");
qmlRegisterUncreatableType<SingleImagePackModel>(
"im.nheko",
1,
0,
"SingleImagePackModel",
"SingleImagePackModel needs to be instantiated on the C++ side");
2021-06-11 03:13:12 +03:00
qmlRegisterUncreatableType<InviteesModel>(
"im.nheko",
1,
0,
"InviteesModel",
"InviteesModel needs to be instantiated on the C++ side");
qmlRegisterUncreatableType<ReadReceiptsProxy>(
"im.nheko",
1,
0,
"ReadReceiptsProxy",
"ReadReceiptsProxy needs to be instantiated on the C++ side");
static auto self = this;
2021-01-16 23:33:33 +03:00
qmlRegisterSingletonType<MainWindow>(
2021-01-30 02:17:44 +03:00
"im.nheko", 1, 0, "MainWindow", [](QQmlEngine *, QJSEngine *) -> QObject * {
2021-04-09 02:47:13 +03:00
auto ptr = MainWindow::instance();
QQmlEngine::setObjectOwnership(ptr, QQmlEngine::CppOwnership);
return ptr;
2021-01-30 02:17:44 +03:00
});
qmlRegisterSingletonType<TimelineViewManager>(
2020-10-08 19:16:30 +03:00
"im.nheko", 1, 0, "TimelineManager", [](QQmlEngine *, QJSEngine *) -> QObject * {
2021-04-09 02:47:13 +03:00
auto ptr = self;
QQmlEngine::setObjectOwnership(ptr, QQmlEngine::CppOwnership);
return ptr;
2020-10-08 19:16:30 +03:00
});
2021-05-19 20:34:10 +03:00
qmlRegisterSingletonType<RoomlistModel>(
"im.nheko", 1, 0, "Rooms", [](QQmlEngine *, QJSEngine *) -> QObject * {
2021-06-11 15:51:29 +03:00
auto ptr = new FilteredRoomlistModel(self->rooms_);
connect(self->communities_,
&CommunitiesModel::currentTagIdChanged,
ptr,
&FilteredRoomlistModel::updateFilterTag);
2021-06-11 18:54:05 +03:00
connect(self->communities_,
&CommunitiesModel::hiddenTagsChanged,
ptr,
&FilteredRoomlistModel::updateHiddenTagsAndSpaces);
2021-06-11 15:51:29 +03:00
return ptr;
2021-05-19 20:34:10 +03:00
});
2021-06-10 00:52:28 +03:00
qmlRegisterSingletonType<RoomlistModel>(
"im.nheko", 1, 0, "Communities", [](QQmlEngine *, QJSEngine *) -> QObject * {
auto ptr = self->communities_;
QQmlEngine::setObjectOwnership(ptr, QQmlEngine::CppOwnership);
return ptr;
});
qmlRegisterSingletonType<UserSettings>(
2020-10-08 19:16:30 +03:00
"im.nheko", 1, 0, "Settings", [](QQmlEngine *, QJSEngine *) -> QObject * {
2021-04-09 02:47:13 +03:00
auto ptr = ChatPage::instance()->userSettings().data();
QQmlEngine::setObjectOwnership(ptr, QQmlEngine::CppOwnership);
return ptr;
});
qmlRegisterSingletonType<CallManager>(
"im.nheko", 1, 0, "CallManager", [](QQmlEngine *, QJSEngine *) -> QObject * {
2021-04-09 02:47:13 +03:00
auto ptr = ChatPage::instance()->callManager();
QQmlEngine::setObjectOwnership(ptr, QQmlEngine::CppOwnership);
return ptr;
});
2021-04-29 22:46:49 +03:00
qmlRegisterSingletonType<Clipboard>(
"im.nheko", 1, 0, "Clipboard", [](QQmlEngine *, QJSEngine *) -> QObject * {
return new Clipboard();
});
2021-05-13 09:23:56 +03:00
qmlRegisterSingletonType<Nheko>(
"im.nheko", 1, 0, "Nheko", [](QQmlEngine *, QJSEngine *) -> QObject * {
return new Nheko();
});
2020-05-27 11:49:26 +03:00
2020-01-11 20:53:32 +03:00
qRegisterMetaType<mtx::events::collections::TimelineEvents>();
2020-07-04 05:24:28 +03:00
qRegisterMetaType<std::vector<DeviceInfo>>();
qmlRegisterType<emoji::EmojiModel>("im.nheko.EmojiModel", 1, 0, "EmojiModel");
qmlRegisterUncreatableType<emoji::Emoji>(
"im.nheko.EmojiModel", 1, 0, "Emoji", "Used by emoji models");
qmlRegisterUncreatableMetaObject(emoji::staticMetaObject,
2020-06-11 07:37:54 +03:00
"im.nheko.EmojiModel",
1,
0,
"EmojiCategory",
"Error: Only enums");
2019-11-09 05:06:10 +03:00
2021-08-12 17:45:42 +03:00
qmlRegisterType<RoomDirectoryModel>("im.nheko", 1, 0, "RoomDirectoryModel");
2019-11-09 05:06:10 +03:00
#ifdef USE_QUICK_VIEW
2021-05-30 01:23:57 +03:00
view = new QQuickView(parent);
2019-11-09 05:06:10 +03:00
container = QWidget::createWindowContainer(view, parent);
#else
view = new QQuickWidget(parent);
container = view;
view->setResizeMode(QQuickWidget::SizeRootObjectToView);
container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
2020-06-27 04:15:36 +03:00
2019-11-09 05:06:10 +03:00
connect(view, &QQuickWidget::statusChanged, this, [](QQuickWidget::Status status) {
nhlog::ui()->debug("Status changed to {}", status);
});
#endif
container->setMinimumSize(200, 200);
updateColorPalette();
view->engine()->addImageProvider("MxcImage", imgProvider);
view->engine()->addImageProvider("colorimage", colorImgProvider);
view->engine()->addImageProvider("blurhash", blurhashProvider);
view->setSource(QUrl("qrc:///qml/Root.qml"));
2019-11-09 05:06:10 +03:00
connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette);
2020-07-29 00:55:47 +03:00
connect(
dynamic_cast<ChatPage *>(parent),
&ChatPage::receivedRoomDeviceVerificationRequest,
2020-07-29 00:55:47 +03:00
this,
[this](const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &message,
TimelineModel *model) {
if (this->isInitialSync_)
return;
auto event_id = QString::fromStdString(message.event_id);
if (!this->dvList.contains(event_id)) {
if (auto flow = DeviceVerificationFlow::NewInRoomVerification(
this,
model,
message.content,
QString::fromStdString(message.sender),
event_id)) {
dvList[event_id] = flow;
emit newDeviceVerificationRequest(flow.data());
2020-06-26 12:40:37 +03:00
}
}
});
connect(dynamic_cast<ChatPage *>(parent),
&ChatPage::receivedDeviceVerificationRequest,
this,
[this](const mtx::events::msg::KeyVerificationRequest &msg, std::string sender) {
if (this->isInitialSync_)
return;
if (!msg.transaction_id)
return;
auto txnid = QString::fromStdString(msg.transaction_id.value());
if (!this->dvList.contains(txnid)) {
if (auto flow = DeviceVerificationFlow::NewToDeviceVerification(
this, msg, QString::fromStdString(sender), txnid)) {
dvList[txnid] = flow;
emit newDeviceVerificationRequest(flow.data());
}
}
});
connect(dynamic_cast<ChatPage *>(parent),
&ChatPage::receivedDeviceVerificationStart,
this,
[this](const mtx::events::msg::KeyVerificationStart &msg, std::string sender) {
if (this->isInitialSync_)
return;
if (!msg.transaction_id)
return;
auto txnid = QString::fromStdString(msg.transaction_id.value());
if (!this->dvList.contains(txnid)) {
if (auto flow = DeviceVerificationFlow::NewToDeviceVerification(
this, msg, QString::fromStdString(sender), txnid)) {
dvList[txnid] = flow;
emit newDeviceVerificationRequest(flow.data());
}
}
});
connect(parent, &ChatPage::loggedOut, this, [this]() {
2020-08-01 21:39:06 +03:00
isInitialSync_ = true;
emit initialSyncChanged(true);
});
connect(this,
&TimelineViewManager::openImageOverlayInternalCb,
this,
&TimelineViewManager::openImageOverlayInternal);
2020-10-27 20:14:06 +03:00
}
void
2021-08-14 00:58:26 +03:00
TimelineViewManager::openRoomMembers(TimelineModel *room)
{
2021-08-14 00:58:26 +03:00
if (!room)
return;
MemberList *memberList = new MemberList(room->roomId(), this);
emit openRoomMembersDialog(memberList, room);
}
void
TimelineViewManager::openRoomSettings(QString room_id)
{
RoomSettings *settings = new RoomSettings(room_id, this);
connect(rooms_->getRoomById(room_id).data(),
&TimelineModel::roomAvatarUrlChanged,
settings,
&RoomSettings::avatarChanged);
emit openRoomSettingsDialog(settings);
}
void
TimelineViewManager::openInviteUsers(QString roomId)
{
InviteesModel *model = new InviteesModel{this};
connect(model, &InviteesModel::accept, this, [this, model, roomId]() {
emit inviteUsers(roomId, model->mxids());
});
emit openInviteUsersDialog(model);
}
2021-07-22 14:55:12 +03:00
void
TimelineViewManager::openGlobalUserProfile(QString userId)
{
UserProfile *profile = new UserProfile{QString{}, userId, this};
emit openProfile(profile);
}
2020-10-27 20:14:06 +03:00
void
TimelineViewManager::setVideoCallItem()
{
WebRTCSession::instance().setVideoItem(
view->rootObject()->findChild<QQuickItem *>("videoCallItem"));
}
void
TimelineViewManager::sync(const mtx::responses::Rooms &rooms_res)
2021-05-19 20:34:10 +03:00
{
this->rooms_->sync(rooms_res);
2021-06-10 00:52:28 +03:00
this->communities_->sync(rooms_res);
2021-05-19 20:34:10 +03:00
if (isInitialSync_) {
this->isInitialSync_ = false;
emit initialSyncChanged(false);
}
}
2021-04-29 20:09:16 +03:00
void
TimelineViewManager::showEvent(const QString &room_id, const QString &event_id)
{
if (auto room = rooms_->getRoomById(room_id)) {
if (rooms_->currentRoom() != room) {
rooms_->setCurrentRoom(room_id);
2021-04-29 20:09:16 +03:00
container->setFocus();
nhlog::ui()->info("Activated room {}", room_id.toStdString());
}
room->showEvent(event_id);
2021-04-29 20:09:16 +03:00
}
}
2020-09-03 20:51:50 +03:00
QString
TimelineViewManager::escapeEmoji(QString str) const
{
return utils::replaceEmoji(str);
}
2017-09-10 12:58:00 +03:00
void
TimelineViewManager::openImageOverlay(QString mxcUrl, QString eventId)
2017-09-10 12:58:00 +03:00
{
2020-11-30 23:10:59 +03:00
if (mxcUrl.isEmpty()) {
2020-11-30 22:56:39 +03:00
return;
}
MxcImageProvider::download(
mxcUrl.remove("mxc://"), QSize(), [this, eventId](QString, QSize, QImage img, QString) {
if (img.isNull()) {
nhlog::ui()->error("Error when retrieving image for overlay.");
return;
}
emit openImageOverlayInternalCb(eventId, std::move(img));
});
}
void
TimelineViewManager::openImagePackSettings(QString roomid)
{
emit showImagePackSettings(new ImagePackListModel(roomid.toStdString(), this));
}
void
TimelineViewManager::openImageOverlayInternal(QString eventId, QImage img)
{
auto pixmap = QPixmap::fromImage(img);
auto imgDialog = new dialogs::ImageOverlay(pixmap);
imgDialog->showFullScreen();
auto room = rooms_->currentRoom();
2021-06-11 22:25:06 +03:00
connect(imgDialog, &dialogs::ImageOverlay::saving, room, [eventId, imgDialog, room]() {
// hide the overlay while presenting the save dialog for better
// cross platform support.
imgDialog->hide();
if (!room->saveMedia(eventId)) {
imgDialog->show();
} else {
imgDialog->close();
}
});
2017-12-01 18:33:49 +03:00
}
void
TimelineViewManager::openLeaveRoomDialog(QString roomid) const
{
MainWindow::instance()->openLeaveRoomDialog(roomid);
}
void
TimelineViewManager::verifyUser(QString userid)
{
auto joined_rooms = cache::joinedRooms();
auto room_infos = cache::getRoomInfo(joined_rooms);
for (std::string room_id : joined_rooms) {
if ((room_infos[QString::fromStdString(room_id)].member_count == 2) &&
cache::isRoomEncrypted(room_id)) {
auto room_members = cache::roomMembers(room_id);
if (std::find(room_members.begin(),
room_members.end(),
(userid).toStdString()) != room_members.end()) {
2021-05-19 20:34:10 +03:00
if (auto model =
rooms_->getRoomById(QString::fromStdString(room_id))) {
2021-05-19 20:34:10 +03:00
auto flow =
DeviceVerificationFlow::InitiateUserVerification(
this, model.data(), userid);
connect(model.data(),
&TimelineModel::updateFlowEventId,
this,
[this, flow](std::string eventId) {
dvList[QString::fromStdString(eventId)] =
flow;
});
emit newDeviceVerificationRequest(flow.data());
return;
}
}
}
}
emit ChatPage::instance()->showNotification(
tr("No encrypted private chat found with this user. Create an "
"encrypted private chat with this user and try again."));
}
2020-10-05 23:58:07 +03:00
void
TimelineViewManager::removeVerificationFlow(DeviceVerificationFlow *flow)
{
for (auto it = dvList.keyValueBegin(); it != dvList.keyValueEnd(); ++it) {
if ((*it).second == flow) {
dvList.remove((*it).first);
2020-10-05 23:58:07 +03:00
return;
}
}
}
void
TimelineViewManager::verifyDevice(QString userid, QString deviceid)
{
auto flow = DeviceVerificationFlow::InitiateDeviceVerification(this, userid, deviceid);
this->dvList[flow->transactionId()] = flow;
emit newDeviceVerificationRequest(flow.data());
}
2017-08-20 13:47:22 +03:00
void
2019-11-09 05:06:10 +03:00
TimelineViewManager::updateReadReceipts(const QString &room_id,
const std::vector<QString> &event_ids)
2017-04-06 02:06:42 +03:00
{
if (auto room = rooms_->getRoomById(room_id)) {
2021-05-19 20:34:10 +03:00
room->markEventsAsRead(event_ids);
2017-08-26 11:33:26 +03:00
}
2017-04-06 02:06:42 +03:00
}
2020-10-20 20:46:37 +03:00
void
TimelineViewManager::receivedSessionKey(const std::string &room_id, const std::string &session_id)
{
if (auto room = rooms_->getRoomById(QString::fromStdString(room_id))) {
2021-05-19 20:34:10 +03:00
room->receivedSessionKey(session_id);
2020-10-20 20:46:37 +03:00
}
}
void
2021-05-24 15:04:07 +03:00
TimelineViewManager::initializeRoomlist()
{
rooms_->initializeRooms();
2021-06-10 00:52:28 +03:00
communities_->initializeSidebar();
}
void
TimelineViewManager::queueReply(const QString &roomid,
const QString &repliedToEvent,
const QString &replyBody)
{
if (auto room = rooms_->getRoomById(roomid)) {
2021-05-19 20:34:10 +03:00
room->setReply(repliedToEvent);
room->input()->message(replyBody);
}
}
2020-07-11 02:19:48 +03:00
void
TimelineViewManager::queueCallMessage(const QString &roomid,
const mtx::events::msg::CallInvite &callInvite)
{
if (auto room = rooms_->getRoomById(roomid))
2021-05-19 20:34:10 +03:00
room->sendMessageEvent(callInvite, mtx::events::EventType::CallInvite);
2020-07-11 02:19:48 +03:00
}
void
TimelineViewManager::queueCallMessage(const QString &roomid,
const mtx::events::msg::CallCandidates &callCandidates)
{
if (auto room = rooms_->getRoomById(roomid))
2021-05-19 20:34:10 +03:00
room->sendMessageEvent(callCandidates, mtx::events::EventType::CallCandidates);
2020-07-11 02:19:48 +03:00
}
void
TimelineViewManager::queueCallMessage(const QString &roomid,
const mtx::events::msg::CallAnswer &callAnswer)
{
if (auto room = rooms_->getRoomById(roomid))
2021-05-19 20:34:10 +03:00
room->sendMessageEvent(callAnswer, mtx::events::EventType::CallAnswer);
2020-07-11 02:19:48 +03:00
}
void
TimelineViewManager::queueCallMessage(const QString &roomid,
const mtx::events::msg::CallHangUp &callHangUp)
{
if (auto room = rooms_->getRoomById(roomid))
2021-05-19 20:34:10 +03:00
room->sendMessageEvent(callHangUp, mtx::events::EventType::CallHangUp);
2017-10-07 20:50:32 +03:00
}
void
TimelineViewManager::focusMessageInput()
{
emit focusInput();
2021-02-14 03:28:28 +03:00
}
QObject *
TimelineViewManager::completerFor(QString completerName, QString roomId)
{
if (completerName == "user") {
auto userModel = new UsersModel(roomId.toStdString());
auto proxy = new CompletionProxyModel(userModel);
userModel->setParent(proxy);
return proxy;
} else if (completerName == "emoji") {
auto emojiModel = new emoji::EmojiModel();
auto proxy = new CompletionProxyModel(emojiModel);
emojiModel->setParent(proxy);
return proxy;
} else if (completerName == "allemoji") {
auto emojiModel = new emoji::EmojiModel();
auto proxy = new CompletionProxyModel(emojiModel, 1, static_cast<size_t>(-1) / 4);
emojiModel->setParent(proxy);
return proxy;
} else if (completerName == "room") {
auto roomModel = new RoomsModel(false);
auto proxy = new CompletionProxyModel(roomModel, 4);
roomModel->setParent(proxy);
return proxy;
} else if (completerName == "roomAliases") {
auto roomModel = new RoomsModel(true);
auto proxy = new CompletionProxyModel(roomModel);
roomModel->setParent(proxy);
return proxy;
2021-07-15 21:37:52 +03:00
} else if (completerName == "stickers") {
2021-07-21 02:03:38 +03:00
auto stickerModel = new CombinedImagePackModel(roomId.toStdString(), true);
2021-07-19 13:43:16 +03:00
auto proxy = new CompletionProxyModel(stickerModel, 1, static_cast<size_t>(-1) / 4);
2021-07-15 21:37:52 +03:00
stickerModel->setParent(proxy);
return proxy;
}
return nullptr;
2021-02-22 22:16:40 +03:00
}
void
TimelineViewManager::focusTimeline()
{
getWidget()->setFocus();
}
2021-04-11 17:31:49 +03:00
void
TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEvents *e,
QString roomId)
2021-04-11 17:31:49 +03:00
{
auto room = rooms_->getRoomById(roomId);
auto content = mtx::accessors::url(*e);
std::optional<mtx::crypto::EncryptedFile> encryptionInfo = mtx::accessors::file(*e);
if (encryptionInfo) {
http::client()->download(
content,
[this, roomId, e, encryptionInfo](const std::string &res,
const std::string &content_type,
const std::string &originalFilename,
mtx::http::RequestErr err) {
2021-04-22 05:49:27 +03:00
if (err)
return;
2021-04-11 17:31:49 +03:00
auto data = mtx::crypto::to_string(
mtx::crypto::decrypt_file(res, encryptionInfo.value()));
http::client()->upload(
data,
content_type,
originalFilename,
[this, roomId, e](const mtx::responses::ContentURI &res,
mtx::http::RequestErr err) mutable {
if (err) {
nhlog::net()->warn("failed to upload media: {} {} ({})",
err->matrix_error.error,
to_string(err->matrix_error.errcode),
static_cast<int>(err->status_code));
return;
}
std::visit(
2021-04-22 05:49:27 +03:00
[this, roomId, url = res.content_uri](auto ev) {
if constexpr (mtx::events::message_content_to_type<
decltype(ev.content)> ==
mtx::events::EventType::RoomMessage) {
if constexpr (messageWithFileAndUrl(ev)) {
ev.content.relations.relations
.clear();
ev.content.file.reset();
ev.content.url = url;
}
if (auto room = rooms_->getRoomById(roomId)) {
2021-05-19 20:34:10 +03:00
removeReplyFallback(ev);
ev.content.relations.relations
.clear();
room->sendMessageEvent(
ev.content,
mtx::events::EventType::
RoomMessage);
}
}
},
*e);
});
return;
2021-04-11 17:31:49 +03:00
});
2021-04-11 17:31:49 +03:00
return;
}
2021-04-11 17:31:49 +03:00
std::visit(
[room](auto e) {
if constexpr (mtx::events::message_content_to_type<decltype(e.content)> ==
mtx::events::EventType::RoomMessage) {
e.content.relations.relations.clear();
removeReplyFallback(e);
2021-05-19 20:34:10 +03:00
room->sendMessageEvent(e.content, mtx::events::EventType::RoomMessage);
2021-04-11 17:31:49 +03:00
}
},
*e);
}
//! WORKAROUND(Nico): for https://bugreports.qt.io/browse/QTBUG-93281
void
TimelineViewManager::fixImageRendering(QQuickTextDocument *t, QQuickItem *i)
{
if (t) {
QObject::connect(
t->textDocument(), SIGNAL(imagesLoaded()), i, SLOT(updateWholeDocument()));
}
}