Add dialog to see who has read this message
You were previously relegated to looking at any avatars or a buggy tooltip, but suffer no longer! If you tap on a message's read marker, a dialog will pop up listing the users who have read it. Uou can also view their profiles from here, etc.
This commit is contained in:
@@ -106,6 +106,7 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
|||||||
qml/NewPollDialog.qml
|
qml/NewPollDialog.qml
|
||||||
qml/UserMenu.qml
|
qml/UserMenu.qml
|
||||||
qml/MeetingDialog.qml
|
qml/MeetingDialog.qml
|
||||||
|
qml/SeenByDialog.qml
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
QtCore
|
QtCore
|
||||||
QtQuick
|
QtQuick
|
||||||
|
|||||||
90
src/app/qml/SeenByDialog.qml
Normal file
90
src/app/qml/SeenByDialog.qml
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026 Joshua Goins <josh@redstrate.com>
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,16 +8,24 @@ import QtQuick.Layouts
|
|||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||||
|
|
||||||
|
import org.kde.neochat
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var avatarSize: Kirigami.Units.iconSizes.small
|
property var avatarSize: Kirigami.Units.iconSizes.small
|
||||||
property alias model: avatarFlowRepeater.model
|
property alias model: root.limiterModel.sourceModel
|
||||||
property string toolTipText
|
property string toolTipText
|
||||||
|
|
||||||
|
property LimiterModel limiterModel: LimiterModel {
|
||||||
|
maximumCount: 5
|
||||||
|
}
|
||||||
|
|
||||||
spacing: -avatarSize / 2
|
spacing: -avatarSize / 2
|
||||||
Repeater {
|
Repeater {
|
||||||
id: avatarFlowRepeater
|
id: avatarFlowRepeater
|
||||||
|
model: root.limiterModel
|
||||||
|
|
||||||
delegate: KirigamiComponents.Avatar {
|
delegate: KirigamiComponents.Avatar {
|
||||||
required property string displayName
|
required property string displayName
|
||||||
required property url avatarUrl
|
required property url avatarUrl
|
||||||
@@ -39,11 +47,11 @@ RowLayout {
|
|||||||
Layout.preferredHeight: Kirigami.Units.iconSizes.small + Kirigami.Units.smallSpacing
|
Layout.preferredHeight: Kirigami.Units.iconSizes.small + Kirigami.Units.smallSpacing
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
visible: text !== ""
|
visible: root.limiterModel.extraCount > 0
|
||||||
color: Kirigami.Theme.textColor
|
color: Kirigami.Theme.textColor
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
text: root.model?.excessReadMarkersString ?? ""
|
text: "+ " + root.limiterModel.extraCount
|
||||||
|
|
||||||
background: Kirigami.ShadowedRectangle {
|
background: Kirigami.ShadowedRectangle {
|
||||||
color: Kirigami.Theme.backgroundColor
|
color: Kirigami.Theme.backgroundColor
|
||||||
|
|||||||
@@ -208,6 +208,15 @@ MessageDelegateBase {
|
|||||||
|
|
||||||
readMarkerComponent: AvatarFlow {
|
readMarkerComponent: AvatarFlow {
|
||||||
model: root.readMarkers
|
model: root.readMarkers
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
onTapped: {
|
||||||
|
const dialog = Qt.createComponent("org.kde.neochat", "SeenByDialog").createObject(root, {
|
||||||
|
model: root.readMarkers
|
||||||
|
}) as SeenByDialog;
|
||||||
|
dialog.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compactBackgroundComponent: Rectangle {
|
compactBackgroundComponent: Rectangle {
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
|
|
||||||
#include <Quotient/roommember.h>
|
#include <Quotient/roommember.h>
|
||||||
|
|
||||||
#define MAXMARKERS 5
|
|
||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
ReadMarkerModel::ReadMarkerModel(const QString &eventId, NeoChatRoom *room)
|
ReadMarkerModel::ReadMarkerModel(const QString &eventId, NeoChatRoom *room)
|
||||||
@@ -85,13 +83,17 @@ QVariant ReadMarkerModel::data(const QModelIndex &index, int role) const
|
|||||||
return member.color();
|
return member.color();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (role == UserIdRole) {
|
||||||
|
return member.id();
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
int ReadMarkerModel::rowCount(const QModelIndex &parent) const
|
int ReadMarkerModel::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent)
|
Q_UNUSED(parent)
|
||||||
return std::min(int(m_markerIds.size()), MAXMARKERS);
|
return m_markerIds.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> ReadMarkerModel::roleNames() const
|
QHash<int, QByteArray> ReadMarkerModel::roleNames() const
|
||||||
@@ -100,6 +102,7 @@ QHash<int, QByteArray> ReadMarkerModel::roleNames() const
|
|||||||
{DisplayNameRole, "displayName"},
|
{DisplayNameRole, "displayName"},
|
||||||
{AvatarUrlRole, "avatarUrl"},
|
{AvatarUrlRole, "avatarUrl"},
|
||||||
{ColorRole, "memberColor"},
|
{ColorRole, "memberColor"},
|
||||||
|
{UserIdRole, "userId"},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,17 +125,4 @@ QString ReadMarkerModel::readMarkersString()
|
|||||||
return 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"
|
#include "moc_readmarkermodel.cpp"
|
||||||
|
|||||||
@@ -26,13 +26,6 @@ class ReadMarkerModel : public QAbstractListModel
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(QString readMarkersString READ readMarkersString NOTIFY reactionUpdated)
|
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:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Defines the model roles.
|
* @brief Defines the model roles.
|
||||||
@@ -41,12 +34,12 @@ public:
|
|||||||
DisplayNameRole = Qt::DisplayRole, /**< The display name of the member in the room. */
|
DisplayNameRole = Qt::DisplayRole, /**< The display name of the member in the room. */
|
||||||
AvatarUrlRole, /**< The avatar for the member in the room. */
|
AvatarUrlRole, /**< The avatar for the member in the room. */
|
||||||
ColorRole, /**< The color for the member. */
|
ColorRole, /**< The color for the member. */
|
||||||
|
UserIdRole, /** The user ID for the member. */
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit ReadMarkerModel(const QString &eventId, NeoChatRoom *room);
|
explicit ReadMarkerModel(const QString &eventId, NeoChatRoom *room);
|
||||||
|
|
||||||
QString readMarkersString();
|
QString readMarkersString();
|
||||||
QString excessReadMarkersString();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the given role value at the given index.
|
* @brief Get the given role value at the given index.
|
||||||
|
|||||||
Reference in New Issue
Block a user