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
|
|
|
|
2020-03-01 21:55:43 +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-02-21 20:40:21 +03:00
|
|
|
#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"
|
2021-04-27 12:33:46 +03:00
|
|
|
#include "EventAccessors.h"
|
2018-07-17 16:37:25 +03:00
|
|
|
#include "Logging.h"
|
2020-09-03 18:01:58 +03:00
|
|
|
#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"
|
2021-02-21 20:40:21 +03:00
|
|
|
#include "RoomsModel.h"
|
2019-11-09 05:06:10 +03:00
|
|
|
#include "UserSettingsPage.h"
|
2021-02-21 20:40:21 +03:00
|
|
|
#include "UsersModel.h"
|
2019-11-09 05:06:10 +03:00
|
|
|
#include "dialogs/ImageOverlay.h"
|
2020-05-13 07:35:26 +03:00
|
|
|
#include "emoji/EmojiModel.h"
|
|
|
|
#include "emoji/Provider.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>)
|
2020-01-11 20:53:32 +03:00
|
|
|
|
2020-06-17 21:28:35 +03:00
|
|
|
namespace msgs = mtx::events::msg;
|
|
|
|
|
2021-04-27 12:33:46 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-17 23:50:18 +03:00
|
|
|
void
|
2019-11-09 05:06:10 +03:00
|
|
|
TimelineViewManager::updateColorPalette()
|
2018-07-17 23:50:18 +03:00
|
|
|
{
|
2020-02-20 22:51:07 +03:00
|
|
|
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);
|
2018-07-17 23:50:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-20 22:51:07 +03:00
|
|
|
QColor
|
|
|
|
TimelineViewManager::userColor(QString id, QColor background)
|
|
|
|
{
|
|
|
|
if (!userColors.contains(id))
|
|
|
|
userColors.insert(
|
|
|
|
id, QColor(utils::generateContrastingHexColor(id, background.name())));
|
|
|
|
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())));
|
|
|
|
}
|
|
|
|
|
2020-06-08 02:45:24 +03:00
|
|
|
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)
|
2019-11-09 05:06:10 +03:00
|
|
|
: imgProvider(new MxcImageProvider())
|
|
|
|
, colorImgProvider(new ColorImageProvider())
|
2020-03-01 21:55:43 +03:00
|
|
|
, blurhashProvider(new BlurhashProvider())
|
2020-07-11 02:19:48 +03:00
|
|
|
, callManager_(callManager)
|
2021-05-19 20:34:10 +03:00
|
|
|
, rooms(new RoomlistModel(this))
|
2017-04-13 04:11:22 +03:00
|
|
|
{
|
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>();
|
|
|
|
|
2019-11-09 05:06:10 +03:00
|
|
|
qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject,
|
2019-11-30 03:43:39 +03:00
|
|
|
"im.nheko",
|
2019-11-09 05:06:10 +03:00
|
|
|
1,
|
|
|
|
0,
|
|
|
|
"MtxEvent",
|
|
|
|
"Can't instantiate enum!");
|
2021-05-07 13:19:46 +03:00
|
|
|
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!");
|
|
|
|
|
2019-11-30 03:43:39 +03:00
|
|
|
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");
|
2020-10-05 23:12:10 +03:00
|
|
|
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-02-11 17:24:09 +03:00
|
|
|
qmlRegisterUncreatableType<RoomSettings>(
|
|
|
|
"im.nheko",
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
"RoomSettingsModel",
|
|
|
|
"Room Settings needs to be instantiated on the C++ side");
|
2020-10-08 18:26:07 +03:00
|
|
|
|
|
|
|
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
|
|
|
});
|
2020-09-10 12:20:10 +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 * {
|
|
|
|
auto ptr = self->rooms;
|
|
|
|
QQmlEngine::setObjectOwnership(ptr, QQmlEngine::CppOwnership);
|
|
|
|
return ptr;
|
|
|
|
});
|
2020-09-10 12:20:10 +03:00
|
|
|
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;
|
2020-09-10 12:20:10 +03:00
|
|
|
});
|
2020-12-10 04:49:48 +03:00
|
|
|
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;
|
2020-12-10 04:49:48 +03:00
|
|
|
});
|
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>>();
|
|
|
|
|
2020-05-13 07:35:26 +03:00
|
|
|
qmlRegisterType<emoji::EmojiModel>("im.nheko.EmojiModel", 1, 0, "EmojiModel");
|
|
|
|
qmlRegisterUncreatableType<emoji::Emoji>(
|
|
|
|
"im.nheko.EmojiModel", 1, 0, "Emoji", "Used by emoji models");
|
2020-06-11 05:34:14 +03:00
|
|
|
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
|
|
|
|
|
|
|
#ifdef USE_QUICK_VIEW
|
|
|
|
view = new QQuickView();
|
|
|
|
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);
|
2020-03-01 21:55:43 +03:00
|
|
|
view->engine()->addImageProvider("blurhash", blurhashProvider);
|
2021-05-14 16:23:32 +03:00
|
|
|
view->setSource(QUrl("qrc:///qml/Root.qml"));
|
2019-11-09 05:06:10 +03:00
|
|
|
|
2020-09-03 18:01:58 +03:00
|
|
|
connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette);
|
2020-07-29 00:55:47 +03:00
|
|
|
connect(
|
|
|
|
dynamic_cast<ChatPage *>(parent),
|
2020-10-05 23:12:10 +03:00
|
|
|
&ChatPage::receivedRoomDeviceVerificationRequest,
|
2020-07-29 00:55:47 +03:00
|
|
|
this,
|
|
|
|
[this](const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &message,
|
|
|
|
TimelineModel *model) {
|
2020-10-05 23:12:10 +03:00
|
|
|
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
|
|
|
}
|
2020-06-20 15:20:43 +03:00
|
|
|
}
|
|
|
|
});
|
2020-10-05 23:12:10 +03:00
|
|
|
connect(dynamic_cast<ChatPage *>(parent),
|
|
|
|
&ChatPage::receivedDeviceVerificationRequest,
|
|
|
|
this,
|
|
|
|
[this](const mtx::events::msg::KeyVerificationRequest &msg, std::string sender) {
|
|
|
|
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 (!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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2020-09-03 18:01:58 +03:00
|
|
|
connect(parent, &ChatPage::loggedOut, this, [this]() {
|
2020-08-01 21:39:06 +03:00
|
|
|
isInitialSync_ = true;
|
|
|
|
emit initialSyncChanged(true);
|
|
|
|
});
|
2021-04-05 14:58:00 +03:00
|
|
|
|
|
|
|
connect(this,
|
|
|
|
&TimelineViewManager::openImageOverlayInternalCb,
|
|
|
|
this,
|
|
|
|
&TimelineViewManager::openImageOverlayInternal);
|
2020-10-27 20:14:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::setVideoCallItem()
|
|
|
|
{
|
|
|
|
WebRTCSession::instance().setVideoItem(
|
|
|
|
view->rootObject()->findChild<QQuickItem *>("videoCallItem"));
|
2017-11-15 19:38:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-05-19 20:34:10 +03:00
|
|
|
TimelineViewManager::sync(const mtx::responses::Rooms &rooms_)
|
|
|
|
{
|
|
|
|
this->rooms->sync(rooms_);
|
2017-09-03 11:43:45 +03:00
|
|
|
|
2021-05-19 20:34:10 +03:00
|
|
|
if (isInitialSync_) {
|
|
|
|
this->isInitialSync_ = false;
|
|
|
|
emit initialSyncChanged(false);
|
2019-12-04 01:34:16 +03:00
|
|
|
}
|
2017-04-13 04:11:22 +03:00
|
|
|
}
|
|
|
|
|
2019-06-10 02:03:18 +03:00
|
|
|
void
|
2019-11-09 05:06:10 +03:00
|
|
|
TimelineViewManager::setHistoryView(const QString &room_id)
|
2019-06-10 02:03:18 +03:00
|
|
|
{
|
2019-11-09 05:06:10 +03:00
|
|
|
nhlog::ui()->info("Trying to activate room {}", room_id.toStdString());
|
2019-06-10 02:03:18 +03:00
|
|
|
|
2021-05-19 20:34:10 +03:00
|
|
|
if (auto room = rooms->getRoomById(room_id)) {
|
|
|
|
timeline_ = room.get();
|
2019-11-09 05:06:10 +03:00
|
|
|
emit activeTimelineChanged(timeline_);
|
2020-11-25 19:38:06 +03:00
|
|
|
container->setFocus();
|
2019-11-09 05:06:10 +03:00
|
|
|
nhlog::ui()->info("Activated room {}", room_id.toStdString());
|
|
|
|
}
|
2019-06-10 02:03:18 +03:00
|
|
|
}
|
|
|
|
|
2021-02-22 21:48:31 +03:00
|
|
|
void
|
|
|
|
TimelineViewManager::highlightRoom(const QString &room_id)
|
|
|
|
{
|
|
|
|
ChatPage::instance()->highlightRoom(room_id);
|
|
|
|
}
|
|
|
|
|
2021-04-29 20:09:16 +03:00
|
|
|
void
|
|
|
|
TimelineViewManager::showEvent(const QString &room_id, const QString &event_id)
|
|
|
|
{
|
2021-05-19 20:34:10 +03:00
|
|
|
if (auto room = rooms->getRoomById(room_id)) {
|
|
|
|
if (timeline_ != room) {
|
|
|
|
timeline_ = room.get();
|
2021-04-29 20:09:16 +03:00
|
|
|
emit activeTimelineChanged(timeline_);
|
|
|
|
container->setFocus();
|
|
|
|
nhlog::ui()->info("Activated room {}", room_id.toStdString());
|
|
|
|
}
|
|
|
|
|
|
|
|
timeline_->showEvent(event_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
2021-04-05 14:58:00 +03:00
|
|
|
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;
|
|
|
|
}
|
2021-04-05 14:58:00 +03:00
|
|
|
|
|
|
|
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::openImageOverlayInternal(QString eventId, QImage img)
|
|
|
|
{
|
|
|
|
auto pixmap = QPixmap::fromImage(img);
|
|
|
|
|
|
|
|
auto imgDialog = new dialogs::ImageOverlay(pixmap);
|
|
|
|
imgDialog->showFullScreen();
|
|
|
|
connect(imgDialog, &dialogs::ImageOverlay::saving, timeline_, [this, eventId, imgDialog]() {
|
|
|
|
// hide the overlay while presenting the save dialog for better
|
|
|
|
// cross platform support.
|
|
|
|
imgDialog->hide();
|
|
|
|
|
|
|
|
if (!timeline_->saveMedia(eventId)) {
|
|
|
|
imgDialog->show();
|
|
|
|
} else {
|
|
|
|
imgDialog->close();
|
2019-12-03 04:26:41 +03:00
|
|
|
}
|
|
|
|
});
|
2017-12-01 18:33:49 +03:00
|
|
|
}
|
|
|
|
|
2020-09-03 18:01:58 +03:00
|
|
|
void
|
|
|
|
TimelineViewManager::openInviteUsersDialog()
|
|
|
|
{
|
|
|
|
MainWindow::instance()->openInviteUsersDialog(
|
|
|
|
[this](const QStringList &invitees) { emit inviteUsers(invitees); });
|
|
|
|
}
|
|
|
|
void
|
|
|
|
TimelineViewManager::openMemberListDialog() const
|
|
|
|
{
|
2020-09-03 20:34:17 +03:00
|
|
|
MainWindow::instance()->openMemberListDialog(timeline_->roomId());
|
2020-09-03 18:01:58 +03:00
|
|
|
}
|
|
|
|
void
|
|
|
|
TimelineViewManager::openLeaveRoomDialog() const
|
|
|
|
{
|
2020-09-03 20:34:17 +03:00
|
|
|
MainWindow::instance()->openLeaveRoomDialog(timeline_->roomId());
|
2020-09-03 18:01:58 +03:00
|
|
|
}
|
|
|
|
|
2020-10-05 23:12:10 +03:00
|
|
|
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))) {
|
|
|
|
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;
|
|
|
|
}
|
2020-10-05 23:12:10 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
emit ChatPage::instance()->showNotification(
|
2020-10-28 03:04:10 +03:00
|
|
|
tr("No encrypted private chat found with this user. Create an "
|
|
|
|
"encrypted private chat with this user and try again."));
|
2020-10-05 23:12:10 +03:00
|
|
|
}
|
2020-10-05 23:58:07 +03:00
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::removeVerificationFlow(DeviceVerificationFlow *flow)
|
|
|
|
{
|
|
|
|
for (auto it = dvList.keyValueBegin(); it != dvList.keyValueEnd(); ++it) {
|
2020-10-08 18:26:07 +03:00
|
|
|
if ((*it).second == flow) {
|
|
|
|
dvList.remove((*it).first);
|
2020-10-05 23:58:07 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-05 23:12:10 +03:00
|
|
|
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
|
|
|
{
|
2021-05-19 20:34:10 +03:00
|
|
|
if (auto room = rooms->getRoomById(room_id)) {
|
|
|
|
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)
|
|
|
|
{
|
2021-05-19 20:34:10 +03:00
|
|
|
if (auto room = rooms->getRoomById(QString::fromStdString(room_id))) {
|
|
|
|
room->receivedSessionKey(session_id);
|
2020-10-20 20:46:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-28 16:16:43 +03:00
|
|
|
void
|
2020-10-28 15:06:28 +03:00
|
|
|
TimelineViewManager::initWithMessages(const std::vector<QString> &roomIds)
|
2018-06-28 16:16:43 +03:00
|
|
|
{
|
2021-05-19 20:34:10 +03:00
|
|
|
rooms->initializeRooms(roomIds);
|
2018-06-28 16:16:43 +03:00
|
|
|
}
|
|
|
|
|
2021-01-07 12:44:59 +03:00
|
|
|
void
|
|
|
|
TimelineViewManager::queueReply(const QString &roomid,
|
|
|
|
const QString &repliedToEvent,
|
|
|
|
const QString &replyBody)
|
|
|
|
{
|
2021-05-19 20:34:10 +03:00
|
|
|
if (auto room = rooms->getRoomById(roomid)) {
|
|
|
|
room->setReply(repliedToEvent);
|
|
|
|
room->input()->message(replyBody);
|
2021-01-07 12:44:59 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-22 04:21:35 +03:00
|
|
|
void
|
2020-07-20 01:42:48 +03:00
|
|
|
TimelineViewManager::queueReactionMessage(const QString &reactedEvent, const QString &reactionKey)
|
2020-05-22 04:21:35 +03:00
|
|
|
{
|
2020-07-20 01:42:48 +03:00
|
|
|
if (!timeline_)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto reactions = timeline_->reactions(reactedEvent.toStdString());
|
|
|
|
|
|
|
|
QString selfReactedEvent;
|
|
|
|
for (const auto &reaction : reactions) {
|
|
|
|
if (reactionKey == reaction.key_) {
|
|
|
|
selfReactedEvent = reaction.selfReactedEvent_;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selfReactedEvent.startsWith("m"))
|
|
|
|
return;
|
|
|
|
|
2020-05-22 04:21:35 +03:00
|
|
|
// If selfReactedEvent is empty, that means we haven't previously reacted
|
|
|
|
if (selfReactedEvent.isEmpty()) {
|
2020-07-20 01:42:48 +03:00
|
|
|
mtx::events::msg::Reaction reaction;
|
2021-01-27 00:36:35 +03:00
|
|
|
mtx::common::Relation rel;
|
|
|
|
rel.rel_type = mtx::common::RelationType::Annotation;
|
|
|
|
rel.event_id = reactedEvent.toStdString();
|
|
|
|
rel.key = reactionKey.toStdString();
|
|
|
|
reaction.relations.relations.push_back(rel);
|
2020-07-20 01:42:48 +03:00
|
|
|
|
2020-08-17 21:40:33 +03:00
|
|
|
timeline_->sendMessageEvent(reaction, mtx::events::EventType::Reaction);
|
2020-05-22 04:21:35 +03:00
|
|
|
// Otherwise, we have previously reacted and the reaction should be redacted
|
|
|
|
} else {
|
2020-07-20 01:42:48 +03:00
|
|
|
timeline_->redactEvent(selfReactedEvent);
|
2020-05-22 04:21:35 +03:00
|
|
|
}
|
|
|
|
}
|
2020-07-11 02:19:48 +03:00
|
|
|
void
|
|
|
|
TimelineViewManager::queueCallMessage(const QString &roomid,
|
|
|
|
const mtx::events::msg::CallInvite &callInvite)
|
|
|
|
{
|
2021-05-19 20:34:10 +03:00
|
|
|
if (auto room = rooms->getRoomById(roomid))
|
|
|
|
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)
|
|
|
|
{
|
2021-05-19 20:34:10 +03:00
|
|
|
if (auto room = rooms->getRoomById(roomid))
|
|
|
|
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)
|
|
|
|
{
|
2021-05-19 20:34:10 +03:00
|
|
|
if (auto room = rooms->getRoomById(roomid))
|
|
|
|
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)
|
|
|
|
{
|
2021-05-19 20:34:10 +03:00
|
|
|
if (auto room = rooms->getRoomById(roomid))
|
|
|
|
room->sendMessageEvent(callHangUp, mtx::events::EventType::CallHangUp);
|
2017-10-07 20:50:32 +03:00
|
|
|
}
|
2021-02-05 20:12:08 +03:00
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::focusMessageInput()
|
|
|
|
{
|
|
|
|
emit focusInput();
|
2021-02-14 03:28:28 +03:00
|
|
|
}
|
2021-02-21 20:40:21 +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;
|
2021-04-18 21:21:03 +03:00
|
|
|
} 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;
|
2021-02-21 20:40:21 +03:00
|
|
|
} else if (completerName == "room") {
|
2021-02-21 21:31:50 +03:00
|
|
|
auto roomModel = new RoomsModel(false);
|
2021-03-06 21:48:24 +03:00
|
|
|
auto proxy = new CompletionProxyModel(roomModel, 4);
|
2021-02-21 21:31:50 +03:00
|
|
|
roomModel->setParent(proxy);
|
|
|
|
return proxy;
|
|
|
|
} else if (completerName == "roomAliases") {
|
2021-02-21 20:40:21 +03:00
|
|
|
auto roomModel = new RoomsModel(true);
|
|
|
|
auto proxy = new CompletionProxyModel(roomModel);
|
|
|
|
roomModel->setParent(proxy);
|
|
|
|
return proxy;
|
|
|
|
}
|
|
|
|
return nullptr;
|
2021-02-22 22:16:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::focusTimeline()
|
|
|
|
{
|
|
|
|
getWidget()->setFocus();
|
2021-04-05 14:58:00 +03:00
|
|
|
}
|
2021-04-11 17:31:49 +03:00
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEvents *e,
|
2021-04-17 20:28:04 +03:00
|
|
|
QString roomId)
|
2021-04-11 17:31:49 +03:00
|
|
|
{
|
2021-05-19 20:34:10 +03:00
|
|
|
auto room = rooms->getRoomById(roomId);
|
2021-04-18 08:52:44 +03:00
|
|
|
auto content = mtx::accessors::url(*e);
|
|
|
|
std::optional<mtx::crypto::EncryptedFile> encryptionInfo = mtx::accessors::file(*e);
|
2021-04-17 19:58:17 +03:00
|
|
|
|
2021-04-17 20:28:04 +03:00
|
|
|
if (encryptionInfo) {
|
2021-04-17 19:58:17 +03:00
|
|
|
http::client()->download(
|
|
|
|
content,
|
|
|
|
[this, roomId, e, encryptionInfo](const std::string &res,
|
2021-04-17 20:28:04 +03:00
|
|
|
const std::string &content_type,
|
|
|
|
const std::string &originalFilename,
|
|
|
|
mtx::http::RequestErr err) {
|
2021-04-22 05:49:27 +03:00
|
|
|
if (err)
|
2021-04-17 19:58:17 +03:00
|
|
|
return;
|
2021-04-11 17:31:49 +03:00
|
|
|
|
2021-04-17 20:28:04 +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) {
|
2021-04-17 20:28:04 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-05-19 20:34:10 +03:00
|
|
|
if (auto room = rooms->getRoomById(roomId)) {
|
|
|
|
removeReplyFallback(ev);
|
|
|
|
ev.content.relations.relations
|
|
|
|
.clear();
|
|
|
|
room->sendMessageEvent(
|
|
|
|
ev.content,
|
|
|
|
mtx::events::EventType::
|
|
|
|
RoomMessage);
|
|
|
|
}
|
2021-04-17 20:28:04 +03:00
|
|
|
}
|
|
|
|
},
|
|
|
|
*e);
|
|
|
|
});
|
2021-04-17 19:58:17 +03:00
|
|
|
|
2021-04-17 20:28:04 +03:00
|
|
|
return;
|
2021-04-11 17:31:49 +03:00
|
|
|
});
|
2021-04-17 19:58:17 +03:00
|
|
|
|
2021-04-11 17:31:49 +03:00
|
|
|
return;
|
2021-04-17 19:58:17 +03:00
|
|
|
}
|
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) {
|
2021-04-17 19:58:17 +03:00
|
|
|
e.content.relations.relations.clear();
|
2021-04-18 08:52:44 +03:00
|
|
|
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
|
|
|
}
|
|
|
|
},
|
2021-04-18 08:52:44 +03:00
|
|
|
*e);
|
2021-04-27 12:33:46 +03:00
|
|
|
}
|