Create a list model for readmarkers

Create a list model for read markers. The primary reason is to stop `RoomMembers` being accessed after their state event is deleted. With this the read marker doesn't pass and `RoomMember` objects to qml.
This commit is contained in:
James Graham
2024-07-20 18:05:15 +00:00
parent cc068f9ebb
commit 73de99f661
11 changed files with 286 additions and 267 deletions

View File

@@ -75,8 +75,6 @@ private Q_SLOTS:
void nullThread();
void location();
void nullLocation();
void readMarkers();
void nullReadMarkers();
};
void EventHandlerTest::initTestCase()
@@ -521,59 +519,5 @@ void EventHandlerTest::nullLocation()
QCOMPARE(emptyHandler.getLocationAssetType(), QString());
}
void EventHandlerTest::readMarkers()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandler.hasReadMarkers(), true);
auto readMarkers = eventHandler.getReadMarkers();
QCOMPARE(readMarkers.size(), 1);
QCOMPARE(readMarkers[0].id(), QStringLiteral("@alice:example.org"));
QCOMPARE(eventHandler.getNumberExcessReadMarkers(), QString());
QCOMPARE(eventHandler.getReadMarkersString(), QStringLiteral("1 user: Alice Margatroid"));
EventHandler eventHandler2(room, room->messageEvents().at(2).get());
QCOMPARE(eventHandler2.hasReadMarkers(), true);
readMarkers = eventHandler2.getReadMarkers();
QCOMPARE(readMarkers.size(), 5);
QCOMPARE(eventHandler2.getNumberExcessReadMarkers(), QStringLiteral("+ 1"));
// There are no guarantees on the order of the users it will be different every time so don't match the whole string.
QCOMPARE(eventHandler2.getReadMarkersString().startsWith(QStringLiteral("6 users:")), true);
}
void EventHandlerTest::nullReadMarkers()
{
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_room set to nullptr.");
QCOMPARE(emptyHandler.hasReadMarkers(), false);
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getReadMarkers(), QList<Quotient::RoomMember>());
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getNumberExcessReadMarkers(), QString());
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getReadMarkersString(), QString());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_event set to nullptr.");
QCOMPARE(noEventHandler.hasReadMarkers(), false);
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getReadMarkers(), QList<Quotient::RoomMember>());
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getNumberExcessReadMarkers(), QString());
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getReadMarkersString(), QString());
}
QTEST_MAIN(EventHandlerTest)
#include "eventhandlertest.moc"

View File

@@ -186,6 +186,8 @@ add_library(neochat STATIC
models/permissionsmodel.h
threepidbindhelper.cpp
threepidbindhelper.h
models/readmarkermodel.cpp
models/readmarkermodel.h
)
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES

View File

