diff --git a/src/Unread.ts b/src/Unread.ts index 7b86a013a595..15f8d5961046 100644 --- a/src/Unread.ts +++ b/src/Unread.ts @@ -72,6 +72,11 @@ export function doesRoomHaveUnreadMessages(room: Room): boolean { } export function doesRoomOrThreadHaveUnreadMessages(roomOrThread: Room | Thread): boolean { + // NOTE: this shares logic with hasUserReadEvent in + // matrix-js-sdk/src/models/read-receipt.ts. They are not combined (yet) + // because hasUserReadEvent is focussed on a single event, and this is + // focussed on the whole room/thread. + // If there are no messages yet in the timeline then it isn't fully initialised // and cannot be unread. if (!roomOrThread || roomOrThread.timeline.length === 0) { @@ -94,26 +99,52 @@ export function doesRoomOrThreadHaveUnreadMessages(roomOrThread: Room | Thread): // N.B. this is NOT a read marker (RM, aka "read up to marker"), // despite the name of the method :(( const readUpToId = roomOrThread.getEventReadUpTo(myUserId!); - - // this just looks at whatever history we have, which if we've only just started - // up probably won't be very much, so if the last couple of events are ones that - // don't count, we don't know if there are any events that do count between where - // we have and the read receipt. We could fetch more history to try & find out, - // but currently we just guess. - - // Loop through messages, starting with the most recent... - for (let i = roomOrThread.timeline.length - 1; i >= 0; --i) { - const ev = roomOrThread.timeline[i]; - - if (ev.getId() == readUpToId) { - // If we've read up to this event, there's nothing more recent - // that counts and we can stop looking because the user's read - // this and everything before. - return false; - } else if (!shouldHideEvent(ev) && eventTriggersUnreadCount(ev)) { - // We've found a message that counts before we hit - // the user's read receipt, so this room is definitely unread. - return true; + const readEvent = roomOrThread.findEventById(readUpToId); + + // If the event we have a receipt for is loaded, it's easy - we just check + // whether there are any events after it + if (readEvent) { + // this just looks at whatever history we have, which if we've only just started + // up probably won't be very much, so if the last couple of events are ones that + // don't count, we don't know if there are any events that do count between where + // we have and the read receipt. We could fetch more history to try & find out, + // but currently we just guess. + + // Loop through messages, starting with the most recent... + for (let i = roomOrThread.timeline.length - 1; i >= 0; --i) { + const ev = roomOrThread.timeline[i]; + + if (ev.getId() == readUpToId) { + // If we've read up to this event, there's nothing more recent + // that counts and we can stop looking because the user's read + // this and everything before. + return false; + } else if (!shouldHideEvent(ev) && eventTriggersUnreadCount(ev)) { + // We've found a message that counts before we hit + // the user's read receipt, so this room is definitely unread. + return true; + } + } + } else { + // Guess based on the timestamp of the receipt + + const receipt = roomOrThread.getReadReceiptForUserId(myUserId); + if (receipt) { + const receiptTimestamp = receipt.data.ts; + for (let i = roomOrThread.timeline.length - 1; i >= 0; --i) { + const ev = roomOrThread.timeline[i]; + + if (ev.getTs() < receiptTimestamp) { + // If we've read up to this event, there's nothing more recent + // that counts and we can stop looking because the user's read + // this and everything before. + return false; + } else if (!shouldHideEvent(ev) && eventTriggersUnreadCount(ev)) { + // We've found a message that counts before we hit + // the user's read receipt, so this room is definitely unread. + return true; + } + } } }