diff --git a/matrix/roomlistmodel.cpp b/matrix/roomlistmodel.cpp index e5e02a6aa..dc511a1c6 100644 --- a/matrix/roomlistmodel.cpp +++ b/matrix/roomlistmodel.cpp @@ -1,79 +1,44 @@ -#include -#include - #include "roomlistmodel.h" -#include "controller.h" +#include "matriqueroom.h" +#include "connection.h" #include "user.h" -RoomListModel::RoomListModel() { +#include -} +RoomListModel::RoomListModel(QObject* parent) + : QAbstractListModel(parent) +{ } -RoomListModel::~RoomListModel() { +void RoomListModel::setConnection(QMatrixClient::Connection* connection) +{ + Q_ASSERT(connection); -} - -void RoomListModel::setConnection(QMatrixClient::Connection *conn) { - m_connection = conn; + using QMatrixClient::Room; beginResetModel(); - m_rooms.clear(); - connect(m_connection, &QMatrixClient::Connection::newRoom, this, &RoomListModel::addRoom); - for(QMatrixClient::Room* r: m_connection->roomMap().values()) { - if (auto* room = static_cast(r)) - { - connect(room, &MatriqueRoom::namesChanged, this, &RoomListModel::namesChanged); - m_rooms.append(room); - } else - { - qCritical() << "Attempt to add nullptr to the room list"; - Q_ASSERT(false); - } - } + m_connection = connection; + connect( connection, &QMatrixClient::Connection::loggedOut, + this, [=]{ deleteConnection(connection); } ); + connect( connection, &QMatrixClient::Connection::invitedRoom, + this, &RoomListModel::updateRoom); + connect( connection, &QMatrixClient::Connection::joinedRoom, + this, &RoomListModel::updateRoom); + connect( connection, &QMatrixClient::Connection::leftRoom, + this, &RoomListModel::updateRoom); + connect( connection, &QMatrixClient::Connection::aboutToDeleteRoom, + this, &RoomListModel::deleteRoom); + + for( auto r: connection->roomMap() ) + doAddRoom(r); endResetModel(); } -MatriqueRoom* RoomListModel::roomAt(int row) { - return m_rooms.at(row); +void RoomListModel::deleteConnection(QMatrixClient::Connection* connection) { + } -void RoomListModel::addRoom(MatriqueRoom* room) { - qDebug() << "Adding room."; - beginInsertRows(QModelIndex(), m_rooms.count(), m_rooms.count()); - connect(room, &MatriqueRoom::namesChanged, this, &RoomListModel::namesChanged ); - m_rooms.append(room); - endInsertRows(); -} - -int RoomListModel::rowCount(const QModelIndex& parent) const { - if( parent.isValid() ) - return 0; - return m_rooms.count(); -} - -QVariant RoomListModel::data(const QModelIndex& index, int role) const { - if(!index.isValid()) - return QVariant(); - - if(index.row() >= m_rooms.count()) { - qDebug() << "UserListModel: something wrong here..."; - return QVariant(); - } - MatriqueRoom* room = m_rooms.at(index.row()); - if(role == NameRole) { - return room->displayName(); - } - if(role == ValueRole) { - return room->topic(); - } - if(role == AvatarRole) { - if(room->avatarUrl().toString() != "") { - return room->avatarUrl(); - } else if(room->users().length() == 2) { - QMatrixClient::User* user = room->users().at(0); - return user->avatarUrl(); - } - } - return QVariant(); +MatriqueRoom* RoomListModel::roomAt(QModelIndex index) const +{ + return m_rooms.at(index.row()); } QModelIndex RoomListModel::indexOf(MatriqueRoom* room) const @@ -81,19 +46,191 @@ QModelIndex RoomListModel::indexOf(MatriqueRoom* room) const return index(m_rooms.indexOf(room), 0); } +void RoomListModel::updateRoom(QMatrixClient::Room* room, + QMatrixClient::Room* prev) +{ + // There are two cases when this method is called: + // 1. (prev == nullptr) adding a new room to the room list + // 2. (prev != nullptr) accepting/rejecting an invitation or inviting to + // the previously left room (in both cases prev has the previous state). + if (prev == room) + { + qCritical() << "RoomListModel::updateRoom: room tried to replace itself"; + refresh(static_cast(room)); + return; + } + if (prev && room->id() != prev->id()) + { + qCritical() << "RoomListModel::updateRoom: attempt to update room" + << room->id() << "to" << prev->id(); + // That doesn't look right but technically we still can do it. + } + // Ok, we're through with pre-checks, now for the real thing. + auto* newRoom = static_cast(room); + const auto it = std::find_if(m_rooms.begin(), m_rooms.end(), + [=](const MatriqueRoom* r) { return r == prev || r == newRoom; }); + if (it != m_rooms.end()) + { + const int row = it - m_rooms.begin(); + // There's no guarantee that prev != newRoom + if (*it == prev && *it != newRoom) + { + prev->disconnect(this); + m_rooms.replace(row, newRoom); + connectRoomSignals(newRoom); + } + emit dataChanged(index(row), index(row)); + } + else + { + beginInsertRows(QModelIndex(), m_rooms.count(), m_rooms.count()); + doAddRoom(newRoom); + endInsertRows(); + } +} + +void RoomListModel::deleteRoom(QMatrixClient::Room* room) +{ + auto i = m_rooms.indexOf(static_cast(room)); + if (i == -1) + return; // Already deleted, nothing to do + + beginRemoveRows(QModelIndex(), i, i); + m_rooms.removeAt(i); + endRemoveRows(); +} + +void RoomListModel::doAddRoom(QMatrixClient::Room* r) +{ + if (auto* room = static_cast(r)) + { + m_rooms.append(room); + connectRoomSignals(room); + } else + { + qCritical() << "Attempt to add nullptr to the room list"; + Q_ASSERT(false); + } +} + +void RoomListModel::connectRoomSignals(MatriqueRoom* room) +{ + connect(room, &MatriqueRoom::displaynameChanged, + this, [=]{ displaynameChanged(room); } ); + connect( room, &MatriqueRoom::unreadMessagesChanged, + this, [=]{ unreadMessagesChanged(room); } ); + connect( room, &MatriqueRoom::notificationCountChanged, + this, [=]{ unreadMessagesChanged(room); } ); + connect( room, &MatriqueRoom::joinStateChanged, + this, [=]{ refresh(room); }); + connect( room, &MatriqueRoom::avatarChanged, + this, [=]{ refresh(room, { Qt::DecorationRole }); }); +} + +int RoomListModel::rowCount(const QModelIndex& parent) const +{ + if( parent.isValid() ) + return 0; + return m_rooms.count(); +} + +QVariant RoomListModel::data(const QModelIndex& index, int role) const +{ + if( !index.isValid() ) + return QVariant(); + + if( index.row() >= m_rooms.count() ) + { + qDebug() << "UserListModel: something wrong here..."; + return QVariant(); + } + auto room = m_rooms.at(index.row()); + using QMatrixClient::JoinState; + switch (role) + { + case Qt::DisplayRole: + return room->displayName(); + case Qt::DecorationRole: + { +// auto avatar = room->avatar(16, 16); +// if (!avatar.isNull()) +// return avatar; +// switch( room->joinState() ) +// { +// case JoinState::Join: +// return QIcon(":/irc-channel-joined.svg"); +// case JoinState::Invite: +// return QIcon(":/irc-channel-invited.svg"); +// case JoinState::Leave: +// return QIcon(":/irc-channel-parted.svg"); +// } + if(room->avatarUrl().toString() != "") { + return room->avatarUrl(); + } else if(room->users().length() == 2) { + QMatrixClient::User* user = room->users().at(0); + return user->avatarUrl(); + } + } + case Qt::StatusTipRole: + { + return room->topic(); + } + case Qt::ToolTipRole: + { + int hlCount = room->highlightCount(); + auto result = QStringLiteral("%1
").arg(room->displayName()); + result += tr("Main alias: %1
").arg(room->canonicalAlias()); + result += tr("Members: %1
").arg(room->memberCount()); + if (hlCount > 0) + result += tr("Unread mentions: %1
").arg(hlCount); + result += tr("ID: %1
").arg(room->id()); + switch (room->joinState()) + { + case JoinState::Join: + result += tr("You joined this room"); + break; + case JoinState::Leave: + result += tr("You left this room"); + break; + default: + result += tr("You were invited into this room"); + } + return result; + } + case HasUnreadRole: + return room->hasUnreadMessages(); + case HighlightCountRole: + return room->highlightCount(); + case JoinStateRole: + return toCString(room->joinState()); // FIXME: better make the enum QVariant-convertible + default: + return QVariant(); + } +} + QHash RoomListModel::roleNames() const { QHash roles; - roles[NameRole] = "name"; - roles[ValueRole] = "value"; - roles[AvatarRole] = "avatar"; + roles[Qt::DisplayRole] = "name"; + roles[Qt::DecorationRole] = "avatar"; + roles[Qt::StatusTipRole] = "value"; return roles; } -void RoomListModel::namesChanged(MatriqueRoom* room) { +void RoomListModel::displaynameChanged(MatriqueRoom* room) +{ + refresh(room); +} + +void RoomListModel::unreadMessagesChanged(MatriqueRoom* room) +{ + refresh(room); +} + +void RoomListModel::refresh(MatriqueRoom* room, const QVector& roles) +{ int row = m_rooms.indexOf(room); - emit dataChanged(index(row), index(row)); -} - -void RoomListModel::unreadMessagesChanged(MatriqueRoom* room) { - + if (row == -1) + qCritical() << "Room" << room->id() << "not found in the room list"; + else + emit dataChanged(index(row), index(row), roles); } diff --git a/matrix/roomlistmodel.h b/matrix/roomlistmodel.h index e99550c90..f2bb6308b 100644 --- a/matrix/roomlistmodel.h +++ b/matrix/roomlistmodel.h @@ -1,57 +1,56 @@ #ifndef ROOMLISTMODEL_H #define ROOMLISTMODEL_H -#include #include -#include "libqmatrixclient/connection.h" -#include "libqmatrixclient/room.h" - #include "matriqueroom.h" -namespace QMatrixClient { +namespace QMatrixClient +{ class Connection; class Room; } -class RoomListModel : public QAbstractListModel +class RoomListModel: public QAbstractListModel { Q_OBJECT - Q_PROPERTY(QMatrixClient::Connection *connection READ getConnection WRITE setConnection NOTIFY connectionChanged) + Q_PROPERTY(QMatrixClient::Connection *connection READ getConnection WRITE setConnection) public: - explicit RoomListModel(); - ~RoomListModel(); - - enum RoomModelRoles { - NameRole, ValueRole, AvatarRole + enum Roles { + HasUnreadRole = Qt::UserRole + 1, + HighlightCountRole, JoinStateRole }; - QMatrixClient::Connection* m_connection; + explicit RoomListModel(QObject* parent = nullptr); + QMatrixClient::Connection* getConnection() { return m_connection; } - void setConnection(QMatrixClient::Connection* conn); + void setConnection(QMatrixClient::Connection* connection); + void deleteConnection(QMatrixClient::Connection* connection); - QHash roleNames() const; - - Q_INVOKABLE MatriqueRoom* roomAt(int row); + MatriqueRoom* roomAt(QModelIndex index) const; + QModelIndex indexOf(MatriqueRoom* room) const; QVariant data(const QModelIndex& index, int role) const override; - QModelIndex indexOf(MatriqueRoom* room) const; - Q_INVOKABLE int rowCount(const QModelIndex& parent=QModelIndex()) const override; - - signals: - void connectionChanged(); - - public slots: + QHash roleNames() const; + int rowCount(const QModelIndex& parent) const override; private slots: - void namesChanged(MatriqueRoom* room); + void displaynameChanged(MatriqueRoom* room); void unreadMessagesChanged(MatriqueRoom* room); - void addRoom(MatriqueRoom* room); + void refresh(MatriqueRoom* room, const QVector& roles = {}); + + void updateRoom(QMatrixClient::Room* room, + QMatrixClient::Room* prev); + void deleteRoom(QMatrixClient::Room* room); private: + QMatrixClient::Connection* m_connection; QList m_rooms; + + void doAddRoom(QMatrixClient::Room* r); + void connectRoomSignals(MatriqueRoom* room); }; #endif // ROOMLISTMODEL_H diff --git a/qml/form/ListForm.qml b/qml/form/ListForm.qml index 29a1875cb..c6eda8f50 100644 --- a/qml/form/ListForm.qml +++ b/qml/form/ListForm.qml @@ -116,7 +116,7 @@ Item { ImageStatus { width: parent.height height: parent.height - source: avatar == null ? "qrc:/asset/img/avatar.png" : "image://mxc/" + avatar + source: avatar == "" ? "qrc:/asset/img/avatar.png" : "image://mxc/" + avatar opaqueBackground: true }