@@ -18,7 +18,7 @@ import MockHttpBackend from "matrix-mock-request";
18
18
19
19
import { MAIN_ROOM_TIMELINE , ReceiptType , WrappedReceipt } from "../../src/@types/read_receipts" ;
20
20
import { MatrixClient } from "../../src/client" ;
21
- import { EventType , MatrixEvent , Room } from "../../src/matrix" ;
21
+ import { EventType , MatrixEvent , RelationType , Room , threadIdForReceipt } from "../../src/matrix" ;
22
22
import { synthesizeReceipt } from "../../src/models/read-receipt" ;
23
23
import { encodeUri } from "../../src/utils" ;
24
24
import * as utils from "../test-utils/test-utils" ;
@@ -258,4 +258,200 @@ describe("Read receipt", () => {
258
258
expect ( room . getEventReadUpTo ( userId ) ) . toBe ( mainTimelineReceipt . eventId ) ;
259
259
} ) ;
260
260
} ) ;
261
+
262
+ describe ( "Determining the right thread ID for a receipt" , ( ) => {
263
+ it ( "provides the thread root ID for a normal threaded message" , ( ) => {
264
+ const event = utils . mkEvent ( {
265
+ event : true ,
266
+ type : EventType . RoomMessage ,
267
+ user : "@bob:matrix.org" ,
268
+ room : "!roomx" ,
269
+ content : {
270
+ "body" : "Hello from a thread" ,
271
+ "m.relates_to" : {
272
+ "event_id" : "$thread1" ,
273
+ "m.in_reply_to" : {
274
+ event_id : "$thread1" ,
275
+ } ,
276
+ "rel_type" : "m.thread" ,
277
+ } ,
278
+ } ,
279
+ } ) ;
280
+
281
+ expect ( threadIdForReceipt ( event ) ) . toEqual ( "$thread1" ) ;
282
+ } ) ;
283
+
284
+ it ( "provides 'main' for a non-thread message" , ( ) => {
285
+ const event = utils . mkEvent ( {
286
+ event : true ,
287
+ type : EventType . RoomMessage ,
288
+ user : "@bob:matrix.org" ,
289
+ room : "!roomx" ,
290
+ content : { body : "Hello" } ,
291
+ } ) ;
292
+
293
+ expect ( threadIdForReceipt ( event ) ) . toEqual ( "main" ) ;
294
+ } ) ;
295
+
296
+ it ( "provides 'main' for a thread root" , ( ) => {
297
+ const event = utils . mkEvent ( {
298
+ event : true ,
299
+ type : EventType . RoomMessage ,
300
+ user : "@bob:matrix.org" ,
301
+ room : "!roomx" ,
302
+ content : { body : "Hello" } ,
303
+ } ) ;
304
+ // Set thread ID to this event's ID, meaning this is the thread root
305
+ event . setThreadId ( event . getId ( ) ) ;
306
+
307
+ expect ( threadIdForReceipt ( event ) ) . toEqual ( "main" ) ;
308
+ } ) ;
309
+
310
+ it ( "provides 'main' for a reaction to a thread root" , ( ) => {
311
+ const event = utils . mkEvent ( {
312
+ event : true ,
313
+ type : EventType . Reaction ,
314
+ user : "@bob:matrix.org" ,
315
+ room : "!roomx" ,
316
+ content : {
317
+ "m.relates_to" : {
318
+ rel_type : RelationType . Annotation ,
319
+ event_id : "$thread1" ,
320
+ key : Math . random ( ) . toString ( ) ,
321
+ } ,
322
+ } ,
323
+ } ) ;
324
+
325
+ // Set thread Id, meaning this looks like it's in the thread (this
326
+ // happens for relations like this, so that they appear in the
327
+ // thread's timeline).
328
+ event . setThreadId ( "$thread1" ) ;
329
+
330
+ // But because it's a reaction to the thread root, it's in main
331
+ expect ( threadIdForReceipt ( event ) ) . toEqual ( "main" ) ;
332
+ } ) ;
333
+
334
+ it ( "provides the thread ID for a reaction to a threaded message" , ( ) => {
335
+ const event = utils . mkEvent ( {
336
+ event : true ,
337
+ type : EventType . Reaction ,
338
+ user : "@bob:matrix.org" ,
339
+ room : "!roomx" ,
340
+ content : {
341
+ "m.relates_to" : {
342
+ rel_type : RelationType . Annotation ,
343
+ event_id : "$withinthread2" ,
344
+ key : Math . random ( ) . toString ( ) ,
345
+ } ,
346
+ } ,
347
+ } ) ;
348
+
349
+ // Set thread Id, to say this message is in the thread. This happens
350
+ // when the message arrived and is classified.
351
+ event . setThreadId ( "$thread1" ) ;
352
+
353
+ // It's in the thread because it refers to something else, not the
354
+ // thread root
355
+ expect ( threadIdForReceipt ( event ) ) . toEqual ( "$thread1" ) ;
356
+ } ) ;
357
+
358
+ it ( "(suprisingly?) provides 'main' for a redaction of a threaded message" , ( ) => {
359
+ const event = utils . mkEvent ( {
360
+ event : true ,
361
+ type : EventType . RoomRedaction ,
362
+ content : {
363
+ reason : "Spamming" ,
364
+ } ,
365
+ redacts : "$withinthread2" ,
366
+ room : "!roomx" ,
367
+ user : "@bob:matrix.org" ,
368
+ } ) ;
369
+
370
+ // Set thread Id, to say this message is in the thread.
371
+ event . setThreadId ( "$thread1" ) ;
372
+
373
+ // Because redacting a message removes all its m.relations, the
374
+ // message is no longer in the thread, so we must send a receipt for
375
+ // it in the main timeline.
376
+ //
377
+ // This is surprising, but it follows the spec (at least up to
378
+ // current latest room version, 11). In fact, the event should no
379
+ // longer have a thread ID set on it, so this testcase should not
380
+ // come up. (At time of writing, this is not the case though - it
381
+ // does still have threadId set.)
382
+ expect ( threadIdForReceipt ( event ) ) . toEqual ( "main" ) ;
383
+ } ) ;
384
+
385
+ it ( "provides the thread ID for an edit of a threaded message" , ( ) => {
386
+ const event = utils . mkEvent ( {
387
+ event : true ,
388
+ type : EventType . RoomRedaction ,
389
+ content : {
390
+ "body" : "Edited!" ,
391
+ "m.new_content" : {
392
+ body : "Edited!" ,
393
+ } ,
394
+ "m.relates_to" : {
395
+ rel_type : RelationType . Replace ,
396
+ event_id : "$withinthread2" ,
397
+ } ,
398
+ } ,
399
+ room : "!roomx" ,
400
+ user : "@bob:matrix.org" ,
401
+ } ) ;
402
+
403
+ // Set thread Id, to say this message is in the thread.
404
+ event . setThreadId ( "$thread1" ) ;
405
+
406
+ // It's in the thread, because it redacts something inside the
407
+ // thread (not the thread root)
408
+ expect ( threadIdForReceipt ( event ) ) . toEqual ( "$thread1" ) ;
409
+ } ) ;
410
+
411
+ it ( "provides 'main' for an edit of a thread root" , ( ) => {
412
+ const event = utils . mkEvent ( {
413
+ event : true ,
414
+ type : EventType . RoomRedaction ,
415
+ content : {
416
+ "body" : "Edited!" ,
417
+ "m.new_content" : {
418
+ body : "Edited!" ,
419
+ } ,
420
+ "m.relates_to" : {
421
+ rel_type : RelationType . Replace ,
422
+ event_id : "$thread1" ,
423
+ } ,
424
+ } ,
425
+ room : "!roomx" ,
426
+ user : "@bob:matrix.org" ,
427
+ } ) ;
428
+
429
+ // Set thread Id, to say this message is in the thread.
430
+ event . setThreadId ( "$thread1" ) ;
431
+
432
+ // It's in the thread, because it redacts something inside the
433
+ // thread (not the thread root)
434
+ expect ( threadIdForReceipt ( event ) ) . toEqual ( "main" ) ;
435
+ } ) ;
436
+
437
+ it ( "provides 'main' for a redaction of the thread root" , ( ) => {
438
+ const event = utils . mkEvent ( {
439
+ event : true ,
440
+ type : EventType . RoomRedaction ,
441
+ content : {
442
+ reason : "Spamming" ,
443
+ } ,
444
+ redacts : "$thread1" ,
445
+ room : "!roomx" ,
446
+ user : "@bob:matrix.org" ,
447
+ } ) ;
448
+
449
+ // Set thread Id, to say this message is in the thread.
450
+ event . setThreadId ( "$thread1" ) ;
451
+
452
+ // It's in the thread, because it redacts something inside the
453
+ // thread (not the thread root)
454
+ expect ( threadIdForReceipt ( event ) ) . toEqual ( "main" ) ;
455
+ } ) ;
456
+ } ) ;
261
457
} ) ;
0 commit comments