@@ -46,10 +46,10 @@ import {
46
46
FILTER_RELATED_BY_SENDERS ,
47
47
ThreadFilterType ,
48
48
} from "./thread" ;
49
- import { TypedEventEmitter } from "./typed-event-emitter" ;
50
49
import { ReceiptType } from "../@types/read_receipts" ;
51
50
import { IStateEventWithRoomId } from "../@types/search" ;
52
51
import { RelationsContainer } from "./relations-container" ;
52
+ import { ReceiptContent , synthesizeReceipt , TimelineReceipts } from "./timeline-receipts" ;
53
53
54
54
// These constants are used as sane defaults when the homeserver doesn't support
55
55
// the m.room_versions capability. In practice, KNOWN_SAFE_ROOM_VERSION should be
@@ -60,23 +60,6 @@ import { RelationsContainer } from "./relations-container";
60
60
export const KNOWN_SAFE_ROOM_VERSION = '9' ;
61
61
const SAFE_ROOM_VERSIONS = [ '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' ] ;
62
62
63
- function synthesizeReceipt ( userId : string , event : MatrixEvent , receiptType : ReceiptType ) : MatrixEvent {
64
- // console.log("synthesizing receipt for "+event.getId());
65
- return new MatrixEvent ( {
66
- content : {
67
- [ event . getId ( ) ] : {
68
- [ receiptType ] : {
69
- [ userId ] : {
70
- ts : event . getTs ( ) ,
71
- } ,
72
- } ,
73
- } ,
74
- } ,
75
- type : EventType . Receipt ,
76
- room_id : event . getRoomId ( ) ,
77
- } ) ;
78
- }
79
-
80
63
interface IOpts {
81
64
storageToken ?: string ;
82
65
pendingEventOrdering ?: PendingEventOrdering ;
@@ -90,40 +73,6 @@ export interface IRecommendedVersion {
90
73
urgent : boolean ;
91
74
}
92
75
93
- interface IReceipt {
94
- ts : number ;
95
- }
96
-
97
- export interface IWrappedReceipt {
98
- eventId : string ;
99
- data : IReceipt ;
100
- }
101
-
102
- interface ICachedReceipt {
103
- type : ReceiptType ;
104
- userId : string ;
105
- data : IReceipt ;
106
- }
107
-
108
- type ReceiptCache = { [ eventId : string ] : ICachedReceipt [ ] } ;
109
-
110
- interface IReceiptContent {
111
- [ eventId : string ] : {
112
- [ key in ReceiptType ] : {
113
- [ userId : string ] : IReceipt ;
114
- } ;
115
- } ;
116
- }
117
-
118
- const ReceiptPairRealIndex = 0 ;
119
- const ReceiptPairSyntheticIndex = 1 ;
120
- // We will only hold a synthetic receipt if we do not have a real receipt or the synthetic is newer.
121
- type Receipts = {
122
- [ receiptType : string ] : {
123
- [ userId : string ] : [ IWrappedReceipt , IWrappedReceipt ] ; // Pair<real receipt, synthetic receipt> (both nullable)
124
- } ;
125
- } ;
126
-
127
76
// When inserting a visibility event affecting event `eventId`, we
128
77
// need to scan through existing visibility events for `eventId`.
129
78
// In theory, this could take an unlimited amount of time if:
@@ -212,12 +161,6 @@ type NotificationCount = Partial<Record<NotificationCountType, number>>;
212
161
export class Room extends TypedEventEmitter < EmittedEvents , RoomEventHandlerMap > {
213
162
public readonly reEmitter : TypedReEmitter < EmittedEvents , RoomEventHandlerMap > ;
214
163
private txnToEvent : Record < string , MatrixEvent > = { } ; // Pending in-flight requests { string: MatrixEvent }
215
- // receipts should clobber based on receipt_type and user_id pairs hence
216
- // the form of this structure. This is sub-optimal for the exposed APIs
217
- // which pass in an event ID and get back some receipts, so we also store
218
- // a pre-cached list for this purpose.
219
- private receipts : Receipts = { } ; // { receipt_type: { user_id: IReceipt } }
220
- private receiptCacheByEventId : ReceiptCache = { } ; // { event_id: ICachedReceipt[] }
221
164
private notificationCounts : NotificationCount = { } ;
222
165
private threadNotifications : Record < string , NotificationCount > = { } ;
223
166
private readonly timelineSets : EventTimelineSet [ ] ;
@@ -2622,7 +2565,7 @@ export class Room extends TypedEventEmitter<EmittedEvents, RoomEventHandlerMap>
2622
2565
2623
2566
let latest = privateReadReceipt ;
2624
2567
[ unstablePrivateReadReceipt , publicReadReceipt ] . forEach ( ( receipt ) => {
2625
- if ( receipt ?. data ?. ts > latest ?. data ?. ts || ! latest ) {
2568
+ if ( receipt ?. data ?. ts > latest ?. data ?. ts ) {
2626
2569
latest = receipt ;
2627
2570
}
2628
2571
} ) ;
@@ -2688,123 +2631,28 @@ export class Room extends TypedEventEmitter<EmittedEvents, RoomEventHandlerMap>
2688
2631
* @param {Boolean } synthetic True if this event is implicit.
2689
2632
*/
2690
2633
public addReceipt ( event : MatrixEvent , synthetic = false ) : void {
2691
- this . addReceiptsToStructure ( event , synthetic ) ;
2692
- // send events after we've regenerated the structure & cache, otherwise things that
2693
- // listened for the event would read stale data.
2694
- this . emit ( RoomEvent . Receipt , event , this ) ;
2695
- }
2696
-
2697
- /**
2698
- * Add a receipt event to the room.
2699
- * @param {MatrixEvent } event The m.receipt event.
2700
- * @param {Boolean } synthetic True if this event is implicit.
2701
- */
2702
- private addReceiptsToStructure ( event : MatrixEvent , synthetic : boolean ) : void {
2703
- const content = event . getContent < IReceiptContent > ( ) ;
2704
- Object . keys ( content ) . forEach ( ( eventId ) => {
2705
- Object . keys ( content [ eventId ] ) . forEach ( ( receiptType ) => {
2706
- Object . keys ( content [ eventId ] [ receiptType ] ) . forEach ( ( userId ) => {
2707
- const receipt = content [ eventId ] [ receiptType ] [ userId ] ;
2708
-
2709
- if ( ! this . receipts [ receiptType ] ) {
2710
- this . receipts [ receiptType ] = { } ;
2711
- }
2712
- if ( ! this . receipts [ receiptType ] [ userId ] ) {
2713
- this . receipts [ receiptType ] [ userId ] = [ null , null ] ;
2714
- }
2715
-
2716
- const pair = this . receipts [ receiptType ] [ userId ] ;
2717
-
2718
- let existingReceipt = pair [ ReceiptPairRealIndex ] ;
2719
- if ( synthetic ) {
2720
- existingReceipt = pair [ ReceiptPairSyntheticIndex ] ?? pair [ ReceiptPairRealIndex ] ;
2721
- }
2722
-
2723
- if ( existingReceipt ) {
2724
- // we only want to add this receipt if we think it is later than the one we already have.
2725
- // This is managed server-side, but because we synthesize RRs locally we have to do it here too.
2726
- const ordering = this . getUnfilteredTimelineSet ( ) . compareEventOrdering (
2727
- existingReceipt . eventId ,
2728
- eventId ,
2729
- ) ;
2730
- if ( ordering !== null && ordering >= 0 ) {
2731
- return ;
2732
- }
2733
- }
2734
-
2735
- const wrappedReceipt : IWrappedReceipt = {
2634
+ const content = event . getContent < ReceiptContent > ( ) ;
2635
+ Object . keys ( content ) . forEach ( ( eventId : string ) => {
2636
+ Object . keys ( content [ eventId ] ) . forEach ( ( receiptType : ReceiptType ) => {
2637
+ Object . keys ( content [ eventId ] [ receiptType ] ) . forEach ( ( userId : string ) => {
2638
+ // hack, threadId should be thread_id
2639
+ const receipt = content [ eventId ] [ receiptType ] [ userId ] as any ;
2640
+
2641
+ const receiptDestination = this . threads . get ( receipt . thread_id ) ?? this ;
2642
+ receiptDestination . addReceiptToStructure (
2736
2643
eventId ,
2737
- data : receipt ,
2738
- } ;
2739
-
2740
- const realReceipt = synthetic ? pair [ ReceiptPairRealIndex ] : wrappedReceipt ;
2741
- const syntheticReceipt = synthetic ? wrappedReceipt : pair [ ReceiptPairSyntheticIndex ] ;
2742
-
2743
- let ordering : number | null = null ;
2744
- if ( realReceipt && syntheticReceipt ) {
2745
- ordering = this . getUnfilteredTimelineSet ( ) . compareEventOrdering (
2746
- realReceipt . eventId ,
2747
- syntheticReceipt . eventId ,
2748
- ) ;
2749
- }
2750
-
2751
- const preferSynthetic = ordering === null || ordering < 0 ;
2752
-
2753
- // we don't bother caching just real receipts by event ID as there's nothing that would read it.
2754
- // Take the current cached receipt before we overwrite the pair elements.
2755
- const cachedReceipt = pair [ ReceiptPairSyntheticIndex ] ?? pair [ ReceiptPairRealIndex ] ;
2756
-
2757
- if ( synthetic && preferSynthetic ) {
2758
- pair [ ReceiptPairSyntheticIndex ] = wrappedReceipt ;
2759
- } else if ( ! synthetic ) {
2760
- pair [ ReceiptPairRealIndex ] = wrappedReceipt ;
2761
-
2762
- if ( ! preferSynthetic ) {
2763
- pair [ ReceiptPairSyntheticIndex ] = null ;
2764
- }
2765
- }
2766
-
2767
- const newCachedReceipt = pair [ ReceiptPairSyntheticIndex ] ?? pair [ ReceiptPairRealIndex ] ;
2768
- if ( cachedReceipt === newCachedReceipt ) return ;
2769
-
2770
- // clean up any previous cache entry
2771
- if ( cachedReceipt && this . receiptCacheByEventId [ cachedReceipt . eventId ] ) {
2772
- const previousEventId = cachedReceipt . eventId ;
2773
- // Remove the receipt we're about to clobber out of existence from the cache
2774
- this . receiptCacheByEventId [ previousEventId ] = (
2775
- this . receiptCacheByEventId [ previousEventId ] . filter ( r => {
2776
- return r . type !== receiptType || r . userId !== userId ;
2777
- } )
2778
- ) ;
2779
-
2780
- if ( this . receiptCacheByEventId [ previousEventId ] . length < 1 ) {
2781
- delete this . receiptCacheByEventId [ previousEventId ] ; // clean up the cache keys
2782
- }
2783
- }
2784
-
2785
- // cache the new one
2786
- if ( ! this . receiptCacheByEventId [ eventId ] ) {
2787
- this . receiptCacheByEventId [ eventId ] = [ ] ;
2788
- }
2789
- this . receiptCacheByEventId [ eventId ] . push ( {
2790
- userId : userId ,
2791
- type : receiptType as ReceiptType ,
2792
- data : receipt ,
2793
- } ) ;
2644
+ receiptType ,
2645
+ userId ,
2646
+ receipt ,
2647
+ synthetic ,
2648
+ ) ;
2794
2649
} ) ;
2795
2650
} ) ;
2796
2651
} ) ;
2797
- }
2798
2652
2799
- /**
2800
- * Add a temporary local-echo receipt to the room to reflect in the
2801
- * client the fact that we've sent one.
2802
- * @param {string } userId The user ID if the receipt sender
2803
- * @param {MatrixEvent } e The event that is to be acknowledged
2804
- * @param {ReceiptType } receiptType The type of receipt
2805
- */
2806
- public addLocalEchoReceipt ( userId : string , e : MatrixEvent , receiptType : ReceiptType ) : void {
2807
- this . addReceipt ( synthesizeReceipt ( userId , e , receiptType ) , true ) ;
2653
+ // send events after we've regenerated the structure & cache, otherwise things that
2654
+ // listened for the event would read stale data.
2655
+ this . emit ( RoomEvent . Receipt , event , this ) ;
2808
2656
}
2809
2657
2810
2658
/**
0 commit comments