diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 59c5855e3..ef8830a0c 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -106,6 +106,7 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE qml/NewPollDialog.qml qml/UserMenu.qml qml/MeetingDialog.qml + qml/SeenByDialog.qml DEPENDENCIES QtCore QtQuick diff --git a/src/app/qml/SeenByDialog.qml b/src/app/qml/SeenByDialog.qml new file mode 100644 index 000000000..0dc2a40cd --- /dev/null +++ b/src/app/qml/SeenByDialog.qml @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2026 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-only + +import QtQuick +import QtQuick.Controls as QQC2 +import QtQuick.Layouts + +import org.kde.kirigami as Kirigami +import org.kde.kirigamiaddons.components as KirigamiComponents +import org.kde.kirigamiaddons.delegates as Delegates + +import org.kde.neochat + +Kirigami.Dialog { + id: root + + property var model + + standardButtons: Kirigami.Dialog.NoButton + + width: Math.min(QQC2.ApplicationWindow.window.width, Kirigami.Units.gridUnit * 24) + maximumHeight: Kirigami.Units.gridUnit * 24 + title: i18nc("@title:menu Seen by/read marker dialog", "Seen By") + + contentItem: ColumnLayout { + spacing: 0 + + QQC2.ScrollView { + Layout.fillWidth: true + Layout.fillHeight: true + + ListView { + id: listView + + model: root.model + spacing: Kirigami.Units.smallSpacing + + onCountChanged: { + if (listView.count === 0) { + root.close(); + } + } + + delegate: Delegates.RoundedItemDelegate { + id: userDelegate + + required property string displayName + required property url avatarUrl + required property color memberColor + required property string userId + + implicitHeight: Kirigami.Units.gridUnit * 2 + + text: displayName + highlighted: false + + onClicked: { + root.close(); + RoomManager.resolveResource(userDelegate.userId); + } + + contentItem: RowLayout { + spacing: Kirigami.Units.smallSpacing + + KirigamiComponents.Avatar { + implicitWidth: height + sourceSize { + height: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5 + width: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5 + } + source: userDelegate.avatarUrl + name: userDelegate.displayName + color: userDelegate.memberColor + + Layout.fillHeight: true + } + QQC2.Label { + text: userDelegate.displayName + textFormat: Text.PlainText + elide: Text.ElideRight + clip: true // Intentional to limit insane Unicode in display names + + Layout.fillWidth: true + } + } + } + } + } + } +} diff --git a/src/timeline/AvatarFlow.qml b/src/timeline/AvatarFlow.qml index c62059ed4..224d675bc 100644 --- a/src/timeline/AvatarFlow.qml +++ b/src/timeline/AvatarFlow.qml @@ -8,16 +8,24 @@ import QtQuick.Layouts import org.kde.kirigami as Kirigami import org.kde.kirigamiaddons.labs.components as KirigamiComponents +import org.kde.neochat + RowLayout { id: root property var avatarSize: Kirigami.Units.iconSizes.small - property alias model: avatarFlowRepeater.model + property alias model: root.limiterModel.sourceModel property string toolTipText + property LimiterModel limiterModel: LimiterModel { + maximumCount: 5 + } + spacing: -avatarSize / 2 Repeater { id: avatarFlowRepeater + model: root.limiterModel + delegate: KirigamiComponents.Avatar { required property string displayName required property url avatarUrl @@ -39,11 +47,11 @@ RowLayout { Layout.preferredHeight: Kirigami.Units.iconSizes.small + Kirigami.Units.smallSpacing Layout.fillHeight: true - visible: text !== "" + visible: root.limiterModel.extraCount > 0 color: Kirigami.Theme.textColor horizontalAlignment: Text.AlignHCenter - text: root.model?.excessReadMarkersString ?? "" + text: "+ " + root.limiterModel.extraCount background: Kirigami.ShadowedRectangle { color: Kirigami.Theme.backgroundColor diff --git a/src/timeline/MessageDelegate.qml b/src/timeline/MessageDelegate.qml index 046102e01..4a2c5d680 100644 --- a/src/timeline/MessageDelegate.qml +++ b/src/timeline/MessageDelegate.qml @@ -208,6 +208,15 @@ MessageDelegateBase { readMarkerComponent: AvatarFlow { model: root.readMarkers + + TapHandler { + onTapped: { + const dialog = Qt.createComponent("org.kde.neochat", "SeenByDialog").createObject(root, { + model: root.readMarkers + }) as SeenByDialog; + dialog.open(); + } + } } compactBackgroundComponent: Rectangle { diff --git a/src/timeline/models/readmarkermodel.cpp b/src/timeline/models/readmarkermodel.cpp index 75d089a0d..320fc97e7 100644 --- a/src/timeline/models/readmarkermodel.cpp +++ b/src/timeline/models/readmarkermodel.cpp @@ -7,8 +7,6 @@ #include -#define MAXMARKERS 5 - using namespace Qt::StringLiterals; ReadMarkerModel::ReadMarkerModel(const QString &eventId, NeoChatRoom *room) @@ -85,13 +83,17 @@ QVariant ReadMarkerModel::data(const QModelIndex &index, int role) const return member.color(); } + if (role == UserIdRole) { + return member.id(); + } + return {}; } int ReadMarkerModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) - return std::min(int(m_markerIds.size()), MAXMARKERS); + return m_markerIds.size(); } QHash ReadMarkerModel::roleNames() const @@ -100,6 +102,7 @@ QHash ReadMarkerModel::roleNames() const {DisplayNameRole, "displayName"}, {AvatarUrlRole, "avatarUrl"}, {ColorRole, "memberColor"}, + {UserIdRole, "userId"}, }; } @@ -122,17 +125,4 @@ QString ReadMarkerModel::readMarkersString() return readMarkersString; } -QString ReadMarkerModel::excessReadMarkersString() -{ - if (m_room == nullptr) { - return {}; - } - - if (m_markerIds.size() > MAXMARKERS) { - return u"+ "_s + QString::number(m_markerIds.size() - MAXMARKERS); - } else { - return QString(); - } -} - #include "moc_readmarkermodel.cpp" diff --git a/src/timeline/models/readmarkermodel.h b/src/timeline/models/readmarkermodel.h index 05b14590c..e09b7beb7 100644 --- a/src/timeline/models/readmarkermodel.h +++ b/src/timeline/models/readmarkermodel.h @@ -26,13 +26,6 @@ class ReadMarkerModel : public QAbstractListModel */ Q_PROPERTY(QString readMarkersString READ readMarkersString NOTIFY reactionUpdated) - /** - * @brief Returns the number of excess user read markers for the event. - * - * This returns a string in the form "+ x" ready for use in the UI. - */ - Q_PROPERTY(QString excessReadMarkersString READ excessReadMarkersString NOTIFY reactionUpdated) - public: /** * @brief Defines the model roles. @@ -41,12 +34,12 @@ public: DisplayNameRole = Qt::DisplayRole, /**< The display name of the member in the room. */ AvatarUrlRole, /**< The avatar for the member in the room. */ ColorRole, /**< The color for the member. */ + UserIdRole, /** The user ID for the member. */ }; explicit ReadMarkerModel(const QString &eventId, NeoChatRoom *room); QString readMarkersString(); - QString excessReadMarkersString(); /** * @brief Get the given role value at the given index.