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.
This commit is contained in:
Joshua Goins
2025-08-30 15:57:31 -04:00
parent e3f618489b
commit 1036384023
3 changed files with 112 additions and 13 deletions

View File

@@ -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;
}
}
}
}

View File

@@ -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 <KIO/Job>
#include <KIO/JobTracker>
#endif
#include <KJobTrackerInterface>
#include <KLocalizedString>
@@ -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<GetOneRoomEventJob>(id(), mostRecentEventId).then([this](const auto &job) {
auto event = fromJson<event_ptr_tt<RoomEvent>>(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<QStringList>(u"additional_creators"_s).contains(userId));
}
QString NeoChatRoom::pinnedMessage() const
{
return m_pinnedMessage;
}
#include "moc_neochatroom.cpp"

View File

@@ -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<QString, std::unique_ptr<NeochatRoomMember>> m_memberObjects;
static std::function<bool(const Quotient::RoomEvent *)> 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.