Fix state event visiblity in timeline

Rework the filtering of state events in the timeline and for lastevent. This is now consistent everywhere and includes the following:
- The timeline settings are obeyed everywhere
- A new setting is added to filter all state events
- Last event obeys the timeline setting in all cases
- The roomlist will show a state event as the latest event if it's visible in the timeline
- Names are no longer hyperlinked in eventToString if plaintext is selected.

BUG: 455048\
Closes network/neochat#148
This commit is contained in:
James Graham
2023-03-13 19:00:22 +00:00
parent 81c73037ca
commit 741cb57105
9 changed files with 101 additions and 93 deletions

View File

@@ -66,8 +66,6 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
roles[VerifiedRole] = "verified";
roles[DisplayNameForInitialsRole] = "displayNameForInitials";
roles[AuthorDisplayNameRole] = "authorDisplayName";
roles[IsNameChangeRole] = "isNameChange";
roles[IsAvatarChangeRole] = "isAvatarChange";
roles[IsRedactedRole] = "isRedacted";
roles[GenericDisplayRole] = "genericDisplay";
roles[IsPendingRole] = "isPending";
@@ -610,9 +608,17 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
return pendingIt->deliveryStatus();
}
auto *memberEvent = timelineIt->viewAs<RoomMemberEvent>();
if (memberEvent) {
if ((memberEvent->isJoin() || memberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) {
if (evt.isStateEvent() && !NeoChatConfig::self()->showStateEvent()) {
return EventStatus::Hidden;
}
if (auto roomMemberEvent = eventCast<const RoomMemberEvent>(&evt)) {
if ((roomMemberEvent->isJoin() || roomMemberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) {
return EventStatus::Hidden;
} else if (roomMemberEvent->isRename() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::self()->showRename()) {
return EventStatus::Hidden;
} else if (roomMemberEvent->isAvatarUpdate() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave()
&& !NeoChatConfig::self()->showAvatarUpdate()) {
return EventStatus::Hidden;
}
}
@@ -958,21 +964,6 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
}
}
if (role == IsNameChangeRole) {
auto roomMemberEvent = eventCast<const RoomMemberEvent>(&evt);
if (roomMemberEvent) {
return roomMemberEvent->isRename();
}
return false;
}
if (role == IsAvatarChangeRole) {
auto roomMemberEvent = eventCast<const RoomMemberEvent>(&evt);
if (roomMemberEvent) {
return roomMemberEvent->isAvatarUpdate();
}
return false;
}
if (role == IsRedactedRole) {
return evt.isRedacted();
}

View File

@@ -73,8 +73,6 @@ public:
DisplayNameForInitialsRole,
// The displayname for the event's sender; for name change events, the old displayname
AuthorDisplayNameRole,
IsNameChangeRole,
IsAvatarChangeRole,
IsRedactedRole,
IsPendingRole,
LastRole, // Keep this last

View File

@@ -11,23 +11,20 @@ using namespace Quotient;
MessageFilterModel::MessageFilterModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
connect(NeoChatConfig::self(), &NeoChatConfig::ShowStateEventChanged, this, [this] {
invalidateFilter();
});
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLeaveJoinEventChanged, this, [this] {
beginResetModel();
endResetModel();
invalidateFilter();
});
connect(NeoChatConfig::self(), &NeoChatConfig::ShowRenameChanged, this, [this] {
beginResetModel();
endResetModel();
invalidateFilter();
});
connect(NeoChatConfig::self(), &NeoChatConfig::ShowAvatarUpdateChanged, this, [this] {
beginResetModel();
endResetModel();
invalidateFilter();
});
connect(NeoChatConfig::self(), &NeoChatConfig::ShowDeletedMessagesChanged, this, [this] {
beginResetModel();
endResetModel();
invalidateFilter();
});
}
@@ -35,18 +32,11 @@ bool MessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
{
const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
const int specialMarks = index.data(MessageEventModel::SpecialMarksRole).toInt();
if (index.data(MessageEventModel::IsNameChangeRole).toBool() && !NeoChatConfig::self()->showRename()) {
return false;
}
if (index.data(MessageEventModel::IsAvatarChangeRole).toBool() && !NeoChatConfig::self()->showAvatarUpdate()) {
return false;
}
if (index.data(MessageEventModel::IsRedactedRole).toBool() && !NeoChatConfig::self()->showDeletedMessages()) {
return false;
}
const int specialMarks = index.data(MessageEventModel::SpecialMarksRole).toInt();
if (specialMarks == EventStatus::Hidden || specialMarks == EventStatus::Replaced) {
return false;
}
@@ -57,9 +47,5 @@ bool MessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
return false;
}
if (!NeoChatConfig::self()->showLeaveJoinEvent() && eventType == MessageEventModel::State) {
return false;
}
return true;
}

