Page MenuHomePhorge

D19.1733020503.diff
No OneTemporary

Size
7 KB
Referenced Files
None
Subscribers
None

D19.1733020503.diff

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

Mime Type
text/plain
Expires
Sat, Nov 30, 6:35 PM (18 h, 50 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41780
Default Alt Text
D19.1733020503.diff (7 KB)

Event Timeline