@@ -961,102 +961,4 @@ QString EventHandler::getLocationAssetType() const
return assetType;
}
bool EventHandler::hasReadMarkers() const
{
if (m_room == nullptr) {
qCWarning(EventHandling) << "hasReadMarkers called with m_room set to nullptr.";
return false;
}
if (m_event == nullptr) {
qCWarning(EventHandling) << "hasReadMarkers called with m_event set to nullptr.";
return false;
}
auto userIds = m_room->userIdsAtEvent(m_event->id());
userIds.remove(m_room->localMember().id());
return userIds.size() > 0;
}
QList<Quotient::RoomMember> EventHandler::getReadMarkers(int maxMarkers) const
{
if (m_room == nullptr) {
qCWarning(EventHandling) << "getReadMarkers called with m_room set to nullptr.";
return {};
}
if (m_event == nullptr) {
qCWarning(EventHandling) << "getReadMarkers called with m_event set to nullptr.";
return {};
}
auto userIds_temp = m_room->userIdsAtEvent(m_event->id());
userIds_temp.remove(m_room->localMember().id());
auto userIds = userIds_temp.values();
if (userIds.count() > maxMarkers) {
userIds = userIds.mid(0, maxMarkers);
}
QList<Quotient::RoomMember> users;
users.reserve(userIds.size());
for (const auto &userId : userIds) {
users += m_room->member(userId);
}
return users;
}
QString EventHandler::getNumberExcessReadMarkers(int maxMarkers) const
{
if (m_room == nullptr) {
qCWarning(EventHandling) << "getNumberExcessReadMarkers called with m_room set to nullptr.";
return {};
}
if (m_event == nullptr) {
qCWarning(EventHandling) << "getNumberExcessReadMarkers called with m_event set to nullptr.";
return {};
}
auto userIds = m_room->userIdsAtEvent(m_event->id());
userIds.remove(m_room->localMember().id());
if (userIds.count() > maxMarkers) {
return QStringLiteral("+ ") + QString::number(userIds.count() - maxMarkers);
} else {
return QString();
}
}
QString EventHandler::getReadMarkersString() const
{
if (m_room == nullptr) {
qCWarning(EventHandling) << "getReadMarkersString called with m_room set to nullptr.";
return {};
}
if (m_event == nullptr) {
qCWarning(EventHandling) << "getReadMarkersString called with m_event set to nullptr.";
return {};
}
auto userIds = m_room->userIdsAtEvent(m_event->id());
userIds.remove(m_room->localMember().id());
/**
* The string ends up in the form
* "x users: user1DisplayName, user2DisplayName, etc."
*/
QString readMarkersString = i18np("1 user: ", "%1 users: ", userIds.size());
for (const auto &userId : userIds) {
auto member = m_room->member(userId);
QString displayName;
if (!m_room->memberIds().contains(userId)) {
displayName = i18nc("A member who is not in the room has been requested.", "unknown member");
} else {
displayName = member.displayName();
}
readMarkersString += displayName + i18nc("list separator", ", ");
}
readMarkersString.chop(2);
return readMarkersString;
}
#include "moc_eventhandler.cpp"

View File

@@ -342,43 +342,6 @@ public:
*/
QString getLocationAssetType() const;
/**
* @brief Whether the event has any read marker for other users.
*/
bool hasReadMarkers() const;
/**
* @brief Returns a list of user read marker for the event.
*
* @param maxMarkers the maximum number of users to return. Usually the number
* of user read makers shown is limited to not clutter the UI.
* This needs to be the same as used in getNumberExcessReadMarkers
* so that the markers line up with the number displayed, i.e.
* the number of users shown plus the excess number will be
* the total number of other user read markers at an event.
*/
QList<Quotient::RoomMember> getReadMarkers(int maxMarkers = 5) const;
/**
* @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.
*
* @param maxMarkers the maximum number of markers shown in the UI. This needs to
* be the same as used in getReadMarkers so that the value lines
* up with the number displayed, i.e. the number of users shown
* plus the excess number will be the total number of other user
* read markers at an event.
*/
QString getNumberExcessReadMarkers(int maxMarkers = 5) const;
/**
* @brief Returns a string with the names of the read markers at the event.
*
* This is in the form "x users: name 1, name 2, ...".
*/
QString getReadMarkersString() const;
private:
const NeoChatRoom *m_room = nullptr;
const Quotient::RoomEvent *m_event = nullptr;

View File