View File

@@ -180,28 +180,7 @@ void RoomListModel::connectRoomSignals(NeoChatRoom *room)
#ifndef QUOTIENT_07
connect(room, &Room::notificationCountChanged, this, &RoomListModel::handleNotifications);
#endif
connect(room, &Room::highlightCountChanged, this, [this, room] {
if (room->highlightCount() == 0) {
return;
}
if (room->timelineSize() == 0) {
return;
}
auto *lastEvent = room->lastEvent();
if (!lastEvent) {
return;
}
if (!lastEvent->isStateEvent()) {
return;
}
User *sender = room->user(lastEvent->senderId());
if (sender == room->localUser()) {
return;
}
Q_EMIT newHighlight(room->id(), lastEvent->id(), room->displayName(), sender->displayname(), room->eventToString(*lastEvent), room->avatar(128));
});
#ifndef QUOTIENT_07
connect(room, &Room::notificationCountChanged, this, &RoomListModel::refreshNotificationCount);
#else

View File

@@ -116,5 +116,4 @@ Q_SIGNALS:
void notificationCountChanged();
void roomAdded(NeoChatRoom *_t1);
void newHighlight(const QString &_t1, const QString &_t2, const QString &_t3, const QString &_t4, const QString &_t5, const QImage &_t6);
};

View File

@@ -29,10 +29,6 @@
<label>Merge Room Lists</label>
<default>false</default>
</entry>
<entry name="ShowLeaveJoinEvent" type="bool">
<label>Show leave and join events in the timeline</label>
<default>true</default>
</entry>
<entry name="AllowQuickEdit" type="bool">
<label>Use s/text/replacement syntax to edit your last message.</label>
<default>false</default>
@@ -72,6 +68,14 @@
<label>Use a compact room list layout</label>
<default>false</default>
</entry>
<entry name="ShowStateEvent" type="bool">
<label>Show state events in the timeline</label>
<default>true</default>
</entry>
<entry name="ShowLeaveJoinEvent" type="bool">
<label>Show leave and join events in the timeline</label>
<default>true</default>
</entry>
<entry name="ShowRename" type="bool">
<label>Show rename events in the timeline</label>
<default>true</default>

View File

