Compare commits
7 Commits
v24.05.0
...
work/carl/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3748b6902c | ||
|
|
a4917d82e9 | ||
|
|
b9bf37089b | ||
|
|
1844a90fc0 | ||
|
|
23099046a3 | ||
|
|
bdf4ee43c8 | ||
|
|
c6300179d8 |
@@ -23,6 +23,11 @@ ecm_add_test(
|
|||||||
TEST_NAME delegatesizehelpertest
|
TEST_NAME delegatesizehelpertest
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ecm_add_test(
|
||||||
|
roomtreemodeltest.cpp
|
||||||
|
LINK_LIBRARIES neochat Qt::Test
|
||||||
|
)
|
||||||
|
|
||||||
ecm_add_test(
|
ecm_add_test(
|
||||||
mediasizehelpertest.cpp
|
mediasizehelpertest.cpp
|
||||||
LINK_LIBRARIES neochat Qt::Test
|
LINK_LIBRARIES neochat Qt::Test
|
||||||
|
|||||||
@@ -5,9 +5,7 @@
|
|||||||
#include <QSignalSpy>
|
#include <QSignalSpy>
|
||||||
#include <QTest>
|
#include <QTest>
|
||||||
|
|
||||||
#include <Quotient/connection.h>
|
#include "neochatconnection.h"
|
||||||
#include <Quotient/quotient_common.h>
|
|
||||||
#include <Quotient/syncdata.h>
|
|
||||||
|
|
||||||
#include "testutils.h"
|
#include "testutils.h"
|
||||||
|
|
||||||
@@ -27,7 +25,8 @@ private Q_SLOTS:
|
|||||||
|
|
||||||
void NeoChatRoomTest::initTestCase()
|
void NeoChatRoomTest::initTestCase()
|
||||||
{
|
{
|
||||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
auto connection = new NeoChatConnection;
|
||||||
|
Connection::makeMockConnection(connection, QStringLiteral("@bob:kde.org"));
|
||||||
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), "test-min-sync.json"_ls);
|
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), "test-min-sync.json"_ls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
70
autotests/roomtreemodeltest.cpp
Normal file
70
autotests/roomtreemodeltest.cpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <QAbstractItemModelTester>
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
#include "enums/neochatroomtype.h"
|
||||||
|
#include "models/roomtreemodel.h"
|
||||||
|
#include "models/sortfilterroomtreemodel.h"
|
||||||
|
#include "neochatconnection.h"
|
||||||
|
#include "testutils.h"
|
||||||
|
|
||||||
|
using namespace Quotient;
|
||||||
|
|
||||||
|
class RoomTreeModelTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void testTreeModel();
|
||||||
|
};
|
||||||
|
|
||||||
|
void RoomTreeModelTest::testTreeModel()
|
||||||
|
{
|
||||||
|
auto connection = new NeoChatConnection;
|
||||||
|
Connection::makeMockConnection(connection, QStringLiteral("@bob:kde.org"));
|
||||||
|
|
||||||
|
auto room = dynamic_cast<NeoChatRoom *>(new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QStringLiteral("test-min-sync.json")));
|
||||||
|
QVERIFY(room);
|
||||||
|
connection->addRoom(room);
|
||||||
|
|
||||||
|
RoomTreeModel model;
|
||||||
|
model.setConnection(connection);
|
||||||
|
|
||||||
|
SortFilterRoomTreeModel filterModel;
|
||||||
|
filterModel.setSourceModel(&model);
|
||||||
|
|
||||||
|
QAbstractItemModelTester tester(&model);
|
||||||
|
QAbstractItemModelTester testerFilter(&filterModel);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), static_cast<int>(NeoChatRoomType::TypesCount));
|
||||||
|
|
||||||
|
// Check data category
|
||||||
|
auto category = static_cast<int>(NeoChatRoomType::typeForRoom(room));
|
||||||
|
QCOMPARE(category, NeoChatRoomType::Normal);
|
||||||
|
auto normalCategoryIdx = model.index(category, 0);
|
||||||
|
QCOMPARE(model.data(normalCategoryIdx, RoomTreeModel::DisplayNameRole).toString(), QStringLiteral("Normal"));
|
||||||
|
QCOMPARE(model.data(normalCategoryIdx, RoomTreeModel::DelegateTypeRole).toString(), QStringLiteral("section"));
|
||||||
|
QCOMPARE(model.data(normalCategoryIdx, RoomTreeModel::IconRole).toString(), QStringLiteral("group"));
|
||||||
|
QCOMPARE(model.data(normalCategoryIdx, RoomTreeModel::CategoryRole).toInt(), category);
|
||||||
|
QCOMPARE(model.rowCount(normalCategoryIdx), 1);
|
||||||
|
|
||||||
|
// Check data room
|
||||||
|
auto roomIdx = model.index(0, 0, normalCategoryIdx);
|
||||||
|
QCOMPARE(model.data(roomIdx, RoomTreeModel::CurrentRoomRole).value<NeoChatRoom *>(), room);
|
||||||
|
QCOMPARE(model.data(roomIdx, RoomTreeModel::CategoryRole).toInt(), category);
|
||||||
|
|
||||||
|
// Move room
|
||||||
|
room->setProperty("isFavorite", true);
|
||||||
|
model.moveRoom(room);
|
||||||
|
|
||||||
|
auto newCategory = static_cast<int>(NeoChatRoomType::typeForRoom(room));
|
||||||
|
QCOMPARE(newCategory, NeoChatRoomType::Favorite);
|
||||||
|
auto newCategoryIdx = model.index(newCategory, 0);
|
||||||
|
QVERIFY(newCategoryIdx != normalCategoryIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(RoomTreeModelTest)
|
||||||
|
|
||||||
|
#include "roomtreemodeltest.moc"
|
||||||
@@ -29,6 +29,7 @@ public:
|
|||||||
Deprioritized, /**< The room is set as low priority. */
|
Deprioritized, /**< The room is set as low priority. */
|
||||||
Space, /**< The room is a space. */
|
Space, /**< The room is a space. */
|
||||||
AddDirect, /**< So we can show the add friend delegate. */
|
AddDirect, /**< So we can show the add friend delegate. */
|
||||||
|
TypesCount, /**< Number of different types. */
|
||||||
};
|
};
|
||||||
Q_ENUM(Types);
|
Q_ENUM(Types);
|
||||||
|
|
||||||
@@ -40,7 +41,8 @@ public:
|
|||||||
if (room->joinState() == Quotient::JoinState::Invite) {
|
if (room->joinState() == Quotient::JoinState::Invite) {
|
||||||
return NeoChatRoomType::Invited;
|
return NeoChatRoomType::Invited;
|
||||||
}
|
}
|
||||||
if (room->isFavourite()) {
|
// HACK for the unit tests
|
||||||
|
if (room->isFavourite() || room->property("isFavorite").toBool()) {
|
||||||
return NeoChatRoomType::Favorite;
|
return NeoChatRoomType::Favorite;
|
||||||
}
|
}
|
||||||
if (room->isLowPriority()) {
|
if (room->isLowPriority()) {
|
||||||
|
|||||||
@@ -13,6 +13,177 @@
|
|||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
|
TreeItem::TreeItem(TreeData treeData, TreeItem *parent)
|
||||||
|
: m_treeData(treeData)
|
||||||
|
, m_parentItem(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TreeItem::appendChild(std::unique_ptr<TreeItem> &&child)
|
||||||
|
{
|
||||||
|
m_childItems.push_back(std::move(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TreeItem::insertChildren(int position, int count, TreeData treeData)
|
||||||
|
{
|
||||||
|
if (position < 0 || position > qsizetype(m_childItems.size()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int row = 0; row < count; ++row) {
|
||||||
|
m_childItems.insert(m_childItems.cbegin() + position, std::make_unique<TreeItem>(treeData, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TreeItem::removeChildren(int position, int count)
|
||||||
|
{
|
||||||
|
if (position < 0 || position + count > qsizetype(m_childItems.size())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int row = 0; row < count; ++row) {
|
||||||
|
m_childItems.erase(m_childItems.cbegin() + position);
|
||||||
|
qWarning() << "removing" << position;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeItem *TreeItem::child(int row)
|
||||||
|
{
|
||||||
|
return row >= 0 && row < childCount() ? m_childItems.at(row).get() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TreeItem::childCount() const
|
||||||
|
{
|
||||||
|
return int(m_childItems.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
int TreeItem::row() const
|
||||||
|
{
|
||||||
|
if (m_parentItem == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const auto it = std::find_if(m_parentItem->m_childItems.cbegin(), m_parentItem->m_childItems.cend(), [this](const std::unique_ptr<TreeItem> &treeItem) {
|
||||||
|
return treeItem.get() == this;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != m_parentItem->m_childItems.cend())
|
||||||
|
return std::distance(m_parentItem->m_childItems.cbegin(), it);
|
||||||
|
Q_ASSERT(false); // should not happen
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant TreeItem::data(int role) const
|
||||||
|
{
|
||||||
|
if (!m_parentItem) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::holds_alternative<NeoChatRoomType::Types>(m_treeData)) {
|
||||||
|
const auto row = this->row();
|
||||||
|
switch (role) {
|
||||||
|
case RoomTreeModel::IsCategoryRole:
|
||||||
|
return true;
|
||||||
|
case RoomTreeModel::DisplayNameRole:
|
||||||
|
return NeoChatRoomType::typeName(row);
|
||||||
|
case RoomTreeModel::DelegateTypeRole:
|
||||||
|
if (row == NeoChatRoomType::Search) {
|
||||||
|
return QStringLiteral("search");
|
||||||
|
}
|
||||||
|
if (row == NeoChatRoomType::AddDirect) {
|
||||||
|
return QStringLiteral("addDirect");
|
||||||
|
}
|
||||||
|
return QStringLiteral("section");
|
||||||
|
case RoomTreeModel::IconRole:
|
||||||
|
return NeoChatRoomType::typeIconName(row);
|
||||||
|
case RoomTreeModel::CategoryRole:
|
||||||
|
return row;
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto room = std::get<NeoChatRoom *>(m_treeData);
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case RoomTreeModel::IsCategoryRole:
|
||||||
|
return false;
|
||||||
|
case RoomTreeModel::DisplayNameRole:
|
||||||
|
return room->displayName();
|
||||||
|
case RoomTreeModel::AvatarRole:
|
||||||
|
return room->avatarMediaId();
|
||||||
|
case RoomTreeModel::CanonicalAliasRole:
|
||||||
|
return room->canonicalAlias();
|
||||||
|
case RoomTreeModel::TopicRole:
|
||||||
|
return room->topic();
|
||||||
|
case RoomTreeModel::CategoryRole:
|
||||||
|
return NeoChatRoomType::typeForRoom(room);
|
||||||
|
case RoomTreeModel::ContextNotificationCountRole:
|
||||||
|
return room->contextAwareNotificationCount();
|
||||||
|
case RoomTreeModel::HasHighlightNotificationsRole:
|
||||||
|
return room->highlightCount() > 0 && room->contextAwareNotificationCount() > 0;
|
||||||
|
case RoomTreeModel::LastActiveTimeRole:
|
||||||
|
return room->lastActiveTime();
|
||||||
|
case RoomTreeModel::JoinStateRole:
|
||||||
|
if (!room->successorId().isEmpty()) {
|
||||||
|
return QStringLiteral("upgraded");
|
||||||
|
}
|
||||||
|
return QVariant::fromValue(room->joinState());
|
||||||
|
case RoomTreeModel::CurrentRoomRole:
|
||||||
|
return QVariant::fromValue(room);
|
||||||
|
case RoomTreeModel::SubtitleTextRole: {
|
||||||
|
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
EventHandler eventHandler(room, room->lastEvent());
|
||||||
|
return eventHandler.subtitleText();
|
||||||
|
}
|
||||||
|
case RoomTreeModel::AvatarImageRole:
|
||||||
|
return room->avatar(128);
|
||||||
|
case RoomTreeModel::RoomIdRole:
|
||||||
|
return room->id();
|
||||||
|
case RoomTreeModel::IsSpaceRole:
|
||||||
|
return room->isSpace();
|
||||||
|
case RoomTreeModel::IsChildSpaceRole:
|
||||||
|
return SpaceHierarchyCache::instance().isChild(room->id());
|
||||||
|
case RoomTreeModel::ReplacementIdRole:
|
||||||
|
return room->successorId();
|
||||||
|
case RoomTreeModel::IsDirectChat:
|
||||||
|
return room->isDirectChat();
|
||||||
|
case RoomTreeModel::DelegateTypeRole:
|
||||||
|
return QStringLiteral("normal");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeItem *TreeItem::parentItem() const
|
||||||
|
{
|
||||||
|
return m_parentItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<int> TreeItem::position(Quotient::Room *room) const
|
||||||
|
{
|
||||||
|
Q_ASSERT_X(std::holds_alternative<NeoChatRoomType::Types>(m_treeData), __FUNCTION__, "containsRoom only works in category items");
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (const auto &child : m_childItems) {
|
||||||
|
if (std::get<NeoChatRoom *>(child->treeData()) == room) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeItem::TreeData TreeItem::treeData() const
|
||||||
|
{
|
||||||
|
return m_treeData;
|
||||||
|
}
|
||||||
|
|
||||||
RoomTreeModel::RoomTreeModel(QObject *parent)
|
RoomTreeModel::RoomTreeModel(QObject *parent)
|
||||||
: QAbstractItemModel(parent)
|
: QAbstractItemModel(parent)
|
||||||
{
|
{
|
||||||
@@ -21,15 +192,18 @@ RoomTreeModel::RoomTreeModel(QObject *parent)
|
|||||||
|
|
||||||
void RoomTreeModel::initializeCategories()
|
void RoomTreeModel::initializeCategories()
|
||||||
{
|
{
|
||||||
for (const auto &key : m_rooms.keys()) {
|
m_rootItem.reset(new TreeItem(nullptr, nullptr));
|
||||||
for (const auto &room : m_rooms[key]) {
|
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
|
||||||
room->disconnect(this);
|
m_rootItem->appendChild(std::make_unique<TreeItem>(NeoChatRoomType::Types(i), m_rootItem.get()));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
m_rooms.clear();
|
}
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
m_rooms[NeoChatRoomType::Types(i)] = {};
|
TreeItem *RoomTreeModel::getItem(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (index.isValid()) {
|
||||||
|
return static_cast<TreeItem *>(index.internalPointer());
|
||||||
}
|
}
|
||||||
|
return m_rootItem.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomTreeModel::setConnection(NeoChatConnection *connection)
|
void RoomTreeModel::setConnection(NeoChatConnection *connection)
|
||||||
@@ -68,24 +242,34 @@ void RoomTreeModel::newRoom(Room *r)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
beginInsertRows(index(type, 0), m_rooms[type].size(), m_rooms[type].size());
|
auto categoryItem = m_rootItem->child(type);
|
||||||
m_rooms[type].append(room);
|
beginInsertRows(index(type, 0), categoryItem->childCount(), categoryItem->childCount());
|
||||||
|
categoryItem->appendChild(std::make_unique<TreeItem>(room, categoryItem));
|
||||||
connectRoomSignals(room);
|
connectRoomSignals(room);
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
|
qWarning() << "adding room" << type << "new count" << categoryItem->childCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomTreeModel::leftRoom(Room *r)
|
void RoomTreeModel::leftRoom(Room *r)
|
||||||
{
|
{
|
||||||
const auto room = dynamic_cast<NeoChatRoom *>(r);
|
const auto room = dynamic_cast<NeoChatRoom *>(r);
|
||||||
const auto type = NeoChatRoomType::typeForRoom(room);
|
|
||||||
auto row = m_rooms[type].indexOf(room);
|
auto idx = indexForRoom(room);
|
||||||
if (row == -1) {
|
if (!idx.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
beginRemoveRows(index(type, 0), row, row);
|
|
||||||
m_rooms[type][row]->disconnect(this);
|
auto parentItem = getItem(idx.parent());
|
||||||
m_rooms[type].removeAt(row);
|
Q_ASSERT(parentItem);
|
||||||
|
|
||||||
|
beginRemoveRows(idx.parent(), idx.row(), idx.row());
|
||||||
|
const bool success = parentItem->removeChildren(idx.row(), 1);
|
||||||
|
room->disconnect(this);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
qWarning() << "Unable to remove room";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomTreeModel::moveRoom(Quotient::Room *room)
|
void RoomTreeModel::moveRoom(Quotient::Room *room)
|
||||||
@@ -94,31 +278,46 @@ void RoomTreeModel::moveRoom(Quotient::Room *room)
|
|||||||
// NeoChatRoomType::typeForRoom doesn't match it's current location. So find the room.
|
// NeoChatRoomType::typeForRoom doesn't match it's current location. So find the room.
|
||||||
NeoChatRoomType::Types oldType;
|
NeoChatRoomType::Types oldType;
|
||||||
int oldRow = -1;
|
int oldRow = -1;
|
||||||
for (const auto &key : m_rooms.keys()) {
|
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
|
||||||
if (m_rooms[key].contains(room)) {
|
auto categoryItem = m_rootItem->child(i);
|
||||||
oldType = key;
|
auto position = categoryItem->position(room);
|
||||||
oldRow = m_rooms[key].indexOf(room);
|
if (position) {
|
||||||
|
oldType = static_cast<NeoChatRoomType::Types>(i);
|
||||||
|
oldRow = *position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldRow == -1) {
|
if (oldRow == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto newType = NeoChatRoomType::typeForRoom(dynamic_cast<NeoChatRoom *>(room));
|
auto neochatRoom = dynamic_cast<NeoChatRoom *>(room);
|
||||||
|
const auto newType = NeoChatRoomType::typeForRoom(neochatRoom);
|
||||||
if (newType == oldType) {
|
if (newType == oldType) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto oldParent = index(oldType, 0, {});
|
const auto oldParent = index(oldType, 0, {});
|
||||||
|
auto oldParentItem = getItem(oldParent);
|
||||||
|
Q_ASSERT(oldParentItem);
|
||||||
|
|
||||||
const auto newParent = index(newType, 0, {});
|
const auto newParent = index(newType, 0, {});
|
||||||
|
auto newParentItem = getItem(newParent);
|
||||||
|
Q_ASSERT(newParentItem);
|
||||||
|
|
||||||
// HACK: We're doing this as a remove then insert because moving doesn't work
|
// HACK: We're doing this as a remove then insert because moving doesn't work
|
||||||
// properly with DelegateChooser for whatever reason.
|
// properly with DelegateChooser for whatever reason.
|
||||||
|
|
||||||
|
Q_ASSERT(checkIndex(index(oldRow, 0, oldParent), QAbstractItemModel::CheckIndexOption::IndexIsValid));
|
||||||
beginRemoveRows(oldParent, oldRow, oldRow);
|
beginRemoveRows(oldParent, oldRow, oldRow);
|
||||||
m_rooms[oldType].removeAt(oldRow);
|
const bool success = oldParentItem->removeChildren(oldRow, 1);
|
||||||
|
Q_ASSERT(success);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
beginInsertRows(newParent, m_rooms[newType].size(), m_rooms[newType].size());
|
|
||||||
m_rooms[newType].append(dynamic_cast<NeoChatRoom *>(room));
|
beginInsertRows(newParent, newParentItem->childCount(), newParentItem->childCount());
|
||||||
|
newParentItem->appendChild(std::make_unique<TreeItem>(neochatRoom, newParentItem));
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
|
|
||||||
|
// Q_ASSERT(checkIndex(index(newParentItem->childCount() - 1, 0, newParent), QAbstractItemModel::CheckIndexOption::IndexIsValid));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomTreeModel::connectRoomSignals(NeoChatRoom *room)
|
void RoomTreeModel::connectRoomSignals(NeoChatRoom *room)
|
||||||
@@ -151,14 +350,12 @@ void RoomTreeModel::connectRoomSignals(NeoChatRoom *room)
|
|||||||
|
|
||||||
void RoomTreeModel::refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles)
|
void RoomTreeModel::refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles)
|
||||||
{
|
{
|
||||||
const auto roomType = NeoChatRoomType::typeForRoom(room);
|
const auto idx = indexForRoom(room);
|
||||||
const auto it = std::find(m_rooms[roomType].begin(), m_rooms[roomType].end(), room);
|
if (!idx.isValid()) {
|
||||||
if (it == m_rooms[roomType].end()) {
|
|
||||||
qCritical() << "Room" << room->id() << "not found in the room list";
|
qCritical() << "Room" << room->id() << "not found in the room list";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto parentIndex = index(roomType, 0, {});
|
|
||||||
const auto idx = index(it - m_rooms[roomType].begin(), 0, parentIndex);
|
|
||||||
Q_EMIT dataChanged(idx, idx, roles);
|
Q_EMIT dataChanged(idx, idx, roles);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,38 +366,42 @@ NeoChatConnection *RoomTreeModel::connection() const
|
|||||||
|
|
||||||
int RoomTreeModel::columnCount(const QModelIndex &parent) const
|
int RoomTreeModel::columnCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent)
|
const TreeItem *parentItem = getItem(parent);
|
||||||
return 1;
|
return parentItem ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RoomTreeModel::rowCount(const QModelIndex &parent) const
|
int RoomTreeModel::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
if (!parent.isValid()) {
|
const TreeItem *parentItem = getItem(parent);
|
||||||
return m_rooms.keys().size();
|
return parentItem ? parentItem->childCount() : 0;
|
||||||
}
|
|
||||||
if (!parent.parent().isValid()) {
|
|
||||||
return m_rooms.values()[parent.row()].size();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex RoomTreeModel::parent(const QModelIndex &index) const
|
QModelIndex RoomTreeModel::parent(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
if (!index.internalPointer()) {
|
if (!index.isValid()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return this->index(NeoChatRoomType::typeForRoom(static_cast<NeoChatRoom *>(index.internalPointer())), 0, QModelIndex());
|
|
||||||
|
TreeItem *childItem = getItem(index);
|
||||||
|
Q_ASSERT(childItem);
|
||||||
|
TreeItem *parentItem = childItem->parentItem();
|
||||||
|
|
||||||
|
return parentItem != m_rootItem.get() ? createIndex(parentItem->row(), 0, parentItem) : QModelIndex{};
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex RoomTreeModel::index(int row, int column, const QModelIndex &parent) const
|
QModelIndex RoomTreeModel::index(int row, int column, const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
if (!parent.isValid()) {
|
if (parent.isValid() && parent.column() != 0) {
|
||||||
return createIndex(row, column, nullptr);
|
|
||||||
}
|
|
||||||
if (row >= rowCount(parent)) {
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return createIndex(row, column, m_rooms[NeoChatRoomType::Types(parent.row())][row]);
|
|
||||||
|
TreeItem *parentItem = getItem(parent);
|
||||||
|
Q_ASSERT(parentItem);
|
||||||
|
|
||||||
|
if (auto *childItem = parentItem->child(row)) {
|
||||||
|
return createIndex(row, column, childItem);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> RoomTreeModel::roleNames() const
|
QHash<int, QByteArray> RoomTreeModel::roleNames() const
|
||||||
@@ -231,103 +432,13 @@ QHash<int, QByteArray> RoomTreeModel::roleNames() const
|
|||||||
// TODO room type changes
|
// TODO room type changes
|
||||||
QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
||||||
{
|
{
|
||||||
if (!index.isValid()) {
|
if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) {
|
||||||
return QVariant();
|
qWarning() << index.row() << rowCount(index.parent());
|
||||||
}
|
Q_ASSERT(false);
|
||||||
|
|
||||||
if (!index.parent().isValid()) {
|
|
||||||
if (role == DisplayNameRole) {
|
|
||||||
return NeoChatRoomType::typeName(index.row());
|
|
||||||
}
|
|
||||||
if (role == DelegateTypeRole) {
|
|
||||||
if (index.row() == NeoChatRoomType::Search) {
|
|
||||||
return QStringLiteral("search");
|
|
||||||
}
|
|
||||||
if (index.row() == NeoChatRoomType::AddDirect) {
|
|
||||||
return QStringLiteral("addDirect");
|
|
||||||
}
|
|
||||||
return QStringLiteral("section");
|
|
||||||
}
|
|
||||||
if (role == IconRole) {
|
|
||||||
return NeoChatRoomType::typeIconName(index.row());
|
|
||||||
}
|
|
||||||
if (role == CategoryRole) {
|
|
||||||
return index.row();
|
|
||||||
}
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const auto room = m_rooms.values()[index.parent().row()][index.row()].get();
|
|
||||||
Q_ASSERT(room);
|
|
||||||
|
|
||||||
if (role == DisplayNameRole) {
|
return getItem(index)->data(role);
|
||||||
return room->displayName();
|
|
||||||
}
|
|
||||||
if (role == AvatarRole) {
|
|
||||||
return room->avatarMediaId();
|
|
||||||
}
|
|
||||||
if (role == CanonicalAliasRole) {
|
|
||||||
return room->canonicalAlias();
|
|
||||||
}
|
|
||||||
if (role == TopicRole) {
|
|
||||||
return room->topic();
|
|
||||||
}
|
|
||||||
if (role == CategoryRole) {
|
|
||||||
return NeoChatRoomType::typeForRoom(room);
|
|
||||||
}
|
|
||||||
if (role == ContextNotificationCountRole) {
|
|
||||||
return int(room->contextAwareNotificationCount());
|
|
||||||
}
|
|
||||||
if (role == HasHighlightNotificationsRole) {
|
|
||||||
return room->highlightCount() > 0 && room->contextAwareNotificationCount() > 0;
|
|
||||||
}
|
|
||||||
if (role == LastActiveTimeRole) {
|
|
||||||
return room->lastActiveTime();
|
|
||||||
}
|
|
||||||
if (role == JoinStateRole) {
|
|
||||||
if (!room->successorId().isEmpty()) {
|
|
||||||
return QStringLiteral("upgraded");
|
|
||||||
}
|
|
||||||
return QVariant::fromValue(room->joinState());
|
|
||||||
}
|
|
||||||
if (role == CurrentRoomRole) {
|
|
||||||
return QVariant::fromValue(room);
|
|
||||||
}
|
|
||||||
if (role == SubtitleTextRole) {
|
|
||||||
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
EventHandler eventHandler(room, room->lastEvent());
|
|
||||||
return eventHandler.subtitleText();
|
|
||||||
}
|
|
||||||
if (role == AvatarImageRole) {
|
|
||||||
return room->avatar(128);
|
|
||||||
}
|
|
||||||
if (role == RoomIdRole) {
|
|
||||||
return room->id();
|
|
||||||
}
|
|
||||||
if (role == IsSpaceRole) {
|
|
||||||
return room->isSpace();
|
|
||||||
}
|
|
||||||
if (role == IsChildSpaceRole) {
|
|
||||||
return SpaceHierarchyCache::instance().isChild(room->id());
|
|
||||||
}
|
|
||||||
if (role == ReplacementIdRole) {
|
|
||||||
return room->successorId();
|
|
||||||
}
|
|
||||||
if (role == IsDirectChat) {
|
|
||||||
return room->isDirectChat();
|
|
||||||
}
|
|
||||||
if (role == DelegateTypeRole) {
|
|
||||||
return QStringLiteral("normal");
|
|
||||||
}
|
|
||||||
if (role == AttentionRole) {
|
|
||||||
return room->notificationCount() + room->highlightCount() > 0;
|
|
||||||
}
|
|
||||||
if (role == FavouriteRole) {
|
|
||||||
return room->isFavourite();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const
|
QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const
|
||||||
@@ -336,18 +447,18 @@ QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try and find by checking type.
|
const auto roomType = NeoChatRoomType::typeForRoom(room);
|
||||||
const auto type = NeoChatRoomType::typeForRoom(room);
|
const auto roomTypeItem = m_rootItem->child(roomType);
|
||||||
auto row = m_rooms[type].indexOf(room);
|
|
||||||
if (row >= 0) {
|
for (int i = 0, count = roomTypeItem->childCount(); i < count; i++) {
|
||||||
return index(row, 0, index(type, 0));
|
auto roomItem = roomTypeItem->child(i);
|
||||||
}
|
if (std::get<NeoChatRoom *>(roomItem->treeData()) == room) {
|
||||||
// Double check that the room isn't in the wrong category.
|
const auto parentIndex = index(roomType, 0, {});
|
||||||
for (const auto &key : m_rooms.keys()) {
|
const auto idx = index(i, 0, parentIndex);
|
||||||
if (m_rooms[key].contains(room)) {
|
return idx;
|
||||||
return index(m_rooms[key].indexOf(room), 0, index(key, 0));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,32 @@ class Room;
|
|||||||
|
|
||||||
class NeoChatConnection;
|
class NeoChatConnection;
|
||||||
class NeoChatRoom;
|
class NeoChatRoom;
|
||||||
|
class RoomTreeModelTest;
|
||||||
|
|
||||||
|
class TreeItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using TreeData = std::variant<NeoChatRoom *, NeoChatRoomType::Types>;
|
||||||
|
|
||||||
|
explicit TreeItem(TreeData data, TreeItem *parentItem);
|
||||||
|
|
||||||
|
TreeItem *child(int row);
|
||||||
|
int childCount() const;
|
||||||
|
QVariant data(int role) const;
|
||||||
|
void appendChild(std::unique_ptr<TreeItem> &&child);
|
||||||
|
bool insertChildren(int position, int count, TreeData treeData);
|
||||||
|
TreeItem *parentItem() const;
|
||||||
|
bool removeChildren(int position, int count);
|
||||||
|
bool removeColumns(int position, int columns);
|
||||||
|
std::optional<int> position(Quotient::Room *room) const;
|
||||||
|
int row() const;
|
||||||
|
TreeData treeData() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::unique_ptr<TreeItem>> m_childItems;
|
||||||
|
TreeData m_treeData;
|
||||||
|
TreeItem *m_parentItem;
|
||||||
|
};
|
||||||
|
|
||||||
class RoomTreeModel : public QAbstractItemModel
|
class RoomTreeModel : public QAbstractItemModel
|
||||||
{
|
{
|
||||||
@@ -49,6 +75,7 @@ public:
|
|||||||
IconRole,
|
IconRole,
|
||||||
AttentionRole, /**< Whether there are any notifications. */
|
AttentionRole, /**< Whether there are any notifications. */
|
||||||
FavouriteRole, /**< Whether the room is favourited. */
|
FavouriteRole, /**< Whether the room is favourited. */
|
||||||
|
IsCategoryRole, /**< Whether the item in the model is a category */
|
||||||
};
|
};
|
||||||
Q_ENUM(EventRoles)
|
Q_ENUM(EventRoles)
|
||||||
explicit RoomTreeModel(QObject *parent = nullptr);
|
explicit RoomTreeModel(QObject *parent = nullptr);
|
||||||
@@ -76,6 +103,8 @@ public:
|
|||||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
||||||
|
TreeItem *getItem(const QModelIndex &index) const;
|
||||||
|
|
||||||
Q_INVOKABLE QModelIndex indexForRoom(NeoChatRoom *room) const;
|
Q_INVOKABLE QModelIndex indexForRoom(NeoChatRoom *room) const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
@@ -83,7 +112,6 @@ Q_SIGNALS:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QPointer<NeoChatConnection> m_connection = nullptr;
|
QPointer<NeoChatConnection> m_connection = nullptr;
|
||||||
QMap<NeoChatRoomType::Types, QList<QPointer<NeoChatRoom>>> m_rooms;
|
|
||||||
|
|
||||||
void initializeCategories();
|
void initializeCategories();
|
||||||
void connectRoomSignals(NeoChatRoom *room);
|
void connectRoomSignals(NeoChatRoom *room);
|
||||||
@@ -93,4 +121,8 @@ private:
|
|||||||
void moveRoom(Quotient::Room *room);
|
void moveRoom(Quotient::Room *room);
|
||||||
|
|
||||||
void refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles = {});
|
void refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles = {});
|
||||||
|
|
||||||
|
std::unique_ptr<TreeItem> m_rootItem;
|
||||||
|
|
||||||
|
friend RoomTreeModelTest;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -119,20 +119,28 @@ QString SortFilterRoomTreeModel::filterText() const
|
|||||||
|
|
||||||
bool SortFilterRoomTreeModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
bool SortFilterRoomTreeModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||||
{
|
{
|
||||||
|
// root node
|
||||||
if (!source_parent.isValid()) {
|
if (!source_parent.isValid()) {
|
||||||
if (sourceModel()->data(sourceModel()->index(source_row, 0), RoomTreeModel::CategoryRole).toInt() == NeoChatRoomType::Search
|
return true;
|
||||||
&& NeoChatConfig::collapsed()) {
|
}
|
||||||
|
|
||||||
|
const QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
|
||||||
|
if (!index.isValid()) {
|
||||||
|
qWarning() << source_row << source_parent << sourceModel()->rowCount(source_parent);
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sourceModel()->data(index, RoomTreeModel::IsCategoryRole).toBool()) {
|
||||||
|
if (sourceModel()->data(index, RoomTreeModel::CategoryRole).toInt() == NeoChatRoomType::Search && NeoChatConfig::collapsed()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (sourceModel()->data(sourceModel()->index(source_row, 0), RoomTreeModel::CategoryRole).toInt() == NeoChatRoomType::AddDirect
|
if (sourceModel()->data(index, RoomTreeModel::CategoryRole).toInt() == NeoChatRoomType::AddDirect && m_mode == DirectChats) {
|
||||||
&& m_mode == DirectChats) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
|
|
||||||
|
|
||||||
bool acceptRoom = sourceModel()->data(index, RoomTreeModel::DisplayNameRole).toString().contains(m_filterText, Qt::CaseInsensitive)
|
bool acceptRoom = sourceModel()->data(index, RoomTreeModel::DisplayNameRole).toString().contains(m_filterText, Qt::CaseInsensitive)
|
||||||
&& sourceModel()->data(index, RoomTreeModel::IsSpaceRole).toBool() == false;
|
&& sourceModel()->data(index, RoomTreeModel::IsSpaceRole).toBool() == false;
|
||||||
|
|
||||||
|
|||||||
@@ -482,4 +482,9 @@ QString NeoChatConnection::accountDataJsonString(const QString &type) const
|
|||||||
return QString::fromUtf8(QJsonDocument(accountDataJson(type)).toJson());
|
return QString::fromUtf8(QJsonDocument(accountDataJson(type)).toJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NeoChatConnection::addRoom(Quotient::Room *room)
|
||||||
|
{
|
||||||
|
Connection::addRoom(room, false);
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_neochatconnection.cpp"
|
#include "moc_neochatconnection.cpp"
|
||||||
|
|||||||
@@ -147,6 +147,12 @@ public:
|
|||||||
|
|
||||||
bool isOnline() const;
|
bool isOnline() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add room directly in the connection.
|
||||||
|
* @internal for tests
|
||||||
|
*/
|
||||||
|
void addRoom(Quotient::Room *room);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void labelChanged();
|
void labelChanged();
|
||||||
void directChatNotificationsChanged();
|
void directChatNotificationsChanged();
|
||||||
|
|||||||
Reference in New Issue
Block a user