2019-11-09 05:06:10 +03:00
|
|
|
#include "TimelineViewManager.h"
|
|
|
|
|
2020-07-09 01:45:37 +03:00
|
|
|
#include <QDesktopServices>
|
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"
|
|
|
|
#include "ColorImageProvider.h"
|
|
|
|
#include "DelegateChooser.h"
|
2020-10-27 19:45:28 +03:00
|
|
|
#include "DeviceVerificationFlow.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"
|
|
|
|
#include "UserSettingsPage.h"
|
|
|
|
#include "dialogs/ImageOverlay.h"
|
2020-05-13 07:35:26 +03:00
|
|
|
#include "emoji/EmojiModel.h"
|
|
|
|
#include "emoji/Provider.h"
|
2017-04-06 02:06:42 +03:00
|
|
|
|
2020-07-29 00:55:47 +03:00
|
|
|
#include <iostream> //only for debugging
|
|
|
|
|
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;
|
|
|
|
|
2020-04-23 02:52:30 +03:00
|
|
|
void
|
|
|
|
TimelineViewManager::updateEncryptedDescriptions()
|
|
|
|
{
|
2020-10-17 01:57:29 +03:00
|
|
|
auto decrypt = ChatPage::instance()->userSettings()->decryptSidebar();
|
2020-04-23 02:52:30 +03:00
|
|
|
QHash<QString, QSharedPointer<TimelineModel>>::iterator i;
|
|
|
|
for (i = models.begin(); i != models.end(); ++i) {
|
|
|
|
auto ptr = i.value();
|
|
|
|
|
|
|
|
if (!ptr.isNull()) {
|
2020-04-24 02:21:20 +03:00
|
|
|
ptr->setDecryptDescription(decrypt);
|
|
|
|
ptr->updateLastMessage();
|
2020-04-23 02:52:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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)
|
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!");
|
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-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");
|
2020-10-08 18:26:07 +03:00
|
|
|
|
|
|
|
static auto self = this;
|
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 * {
|
|
|
|
return self;
|
|
|
|
});
|
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 * {
|
2020-10-17 01:57:29 +03:00
|
|
|
return ChatPage::instance()->userSettings().data();
|
2020-09-10 12:20:10 +03:00
|
|
|
});
|
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");
|
|
|
|
qmlRegisterType<emoji::EmojiProxyModel>("im.nheko.EmojiModel", 1, 0, "EmojiProxyModel");
|
|
|
|
qmlRegisterUncreatableType<QAbstractItemModel>(
|
|
|
|
"im.nheko.EmojiModel", 1, 0, "QAbstractItemModel", "Used by proxy models");
|
|
|
|
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
|
|
|
|
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
|
2020-06-19 20:34:03 +03:00
|
|
|
view->quickWindow()->setTextRenderType(QQuickWindow::NativeTextRendering);
|
2020-06-27 04:15:36 +03:00
|
|
|
#endif
|
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);
|
2019-11-09 05:06:10 +03:00
|
|
|
view->setSource(QUrl("qrc:///qml/TimelineView.qml"));
|
|
|
|
|
2020-09-03 18:01:58 +03:00
|
|
|
connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette);
|
|
|
|
connect(parent,
|
2020-04-23 02:52:30 +03:00
|
|
|
&ChatPage::decryptSidebarChanged,
|
|
|
|
this,
|
|
|
|
&TimelineViewManager::updateEncryptedDescriptions);
|
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);
|
|
|
|
});
|
2020-09-22 19:14:15 +03:00
|
|
|
connect(&WebRTCSession::instance(),
|
|
|
|
&WebRTCSession::stateChanged,
|
|
|
|
this,
|
|
|
|
&TimelineViewManager::callStateChanged);
|
2020-09-25 17:26:36 +03:00
|
|
|
connect(
|
|
|
|
callManager_, &CallManager::newCallParty, this, &TimelineViewManager::callPartyChanged);
|
2017-11-15 19:38:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-11-09 05:06:10 +03:00
|
|
|
TimelineViewManager::sync(const mtx::responses::Rooms &rooms)
|
2017-04-13 04:11:22 +03:00
|
|
|
{
|
2020-01-17 03:25:14 +03:00
|
|
|
for (const auto &[room_id, room] : rooms.join) {
|
2019-11-09 05:06:10 +03:00
|
|
|
// addRoom will only add the room, if it doesn't exist
|
2020-01-17 03:25:14 +03:00
|
|
|
addRoom(QString::fromStdString(room_id));
|
|
|
|
const auto &room_model = models.value(QString::fromStdString(room_id));
|
2020-07-11 02:19:48 +03:00
|
|
|
if (!isInitialSync_)
|
|
|
|
connect(room_model.data(),
|
|
|
|
&TimelineModel::newCallEvent,
|
|
|
|
callManager_,
|
|
|
|
&CallManager::syncEvent);
|
2020-09-03 18:01:58 +03:00
|
|
|
room_model->syncState(room.state);
|
2020-01-17 03:25:14 +03:00
|
|
|
room_model->addEvents(room.timeline);
|
2020-07-11 02:19:48 +03:00
|
|
|
if (!isInitialSync_)
|
|
|
|
disconnect(room_model.data(),
|
|
|
|
&TimelineModel::newCallEvent,
|
|
|
|
callManager_,
|
|
|
|
&CallManager::syncEvent);
|
2020-01-17 03:25:14 +03:00
|
|
|
|
2020-05-26 23:53:21 +03:00
|
|
|
if (ChatPage::instance()->userSettings()->typingNotifications()) {
|
2020-01-17 03:25:14 +03:00
|
|
|
std::vector<QString> typing;
|
|
|
|
typing.reserve(room.ephemeral.typing.size());
|
|
|
|
for (const auto &user : room.ephemeral.typing) {
|
|
|
|
if (user != http::client()->user_id().to_string())
|
|
|
|
typing.push_back(QString::fromStdString(user));
|
|
|
|
}
|
|
|
|
room_model->updateTypingUsers(typing);
|
|
|
|
}
|
2019-11-09 05:06:10 +03:00
|
|
|
}
|
2019-11-10 02:30:02 +03:00
|
|
|
|
|
|
|
this->isInitialSync_ = false;
|
|
|
|
emit initialSyncChanged(false);
|
2017-09-03 11:43:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-11-09 05:06:10 +03:00
|
|
|
TimelineViewManager::addRoom(const QString &room_id)
|
2017-09-03 11:43:45 +03:00
|
|
|
{
|
2019-12-04 01:34:16 +03:00
|
|
|
if (!models.contains(room_id)) {
|
|
|
|
QSharedPointer<TimelineModel> newRoom(new TimelineModel(this, room_id));
|
2020-10-17 01:57:29 +03:00
|
|
|
newRoom->setDecryptDescription(
|
|
|
|
ChatPage::instance()->userSettings()->decryptSidebar());
|
2020-04-24 02:21:20 +03:00
|
|
|
|
2019-12-04 01:34:16 +03:00
|
|
|
connect(newRoom.data(),
|
|
|
|
&TimelineModel::newEncryptedImage,
|
|
|
|
imgProvider,
|
|
|
|
&MxcImageProvider::addEncryptionInfo);
|
|
|
|
models.insert(room_id, std::move(newRoom));
|
|
|
|
}
|
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
|
|
|
|
2019-11-09 05:06:10 +03:00
|
|
|
auto room = models.find(room_id);
|
|
|
|
if (room != models.end()) {
|
|
|
|
timeline_ = room.value().data();
|
|
|
|
emit activeTimelineChanged(timeline_);
|
|
|
|
nhlog::ui()->info("Activated room {}", room_id.toStdString());
|
|
|
|
}
|
2019-06-10 02:03:18 +03:00
|
|
|
}
|
|
|
|
|
2020-09-03 20:51:50 +03:00
|
|
|
QString
|
|
|
|
TimelineViewManager::escapeEmoji(QString str) const
|
|
|
|
{
|
|
|
|
return utils::replaceEmoji(str);
|
|
|
|
}
|
|
|
|
|
2020-09-25 17:26:36 +03:00
|
|
|
void
|
|
|
|
TimelineViewManager::toggleMicMute()
|
|
|
|
{
|
|
|
|
WebRTCSession::instance().toggleMicMute();
|
|
|
|
emit micMuteChanged();
|
|
|
|
}
|
|
|
|
|
2017-09-10 12:58:00 +03:00
|
|
|
void
|
2019-12-03 04:26:41 +03:00
|
|
|
TimelineViewManager::openImageOverlay(QString mxcUrl, QString eventId) const
|
2017-09-10 12:58:00 +03:00
|
|
|
{
|
2019-11-09 05:06:10 +03:00
|
|
|
QQuickImageResponse *imgResponse =
|
|
|
|
imgProvider->requestImageResponse(mxcUrl.remove("mxc://"), QSize());
|
2019-12-03 04:26:41 +03:00
|
|
|
connect(imgResponse, &QQuickImageResponse::finished, this, [this, eventId, 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());
|
2019-11-09 05:06:10 +03:00
|
|
|
|
2019-12-03 04:26:41 +03:00
|
|
|
auto imgDialog = new dialogs::ImageOverlay(pixmap);
|
2020-02-13 17:11:15 +03:00
|
|
|
imgDialog->showFullScreen();
|
2020-03-20 16:05:12 +03:00
|
|
|
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-07-09 01:45:37 +03:00
|
|
|
void
|
|
|
|
TimelineViewManager::openLink(QString link) const
|
|
|
|
{
|
|
|
|
QDesktopServices::openUrl(link);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
void
|
|
|
|
TimelineViewManager::openRoomSettings() const
|
|
|
|
{
|
2020-09-03 20:34:17 +03:00
|
|
|
MainWindow::instance()->openRoomSettings(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()) {
|
|
|
|
auto model = models.value(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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
emit ChatPage::instance()->showNotification(
|
|
|
|
tr("No share room with this user found. Create an "
|
|
|
|
"encrypted room 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) {
|
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
|
|
|
{
|
2019-11-09 05:06:10 +03:00
|
|
|
auto room = models.find(room_id);
|
|
|
|
if (room != models.end()) {
|
|
|
|
room.value()->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)
|
|
|
|
{
|
|
|
|
auto room = models.find(QString::fromStdString(room_id));
|
|
|
|
if (room != models.end()) {
|
|
|
|
room.value()->receivedSessionKey(session_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-28 16:16:43 +03:00
|
|
|
void
|
|
|
|
TimelineViewManager::initWithMessages(const std::map<QString, mtx::responses::Timeline> &msgs)
|
|
|
|
{
|
2019-11-09 05:06:10 +03:00
|
|
|
for (const auto &e : msgs) {
|
|
|
|
addRoom(e.first);
|
2018-06-28 16:16:43 +03:00
|
|
|
|
2019-11-09 05:06:10 +03:00
|
|
|
models.value(e.first)->addEvents(e.second);
|
2018-06-28 16:16:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
2020-04-13 17:22:30 +03:00
|
|
|
TimelineViewManager::queueTextMessage(const QString &msg)
|
2017-07-29 11:49:00 +03:00
|
|
|
{
|
2020-04-13 17:22:30 +03:00
|
|
|
if (!timeline_)
|
|
|
|
return;
|
|
|
|
|
2019-11-09 05:06:10 +03:00
|
|
|
mtx::events::msg::Text text = {};
|
|
|
|
text.body = msg.trimmed().toStdString();
|
2020-01-27 19:05:40 +03:00
|
|
|
|
2020-10-17 01:57:29 +03:00
|
|
|
if (ChatPage::instance()->userSettings()->markdown()) {
|
2020-01-27 19:05:40 +03:00
|
|
|
text.formatted_body = utils::markdownToHtml(msg).toStdString();
|
2020-01-27 19:25:09 +03:00
|
|
|
|
|
|
|
// Don't send formatted_body, when we don't need to
|
2020-02-20 19:09:49 +03:00
|
|
|
if (text.formatted_body.find("<") == std::string::npos)
|
2020-01-27 19:25:09 +03:00
|
|
|
text.formatted_body = "";
|
|
|
|
else
|
|
|
|
text.format = "org.matrix.custom.html";
|
2020-01-27 19:05:40 +03:00
|
|
|
}
|
2019-11-09 05:06:10 +03:00
|
|
|
|
2020-04-13 17:22:30 +03:00
|
|
|
if (!timeline_->reply().isEmpty()) {
|
|
|
|
auto related = timeline_->relatedInfo(timeline_->reply());
|
|
|
|
|
2020-01-12 18:39:01 +03:00
|
|
|
QString body;
|
|
|
|
bool firstLine = true;
|
2020-04-13 17:22:30 +03:00
|
|
|
for (const auto &line : related.quoted_body.split("\n")) {
|
2020-01-12 18:39:01 +03:00
|
|
|
if (firstLine) {
|
|
|
|
firstLine = false;
|
2020-04-13 17:22:30 +03:00
|
|
|
body = QString("> <%1> %2\n").arg(related.quoted_user).arg(line);
|
2020-01-12 18:39:01 +03:00
|
|
|
} else {
|
|
|
|
body = QString("%1\n> %2\n").arg(body).arg(line);
|
|
|
|
}
|
2019-11-09 05:06:10 +03:00
|
|
|
}
|
2017-08-26 11:33:26 +03:00
|
|
|
|
2020-01-12 18:39:01 +03:00
|
|
|
text.body = QString("%1\n%2").arg(body).arg(msg).toStdString();
|
2017-08-26 11:33:26 +03:00
|
|
|
|
2020-01-27 19:05:40 +03:00
|
|
|
// NOTE(Nico): rich replies always need a formatted_body!
|
|
|
|
text.format = "org.matrix.custom.html";
|
2020-10-17 01:57:29 +03:00
|
|
|
if (ChatPage::instance()->userSettings()->markdown())
|
2020-01-27 19:05:40 +03:00
|
|
|
text.formatted_body =
|
2020-04-13 17:22:30 +03:00
|
|
|
utils::getFormattedQuoteBody(related, utils::markdownToHtml(msg))
|
2020-01-27 19:05:40 +03:00
|
|
|
.toStdString();
|
|
|
|
else
|
|
|
|
text.formatted_body =
|
2020-04-13 17:22:30 +03:00
|
|
|
utils::getFormattedQuoteBody(related, msg.toHtmlEscaped()).toStdString();
|
2020-01-27 19:05:40 +03:00
|
|
|
|
2020-04-13 17:22:30 +03:00
|
|
|
text.relates_to.in_reply_to.event_id = related.related_event;
|
|
|
|
timeline_->resetReply();
|
2020-01-12 18:39:01 +03:00
|
|
|
}
|
2017-08-26 11:33:26 +03:00
|
|
|
|
2020-07-11 02:19:48 +03:00
|
|
|
timeline_->sendMessageEvent(text, mtx::events::EventType::RoomMessage);
|
2017-10-01 19:49:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-11-09 05:06:10 +03:00
|
|
|
TimelineViewManager::queueEmoteMessage(const QString &msg)
|
2017-10-01 19:49:36 +03:00
|
|
|
{
|
2019-11-09 05:06:10 +03:00
|
|
|
auto html = utils::markdownToHtml(msg);
|
2018-04-21 16:34:50 +03:00
|
|
|
|
2019-11-09 05:06:10 +03:00
|
|
|
mtx::events::msg::Emote emote;
|
|
|
|
emote.body = msg.trimmed().toStdString();
|
2017-10-01 19:49:36 +03:00
|
|
|
|
2020-10-17 01:57:29 +03:00
|
|
|
if (html != msg.trimmed().toHtmlEscaped() &&
|
|
|
|
ChatPage::instance()->userSettings()->markdown()) {
|
2019-11-09 05:06:10 +03:00
|
|
|
emote.formatted_body = html.toStdString();
|
2020-01-27 17:59:25 +03:00
|
|
|
emote.format = "org.matrix.custom.html";
|
|
|
|
}
|
2017-10-01 19:49:36 +03:00
|
|
|
|
2020-04-13 17:22:30 +03:00
|
|
|
if (!timeline_->reply().isEmpty()) {
|
|
|
|
emote.relates_to.in_reply_to.event_id = timeline_->reply().toStdString();
|
|
|
|
timeline_->resetReply();
|
|
|
|
}
|
|
|
|
|
2019-11-09 05:06:10 +03:00
|
|
|
if (timeline_)
|
2020-07-11 02:19:48 +03:00
|
|
|
timeline_->sendMessageEvent(emote, mtx::events::EventType::RoomMessage);
|
2017-07-29 11:49:00 +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;
|
|
|
|
reaction.relates_to.rel_type = mtx::common::RelationType::Annotation;
|
|
|
|
reaction.relates_to.event_id = reactedEvent.toStdString();
|
|
|
|
reaction.relates_to.key = reactionKey.toStdString();
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
2019-11-09 05:06:10 +03:00
|
|
|
TimelineViewManager::queueImageMessage(const QString &roomid,
|
|
|
|
const QString &filename,
|
2019-12-14 19:08:36 +03:00
|
|
|
const std::optional<mtx::crypto::EncryptedFile> &file,
|
2019-11-09 05:06:10 +03:00
|
|
|
const QString &url,
|
|
|
|
const QString &mime,
|
|
|
|
uint64_t dsize,
|
2020-01-12 18:39:01 +03:00
|
|
|
const QSize &dimensions,
|
2020-04-13 17:22:30 +03:00
|
|
|
const QString &blurhash)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
2019-11-09 05:06:10 +03:00
|
|
|
mtx::events::msg::Image image;
|
|
|
|
image.info.mimetype = mime.toStdString();
|
|
|
|
image.info.size = dsize;
|
2020-03-01 21:55:43 +03:00
|
|
|
image.info.blurhash = blurhash.toStdString();
|
2019-11-09 05:06:10 +03:00
|
|
|
image.body = filename.toStdString();
|
|
|
|
image.info.h = dimensions.height();
|
|
|
|
image.info.w = dimensions.width();
|
2020-07-23 15:33:04 +03:00
|
|
|
|
|
|
|
if (file)
|
|
|
|
image.file = file;
|
|
|
|
else
|
|
|
|
image.url = url.toStdString();
|
2020-01-12 18:39:01 +03:00
|
|
|
|
2020-04-13 17:22:30 +03:00
|
|
|
auto model = models.value(roomid);
|
|
|
|
if (!model->reply().isEmpty()) {
|
|
|
|
image.relates_to.in_reply_to.event_id = model->reply().toStdString();
|
|
|
|
model->resetReply();
|
|
|
|
}
|
2020-01-12 18:39:01 +03:00
|
|
|
|
2020-07-11 02:19:48 +03:00
|
|
|
model->sendMessageEvent(image, mtx::events::EventType::RoomMessage);
|
2017-04-06 02:06:42 +03:00
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
2019-12-05 17:31:53 +03:00
|
|
|
TimelineViewManager::queueFileMessage(
|
|
|
|
const QString &roomid,
|
|
|
|
const QString &filename,
|
2019-12-14 19:08:36 +03:00
|
|
|
const std::optional<mtx::crypto::EncryptedFile> &encryptedFile,
|
2019-12-05 17:31:53 +03:00
|
|
|
const QString &url,
|
|
|
|
const QString &mime,
|
2020-04-13 17:22:30 +03:00
|
|
|
uint64_t dsize)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
2019-11-09 05:06:10 +03:00
|
|
|
mtx::events::msg::File file;
|
|
|
|
file.info.mimetype = mime.toStdString();
|
|
|
|
file.info.size = dsize;
|
|
|
|
file.body = filename.toStdString();
|
2020-07-23 15:33:04 +03:00
|
|
|
|
|
|
|
if (encryptedFile)
|
|
|
|
file.file = encryptedFile;
|
|
|
|
else
|
|
|
|
file.url = url.toStdString();
|
2020-01-12 18:39:01 +03:00
|
|
|
|
2020-04-13 17:22:30 +03:00
|
|
|
auto model = models.value(roomid);
|
|
|
|
if (!model->reply().isEmpty()) {
|
|
|
|
file.relates_to.in_reply_to.event_id = model->reply().toStdString();
|
|
|
|
model->resetReply();
|
|
|
|
}
|
2020-01-12 18:39:01 +03:00
|
|
|
|
2020-07-11 02:19:48 +03:00
|
|
|
model->sendMessageEvent(file, mtx::events::EventType::RoomMessage);
|
2017-04-06 02:06:42 +03:00
|
|
|
}
|
2017-04-11 22:48:02 +03:00
|
|
|
|
2019-11-09 05:06:10 +03:00
|
|
|
void
|
|
|
|
TimelineViewManager::queueAudioMessage(const QString &roomid,
|
|
|
|
const QString &filename,
|
2019-12-14 19:08:36 +03:00
|
|
|
const std::optional<mtx::crypto::EncryptedFile> &file,
|
2019-11-09 05:06:10 +03:00
|
|
|
const QString &url,
|
|
|
|
const QString &mime,
|
2020-04-13 17:22:30 +03:00
|
|
|
uint64_t dsize)
|
2017-04-11 22:48:02 +03:00
|
|
|
{
|
2019-11-09 05:06:10 +03:00
|
|
|
mtx::events::msg::Audio audio;
|
|
|
|
audio.info.mimetype = mime.toStdString();
|
|
|
|
audio.info.size = dsize;
|
|
|
|
audio.body = filename.toStdString();
|
|
|
|
audio.url = url.toStdString();
|
2020-07-23 15:33:04 +03:00
|
|
|
|
|
|
|
if (file)
|
|
|
|
audio.file = file;
|
|
|
|
else
|
|
|
|
audio.url = url.toStdString();
|
2020-01-12 18:39:01 +03:00
|
|
|
|
2020-04-13 17:22:30 +03:00
|
|
|
auto model = models.value(roomid);
|
|
|
|
if (!model->reply().isEmpty()) {
|
|
|
|
audio.relates_to.in_reply_to.event_id = model->reply().toStdString();
|
|
|
|
model->resetReply();
|
|
|
|
}
|
2020-01-12 18:39:01 +03:00
|
|
|
|
2020-07-11 02:19:48 +03:00
|
|
|
model->sendMessageEvent(audio, mtx::events::EventType::RoomMessage);
|
2017-04-13 04:11:22 +03:00
|
|
|
}
|
2017-05-08 19:44:01 +03:00
|
|
|
|
2019-11-09 05:06:10 +03:00
|
|
|
void
|
|
|
|
TimelineViewManager::queueVideoMessage(const QString &roomid,
|
|
|
|
const QString &filename,
|
2019-12-14 19:08:36 +03:00
|
|
|
const std::optional<mtx::crypto::EncryptedFile> &file,
|
2019-11-09 05:06:10 +03:00
|
|
|
const QString &url,
|
|
|
|
const QString &mime,
|
2020-04-13 17:22:30 +03:00
|
|
|
uint64_t dsize)
|
2017-10-07 20:50:32 +03:00
|
|
|
{
|
2019-11-09 05:06:10 +03:00
|
|
|
mtx::events::msg::Video video;
|
|
|
|
video.info.mimetype = mime.toStdString();
|
|
|
|
video.info.size = dsize;
|
|
|
|
video.body = filename.toStdString();
|
2020-07-23 15:33:04 +03:00
|
|
|
|
|
|
|
if (file)
|
|
|
|
video.file = file;
|
|
|
|
else
|
|
|
|
video.url = url.toStdString();
|
2020-01-12 18:39:01 +03:00
|
|
|
|
2020-04-13 17:22:30 +03:00
|
|
|
auto model = models.value(roomid);
|
|
|
|
if (!model->reply().isEmpty()) {
|
|
|
|
video.relates_to.in_reply_to.event_id = model->reply().toStdString();
|
|
|
|
model->resetReply();
|
|
|
|
}
|
2020-01-12 18:39:01 +03:00
|
|
|
|
2020-07-11 02:19:48 +03:00
|
|
|
model->sendMessageEvent(video, mtx::events::EventType::RoomMessage);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::queueCallMessage(const QString &roomid,
|
|
|
|
const mtx::events::msg::CallInvite &callInvite)
|
|
|
|
{
|
|
|
|
models.value(roomid)->sendMessageEvent(callInvite, mtx::events::EventType::CallInvite);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::queueCallMessage(const QString &roomid,
|
|
|
|
const mtx::events::msg::CallCandidates &callCandidates)
|
|
|
|
{
|
|
|
|
models.value(roomid)->sendMessageEvent(callCandidates,
|
|
|
|
mtx::events::EventType::CallCandidates);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::queueCallMessage(const QString &roomid,
|
|
|
|
const mtx::events::msg::CallAnswer &callAnswer)
|
|
|
|
{
|
|
|
|
models.value(roomid)->sendMessageEvent(callAnswer, mtx::events::EventType::CallAnswer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimelineViewManager::queueCallMessage(const QString &roomid,
|
|
|
|
const mtx::events::msg::CallHangUp &callHangUp)
|
|
|
|
{
|
|
|
|
models.value(roomid)->sendMessageEvent(callHangUp, mtx::events::EventType::CallHangUp);
|
2017-10-07 20:50:32 +03:00
|
|
|
}
|