From 52f48f742246828855c508b6021b0c820c029a68 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 13 Feb 2019 18:19:18 -0700 Subject: [PATCH] Order by timestamp within the categorized room lists When we load the page, all encrypted events arrive well after we've generated our initial grouping which can cause them to jump to the top of their categories wrongly. For direct chats, this meant that people who don't have a lot of unread messages would see ancient rooms bubbling to the top for no reason after the page has loaded. We still have to track when the last category change was (ie: when we switched from red -> grey) so that when the category doesn't exist in the list we can insert the room at the right place (the start of the last category when we switch beyond the order expected). --- src/stores/RoomListStore.js | 39 ++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/stores/RoomListStore.js b/src/stores/RoomListStore.js index 30d14351e0..84939fa129 100644 --- a/src/stores/RoomListStore.js +++ b/src/stores/RoomListStore.js @@ -231,24 +231,22 @@ class RoomListStore extends Store { } } - _setRoomCategory(room, category, targetTags = []) { + _setRoomCategory(room, category) { const listsClone = {}; const targetCatIndex = CATEGORY_ORDER.indexOf(category); + const targetTs = this._tsOfNewestEvent(room); const myMembership = room.getMyMembership(); let doInsert = true; + let targetTags = []; if (myMembership !== "join" && myMembership !== "invite") { doInsert = false; } else { const dmRoomMap = DMRoomMap.shared(); if (dmRoomMap.getUserIdForRoomId(room.roomId)) { - if (!targetTags.includes('im.vector.fake.direct')) { - targetTags.push('im.vector.fake.direct'); - } + targetTags.push('im.vector.fake.direct'); } else { - if (!targetTags.includes('im.vector.fake.recent')) { - targetTags.push('im.vector.fake.recent'); - } + targetTags.push('im.vector.fake.recent'); } } @@ -262,21 +260,40 @@ class RoomListStore extends Store { listsClone[key] = []; let pushedEntry = false; const hasRoom = !!this._state.lists[key].find((e) => e.room.roomId === room.roomId); + let lastCategoryBoundary = 0; + let lastCategoryIndex = 0; for (const entry of this._state.lists[key]) { + // if the list is a recent list, and the room appears in this list, and we're not looking at a sticky // room (sticky rooms have unreliable categories), try to slot the new room in if (LIST_ORDERS[key] === 'recent' && hasRoom && entry.room.roomId !== this._state.stickyRoomId) { - const inTag = targetTags.length === 0 || targetTags.includes('im.vector.ake.recent'); + const inTag = targetTags.length === 0 || targetTags.includes(key); if (!pushedEntry && doInsert && inTag) { + const entryTs = this._tsOfNewestEvent(entry.room); + const entryCategory = CATEGORY_ORDER.indexOf(entry.category); + // If we've hit the top of a boundary (either because there's no rooms in the target or // we've reached the grouping of rooms), insert our room ahead of the others in the category. // This ensures that our room is on top (more recent) than the others. - const changedBoundary = CATEGORY_ORDER.indexOf(entry.category) >= targetCatIndex; - if (changedBoundary) { - listsClone[key].push({room: room, category: category}); + const changedBoundary = entryCategory > targetCatIndex; + const currentCategory = entryCategory === targetCatIndex; + if (changedBoundary || (currentCategory && targetTs >= entryTs)) { + if (changedBoundary) { + // If we changed a boundary, then we've gone too far - go to the top of the last + // section instead. + listsClone[key].splice(lastCategoryBoundary, 0, {room, category}); + } else { + // If we're ordering by timestamp, just insert normally + listsClone[key].push({room, category}); + } pushedEntry = true; inserted = true; } + + if (entryCategory !== lastCategoryIndex) { + lastCategoryBoundary = listsClone[key].length - 1; + } + lastCategoryIndex = entryCategory; } // We insert our own record as needed, so don't let the old one through.