Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F116520
D19.1733020503.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
7 KB
Referenced Files
None
Subscribers
None
D19.1733020503.diff
View Options
diff --git a/src/contents/ui/RoomListViewItemDelegate.qml b/src/contents/ui/RoomListViewItemDelegate.qml
--- a/src/contents/ui/RoomListViewItemDelegate.qml
+++ b/src/contents/ui/RoomListViewItemDelegate.qml
@@ -16,10 +16,17 @@
import 'room-settings' as RoomSettings
Kirigami.SwipeListItem {
+ id: upper
property var item
property var iconSize: 1
property var isFavourite: item.tagIds.includes('m.favourite')
property var roomDisplayName: Helpers.roomNameOrHeroes(item, l10n)
+ property var roomTimeline: item.timeline()
+ property var lastUnreadMessage: getLastUnreadMessage(roomTimeline, roomTimeline.count)
+ property var lastUnreadMessageReaders: lastUnreadMessage ? lastUnreadMessage.readers() : null
+ // The readers count is to make hasUnreadMessages prop reactive
+ // against our newly-posted receipts
+ property var hasUnreadMessages: getHasUnreadMessages(roomTimeline, roomTimeline.count, lastUnreadMessageReaders ? lastUnreadMessageReaders.count : 0)
onClicked: {
root.sdkVars.currentRoomId = item.roomId
@@ -61,6 +68,17 @@
elide: Text.ElideRight
color: item.membership === MK.MatrixRoom.Leave ? Kirigami.Theme.disabledTextColor : Kirigami.Theme.textColor
}
+
+ Rectangle {
+ Layout.alignment: Qt.AlignRight
+ Layout.preferredWidth: Kirigami.Units.iconSizes.small
+ Layout.preferredHeight: Kirigami.Units.iconSizes.small
+ radius: Kirigami.Units.iconSizes.small / 2
+ color: Kirigami.Theme.activeTextColor
+ visible: hasUnreadMessages
+ Accessible.role: Qt.Indicator
+ Accessible.name: l10n.get('room-list-view-room-item-unread-indicator')
+ }
}
actions: [
Kirigami.Action {
@@ -84,4 +102,26 @@
property var favouriteToggler: RoomSettings.RoomTagHandler {
room: item
}
+
+ function getHasUnreadMessages(timeline, timelineCount, readersCount) {
+ return !!getLastUnreadMessage(timeline, timelineCount);
+ }
+
+ function getLastUnreadMessage(timeline, timelineCount) {
+ for (let i = 0; i < timelineCount; ++i) {
+ const event = timeline.at(i);
+ if (event.isLocalEcho) {
+ // Skip local echoes
+ } else if (event.sender === matrixSdk.userId) {
+ // Own message here
+ return undefined;
+ } else if (Helpers.isEventReadBy(event, matrixSdk.userId)) {
+ // This message is read
+ return undefined;
+ } else {
+ // The latest message is not read and not sent by the current user
+ return event;
+ }
+ }
+ }
}
diff --git a/src/contents/ui/RoomPage.qml b/src/contents/ui/RoomPage.qml
--- a/src/contents/ui/RoomPage.qml
+++ b/src/contents/ui/RoomPage.qml
@@ -194,15 +194,7 @@
}
const event = roomPage.room.messageById(eventId);
- const readers = event.readers();
- let readByMe = false;
- for (let i = 0; i < readers.count; ++i) {
- const reader = readers.at(i);
- if (reader.userId === matrixSdk.userId) {
- readByMe = true;
- break;
- }
- }
+ const readByMe = Helpers.isEventReadBy(event, matrixSdk.userId);
if (!readByMe) {
roomPage.room.postReadReceipt(eventId);
}
diff --git a/src/js/matrix-helpers.js b/src/js/matrix-helpers.js
--- a/src/js/matrix-helpers.js
+++ b/src/js/matrix-helpers.js
@@ -40,3 +40,22 @@
return user.userId;
}
}
+
+/**
+ * Check whether the event is read by a user.
+ *
+ * @param event The event to check.
+ * @param userId The id of the user to check.
+ * @return Whether @c event is read by the user whose id is @c userId.
+ */
+function isEventReadBy(event, userId)
+{
+ const readers = event.readers();
+ for (let i = 0; i < readers.count; ++i) {
+ const reader = readers.at(i);
+ if (reader.userId === userId) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/src/l10n/cmn-Hans/100-ui.ftl b/src/l10n/cmn-Hans/100-ui.ftl
--- a/src/l10n/cmn-Hans/100-ui.ftl
+++ b/src/l10n/cmn-Hans/100-ui.ftl
@@ -55,6 +55,7 @@
}
room-list-view-room-item-title-id = 未命名房间({ $roomId })
room-list-view-room-item-fav-action = 设为最爱
+room-list-view-room-item-unread-indicator = 未读
room-tags-fav-action-notification = 把 { $name } 设为了最爱
room-tags-fav-action-notification-failed = 不能把 { $name } 设为最爱。错误代码:{ $errorCode }。错误讯息:{ $errorMsg }。
room-tags-unfav-action-notification = 把 { $name } 从最爱中移除了
diff --git a/src/l10n/en/100-ui.ftl b/src/l10n/en/100-ui.ftl
--- a/src/l10n/en/100-ui.ftl
+++ b/src/l10n/en/100-ui.ftl
@@ -55,6 +55,7 @@
}
room-list-view-room-item-title-id = Unnamed room ({ $roomId })
room-list-view-room-item-fav-action = Set as favourite
+room-list-view-room-item-unread-indicator = Unread
room-tags-fav-action-notification = Set { $name } as favourite
room-tags-fav-action-notification-failed = Cannot set { $name } as favourite. Error code: { $errorCode }. Error message: { $errorMsg }.
room-tags-unfav-action-notification = Removed { $name } from favourites
diff --git a/src/tests/quick-tests/tst_RoomListViewItemDelegate.qml b/src/tests/quick-tests/tst_RoomListViewItemDelegate.qml
--- a/src/tests/quick-tests/tst_RoomListViewItemDelegate.qml
+++ b/src/tests/quick-tests/tst_RoomListViewItemDelegate.qml
@@ -31,8 +31,96 @@
membership: MK.MatrixRoom.Leave,
})
+ property var timelineUnread: QtObject {
+ property var count: 5
+ function at(i) {
+ if (i < 2) {
+ return {
+ isLocalEcho: true,
+ readers: () => ({ count: 0 }),
+ };
+ }
+ if (i < 3) {
+ return {
+ isLocalEcho: false,
+ sender: '@bar:example.org',
+ eventId: `$event${i}`,
+ readers: () => ({ count: 0 }),
+ };
+ }
+
+ return {
+ isLocalEcho: false,
+ sender: '@foo:example.org',
+ eventId: `$event${i}`,
+ readers: () => ({ count: 0 }),
+ };
+ }
+ }
+
+ property var timelineLastSentIsMine: QtObject {
+ property var count: 5
+ function at(i) {
+ if (i < 2) {
+ return {
+ isLocalEcho: true,
+ readers: () => ({ count: 0 }),
+ };
+ }
+ if (i < 3) {
+ return {
+ isLocalEcho: false,
+ sender: '@foo:example.org',
+ eventId: `$event${i}`,
+ readers: () => ({ count: 0 }),
+ };
+ }
+
+ return {
+ isLocalEcho: false,
+ sender: '@bar:example.org',
+ eventId: `$event${i}`,
+ readers: () => ({ count: 0 }),
+ };
+ }
+ }
+
+ property var timelineRead: QtObject {
+ property var count: 5
+ function at(i) {
+ if (i < 2) {
+ return {
+ isLocalEcho: true,
+ readers: () => ({ count: 0 }),
+ };
+ }
+ if (i < 3) {
+ return {
+ isLocalEcho: false,
+ sender: '@bar:example.org',
+ eventId: `$event${i}`,
+ readers: () => ({
+ count: 1,
+ at() {
+ return { userId: '@foo:example.org' };
+ },
+ }),
+ };
+ }
+
+ return {
+ isLocalEcho: false,
+ sender: '@foo:example.org',
+ eventId: `$event${i}`,
+ readers: () => ({ count: 0 }),
+ };
+ }
+ }
+
property var l10n: Helpers.fluentMock
- property var matrixSdk: TestHelpers.MatrixSdkMock {}
+ property var matrixSdk: TestHelpers.MatrixSdkMock {
+ property var userId: '@foo:example.org'
+ }
property var sdkVars: ({})
ColumnLayout {
@@ -86,5 +174,13 @@
verify(leaveIndicator);
verify(leaveIndicator.visible);
}
+
+ function test_lastUnreadMessage() {
+ compare(delegateJoin.getLastUnreadMessage(timelineUnread, timelineUnread.count).eventId, '$event2');
+
+ compare(delegateJoin.getLastUnreadMessage(timelineLastSentIsMine, timelineLastSentIsMine.count), undefined);
+
+ compare(delegateJoin.getLastUnreadMessage(timelineRead, timelineRead.count), undefined);
+ }
}
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 30, 6:35 PM (15 h, 50 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41780
Default Alt Text
D19.1733020503.diff (7 KB)
Attached To
Mode
D19: Display unread indicator in room list
Attached
Detach File
Event Timeline
Log In to Comment