Cherrypick Other user read markers 23.04

Add the ability to see other user's read markers. 

![image](/uploads/a4bdc35a60ab10685c7d3ea4d9b13c87/image.png)

Implements network/neochat#133


(cherry picked from commit 23303c0483)
This commit is contained in:
James Graham
2023-03-13 16:01:20 +00:00
parent 8983129520
commit 498cfedfea
6 changed files with 123 additions and 0 deletions

View File

@@ -54,6 +54,9 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
roles[UserMarkerRole] = "userMarker";
roles[ShowAuthorRole] = "showAuthor";
roles[ShowSectionRole] = "showSection";
roles[ReadMarkersRole] = "readMarkers";
roles[ReadMarkersStringRole] = "readMarkersString";
roles[ShowReadMarkersRole] = "showReadMarkers";
roles[ReactionRole] = "reaction";
roles[IsEditedRole] = "isEdited";
roles[SourceRole] = "source";
@@ -216,6 +219,12 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
}
refreshEventRoles(eventId, {ReactionRole, Qt::DisplayRole});
});
connect(m_currentRoom, &Room::changed, this, [this]() {
for (auto it = m_currentRoom->messageEvents().rbegin(); it != m_currentRoom->messageEvents().rend(); ++it) {
auto event = it->event();
refreshEventRoles(event->id(), {ReadMarkersRole, ReadMarkersStringRole});
}
});
connect(m_currentRoom, &Room::newFileTransfer, this, &MessageEventModel::refreshEvent);
connect(m_currentRoom, &Room::fileTransferProgress, this, &MessageEventModel::refreshEvent);
connect(m_currentRoom, &Room::fileTransferCompleted, this, &MessageEventModel::refreshEvent);
@@ -791,6 +800,65 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
return false;
}
if (role == ReadMarkersRole) {
#ifdef QUOTIENT_07
auto userIds = room()->userIdsAtEvent(evt.id());
userIds.remove(m_currentRoom->localUser()->id());
#else
auto userIds = room()->usersAtEventId(evt.id());
userIds.removeAll(m_currentRoom->localUser());
#endif
QVariantList users;
users.reserve(userIds.size());
for (const auto &userId : userIds) {
#ifdef QUOTIENT_07
auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId));
#else
auto user = static_cast<NeoChatUser *>(userId);
#endif
users += userAtEvent(user, m_currentRoom, evt);
}
return users;
}
if (role == ReadMarkersStringRole) {
#ifdef QUOTIENT_07
auto userIds = room()->userIdsAtEvent(evt.id());
userIds.remove(m_currentRoom->localUser()->id());
#else
auto userIds = room()->usersAtEventId(evt.id());
userIds.removeAll(m_currentRoom->localUser());
#endif
/**
* 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) {
#ifdef QUOTIENT_07
auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId));
#else
auto user = static_cast<NeoChatUser *>(userId);
#endif
readMarkersString += user->displayname(m_currentRoom) + i18nc("list separator", ", ");
}
readMarkersString.chop(2);
return readMarkersString;
}
if (role == ShowReadMarkersRole) {
#ifdef QUOTIENT_07
auto userIds = room()->userIdsAtEvent(evt.id());
userIds.remove(m_currentRoom->localUser()->id());
#else
auto userIds = room()->usersAtEventId(evt.id());
userIds.removeAll(m_currentRoom->localUser());
#endif
return userIds.size() > 0;
}
if (role == ReactionRole) {
const auto &annotations = m_currentRoom->relatedEvents(evt, EventRelation::Annotation());
if (annotations.isEmpty()) {

View File

@@ -57,6 +57,9 @@ public:
ShowAuthorRole,
ShowSectionRole,
ReadMarkersRole, /**< QVariantList of users at the event for read marker tracking. */
ReadMarkersStringRole, /**< QString with the display name and mxID of the users at the event. */
ShowReadMarkersRole, /**< bool with whether there are any other user read markers to be shown. */
ReactionRole,
IsEditedRole,

View File

@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import org.kde.kirigami 2.15 as Kirigami
Flow {
id: root
property var avatarSize: Kirigami.Units.iconSizes.small
property alias model: avatarFlowRepeater.model
property string toolTipText
spacing: -avatarSize / 2
Repeater {
id: avatarFlowRepeater
delegate: Kirigami.Avatar {
implicitWidth: avatarSize
implicitHeight: avatarSize
name: modelData.displayName
source: modelData.avatarMediaId ? ("image://mxc/" + modelData.avatarMediaId) : ""
color: modelData.color
}
}
QQC2.ToolTip.text: toolTipText
QQC2.ToolTip.visible: hoverHandler.hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
HoverHandler {
id: hoverHandler
margin: Kirigami.Units.smallSpacing
}
}

View File

@@ -134,5 +134,12 @@ QQC2.Control {
folded = !folded
foldedChanged()
}
AvatarFlow {
Layout.alignment: Qt.AlignRight
Layout.rightMargin: Kirigami.Units.largeSpacing
visible: showReadMarkers
model: readMarkers
toolTipText: readMarkersString
}
}
}

View File

@@ -338,6 +338,13 @@ ColumnLayout {
visible: eventType !== MessageEventModel.State && eventType !== MessageEventModel.Notice && reaction != undefined && reaction.length > 0
}
AvatarFlow {
Layout.alignment: Qt.AlignRight
Layout.rightMargin: Kirigami.Units.largeSpacing
visible: showReadMarkers
model: readMarkers
toolTipText: readMarkersString
}
function isVisibleInTimeline() {
let yoff = Math.round(y - ListView.view.contentY);

View File

@@ -49,6 +49,7 @@
<file alias="MimeComponent.qml">qml/Component/Timeline/MimeComponent.qml</file>
<file alias="StateComponent.qml">qml/Component/Timeline/StateComponent.qml</file>
<file alias="MessageEditComponent.qml">qml/Component/Timeline/MessageEditComponent.qml</file>
<file alias="AvatarFlow.qml">qml/Component/Timeline/AvatarFlow.qml</file>
<file alias="LoginStep.qml">qml/Component/Login/LoginStep.qml</file>
<file alias="Login.qml">qml/Component/Login/Login.qml</file>
<file alias="Password.qml">qml/Component/Login/Password.qml</file>