Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 151b4cc

Browse files
authored
Merge pull request #2265 from matrix-org/travis/fix-pinned-rooms
Improve performance of room list and fix timestamp ordering when pinning rooms
2 parents 57b9989 + 3960ae2 commit 151b4cc

File tree

1 file changed

+92
-19
lines changed

1 file changed

+92
-19
lines changed

src/stores/RoomListStore.js

Lines changed: 92 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,24 @@ class RoomListStore extends Store {
5454
"im.vector.fake.archived": [],
5555
},
5656
ready: false,
57+
58+
// The room cache stores a mapping of roomId to cache record.
59+
// Each cache record is a key/value pair for various bits of
60+
// data used to sort the room list. Currently this stores the
61+
// following bits of informations:
62+
// "timestamp": number, The timestamp of the last relevant
63+
// event in the room.
64+
// "notifications": boolean, Whether or not the user has been
65+
// highlighted on any unread events.
66+
// "unread": boolean, Whether or not the user has any
67+
// unread events.
68+
//
69+
// All of the cached values are lazily loaded on read in the
70+
// recents comparator. When an event is received for a particular
71+
// room, all the cached values are invalidated - forcing the
72+
// next read to set new values. The entries do not expire on
73+
// their own.
74+
roomCache: {},
5775
};
5876
}
5977

@@ -85,6 +103,8 @@ class RoomListStore extends Store {
85103
!payload.isLiveUnfilteredRoomTimelineEvent ||
86104
!this._eventTriggersRecentReorder(payload.event)
87105
) break;
106+
107+
this._clearCachedRoomState(payload.event.getRoomId());
88108
this._generateRoomLists();
89109
}
90110
break;
@@ -112,6 +132,8 @@ class RoomListStore extends Store {
112132
if (liveTimeline !== eventTimeline ||
113133
!this._eventTriggersRecentReorder(payload.event)
114134
) break;
135+
136+
this._clearCachedRoomState(payload.event.getRoomId());
115137
this._generateRoomLists();
116138
}
117139
break;
@@ -217,11 +239,18 @@ class RoomListStore extends Store {
217239
}
218240
});
219241

242+
// Note: we check the settings up here instead of in the forEach or
243+
// in the _recentsComparator to avoid hitting the SettingsStore a few
244+
// thousand times.
245+
const pinUnread = SettingsStore.getValue("pinUnreadRooms");
246+
const pinMentioned = SettingsStore.getValue("pinMentionedRooms");
220247
Object.keys(lists).forEach((listKey) => {
221248
let comparator;
222249
switch (RoomListStore._listOrders[listKey]) {
223250
case "recent":
224-
comparator = this._recentsComparator;
251+
comparator = (roomA, roomB) => {
252+
return this._recentsComparator(roomA, roomB, pinUnread, pinMentioned);
253+
};
225254
break;
226255
case "manual":
227256
default:
@@ -237,6 +266,44 @@ class RoomListStore extends Store {
237266
});
238267
}
239268

269+
_updateCachedRoomState(roomId, type, value) {
270+
const roomCache = this._state.roomCache;
271+
if (!roomCache[roomId]) roomCache[roomId] = {};
272+
273+
if (value) roomCache[roomId][type] = value;
274+
else delete roomCache[roomId][type];
275+
276+
this._setState({roomCache});
277+
}
278+
279+
_clearCachedRoomState(roomId) {
280+
const roomCache = this._state.roomCache;
281+
delete roomCache[roomId];
282+
this._setState({roomCache});
283+
}
284+
285+
_getRoomState(room, type) {
286+
const roomId = room.roomId;
287+
const roomCache = this._state.roomCache;
288+
if (roomCache[roomId] && typeof roomCache[roomId][type] !== 'undefined') {
289+
return roomCache[roomId][type];
290+
}
291+
292+
if (type === "timestamp") {
293+
const ts = this._tsOfNewestEvent(room);
294+
this._updateCachedRoomState(roomId, "timestamp", ts);
295+
return ts;
296+
} else if (type === "unread") {
297+
const unread = room.getUnreadNotificationCount() > 0;
298+
this._updateCachedRoomState(roomId, "unread", unread);
299+
return unread;
300+
} else if (type === "notifications") {
301+
const notifs = room.getUnreadNotificationCount("highlight") > 0;
302+
this._updateCachedRoomState(roomId, "notifications", notifs);
303+
return notifs;
304+
} else throw new Error("Unrecognized room cache type: " + type);
305+
}
306+
240307
_eventTriggersRecentReorder(ev) {
241308
return ev.getTs() && (
242309
Unread.eventTriggersUnreadCount(ev) ||
@@ -262,34 +329,40 @@ class RoomListStore extends Store {
262329
}
263330
}
264331

265-
_recentsComparator(roomA, roomB) {
266-
const pinUnread = SettingsStore.getValue("pinUnreadRooms");
267-
const pinMentioned = SettingsStore.getValue("pinMentionedRooms");
268-
332+
_recentsComparator(roomA, roomB, pinUnread, pinMentioned) {
269333
// We try and set the ordering to be Mentioned > Unread > Recent
270-
// assuming the user has the right settings, of course
334+
// assuming the user has the right settings, of course.
335+
336+
const timestampA = this._getRoomState(roomA, "timestamp");
337+
const timestampB = this._getRoomState(roomB, "timestamp");
338+
const timestampDiff = timestampB - timestampA;
271339

272340
if (pinMentioned) {
273-
const mentionsA = roomA.getUnreadNotificationCount("highlight") > 0;
274-
const mentionsB = roomB.getUnreadNotificationCount("highlight") > 0;
275-
if (mentionsA && !mentionsB) return -1;
276-
if (!mentionsA && mentionsB) return 1;
277-
if (mentionsA && mentionsB) return 0;
278-
// If neither have mentions, fall through to remaining checks
341+
const mentionsA = this._getRoomState(roomA, "notifications");
342+
const mentionsB = this._getRoomState(roomB, "notifications");
343+
if (mentionsA && !mentionsB) return -1;
344+
if (!mentionsA && mentionsB) return 1;
345+
346+
// If they both have notifications, sort by timestamp.
347+
// If neither have notifications (the fourth check not shown
348+
// here), then try and sort by unread messages and finally by
349+
// timestamp.
350+
if (mentionsA && mentionsB) return timestampDiff;
279351
}
280352

281353
if (pinUnread) {
282-
const unreadA = Unread.doesRoomHaveUnreadMessages(roomA);
283-
const unreadB = Unread.doesRoomHaveUnreadMessages(roomB);
354+
const unreadA = this._getRoomState(roomA, "unread");
355+
const unreadB = this._getRoomState(roomB, "unread");
284356
if (unreadA && !unreadB) return -1;
285357
if (!unreadA && unreadB) return 1;
286-
if (unreadA && unreadB) return 0;
287-
// If neither have unread messages, fall through to remaining checks
358+
359+
// If they both have unread messages, sort by timestamp
360+
// If nether have unread message (the fourth check not shown
361+
// here), then just sort by timestamp anyways.
362+
if (unreadA && unreadB) return timestampDiff;
288363
}
289364

290-
// XXX: We could use a cache here and update it when we see new
291-
// events that trigger a reorder
292-
return this._tsOfNewestEvent(roomB) - this._tsOfNewestEvent(roomA);
365+
return timestampDiff;
293366
}
294367

295368
_lexicographicalComparator(roomA, roomB) {

0 commit comments

Comments
 (0)