File indexing completed on 2025-02-16 04:48:28

0001 // SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 #include "matrixroomsmodel.h"
0005 
0006 #include <Quotient/room.h>
0007 
0008 using namespace Quotient;
0009 
0010 Q_DECLARE_METATYPE(Quotient::JoinState)
0011 
0012 MatrixRoomsModel::MatrixRoomsModel(QObject *parent)
0013         : QAbstractListModel(parent)
0014 {
0015 }
0016 
0017 void MatrixRoomsModel::setConnection(Connection *connection)
0018 {
0019     if (connection == m_connection) {
0020         return;
0021     }
0022     if (m_connection) {
0023         m_connection->disconnect(this);
0024     }
0025     if (!connection) {
0026         m_connection = nullptr;
0027         beginResetModel();
0028         m_rooms.clear();
0029         endResetModel();
0030         return;
0031     }
0032 
0033     m_connection = connection;
0034 
0035     for (Room *room : std::as_const(m_rooms)) {
0036         room->disconnect(this);
0037     }
0038 
0039     connect(connection, &Connection::connected, this, &MatrixRoomsModel::doResetModel);
0040     connect(connection, &Connection::invitedRoom, this, &MatrixRoomsModel::updateRoom);
0041     connect(connection, &Connection::joinedRoom, this, &MatrixRoomsModel::updateRoom);
0042     connect(connection, &Connection::leftRoom, this, &MatrixRoomsModel::updateRoom);
0043     connect(connection, &Connection::aboutToDeleteRoom, this, &MatrixRoomsModel::deleteRoom);
0044     connect(connection, &Connection::directChatsListChanged, this, [this, connection](Quotient::DirectChatsMap additions, Quotient::DirectChatsMap removals) {
0045         auto refreshRooms = [this, &connection](Quotient::DirectChatsMap rooms) {
0046             for (const QString &roomID : std::as_const(rooms)) {
0047                 auto room = connection->room(roomID);
0048                 if (room) {
0049                     refresh(room);
0050                 }
0051             }
0052         };
0053 
0054         refreshRooms(std::move(additions));
0055         refreshRooms(std::move(removals));
0056     });
0057 
0058     doResetModel();
0059 
0060     Q_EMIT connectionChanged();
0061 }
0062 
0063 void MatrixRoomsModel::doResetModel()
0064 {
0065     beginResetModel();
0066     m_rooms.clear();
0067     const auto rooms = m_connection->allRooms();
0068     for (const auto &room : rooms) {
0069         doAddRoom(room);
0070     }
0071     endResetModel();
0072 }
0073 
0074 Room *MatrixRoomsModel::roomAt(int row) const
0075 {
0076     return m_rooms.at(row);
0077 }
0078 
0079 void MatrixRoomsModel::doAddRoom(Room *room)
0080 {
0081     if (room) {
0082         if (!room->successorId().isEmpty()) {
0083             return;
0084         }
0085         m_rooms.append(room);
0086         connectRoomSignals(room);
0087     } else {
0088         Q_ASSERT(false);
0089     }
0090 }
0091 
0092 void MatrixRoomsModel::connectRoomSignals(Room *room)
0093 {
0094     connect(room, &Room::displaynameChanged, this, [this, room] {
0095         refresh(room);
0096     });
0097     connect(room, &Room::avatarChanged, this, [this, room] {
0098         refresh(room, {AvatarRole, AvatarImageRole});
0099     });
0100 }
0101 
0102 void MatrixRoomsModel::updateRoom(Room *room, Room *prev)
0103 {
0104     if (prev == room) {
0105         refresh(room);
0106         return;
0107     }
0108     const auto newRoom = room;
0109     const auto it = std::find_if(m_rooms.begin(), m_rooms.end(), [=](const Room *r) {
0110         return r == prev || r == newRoom;
0111     });
0112     if (it != m_rooms.end()) {
0113         const int row = it - m_rooms.begin();
0114         // There's no guarantee that prev != newRoom
0115         if (*it == prev && *it != newRoom) {
0116             prev->disconnect(this);
0117             m_rooms.replace(row, newRoom);
0118             connectRoomSignals(newRoom);
0119         }
0120         Q_EMIT dataChanged(index(row), index(row));
0121     } else {
0122         beginInsertRows(QModelIndex(), m_rooms.count(), m_rooms.count());
0123         doAddRoom(newRoom);
0124         endInsertRows();
0125     }
0126 }
0127 
0128 void MatrixRoomsModel::deleteRoom(Room *room)
0129 {
0130     const auto it = std::find(m_rooms.begin(), m_rooms.end(), room);
0131     if (it == m_rooms.end()) {
0132         return;
0133     }
0134     const int row = it - m_rooms.begin();
0135     beginRemoveRows(QModelIndex(), row, row);
0136     m_rooms.erase(it);
0137     endRemoveRows();
0138 }
0139 
0140 int MatrixRoomsModel::rowCount(const QModelIndex &parent) const
0141 {
0142     if (parent.isValid()) {
0143         return 0;
0144     }
0145     return m_rooms.count();
0146 }
0147 
0148 QVariant MatrixRoomsModel::data(const QModelIndex &index, int role) const
0149 {
0150     if (!index.isValid()) {
0151         return {};
0152     }
0153 
0154     if (index.row() >= m_rooms.count()) {
0155         return {};
0156     }
0157     Room *room = m_rooms.at(index.row());
0158     switch (role) {
0159         case DisplayNameRole:
0160             return room->displayName();
0161         case AvatarRole:
0162             return room->avatarMediaId();
0163         case CanonicalAliasRole:
0164             return room->canonicalAlias();
0165         case TopicRole:
0166             return room->topic();
0167         case IdRole:
0168             return room->id();
0169         case AvatarImageRole:
0170             return room->avatar(48);
0171         case CategoryRole:
0172             if (room->joinState() == JoinState::Invite) {
0173                 return InvitedRoom;
0174             }
0175             if (room->isFavourite()) {
0176                 return FavoriteRoom;
0177             }
0178             if (room->isLowPriority()) {
0179                 return LowPriorityRoom;
0180             }
0181             if (room->isDirectChat()) {
0182                 return DirectChatRoom;
0183             }
0184             if (const RoomCreateEvent *creationEvent = room->creation(); creationEvent) {
0185                 const auto contentJson = creationEvent->contentJson();
0186                 if (contentJson.value(Quotient::TypeKey) == QLatin1StringView("m.space")) {
0187                     return Space;
0188                 }
0189             }
0190             return RegularRoom;
0191     }
0192     return {};
0193 }
0194 
0195 void MatrixRoomsModel::refresh(Room *room, const QVector<int> &roles)
0196 {
0197     const auto it = std::find(m_rooms.begin(), m_rooms.end(), room);
0198     if (it == m_rooms.end()) {
0199         return;
0200     }
0201     const auto idx = index(it - m_rooms.begin());
0202     Q_EMIT dataChanged(idx, idx, roles);
0203 }
0204 
0205 QHash<int, QByteArray> MatrixRoomsModel::roleNames() const
0206 {
0207     QHash<int, QByteArray> roles;
0208     roles[DisplayNameRole] = "displayName";
0209     roles[AvatarRole] = "avatar";
0210     roles[CanonicalAliasRole] = "canonicalAlias";
0211     roles[TopicRole] = "topic";
0212     roles[IdRole] = "id";
0213     roles[AvatarImageRole] = "avatarImage";
0214     roles[CategoryRole] = "category";
0215     return roles;
0216 }
0217 
0218 #include "moc_matrixroomsmodel.cpp"