@@ -215,7 +215,7 @@ void NeoChatRoom::sendTypingNotification(bool isTyping)
connection()->callApi<SetTypingJob>(BackgroundRequest, localUser()->id(), id(), isTyping, 10000);
}
const RoomEvent *NeoChatRoom::lastEvent(bool ignoreStateEvent) const
const RoomEvent *NeoChatRoom::lastEvent() const
{
for (auto timelineItem = messageEvents().rbegin(); timelineItem < messageEvents().rend(); timelineItem++) {
const RoomEvent *event = timelineItem->get();
@@ -227,8 +227,21 @@ const RoomEvent *NeoChatRoom::lastEvent(bool ignoreStateEvent) const
continue;
}
if (event->isStateEvent()
&& (ignoreStateEvent || !NeoChatConfig::self()->showLeaveJoinEvent() || static_cast<const StateEventBase &>(*event).repeatsState())) {
if (event->isStateEvent() && !NeoChatConfig::self()->showStateEvent()) {
continue;
}
if (auto roomMemberEvent = eventCast<const RoomMemberEvent>(event)) {
if ((roomMemberEvent->isJoin() || roomMemberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) {
continue;
} else if (roomMemberEvent->isRename() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::self()->showRename()) {
continue;
} else if (roomMemberEvent->isAvatarUpdate() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave()
&& !NeoChatConfig::self()->showAvatarUpdate()) {
continue;
}
}
if (event->isStateEvent() && static_cast<const StateEventBase &>(*event).repeatsState()) {
continue;
}
@@ -242,6 +255,14 @@ const RoomEvent *NeoChatRoom::lastEvent(bool ignoreStateEvent) const
continue;
}
#ifdef QUOTIENT_07
if (auto lastEvent = eventCast<const StateEvent>(event)) {
#else
if (auto lastEvent = eventCast<const StateEventBase>(event)) {
#endif
return lastEvent;
}
if (auto lastEvent = eventCast<const RoomMessageEvent>(event)) {
return lastEvent;
}
@@ -332,7 +353,7 @@ QDateTime NeoChatRoom::lastActiveTime()
return QDateTime();
}
if (auto event = lastEvent(true)) {
if (auto event = lastEvent()) {
return event->originTimestamp();
}
@@ -475,7 +496,7 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format,
[](const StickerEvent &e) {
return e.body();
},
[this](const RoomMemberEvent &e) {
[this, prettyPrint](const RoomMemberEvent &e) {
// FIXME: Rewind to the name that was at the time of this event
auto subjectName = this->htmlSafeMemberName(e.userId());
if (e.membership() == MembershipType::Leave) {
@@ -488,8 +509,11 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format,
#endif
}
}
subjectName = QStringLiteral("<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a>")
.arg(e.userId(), static_cast<NeoChatUser *>(user(e.userId()))->color().name(), subjectName);
if (prettyPrint) {
subjectName = QStringLiteral("<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a>")
.arg(e.userId(), static_cast<NeoChatUser *>(user(e.userId()))->color().name(), subjectName);
}
// The below code assumes senderName output in AuthorRole
switch (e.membership()) {

View File

@@ -131,7 +131,7 @@ public:
/// This function respect the showLeaveJoinEvent setting and discard
/// other not interesting events. This function can return an empty pointer
/// when the room is empty of RoomMessageEvent.
[[nodiscard]] const Quotient::RoomEvent *lastEvent(bool ignoreStateEvent = false) const;
[[nodiscard]] const Quotient::RoomEvent *lastEvent() const;
/// Convenient way to get the last event but in a string format.
///

View File

@@ -77,8 +77,38 @@ Kirigami.ScrollablePage {
title: i18n("Timeline Events")
}
MobileForm.FormCheckDelegate {
id: showDeletedMessages
text: i18n("Show deleted messages")
checked: Config.showDeletedMessages
enabled: !Config.isShowDeletedMessagesImmutable
onToggled: {
Config.showDeletedMessages = checked
Config.save()
}
}
MobileForm.FormDelegateSeparator { above: showDeletedMessages; below: showStateEvents }
MobileForm.FormCheckDelegate {
id: showStateEvents
text: i18n("Show state events")
checked: Config.showStateEvent
enabled: !Config.isShowStateEventImmutable
onToggled: {
Config.showStateEvent = checked
Config.save()
}
}
MobileForm.FormDelegateSeparator {
visible: Config.showStateEvent
above: showStateEvents
below: showLeaveJoinEventDelegate }
MobileForm.FormCheckDelegate {
id: showLeaveJoinEventDelegate
visible: Config.showStateEvent
text: i18n("Show leave and join events")
checked: Config.showLeaveJoinEvent
enabled: !Config.isShowLeaveJoinEventImmutable
@@ -88,10 +118,15 @@ Kirigami.ScrollablePage {
}
}
MobileForm.FormDelegateSeparator { above: showLeaveJoinEventDelegate; below: showNameDelegate }
MobileForm.FormDelegateSeparator {
visible: Config.showStateEvent
above: showLeaveJoinEventDelegate
below: showNameDelegate
}
MobileForm.FormCheckDelegate {
id: showNameDelegate
visible: Config.showStateEvent
text: i18n("Show name change events")
checked: Config.showRename
enabled: !Config.isShowRenameImmutable
@@ -101,10 +136,15 @@ Kirigami.ScrollablePage {
}
}
MobileForm.FormDelegateSeparator { above: showNameDelegate; below: showAvatarChangeDelegate }
MobileForm.FormDelegateSeparator {
visible: Config.showStateEvent
above: showNameDelegate
below: showAvatarChangeDelegate
}
MobileForm.FormCheckDelegate {
id: showAvatarChangeDelegate
visible: Config.showStateEvent
text: i18n("Show avatar update events")
checked: Config.showAvatarUpdate
enabled: !Config.isShowAvatarUpdateImmutable
@@ -113,19 +153,6 @@ Kirigami.ScrollablePage {
Config.save()
}
}
MobileForm.FormDelegateSeparator { above: showAvatarChangeDelegate; below: showDeletedMessages }
MobileForm.FormCheckDelegate {
id: showDeletedMessages
text: i18n("Show deleted messages")
checked: Config.showDeletedMessages
enabled: !Config.isShowDeletedMessagesImmutable
onToggled: {
Config.showDeletedMessages = checked
Config.save()
}
}
}
}