@@ -72,13 +72,18 @@ export function doesRoomHaveUnreadMessages(room: Room): boolean {
7272}
7373
7474export function doesRoomOrThreadHaveUnreadMessages ( roomOrThread : Room | Thread ) : boolean {
75+ // NOTE: this shares logic with hasUserReadEvent in
76+ // matrix-js-sdk/src/models/read-receipt.ts. They are not combined (yet)
77+ // because hasUserReadEvent is focussed on a single event, and this is
78+ // focussed on the whole room/thread.
79+
7580 // If there are no messages yet in the timeline then it isn't fully initialised
7681 // and cannot be unread.
7782 if ( ! roomOrThread || roomOrThread . timeline . length === 0 ) {
7883 return false ;
7984 }
8085
81- const myUserId = MatrixClientPeg . get ( ) . getUserId ( ) ;
86+ const myUserId = MatrixClientPeg . get ( ) . getUserId ( ) ! ;
8287
8388 // as we don't send RRs for our own messages, make sure we special case that
8489 // if *we* sent the last message into the room, we consider it not unread!
@@ -90,34 +95,26 @@ export function doesRoomOrThreadHaveUnreadMessages(roomOrThread: Room | Thread):
9095 return false ;
9196 }
9297
93- // get the most recent read receipt sent by our account.
94- // N.B. this is NOT a read marker (RM, aka "read up to marker"),
95- // despite the name of the method :((
96- const readUpToId = roomOrThread . getEventReadUpTo ( myUserId ! ) ;
97-
98- // this just looks at whatever history we have, which if we've only just started
99- // up probably won't be very much, so if the last couple of events are ones that
100- // don't count, we don't know if there are any events that do count between where
101- // we have and the read receipt. We could fetch more history to try & find out,
102- // but currently we just guess.
98+ const readUpToId = roomOrThread . getEventReadUpTo ( myUserId ) ;
99+ const hasReceipt = makeHasReceipt ( roomOrThread , readUpToId , myUserId ) ;
103100
104101 // Loop through messages, starting with the most recent...
105102 for ( let i = roomOrThread . timeline . length - 1 ; i >= 0 ; -- i ) {
106103 const ev = roomOrThread . timeline [ i ] ;
107104
108- if ( ev . getId ( ) == readUpToId ) {
105+ if ( hasReceipt ( ev ) ) {
109106 // If we've read up to this event, there's nothing more recent
110107 // that counts and we can stop looking because the user's read
111108 // this and everything before.
112109 return false ;
113- } else if ( ! shouldHideEvent ( ev ) && eventTriggersUnreadCount ( ev ) ) {
110+ } else if ( isImportantEvent ( ev ) ) {
114111 // We've found a message that counts before we hit
115112 // the user's read receipt, so this room is definitely unread.
116113 return true ;
117114 }
118115 }
119116
120- // If we got here, we didn't find a message that counted but didn't find
117+ // If we got here, we didn't find a message was important but didn't find
121118 // the user's read receipt either, so we guess and say that the room is
122119 // unread on the theory that false positives are better than false
123120 // negatives here.
@@ -127,3 +124,49 @@ export function doesRoomOrThreadHaveUnreadMessages(roomOrThread: Room | Thread):
127124 } ) ;
128125 return true ;
129126}
127+
128+ /**
129+ * Given this event does not have a receipt, is it important enough to make
130+ * this room unread?
131+ */
132+ function isImportantEvent ( event : MatrixEvent ) : boolean {
133+ return ! shouldHideEvent ( event ) && eventTriggersUnreadCount ( event ) ;
134+ }
135+
136+ /**
137+ * @returns a function that tells us whether a given event matches our read
138+ * receipt.
139+ *
140+ * We have the ID of an event based on a read receipt. If we can find the
141+ * corresponding event, then it's easy - our returned function just decides
142+ * whether the receipt refers to the event we are asking about.
143+ *
144+ * If we can't find the event, we guess by saying of the receipt's timestamp is
145+ * after this event's timestamp, then it's probably saying this event is read.
146+ */
147+ function makeHasReceipt (
148+ roomOrThread : Room | Thread ,
149+ readUpToId : string ,
150+ myUserId : string ,
151+ ) : ( event : MatrixEvent ) => boolean {
152+ // get the most recent read receipt sent by our account.
153+ // N.B. this is NOT a read marker (RM, aka "read up to marker"),
154+ // despite the name of the method :((
155+ const readEvent = roomOrThread . findEventById ( readUpToId ) ;
156+
157+ if ( readEvent ) {
158+ // If we found an event matching our receipt, then it's easy: this event
159+ // has a receipt if its ID is the same as the one in the receipt.
160+ return ( ev ) => ev . getId ( ) == readUpToId ;
161+ } else {
162+ // If we didn't, we have to guess by saying if this event is before the
163+ // receipt's ts, then it we pretend it has a receipt.
164+ const receipt = roomOrThread . getReadReceiptForUserId ( myUserId ) ;
165+ if ( receipt ) {
166+ const receiptTimestamp = receipt . data . ts ;
167+ return ( ev ) => ev . getTs ( ) < receiptTimestamp ;
168+ } else {
169+ return ( _ev ) => false ;
170+ }
171+ }
172+ }
0 commit comments