@@ -26,6 +26,7 @@
#include "messagecontentmodel.h"
#include "models/messagefiltermodel.h"
#include "models/reactionmodel.h"
#include "readmarkermodel.h"
#include "texthandler.h"
using namespace Quotient;
@@ -45,8 +46,6 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
roles[ThreadRootRole] = "threadRoot";
roles[ShowSectionRole] = "showSection";
roles[ReadMarkersRole] = "readMarkers";
roles[ExcessReadMarkersRole] = "excessReadMarkers";
roles[ReadMarkersStringRole] = "readMarkersString";
roles[ShowReadMarkersRole] = "showReadMarkers";
roles[ReactionRole] = "reaction";
roles[ShowReactionsRole] = "showReactions";
@@ -87,6 +86,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
// HACK: Reset the model to a null room first to make sure QML dismantles
// last room's objects before the room is actually changed
beginResetModel();
m_readMarkerModels.clear();
m_currentRoom->disconnect(this);
m_currentRoom = nullptr;
endResetModel();
@@ -100,9 +100,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
room->setDisplayed();
for (auto event = m_currentRoom->messageEvents().begin(); event != m_currentRoom->messageEvents().end(); ++event) {
if (const auto &roomMessageEvent = &*event->viewAs<RoomMessageEvent>()) {
createEventObjects(roomMessageEvent);
}
createEventObjects(&*event->viewAs<RoomEvent>());
if (event->event()->is<PollStartEvent>()) {
m_currentRoom->createPollHandler(eventCast<const PollStartEvent>(event->event()));
}
@@ -115,11 +113,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
connect(m_currentRoom, &Room::aboutToAddNewMessages, this, [this](RoomEventsRange events) {
for (auto &&event : events) {
const RoomMessageEvent *message = dynamic_cast<RoomMessageEvent *>(event.get());
if (message != nullptr) {
createEventObjects(message);
}
createEventObjects(event.get());
if (event->is<PollStartEvent>()) {
m_currentRoom->createPollHandler(eventCast<const PollStartEvent>(event.get()));
}
@@ -129,9 +123,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
});
connect(m_currentRoom, &Room::aboutToAddHistoricalMessages, this, [this](RoomEventsRange events) {
for (auto &event : events) {
if (const auto &roomMessageEvent = dynamic_cast<RoomMessageEvent *>(event.get())) {
createEventObjects(roomMessageEvent);
}
createEventObjects(event.get());
if (event->is<PollStartEvent>()) {
m_currentRoom->createPollHandler(eventCast<const PollStartEvent>(event.get()));
}
@@ -195,10 +187,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
moveReadMarker(toEventId);
});
connect(m_currentRoom, &Room::replacedEvent, this, [this](const RoomEvent *newEvent) {
const RoomMessageEvent *message = eventCast<const RoomMessageEvent>(newEvent);
if (message != nullptr) {
createEventObjects(message);
}
createEventObjects(newEvent);
});
connect(m_currentRoom, &Room::updatedEvent, this, [this](const QString &eventId) {
if (eventId.isEmpty()) { // How did we get here?
@@ -206,9 +195,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
}
const auto eventIt = m_currentRoom->findInTimeline(eventId);
if (eventIt != m_currentRoom->historyEdge()) {
if (const auto &event = dynamic_cast<const RoomMessageEvent *>(&**eventIt)) {
createEventObjects(event);
}
createEventObjects(eventIt->event());
if (eventIt->event()->is<PollStartEvent>()) {
m_currentRoom->createPollHandler(eventCast<const PollStartEvent>(eventIt->event()));
}
@@ -216,11 +203,10 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
refreshEventRoles(eventId, {Qt::DisplayRole});
});
connect(m_currentRoom, &Room::changed, this, [this](Room::Changes changes) {
if (changes & (Room::Change::PartiallyReadStats | Room::Change::UnreadStats | Room::Change::Other | Room::Change::Members)) {
if (changes.testFlag(Quotient::Room::Change::Other)) {
// this is slow
for (auto it = m_currentRoom->messageEvents().rbegin(); it != m_currentRoom->messageEvents().rend(); ++it) {
auto event = it->event();
refreshEventRoles(event->id(), {ReadMarkersRole, ReadMarkersStringRole, ExcessReadMarkersRole});
createEventObjects(it->event());
}
}
});
@@ -557,19 +543,15 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
}
if (role == ReadMarkersRole) {
return QVariant::fromValue(eventHandler.getReadMarkers());
}
if (role == ExcessReadMarkersRole) {
return eventHandler.getNumberExcessReadMarkers();
}
if (role == ReadMarkersStringRole) {
return eventHandler.getReadMarkersString();
if (m_readMarkerModels.contains(evt.id())) {
return QVariant::fromValue<ReadMarkerModel *>(m_readMarkerModels[evt.id()].get());
} else {
return QVariantList();
}
}
if (role == ShowReadMarkersRole) {
return eventHandler.hasReadMarkers();
return m_readMarkerModels.contains(evt.id());
}
if (role == ReactionRole) {
@@ -630,30 +612,61 @@ int MessageEventModel::eventIdToRow(const QString &eventID) const
return it - m_currentRoom->messageEvents().rbegin() + timelineBaseIndex();
}
void MessageEventModel::createEventObjects(const Quotient::RoomMessageEvent *event)
void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event)
{
if (event == nullptr) {
return;
}
auto eventId = event->id();
// ReactionModel handles updates to add and remove reactions, we only need to
// ReadMarkerModel handles updates to add and remove markers, we only need to
// handle adding and removing whole models here.
if (m_reactionModels.contains(eventId)) {
if (m_readMarkerModels.contains(eventId)) {
// If a model already exists but now has no reactions remove it
if (m_reactionModels[eventId]->rowCount() <= 0) {
m_reactionModels.remove(eventId);
if (m_readMarkerModels[eventId]->rowCount() <= 0) {
m_readMarkerModels.remove(eventId);
if (!resetting) {
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
refreshEventRoles(eventId, {ReadMarkersRole, ShowReadMarkersRole});
}
}
} else {
if (m_currentRoom->relatedEvents(*event, Quotient::EventRelation::AnnotationType).count() > 0) {
auto memberIds = m_currentRoom->userIdsAtEvent(eventId);
memberIds.remove(m_currentRoom->localMember().id());
if (memberIds.size() > 0) {
// If a model doesn't exist and there are reactions add it.
auto reactionModel = QSharedPointer<ReactionModel>(new ReactionModel(event, m_currentRoom));
if (reactionModel->rowCount() > 0) {
m_reactionModels[eventId] = reactionModel;
auto newModel = QSharedPointer<ReadMarkerModel>(new ReadMarkerModel(eventId, m_currentRoom));
if (newModel->rowCount() > 0) {
m_readMarkerModels[eventId] = newModel;
if (!resetting) {
refreshEventRoles(eventId, {ReadMarkersRole, ShowReadMarkersRole});
}
}
}
}
if (const auto roomEvent = eventCast<const RoomMessageEvent>(event)) {
// ReactionModel handles updates to add and remove reactions, we only need to
// handle adding and removing whole models here.
if (m_reactionModels.contains(eventId)) {
// If a model already exists but now has no reactions remove it
if (m_reactionModels[eventId]->rowCount() <= 0) {
m_reactionModels.remove(eventId);
if (!resetting) {
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
}
}
} else {
if (m_currentRoom->relatedEvents(*event, Quotient::EventRelation::AnnotationType).count() > 0) {
// If a model doesn't exist and there are reactions add it.
auto reactionModel = QSharedPointer<ReactionModel>(new ReactionModel(roomEvent, m_currentRoom));
if (reactionModel->rowCount() > 0) {
m_reactionModels[eventId] = reactionModel;
if (!resetting) {
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
}
}
}
}
}
}

View File

@@ -10,6 +10,7 @@
#include "linkpreviewer.h"
#include "neochatroom.h"
#include "pollhandler.h"
#include "readmarkermodel.h"
class ReactionModel;
@@ -58,8 +59,6 @@ public:
ShowSectionRole, /**< Whether the section header should be shown. */
ReadMarkersRole, /**< The first 5 other users at the event for read marker tracking. */
ExcessReadMarkersRole, /**< The number of other users at the event after the first 5. */
ReadMarkersStringRole, /**< String with the display name and mxID of the users at the event. */
ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
ReactionRole, /**< List model for this event. */
ShowReactionsRole, /**< Whether there are any reactions to be shown. */
@@ -116,6 +115,7 @@ private:
bool movingEvent = false;
KFormat m_format;
QMap<QString, QSharedPointer<ReadMarkerModel>> m_readMarkerModels;
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
[[nodiscard]] int timelineBaseIndex() const;
@@ -130,7 +130,7 @@ private:
int refreshEventRoles(const QString &eventId, const QList<int> &roles = {});
void moveReadMarker(const QString &toEventId);
void createEventObjects(const Quotient::RoomMessageEvent *event);
void createEventObjects(const Quotient::RoomEvent *event);
// Hack to ensure that we don't call endInsertRows when we haven't called beginInsertRows
bool m_initialized = false;

View File

@@ -0,0 +1,136 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "readmarkermodel.h"
#include <KLocalizedString>
#include <Quotient/roommember.h>
#define MAXMARKERS 5
ReadMarkerModel::ReadMarkerModel(const QString &eventId, NeoChatRoom *room)
: QAbstractListModel(nullptr)
, m_room(room)
, m_eventId(eventId)
{
Q_ASSERT(!m_eventId.isEmpty());
Q_ASSERT(m_room != nullptr);
connect(m_room, &NeoChatRoom::changed, this, [this](Quotient::Room::Changes changes) {
if (m_room != nullptr && changes.testFlag(Quotient::Room::Change::Other)) {
auto memberIds = m_room->userIdsAtEvent(m_eventId).values();
if (memberIds == m_markerIds) {
return;
}
beginResetModel();
m_markerIds.clear();
endResetModel();
beginResetModel();
memberIds.removeAll(m_room->localMember().id());
m_markerIds = memberIds;
endResetModel();
Q_EMIT reactionUpdated();
}
});
connect(m_room, &NeoChatRoom::memberNameUpdated, this, [this](Quotient::RoomMember member) {
if (m_markerIds.contains(member.id())) {
const auto memberIndex = index(m_markerIds.indexOf(member.id()));
Q_EMIT dataChanged(memberIndex, memberIndex);
}
});
connect(m_room, &NeoChatRoom::memberAvatarUpdated, this, [this](Quotient::RoomMember member) {
if (m_markerIds.contains(member.id())) {
const auto memberIndex = index(m_markerIds.indexOf(member.id()));
Q_EMIT dataChanged(memberIndex, memberIndex);
}
});
beginResetModel();
auto userIds = m_room->userIdsAtEvent(m_eventId);
userIds.remove(m_room->localMember().id());
m_markerIds = userIds.values();
endResetModel();
Q_EMIT reactionUpdated();
}
QVariant ReadMarkerModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return {};
}
if (index.row() >= rowCount()) {
qDebug() << "ReactionModel, something's wrong: index.row() >= rowCount()";
return {};
}
const auto member = m_room->member(m_markerIds.value(index.row()));
if (role == DisplayNameRole) {
return member.htmlSafeDisplayName();
}
if (role == AvatarUrlRole) {
return member.avatarUrl();
}
if (role == ColorRole) {
return member.color();
}
return {};
}
int ReadMarkerModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return std::min(int(m_markerIds.size()), MAXMARKERS);
}
QHash<int, QByteArray> ReadMarkerModel::roleNames() const
{
return {
{DisplayNameRole, "displayName"},
{AvatarUrlRole, "avatarUrl"},
{ColorRole, "memberColor"},
};
}
QString ReadMarkerModel::readMarkersString()
{
/**
* The string ends up in the form
* "x users: user1DisplayName, user2DisplayName, etc."
*/
QString readMarkersString = i18np("1 user: ", "%1 users: ", m_markerIds.size());
for (const auto &memberId : m_markerIds) {
auto member = m_room->member(memberId);
QString displayName = member.htmlSafeDisambiguatedName();
if (displayName.isEmpty()) {
displayName = i18nc("A member who is not in the room has been requested.", "unknown member");
}
readMarkersString += displayName + i18nc("list separator", ", ");
}
readMarkersString.chop(2);
return readMarkersString;
}
QString ReadMarkerModel::excessReadMarkersString()
{
if (m_room == nullptr) {
return {};
}
if (m_markerIds.size() > MAXMARKERS) {
return QStringLiteral("+ ") + QString::number(m_markerIds.size() - MAXMARKERS);
} else {
return QString();
}
}
#include "moc_readmarkermodel.cpp"

View File

@@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QAbstractListModel>
#include <qtmetamacros.h>
#include "neochatroom.h"
/**
* @class ReadMarkerModel
*
* This class defines the model for visualising a list of reactions to an event.
*/
class ReadMarkerModel : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
/**
* @brief Returns a string with the names of the read markers at the event.
*
* This is in the form "x users: name 1, name 2, ...".
*/
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.
*/
enum Roles {
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. */
};
explicit ReadMarkerModel(const QString &eventId, NeoChatRoom *room);
QString readMarkersString();
QString excessReadMarkersString();
/**
* @brief Get the given role value at the given index.
*
* @sa QAbstractItemModel::data
*/
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
/**
* @brief Number of rows in the model.
*
* @sa QAbstractItemModel::rowCount
*/
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
/**
* @brief Returns a mapping from Role enum values to role names.
*
* @sa Roles, QAbstractItemModel::roleNames()
*/
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
Q_SIGNALS:
void reactionUpdated();
private:
QPointer<NeoChatRoom> m_room;
QString m_eventId;
QList<QString> m_markerIds;
};

View File

@@ -13,20 +13,21 @@ Flow {
property var avatarSize: Kirigami.Units.iconSizes.small
property alias model: avatarFlowRepeater.model
property string toolTipText
property alias excessAvatars: excessAvatarsLabel.text
spacing: -avatarSize / 2
Repeater {
id: avatarFlowRepeater
delegate: KirigamiComponents.Avatar {
required property var modelData
required property string displayName
required property url avatarUrl
required property color memberColor
implicitWidth: root.avatarSize
implicitHeight: root.avatarSize
name: modelData.displayName
source: modelData.avatarUrl
color: modelData.color
name: displayName
source: avatarUrl
color: memberColor
}
}
QQC2.Label {
@@ -34,6 +35,9 @@ Flow {
visible: text !== ""
color: Kirigami.Theme.textColor
horizontalAlignment: Text.AlignHCenter
text: root.model?.excessReadMarkersString ?? ""
background: Kirigami.ShadowedRectangle {
color: Kirigami.Theme.backgroundColor
Kirigami.Theme.inherit: false
@@ -54,7 +58,7 @@ Flow {
}
}
QQC2.ToolTip.text: toolTipText
QQC2.ToolTip.text: root.model?.readMarkersString ?? ""
QQC2.ToolTip.visible: hoverHandler.hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay

View File

@@ -82,16 +82,6 @@ TimelineDelegate {
*/
required property var readMarkers
/**
* @brief String with the display name and matrix ID of the other user read markers.
*/
required property string readMarkersString
/**
* @brief The number of other users at the event after the first 5.
*/
required property var excessReadMarkers
/**
* @brief Whether the other user read marker component should be shown.
*/
@@ -342,8 +332,6 @@ TimelineDelegate {
Layout.rightMargin: Kirigami.Units.largeSpacing
visible: root.showReadMarkers
model: root.readMarkers
toolTipText: root.readMarkersString
excessAvatars: root.excessReadMarkers
}
DelegateSizeHelper {

View File

@@ -53,16 +53,6 @@ TimelineDelegate {
*/
required property var readMarkers
/**
* @brief String with the display name and matrix ID of the other user read markers.
*/
required property string readMarkersString
/**
* @brief The number of other users at the event after the first 5.
*/
required property var excessReadMarkers
/**
* @brief Whether the other user read marker component should be shown.
*/
@@ -197,8 +187,6 @@ TimelineDelegate {
Layout.alignment: Qt.AlignRight
visible: root.showReadMarkers
model: root.readMarkers
toolTipText: root.readMarkersString
excessAvatars: root.excessReadMarkers
}
}