From 1036384023163ed519091d8dc69f03f022137e01 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sat, 30 Aug 2025 15:57:31 -0400 Subject: [PATCH] Add recently pinned message to the top of the window This makes pinned messages actually quite useful, as rooms can stick persistent and important information for users to see. (For example, the KWin has crash triage meeting information pinned in their room.) For simplicity and space reasons, only one line of the generic message body is shown. --- src/app/qml/RoomPage.qml | 90 +++++++++++++++++++++++++++++----- src/libneochat/neochatroom.cpp | 22 +++++++++ src/libneochat/neochatroom.h | 13 +++++ 3 files changed, 112 insertions(+), 13 deletions(-) diff --git a/src/app/qml/RoomPage.qml b/src/app/qml/RoomPage.qml index 76dfa47bb..883e6df21 100644 --- a/src/app/qml/RoomPage.qml +++ b/src/app/qml/RoomPage.qml @@ -7,6 +7,7 @@ pragma ComponentBehavior: Bound import QtQuick import QtQuick.Controls as QQC2 import QtQuick.Window +import QtQuick.Layouts import org.kde.kirigami as Kirigami @@ -98,24 +99,87 @@ Kirigami.Page { } } - header: Kirigami.InlineMessage { - id: banner + header: ColumnLayout { + id: headerLayout - // Used to keep track of messages so we can hide the right one at the right time - property string messageId + spacing: 0 - showCloseButton: true - visible: false - position: Kirigami.InlineMessage.Position.Header + readonly property bool shouldShowPins: root.currentRoom.pinnedMessage.length > 0 && !Kirigami.Settings.isMobile - function show(msgid: string): void { - messageId = msgid; - visible = true; + QQC2.Control { + id: pinControl + + visible: headerLayout.shouldShowPins + + Layout.fillWidth: true + + background: Rectangle { + color: Kirigami.Theme.backgroundColor + + Kirigami.Theme.colorSet: Kirigami.Theme.View + Kirigami.Theme.inherit: false + } + + contentItem: RowLayout { + spacing: Kirigami.Units.smallSpacing + + Kirigami.Icon { + source: "pin-symbolic" + + Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium + Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium + } + + QQC2.Label { + text: root.currentRoom.pinnedMessage + maximumLineCount: 1 + elide: Text.ElideRight + + onLinkActivated: link => UrlHelper.openUrl(link) + onHoveredLinkChanged: if (hoveredLink.length > 0 && hoveredLink !== "1") { + (QQC2.ApplicationWindow.window as Main).hoverLinkIndicator.text = hoveredLink; + } else { + (QQC2.ApplicationWindow.window as Main).hoverLinkIndicator.text = ""; + } + + Layout.fillWidth: true + } + } + + TapHandler { + onTapped: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'RoomPinnedMessagesPage'), { + room: root.currentRoom + }, { + title: i18nc("@title", "Pinned Messages") + }); + } } - function hideIf(msgid: string): void { - if (messageId == msgid) { - visible = false; + Kirigami.Separator { + visible: headerLayout.shouldShowPins + + Layout.fillWidth: true + } + + Kirigami.InlineMessage { + id: banner + + // Used to keep track of messages so we can hide the right one at the right time + property string messageId + + showCloseButton: true + visible: false + position: Kirigami.InlineMessage.Position.Header + + function show(msgid: string): void { + messageId = msgid; + visible = true; + } + + function hideIf(msgid: string): void { + if (messageId == msgid) { + visible = false; + } } } } diff --git a/src/libneochat/neochatroom.cpp b/src/libneochat/neochatroom.cpp index 11841c1a9..9f36f81f2 100644 --- a/src/libneochat/neochatroom.cpp +++ b/src/libneochat/neochatroom.cpp @@ -44,6 +44,7 @@ #include "chatbarcache.h" #include "clipboard.h" +#include "eventhandler.h" #include "events/pollevent.h" #include "filetransferpseudojob.h" #include "neochatconnection.h" @@ -55,6 +56,7 @@ #include #include #endif + #include #include @@ -130,11 +132,13 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS this, [this]() { updatePushNotificationState(u"m.push_rules"_s); + loadPinnedMessage(); Q_EMIT canEncryptRoomChanged(); Q_EMIT inviteTimestampChanged(); }, Qt::SingleShotConnection); + connect(this, &Room::pinnedEventsChanged, this, &NeoChatRoom::loadPinnedMessage); connect(this, &Room::changed, this, [this] { Q_EMIT canEncryptRoomChanged(); Q_EMIT parentIdsChanged(); @@ -1170,6 +1174,19 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state) Q_EMIT pushNotificationStateChanged(m_currentPushNotificationState); } +void NeoChatRoom::loadPinnedMessage() +{ + const auto events = pinnedEventIds(); + if (!events.isEmpty()) { + const QString &mostRecentEventId = events.last(); + connection()->callApi(id(), mostRecentEventId).then([this](const auto &job) { + auto event = fromJson>(job->jsonData()); + m_pinnedMessage = EventHandler::richBody(this, event.get()); + Q_EMIT pinnedMessageChanged(); + }); + } +} + void NeoChatRoom::updatePushNotificationState(QString type) { if (type != "m.push_rules"_L1 || m_pushNotificationStateUpdating) { @@ -1810,4 +1827,9 @@ bool NeoChatRoom::isCreator(const QString &userId) const && (createEvent->senderId() == userId || createEvent->contentPart(u"additional_creators"_s).contains(userId)); } +QString NeoChatRoom::pinnedMessage() const +{ + return m_pinnedMessage; +} + #include "moc_neochatroom.cpp" diff --git a/src/libneochat/neochatroom.h b/src/libneochat/neochatroom.h index a69e9dfae..56ec51a0b 100644 --- a/src/libneochat/neochatroom.h +++ b/src/libneochat/neochatroom.h @@ -203,6 +203,11 @@ class NeoChatRoom : public Quotient::Room */ Q_PROPERTY(QString invitingUserId READ invitingUserId NOTIFY baseStateLoaded) + /** + * @brief The most recently pinned message in the room. + */ + Q_PROPERTY(QString pinnedMessage READ pinnedMessage NOTIFY pinnedMessageChanged) + public: explicit NeoChatRoom(Quotient::Connection *connection, QString roomId, Quotient::JoinState joinState = {}); @@ -612,6 +617,11 @@ public: */ bool isCreator(const QString &userId) const; + /** + * @return The most recent pinned message in the room. + */ + QString pinnedMessage() const; + private: bool m_visible = false; @@ -644,6 +654,8 @@ private: std::unordered_map> m_memberObjects; static std::function m_hiddenFilter; + QString m_pinnedMessage; + void loadPinnedMessage(); private Q_SLOTS: void updatePushNotificationState(QString type); @@ -674,6 +686,7 @@ Q_SIGNALS: void extraEventLoaded(const QString &eventId); void extraEventNotFound(const QString &eventId); void inviteTimestampChanged(); + void pinnedMessageChanged(); /** * @brief Request a message be shown to the user of the given type.