From f98b289ba2f8241ebe33da3e4aac9a6e71ca5c3e Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Tue, 27 Dec 2022 01:40:03 +0100 Subject: [PATCH] Show invite reason in the UI (hidden by default) --- CMakeLists.txt | 2 +- io.github.NhekoReborn.Nheko.yaml | 2 +- resources/qml/TimelineView.qml | 39 ++++++++++++++++++++++++++++++++ src/Cache.cpp | 29 +++++++++++++++++++++++- src/CacheStructs.h | 1 + src/Cache_p.h | 8 ++++--- src/timeline/RoomlistModel.cpp | 14 ++++++++++++ src/timeline/RoomlistModel.h | 4 +++- 8 files changed, 92 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3af559a6..8658b228 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -595,7 +595,7 @@ if(USE_BUNDLED_MTXCLIENT) FetchContent_Declare( MatrixClient GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git - GIT_TAG 6252a4a902053fb227b61e65e76c1c29bc905a45 + GIT_TAG d187c63a27710fa87a44ab44d43b7cfa2023132a ) set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "") set(BUILD_LIB_TESTS OFF CACHE INTERNAL "") diff --git a/io.github.NhekoReborn.Nheko.yaml b/io.github.NhekoReborn.Nheko.yaml index a150f97b..e8b6e9f2 100644 --- a/io.github.NhekoReborn.Nheko.yaml +++ b/io.github.NhekoReborn.Nheko.yaml @@ -182,7 +182,7 @@ modules: buildsystem: cmake-ninja name: mtxclient sources: - - commit: 6252a4a902053fb227b61e65e76c1c29bc905a45 + - commit: d187c63a27710fa87a44ab44d43b7cfa2023132a #tag: v0.8.2 type: git url: https://github.com/Nheko-Reborn/mtxclient.git diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 14b74e68..dcbcc87e 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -170,6 +170,7 @@ Item { property string roomName: room ? room.roomName : (roomPreview ? roomPreview.roomName : "") property string roomTopic: room ? room.roomTopic : (roomPreview ? roomPreview.roomTopic : "") property string avatarUrl: room ? room.roomAvatarUrl : (roomPreview ? roomPreview.roomAvatarUrl : "") + property string reason: roomPreview ? roomPreview.reason : "" visible: room != null && room.isSpace || roomPreview != null enabled: visible @@ -277,6 +278,44 @@ Item { onClicked: Rooms.declineInvite(roomPreview.roomid) } + ScrollView { + id: reasonField + property bool showReason: false + + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.leftMargin: Nheko.paddingLarge + Layout.rightMargin: Nheko.paddingLarge + visible: preview.reason !== "" && showReason + + TextArea { + text: TimelineManager.escapeEmoji(preview.reason) + wrapMode: TextEdit.WordWrap + textFormat: TextEdit.RichText + readOnly: true + background: null + selectByMouse: true + color: Nheko.colors.text + horizontalAlignment: TextEdit.AlignHCenter + } + + } + + Button { + id: showReasonButton + + Layout.alignment: Qt.AlignHCenter + //Layout.fillWidth: true + Layout.leftMargin: Nheko.paddingLarge + Layout.rightMargin: Nheko.paddingLarge + + visible: preview.reason !== "" + text: reasonField.showReason ? qsTr("Hide invite reason") : qsTr("Show invite reason") + onClicked: { + reasonField.showReason = !reasonField.showReason; + } + } + Item { visible: room != null Layout.preferredHeight: Math.ceil(fontMetrics.lineSpacing * 2) diff --git a/src/Cache.cpp b/src/Cache.cpp index 809d5e17..5d87f9f2 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -2018,7 +2018,8 @@ Cache::saveInvite(lmdb::txn &txn, auto display_name = msg->content.display_name.empty() ? msg->state_key : msg->content.display_name; - MemberInfo tmp{display_name, msg->content.avatar_url, msg->content.is_direct}; + MemberInfo tmp{ + display_name, msg->content.avatar_url, msg->content.reason, msg->content.is_direct}; membersdb.put(txn, msg->state_key, nlohmann::json(tmp).dump()); } else { @@ -3144,6 +3145,29 @@ Cache::getMembers(const std::string &room_id, std::size_t startIndex, std::size_ } } +std::optional +Cache::getInviteMember(const std::string &room_id, const std::string &user_id) +{ + if (user_id.empty() || !env_.handle()) + return std::nullopt; + + try { + auto txn = ro_txn(env_); + + auto membersdb = getInviteMembersDb(txn, room_id); + + std::string_view info; + if (membersdb.get(txn, user_id, info)) { + MemberInfo m = nlohmann::json::parse(info).get(); + return m; + } + } catch (std::exception &e) { + nhlog::db()->warn( + "Failed to read member ({}) in invite room ({}): {}", user_id, room_id, e.what()); + } + return std::nullopt; +} + std::vector Cache::getMembersFromInvite(const std::string &room_id, std::size_t startIndex, std::size_t len) { @@ -4959,6 +4983,8 @@ to_json(nlohmann::json &j, const MemberInfo &info) j["avatar_url"] = info.avatar_url; if (info.is_direct) j["is_direct"] = info.is_direct; + if (!info.reason.empty()) + j["reason"] = info.reason; } void @@ -4967,6 +4993,7 @@ from_json(const nlohmann::json &j, MemberInfo &info) info.name = j.at("name").get(); info.avatar_url = j.at("avatar_url").get(); info.is_direct = j.value("is_direct", false); + info.reason = j.value("reason", ""); } void diff --git a/src/CacheStructs.h b/src/CacheStructs.h index 1f035fee..535807fe 100644 --- a/src/CacheStructs.h +++ b/src/CacheStructs.h @@ -107,6 +107,7 @@ struct MemberInfo { std::string name; std::string avatar_url; + std::string reason; bool is_direct = false; }; diff --git a/src/Cache_p.h b/src/Cache_p.h index 5a42c7f9..6712e48e 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -244,6 +244,8 @@ public: //! Check if a user is a member of the room. bool isRoomMember(const std::string &user_id, const std::string &room_id); + std::optional + getInviteMember(const std::string &room_id, const std::string &user_id); // // Outbound Megolm Sessions @@ -396,7 +398,7 @@ private: e->content.display_name.empty() ? e->state_key : e->content.display_name; // Lightweight representation of a member. - MemberInfo tmp{display_name, e->content.avatar_url}; + MemberInfo tmp{display_name, e->content.avatar_url, e->content.reason}; membersdb.put(txn, e->state_key, nlohmann::json(tmp).dump()); break; @@ -406,8 +408,8 @@ private: break; } } - - return; + // fallthrough to also store it as state event to eventually migrate away from a + // separate members db. } else if (std::holds_alternative>(event)) { setEncryptedRoom(txn, room_id); return; diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index 82b3fc3d..827cbed1 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -723,6 +723,13 @@ RoomlistModel::getRoomPreviewById(QString roomid) const if (invites.contains(roomid)) { i = invites.value(roomid); preview.isInvite_ = true; + + auto member = cache::client()->getInviteMember(roomid.toStdString(), + http::client()->user_id().to_string()); + + if (member) { + preview.reason_ = QString::fromStdString(member->reason); + } } else { i = previewedRooms.value(roomid); preview.isInvite_ = false; @@ -769,6 +776,13 @@ RoomlistModel::setCurrentRoom(const QString &roomid) if (invites.contains(roomid)) { i = invites.value(roomid); p.isInvite_ = true; + + auto member = cache::client()->getInviteMember(roomid.toStdString(), + http::client()->user_id().to_string()); + + if (member) { + p.reason_ = QString::fromStdString(member->reason); + } } else { i = previewedRooms.value(roomid); p.isInvite_ = false; diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h index 0eb57547..0d52102d 100644 --- a/src/timeline/RoomlistModel.h +++ b/src/timeline/RoomlistModel.h @@ -31,6 +31,7 @@ class RoomPreview Q_PROPERTY(QString roomName READ roomName CONSTANT) Q_PROPERTY(QString roomTopic READ roomTopic CONSTANT) Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl CONSTANT) + Q_PROPERTY(QString reason READ reason CONSTANT) Q_PROPERTY(bool isInvite READ isInvite CONSTANT) public: @@ -40,9 +41,10 @@ public: QString roomName() const { return roomName_; } QString roomTopic() const { return roomTopic_; } QString roomAvatarUrl() const { return roomAvatarUrl_; } + QString reason() const { return reason_; } bool isInvite() const { return isInvite_; } - QString roomid_, roomName_, roomAvatarUrl_, roomTopic_; + QString roomid_, roomName_, roomAvatarUrl_, roomTopic_, reason_; bool isInvite_ = false; };