diff --git a/src/models/messageeventmodel.cpp b/src/models/messageeventmodel.cpp index 548f8d53f..06f609f13 100644 --- a/src/models/messageeventmodel.cpp +++ b/src/models/messageeventmodel.cpp @@ -431,33 +431,6 @@ void MessageEventModel::fetchMore(const QModelIndex &parent) } } -static const QVariantMap emptyUser = { - {"isLocalUser", false}, - {"id", QString()}, - {"avatarSource", QUrl()}, - {"avatarMediaId", QString()}, - {"avatarUrl", QString()}, - {"displayName", QString()}, - {"display", QString()}, - {"color", QColor()}, - {"object", QVariant()}, -}; - -inline QVariantMap userInContext(NeoChatUser *user, NeoChatRoom *room) -{ - return QVariantMap{ - {"isLocalUser", user->id() == room->localUser()->id()}, - {"id", user->id()}, - {"avatarSource", room->avatarForMember(user)}, - {"avatarMediaId", user->avatarMediaId(room)}, - {"avatarUrl", user->avatarUrl(room)}, - {"displayName", user->displayname(room)}, - {"display", user->name()}, - {"color", user->color()}, - {"object", QVariant::fromValue(user)}, - }; -} - static LinkPreviewer *emptyLinkPreview = new LinkPreviewer; QVariant MessageEventModel::data(const QModelIndex &idx, int role) const @@ -572,7 +545,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const if (role == AuthorRole) { auto author = static_cast(isPending ? m_currentRoom->localUser() : m_currentRoom->user(evt.senderId())); - return userInContext(author, m_currentRoom); + return m_currentRoom->getUser(author); } if (role == ContentRole) { @@ -715,9 +688,9 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const if (replyPtr) { auto replyUser = static_cast(m_currentRoom->user(replyPtr->senderId())); - return userInContext(replyUser, m_currentRoom); + return m_currentRoom->getUser(replyUser); } else { - return emptyUser; + return m_currentRoom->getUser(nullptr); } } @@ -853,7 +826,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const #else auto user = static_cast(userId); #endif - users += userInContext(user, m_currentRoom); + users += m_currentRoom->getUser(user); } return users; @@ -1195,7 +1168,7 @@ void MessageEventModel::createReactionModelForEvent(const Quotient::RoomMessageE while (i != reactions.constEnd()) { QVariantList authors; for (const auto &author : i.value()) { - authors.append(userInContext(author, m_currentRoom)); + authors.append(m_currentRoom->getUser(author)); } res.append(ReactionModel::Reaction{i.key(), authors}); diff --git a/src/neochatroom.cpp b/src/neochatroom.cpp index e974ccea9..a98b6cdbe 100644 --- a/src/neochatroom.cpp +++ b/src/neochatroom.cpp @@ -459,13 +459,38 @@ QVariantList NeoChatRoom::getUsers(const QString &keyword, int limit) const return matchedList; } +// An empty user is useful for returning as a model value to avoid properties being undefined. +static const QVariantMap emptyUser = { + {"isLocalUser", false}, + {"id", QString()}, + {"displayName", QString()}, + {"avatarSource", QUrl()}, + {"avatarMediaId", QString()}, + {"color", QColor()}, + {"object", QVariant()}, +}; + QVariantMap NeoChatRoom::getUser(const QString &userID) const { - NeoChatUser user(userID, connection()); - return QVariantMap{{QStringLiteral("id"), user.id()}, - {QStringLiteral("displayName"), user.displayname(this)}, - {QStringLiteral("avatarMediaId"), user.avatarMediaId(this)}, - {QStringLiteral("color"), user.color()}}; + NeoChatUser *userObject = static_cast(user(userID)); + return getUser(userObject); +} + +QVariantMap NeoChatRoom::getUser(NeoChatUser *user) const +{ + if (user == nullptr) { + return emptyUser; + } + + return QVariantMap{ + {QStringLiteral("isLocalUser"), user->id() == localUser()->id()}, + {QStringLiteral("id"), user->id()}, + {QStringLiteral("displayName"), user->displayname(this)}, + {QStringLiteral("avatarSource"), avatarForMember(user)}, + {QStringLiteral("avatarMediaId"), user->avatarMediaId(this)}, + {QStringLiteral("color"), user->color()}, + {QStringLiteral("object"), QVariant::fromValue(user)}, + }; } QString NeoChatRoom::avatarMediaId() const @@ -1768,12 +1793,12 @@ void NeoChatRoom::setChatBoxEditId(const QString &editId) Q_EMIT chatBoxEditIdChanged(); } -NeoChatUser *NeoChatRoom::chatBoxReplyUser() const +QVariantMap NeoChatRoom::chatBoxReplyUser() const { if (m_chatBoxReplyId.isEmpty()) { - return nullptr; + return emptyUser; } - return static_cast(user((*findInTimeline(m_chatBoxReplyId))->senderId())); + return getUser(static_cast(user((*findInTimeline(m_chatBoxReplyId))->senderId()))); } QString NeoChatRoom::chatBoxReplyMessage() const @@ -1784,12 +1809,12 @@ QString NeoChatRoom::chatBoxReplyMessage() const return eventToString(*static_cast(&**findInTimeline(m_chatBoxReplyId))); } -NeoChatUser *NeoChatRoom::chatBoxEditUser() const +QVariantMap NeoChatRoom::chatBoxEditUser() const { if (m_chatBoxEditId.isEmpty()) { - return nullptr; + return emptyUser; } - return static_cast(user((*findInTimeline(m_chatBoxEditId))->senderId())); + return getUser(static_cast(user((*findInTimeline(m_chatBoxEditId))->senderId()))); } QString NeoChatRoom::chatBoxEditMessage() const diff --git a/src/neochatroom.h b/src/neochatroom.h index 3bcca737e..d0795f383 100644 --- a/src/neochatroom.h +++ b/src/neochatroom.h @@ -315,11 +315,26 @@ class NeoChatRoom : public Quotient::Room Q_PROPERTY(QString chatBoxEditId READ chatBoxEditId WRITE setChatBoxEditId NOTIFY chatBoxEditIdChanged) /** - * @brief Get the user object for the message being replied to. + * @brief Get the user for the message being replied to. * - * Returns a nullptr if not replying to a message. + * This is different to getting a NeoChatUser object or Quotient::User object + * as neither of those can provide details like the displayName or avatarMediaId + * without the room context as these can vary from room to room. + * + * Returns an empty user if not replying to a message. + * + * The user QVariantMap has the following properties: + * - isLocalUser - Whether the user is the local user. + * - id - The matrix ID of the user. + * - displayName - Display name in the context of this room. + * - avatarSource - The mxc URL for the user's avatar in the current room. + * - avatarMediaId - Avatar id in the context of this room. + * - color - Color for the user. + * - object - The NeoChatUser object for the user. + * + * @sa getUser, Quotient::User, NeoChatUser */ - Q_PROPERTY(NeoChatUser *chatBoxReplyUser READ chatBoxReplyUser NOTIFY chatBoxReplyIdChanged) + Q_PROPERTY(QVariantMap chatBoxReplyUser READ chatBoxReplyUser NOTIFY chatBoxReplyIdChanged) /** * @brief The content of the message being replied to. @@ -329,11 +344,26 @@ class NeoChatRoom : public Quotient::Room Q_PROPERTY(QString chatBoxReplyMessage READ chatBoxReplyMessage NOTIFY chatBoxReplyIdChanged) /** - * @brief Get the user object for the message being edited. + * @brief Get the user for the message being edited. * - * Returns a nullptr if not editing a message. + * This is different to getting a NeoChatUser object or Quotient::User object + * as neither of those can provide details like the displayName or avatarMediaId + * without the room context as these can vary from room to room. + * + * Returns an empty user if not replying to a message. + * + * The user QVariantMap has the following properties: + * - isLocalUser - Whether the user is the local user. + * - id - The matrix ID of the user. + * - displayName - Display name in the context of this room. + * - avatarSource - The mxc URL for the user's avatar in the current room. + * - avatarMediaId - Avatar id in the context of this room. + * - color - Color for the user. + * - object - The NeoChatUser object for the user. + * + * @sa getUser, Quotient::User, NeoChatUser */ - Q_PROPERTY(NeoChatUser *chatBoxEditUser READ chatBoxEditUser NOTIFY chatBoxEditIdChanged) + Q_PROPERTY(QVariantMap chatBoxEditUser READ chatBoxEditUser NOTIFY chatBoxEditIdChanged) /** * @brief The content of the message being edited. @@ -390,18 +420,50 @@ public: * without the room context as these can vary from room to room. This function * provides the room context and outputs the result as QVariantMap. * + * Can be called with an empty QString to return an empty user, which is a useful return + * from models to avoid undefined properties. + * * @param userID the ID of the user to output. * * @return a QVariantMap for the user with the following properties: - * - id - User ID. + * - isLocalUser - Whether the user is the local user. + * - id - The matrix ID of the user. * - displayName - Display name in the context of this room. + * - avatarSource - The mxc URL for the user's avatar in the current room. * - avatarMediaId - Avatar id in the context of this room. * - color - Color for the user. + * - object - The NeoChatUser object for the user. * * @sa Quotient::User, NeoChatUser */ Q_INVOKABLE [[nodiscard]] QVariantMap getUser(const QString &userID) const; + /** + * @brief Get a user in the context of this room. + * + * This is different to getting a NeoChatUser object or Quotient::User object + * as neither of those can provide details like the displayName or avatarMediaId + * without the room context as these can vary from room to room. This function + * provides the room context and outputs the result as QVariantMap. + * + * Can be called with a nullptr to return an empty user, which is a useful return + * from models to avoid undefined properties. + * + * @param user the user to output. + * + * @return a QVariantMap for the user with the following properties: + * - isLocalUser - Whether the user is the local user. + * - id - The matrix ID of the user. + * - displayName - Display name in the context of this room. + * - avatarSource - The mxc URL for the user's avatar in the current room. + * - avatarMediaId - Avatar id in the context of this room. + * - color - Color for the user. + * - object - The NeoChatUser object for the user. + * + * @sa Quotient::User, NeoChatUser + */ + Q_INVOKABLE [[nodiscard]] QVariantMap getUser(NeoChatUser *user) const; + [[nodiscard]] QVariantList getUsersTyping() const; [[nodiscard]] QDateTime lastActiveTime(); @@ -704,13 +766,13 @@ public: QString chatBoxReplyId() const; void setChatBoxReplyId(const QString &replyId); - NeoChatUser *chatBoxReplyUser() const; + QVariantMap chatBoxReplyUser() const; QString chatBoxReplyMessage() const; QString chatBoxEditId() const; void setChatBoxEditId(const QString &editId); - NeoChatUser *chatBoxEditUser() const; + QVariantMap chatBoxEditUser() const; QString chatBoxEditMessage() const; QString chatBoxAttachmentPath() const; diff --git a/src/qml/Component/ChatBox/ChatBar.qml b/src/qml/Component/ChatBox/ChatBar.qml index d30aa3be8..177a1e1fe 100644 --- a/src/qml/Component/ChatBox/ChatBar.qml +++ b/src/qml/Component/ChatBox/ChatBar.qml @@ -15,7 +15,6 @@ QQC2.Control { property alias textField: textField property bool isReplying: currentRoom.chatBoxReplyId.length > 0 - property NeoChatUser replyUser: currentRoom.chatBoxReplyUser property bool attachmentPaneVisible: currentRoom.chatBoxAttachmentPath.length > 0 signal messageSent() @@ -244,9 +243,9 @@ QQC2.Control { Component { id: replyPane ReplyPane { - userName: root.replyUser ? root.replyUser.displayName : "" - userColor: root.replyUser ? root.replyUser.color : "" - userAvatar: root.replyUser ? "image://mxc/" + currentRoom.getUser(root.replyUser.id).avatarMediaId : "" + userName: currentRoom.chatBoxReplyUser.displayName + userColor: currentRoom.chatBoxReplyUser.color + userAvatar: currentRoom.chatBoxReplyUser.avatarSource text: currentRoom.chatBoxReplyMessage } } diff --git a/src/qml/Component/NeochatMaximizeComponent.qml b/src/qml/Component/NeochatMaximizeComponent.qml index 1c07f88f8..5d9ea369e 100644 --- a/src/qml/Component/NeochatMaximizeComponent.qml +++ b/src/qml/Component/NeochatMaximizeComponent.qml @@ -54,7 +54,7 @@ Components.AlbumMaximizeComponent { implicitWidth: Kirigami.Units.iconSizes.medium implicitHeight: Kirigami.Units.iconSizes.medium - name: root.author.name ?? root.author.displayName + name: root.author.displayName source: root.author.avatarSource color: root.author.color } @@ -62,7 +62,7 @@ Components.AlbumMaximizeComponent { spacing: 0 QQC2.Label { id: userLabel - text: root.author.name ?? root.author.displayName + text: root.author.displayName color: root.author.color font.weight: Font.Bold elide: Text.ElideRight diff --git a/src/qml/Component/Timeline/LocationDelegate.qml b/src/qml/Component/Timeline/LocationDelegate.qml index cac8479aa..2228dcef0 100644 --- a/src/qml/Component/Timeline/LocationDelegate.qml +++ b/src/qml/Component/Timeline/LocationDelegate.qml @@ -93,7 +93,7 @@ TimelineContainer { visible: root.asset === "m.self" width: height height: parent.height / 3 + 1 - name: root.author.name ?? root.author.displayName + name: root.author.displayName source: root.author.avatarSource color: root.author.color } diff --git a/src/qml/Component/Timeline/ReplyComponent.qml b/src/qml/Component/Timeline/ReplyComponent.qml index 4a53f1747..b402b3f46 100644 --- a/src/qml/Component/Timeline/ReplyComponent.qml +++ b/src/qml/Component/Timeline/ReplyComponent.qml @@ -100,7 +100,7 @@ Item { implicitHeight: Kirigami.Units.iconSizes.small source: root.author.avatarSource - name: root.author.displayName || "" + name: root.author.displayName color: root.author.color } QQC2.Label { diff --git a/src/qml/Component/Timeline/TimelineContainer.qml b/src/qml/Component/Timeline/TimelineContainer.qml index 6f9bf37b2..56870a6ac 100644 --- a/src/qml/Component/Timeline/TimelineContainer.qml +++ b/src/qml/Component/Timeline/TimelineContainer.qml @@ -381,7 +381,7 @@ ColumnLayout { visible: root.showAuthor && Config.showAvatarInTimeline && (Config.compactLayout || !showUserMessageOnRight) - name: root.author.name ?? root.author.displayName + name: root.author.displayName source: root.author.avatarSource color: root.author.color