diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp
new file mode 100644
index 00000000..6a1fc3c5
--- /dev/null
+++ b/src/timeline/RoomlistModel.cpp
@@ -0,0 +1,146 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "RoomlistModel.h"
+
+#include "ChatPage.h"
+#include "MatrixClient.h"
+#include "MxcImageProvider.h"
+#include "TimelineModel.h"
+#include "TimelineViewManager.h"
+#include "UserSettingsPage.h"
+
+RoomlistModel::RoomlistModel(TimelineViewManager *parent)
+ : manager(parent)
+{
+ connect(ChatPage::instance(), &ChatPage::decryptSidebarChanged, this, [this]() {
+ auto decrypt = ChatPage::instance()->userSettings()->decryptSidebar();
+ QHash<QString, QSharedPointer<TimelineModel>>::iterator i;
+ for (i = models.begin(); i != models.end(); ++i) {
+ auto ptr = i.value();
+
+ if (!ptr.isNull()) {
+ ptr->setDecryptDescription(decrypt);
+ ptr->updateLastMessage();
+ }
+ }
+ });
+}
+
+QHash<int, QByteArray>
+RoomlistModel::roleNames() const
+{
+ return {
+ {AvatarUrl, "avatarUrl"},
+ {RoomName, "roomName"},
+ {LastMessage, "lastMessage"},
+ {HasUnreadMessages, "hasUnreadMessages"},
+ {NotificationCount, "notificationCount"},
+ };
+}
+
+QVariant
+RoomlistModel::data(const QModelIndex &index, int role) const
+{
+ if (index.row() >= 0 && static_cast<size_t>(index.row()) < roomids.size()) {
+ auto room = models.value(roomids.at(index.row()));
+ switch (role) {
+ case Roles::AvatarUrl:
+ return room->roomAvatarUrl();
+ case Roles::RoomName:
+ return room->roomName();
+ case Roles::LastMessage:
+ return QString("Nico: Hahaha, this is funny!");
+ case Roles::HasUnreadMessages:
+ return true;
+ case Roles::NotificationCount:
+ return 5;
+ default:
+ return {};
+ }
+ } else {
+ return {};
+ }
+}
+
+void
+RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification)
+{
+ if (!models.contains(room_id)) {
+ QSharedPointer<TimelineModel> newRoom(new TimelineModel(manager, room_id));
+ newRoom->setDecryptDescription(
+ ChatPage::instance()->userSettings()->decryptSidebar());
+
+ connect(newRoom.data(),
+ &TimelineModel::newEncryptedImage,
+ manager->imageProvider(),
+ &MxcImageProvider::addEncryptionInfo);
+ connect(newRoom.data(),
+ &TimelineModel::forwardToRoom,
+ manager,
+ &TimelineViewManager::forwardMessageToRoom);
+
+ if (!suppressInsertNotification)
+ beginInsertRows(QModelIndex(), (int)roomids.size(), (int)roomids.size());
+ models.insert(room_id, std::move(newRoom));
+ roomids.push_back(room_id);
+ if (!suppressInsertNotification)
+ endInsertRows();
+ }
+}
+
+void
+RoomlistModel::sync(const mtx::responses::Rooms &rooms)
+{
+ for (const auto &[room_id, room] : rooms.join) {
+ // addRoom will only add the room, if it doesn't exist
+ addRoom(QString::fromStdString(room_id));
+ const auto &room_model = models.value(QString::fromStdString(room_id));
+ room_model->syncState(room.state);
+ room_model->addEvents(room.timeline);
+ connect(room_model.data(),
+ &TimelineModel::newCallEvent,
+ manager->callManager(),
+ &CallManager::syncEvent,
+ Qt::UniqueConnection);
+
+ if (ChatPage::instance()->userSettings()->typingNotifications()) {
+ for (const auto &ev : room.ephemeral.events) {
+ if (auto t = std::get_if<
+ mtx::events::EphemeralEvent<mtx::events::ephemeral::Typing>>(
+ &ev)) {
+ std::vector<QString> typing;
+ typing.reserve(t->content.user_ids.size());
+ for (const auto &user : t->content.user_ids) {
+ if (user != http::client()->user_id().to_string())
+ typing.push_back(
+ QString::fromStdString(user));
+ }
+ room_model->updateTypingUsers(typing);
+ }
+ }
+ }
+ }
+}
+
+void
+RoomlistModel::initializeRooms(const std::vector<QString> &roomIds_)
+{
+ beginResetModel();
+ models.clear();
+ roomids.clear();
+ roomids = roomIds_;
+ for (const auto &id : roomIds_)
+ addRoom(id, true);
+ endResetModel();
+}
+
+void
+RoomlistModel::clear()
+{
+ beginResetModel();
+ models.clear();
+ roomids.clear();
+ endResetModel();
+}
diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h
new file mode 100644
index 00000000..44fcf032
--- /dev/null
+++ b/src/timeline/RoomlistModel.h
@@ -0,0 +1,58 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <QAbstractListModel>
+#include <QHash>
+#include <QSharedPointer>
+#include <QString>
+
+#include <mtx/responses/sync.hpp>
+
+class TimelineModel;
+class TimelineViewManager;
+
+class RoomlistModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ enum Roles
+ {
+ AvatarUrl = Qt::UserRole,
+ RoomName,
+ LastMessage,
+ HasUnreadMessages,
+ NotificationCount,
+ };
+
+ RoomlistModel(TimelineViewManager *parent = nullptr);
+ QHash<int, QByteArray> roleNames() const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override
+ {
+ (void)parent;
+ return (int)roomids.size();
+ }
+ QVariant data(const QModelIndex &index, int role) const override;
+ QSharedPointer<TimelineModel> getRoomById(QString id) const
+ {
+ if (models.contains(id))
+ return models.value(id);
+ else
+ return {};
+ }
+
+public slots:
+ void initializeRooms(const std::vector<QString> &roomids);
+ void sync(const mtx::responses::Rooms &rooms);
+ void clear();
+
+private:
+ void addRoom(const QString &room_id, bool suppressInsertNotification = false);
+
+ TimelineViewManager *manager = nullptr;
+ std::vector<QString> roomids;
+ QHash<QString, QSharedPointer<TimelineModel>> models;
+};
+
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index e8e57fd8..b0c13b03 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -87,21 +87,6 @@ removeReplyFallback(mtx::events::Event<T> &e)
}
void
-TimelineViewManager::updateEncryptedDescriptions()
-{
- auto decrypt = ChatPage::instance()->userSettings()->decryptSidebar();
- QHash<QString, QSharedPointer<TimelineModel>>::iterator i;
- for (i = models.begin(); i != models.end(); ++i) {
- auto ptr = i.value();
-
- if (!ptr.isNull()) {
- ptr->setDecryptDescription(decrypt);
- ptr->updateLastMessage();
- }
- }
-}
-
-void
TimelineViewManager::updateColorPalette()
{
userColors.clear();
@@ -148,6 +133,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
, colorImgProvider(new ColorImageProvider())
, blurhashProvider(new BlurhashProvider())
, callManager_(callManager)
+ , rooms(new RoomlistModel(this))
{
qRegisterMetaType<mtx::events::msg::KeyVerificationAccept>();
qRegisterMetaType<mtx::events::msg::KeyVerificationCancel>();
@@ -205,6 +191,12 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
QQmlEngine::setObjectOwnership(ptr, QQmlEngine::CppOwnership);
return ptr;
});
+ qmlRegisterSingletonType<RoomlistModel>(
+ "im.nheko", 1, 0, "Rooms", [](QQmlEngine *, QJSEngine *) -> QObject * {
+ auto ptr = self->rooms;
+ QQmlEngine::setObjectOwnership(ptr, QQmlEngine::CppOwnership);
+ return ptr;
+ });
qmlRegisterSingletonType<UserSettings>(
"im.nheko", 1, 0, "Settings", [](QQmlEngine *, QJSEngine *) -> QObject * {
auto ptr = ChatPage::instance()->userSettings().data();
@@ -260,10 +252,6 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
view->setSource(QUrl("qrc:///qml/Root.qml"));
connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette);
- connect(parent,
- &ChatPage::decryptSidebarChanged,
- this,
- &TimelineViewManager::updateEncryptedDescriptions);
connect(
dynamic_cast<ChatPage *>(parent),
&ChatPage::receivedRoomDeviceVerificationRequest,
@@ -334,64 +322,13 @@ TimelineViewManager::setVideoCallItem()
}
void
-TimelineViewManager::sync(const mtx::responses::Rooms &rooms)
-{
- for (const auto &[room_id, room] : rooms.join) {
- // addRoom will only add the room, if it doesn't exist
- addRoom(QString::fromStdString(room_id));
- const auto &room_model = models.value(QString::fromStdString(room_id));
- if (!isInitialSync_)
- connect(room_model.data(),
- &TimelineModel::newCallEvent,
- callManager_,
- &CallManager::syncEvent);
- room_model->syncState(room.state);
- room_model->addEvents(room.timeline);
- if (!isInitialSync_)
- disconnect(room_model.data(),
- &TimelineModel::newCallEvent,
- callManager_,
- &CallManager::syncEvent);
-
- if (ChatPage::instance()->userSettings()->typingNotifications()) {
- for (const auto &ev : room.ephemeral.events) {
- if (auto t = std::get_if<
- mtx::events::EphemeralEvent<mtx::events::ephemeral::Typing>>(
- &ev)) {
- std::vector<QString> typing;
- typing.reserve(t->content.user_ids.size());
- for (const auto &user : t->content.user_ids) {
- if (user != http::client()->user_id().to_string())
- typing.push_back(
- QString::fromStdString(user));
- }
- room_model->updateTypingUsers(typing);
- }
- }
- }
- }
-
- this->isInitialSync_ = false;
- emit initialSyncChanged(false);
-}
+TimelineViewManager::sync(const mtx::responses::Rooms &rooms_)
+{
+ this->rooms->sync(rooms_);
-void
-TimelineViewManager::addRoom(const QString &room_id)
-{
- if (!models.contains(room_id)) {
- QSharedPointer<TimelineModel> newRoom(new TimelineModel(this, room_id));
- newRoom->setDecryptDescription(
- ChatPage::instance()->userSettings()->decryptSidebar());
-
- connect(newRoom.data(),
- &TimelineModel::newEncryptedImage,
- imgProvider,
- &MxcImageProvider::addEncryptionInfo);
- connect(newRoom.data(),
- &TimelineModel::forwardToRoom,
- this,
- &TimelineViewManager::forwardMessageToRoom);
- models.insert(room_id, std::move(newRoom));
+ if (isInitialSync_) {
+ this->isInitialSync_ = false;
+ emit initialSyncChanged(false);
}
}
@@ -400,9 +337,8 @@ 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()) {
- timeline_ = room.value().data();
+ if (auto room = rooms->getRoomById(room_id)) {
+ timeline_ = room.get();
emit activeTimelineChanged(timeline_);
container->setFocus();
nhlog::ui()->info("Activated room {}", room_id.toStdString());
@@ -418,10 +354,9 @@ TimelineViewManager::highlightRoom(const QString &room_id)
void
TimelineViewManager::showEvent(const QString &room_id, const QString &event_id)
{
- auto room = models.find(room_id);
- if (room != models.end()) {
- if (timeline_ != room.value().data()) {
- timeline_ = room.value().data();
+ if (auto room = rooms->getRoomById(room_id)) {
+ if (timeline_ != room) {
+ timeline_ = room.get();
emit activeTimelineChanged(timeline_);
container->setFocus();
nhlog::ui()->info("Activated room {}", room_id.toStdString());
@@ -505,17 +440,21 @@ TimelineViewManager::verifyUser(QString userid)
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;
+ 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;
+ }
}
}
}
@@ -548,26 +487,23 @@ 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);
+ if (auto room = rooms->getRoomById(room_id)) {
+ room->markEventsAsRead(event_ids);
}
}
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);
+ if (auto room = rooms->getRoomById(QString::fromStdString(room_id))) {
+ room->receivedSessionKey(session_id);
}
}
void
TimelineViewManager::initWithMessages(const std::vector<QString> &roomIds)
{
- for (const auto &roomId : roomIds)
- addRoom(roomId);
+ rooms->initializeRooms(roomIds);
}
void
@@ -575,10 +511,9 @@ TimelineViewManager::queueReply(const QString &roomid,
const QString &repliedToEvent,
const QString &replyBody)
{
- auto room = models.find(roomid);
- if (room != models.end()) {
- room.value()->setReply(repliedToEvent);
- room.value()->input()->message(replyBody);
+ if (auto room = rooms->getRoomById(roomid)) {
+ room->setReply(repliedToEvent);
+ room->input()->message(replyBody);
}
}
@@ -620,29 +555,32 @@ void
TimelineViewManager::queueCallMessage(const QString &roomid,
const mtx::events::msg::CallInvite &callInvite)
{
- models.value(roomid)->sendMessageEvent(callInvite, mtx::events::EventType::CallInvite);
+ if (auto room = rooms->getRoomById(roomid))
+ room->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);
+ if (auto room = rooms->getRoomById(roomid))
+ room->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);
+ if (auto room = rooms->getRoomById(roomid))
+ room->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);
+ if (auto room = rooms->getRoomById(roomid))
+ room->sendMessageEvent(callHangUp, mtx::events::EventType::CallHangUp);
}
void
@@ -693,7 +631,7 @@ void
TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEvents *e,
QString roomId)
{
- auto room = models.find(roomId);
+ auto room = rooms->getRoomById(roomId);
auto content = mtx::accessors::url(*e);
std::optional<mtx::crypto::EncryptedFile> encryptionInfo = mtx::accessors::file(*e);
@@ -736,12 +674,15 @@ TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEven
ev.content.url = url;
}
- auto room = models.find(roomId);
- removeReplyFallback(ev);
- ev.content.relations.relations.clear();
- room.value()->sendMessageEvent(
- ev.content,
- mtx::events::EventType::RoomMessage);
+ if (auto room = rooms->getRoomById(roomId)) {
+ removeReplyFallback(ev);
+ ev.content.relations.relations
+ .clear();
+ room->sendMessageEvent(
+ ev.content,
+ mtx::events::EventType::
+ RoomMessage);
+ }
}
},
*e);
@@ -759,8 +700,7 @@ TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEven
mtx::events::EventType::RoomMessage) {
e.content.relations.relations.clear();
removeReplyFallback(e);
- room.value()->sendMessageEvent(e.content,
- mtx::events::EventType::RoomMessage);
+ room->sendMessageEvent(e.content, mtx::events::EventType::RoomMessage);
}
},
*e);
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 0665b663..f4297243 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -22,6 +22,7 @@
#include "WebRTCSession.h"
#include "emoji/EmojiModel.h"
#include "emoji/Provider.h"
+#include "timeline/RoomlistModel.h"
class MxcImageProvider;
class BlurhashProvider;
@@ -48,13 +49,15 @@ public:
QWidget *getWidget() const { return container; }
void sync(const mtx::responses::Rooms &rooms);
- void addRoom(const QString &room_id);
+
+ MxcImageProvider *imageProvider() { return imgProvider; }
+ CallManager *callManager() { return callManager_; }
void clearAll()
{
timeline_ = nullptr;
emit activeTimelineChanged(nullptr);
- models.clear();
+ rooms->clear();
}
Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; }
@@ -109,11 +112,7 @@ public slots:
void focusTimeline();
TimelineModel *getHistoryView(const QString &room_id)
{
- auto room = models.find(room_id);
- if (room != models.end())
- return room.value().data();
- else
- return nullptr;
+ return rooms->getRoomById(room_id).get();
}
void updateColorPalette();
@@ -126,7 +125,6 @@ public slots:
void queueCallMessage(const QString &roomid, const mtx::events::msg::CallAnswer &);
void queueCallMessage(const QString &roomid, const mtx::events::msg::CallHangUp &);
- void updateEncryptedDescriptions();
void setVideoCallItem();
void enableBackButton()
@@ -163,7 +161,6 @@ private:
ColorImageProvider *colorImgProvider;
BlurhashProvider *blurhashProvider;
- QHash<QString, QSharedPointer<TimelineModel>> models;
TimelineModel *timeline_ = nullptr;
CallManager *callManager_ = nullptr;
@@ -171,6 +168,8 @@ private:
bool isNarrowView_ = false;
bool isWindowFocused_ = false;
+ RoomlistModel *rooms = nullptr;
+
QHash<QString, QColor> userColors;
QHash<QString, QSharedPointer<DeviceVerificationFlow>> dvList;
|