@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
14
14
limitations under the License.
15
15
*/
16
16
17
+ import EventEmitter from 'events' ;
18
+
17
19
/**
18
20
* A container for relation events that supports easy access to common ways of
19
21
* aggregating such events. Each instance holds events that of a single relation
@@ -22,21 +24,29 @@ limitations under the License.
22
24
* The typical way to get one of these containers is via
23
25
* EventTimelineSet#getRelationsForEvent.
24
26
*/
25
- export default class Relations {
27
+ export default class Relations extends EventEmitter {
26
28
/**
27
29
* @param {String } relationType
28
30
* The type of relation involved, such as "m.annotation", "m.reference",
29
31
* "m.replace", etc.
30
32
* @param {String } eventType
31
33
* The relation event's type, such as "m.reaction", etc.
34
+ * @param {?Room } room
35
+ * Room for this container. May be null for non-room cases, such as the
36
+ * notification timeline.
32
37
*/
33
- constructor ( relationType , eventType ) {
38
+ constructor ( relationType , eventType , room ) {
39
+ super ( ) ;
34
40
this . relationType = relationType ;
35
41
this . eventType = eventType ;
36
- this . _events = [ ] ;
42
+ this . _relations = new Set ( ) ;
37
43
this . _annotationsByKey = { } ;
38
44
this . _annotationsBySender = { } ;
39
45
this . _sortedAnnotationsByKey = [ ] ;
46
+
47
+ if ( room ) {
48
+ room . on ( "Room.beforeRedaction" , this . _onBeforeRedaction ) ;
49
+ }
40
50
}
41
51
42
52
/**
@@ -66,11 +76,11 @@ export default class Relations {
66
76
this . _aggregateAnnotation ( key , event ) ;
67
77
}
68
78
69
- this . _events . push ( event ) ;
79
+ this . _relations . add ( event ) ;
70
80
}
71
81
72
82
/**
73
- * Get all events in this collection.
83
+ * Get all relation events in this collection.
74
84
*
75
85
* These are currently in the order of insertion to this collection, which
76
86
* won't match timeline order in the case of scrollback.
@@ -79,8 +89,8 @@ export default class Relations {
79
89
* @return {Array }
80
90
* Relation events in insertion order.
81
91
*/
82
- getEvents ( ) {
83
- return this . _events ;
92
+ getRelations ( ) {
93
+ return [ ... this . _relations ] ;
84
94
}
85
95
86
96
_aggregateAnnotation ( key , event ) {
@@ -90,16 +100,16 @@ export default class Relations {
90
100
91
101
let eventsForKey = this . _annotationsByKey [ key ] ;
92
102
if ( ! eventsForKey ) {
93
- eventsForKey = this . _annotationsByKey [ key ] = [ ] ;
103
+ eventsForKey = this . _annotationsByKey [ key ] = new Set ( ) ;
94
104
this . _sortedAnnotationsByKey . push ( [ key , eventsForKey ] ) ;
95
105
}
96
- // Add the new event to the list for this key
97
- eventsForKey . push ( event ) ;
106
+ // Add the new event to the set for this key
107
+ eventsForKey . add ( event ) ;
98
108
// Re-sort the [key, events] pairs in descending order of event count
99
109
this . _sortedAnnotationsByKey . sort ( ( a , b ) => {
100
110
const aEvents = a [ 1 ] ;
101
111
const bEvents = b [ 1 ] ;
102
- return bEvents . length - aEvents . length ;
112
+ return bEvents . size - aEvents . size ;
103
113
} ) ;
104
114
105
115
const sender = event . getSender ( ) ;
@@ -111,6 +121,49 @@ export default class Relations {
111
121
eventsFromSender . push ( event ) ;
112
122
}
113
123
124
+ /**
125
+ * For relations that are about to be redacted, remove them from aggregation
126
+ * data sets and emit an update event.
127
+ *
128
+ * @param {MatrixEvent } redactedEvent
129
+ * The original relation event that is about to be redacted.
130
+ */
131
+ _onBeforeRedaction = ( redactedEvent ) => {
132
+ if ( ! this . _relations . has ( redactedEvent ) ) {
133
+ return ;
134
+ }
135
+
136
+ if ( this . relationType === "m.annotation" ) {
137
+ // Remove the redacted annotation from aggregation by key
138
+ const content = redactedEvent . getContent ( ) ;
139
+ const relation = content && content [ "m.relates_to" ] ;
140
+ if ( ! relation ) {
141
+ return ;
142
+ }
143
+
144
+ const key = relation . key ;
145
+ const eventsForKey = this . _annotationsByKey [ key ] ;
146
+ if ( ! eventsForKey ) {
147
+ return ;
148
+ }
149
+ eventsForKey . delete ( redactedEvent ) ;
150
+
151
+ // Re-sort the [key, events] pairs in descending order of event count
152
+ this . _sortedAnnotationsByKey . sort ( ( a , b ) => {
153
+ const aEvents = a [ 1 ] ;
154
+ const bEvents = b [ 1 ] ;
155
+ return bEvents . size - aEvents . size ;
156
+ } ) ;
157
+ }
158
+
159
+ // Dispatch a redaction event on this collection. `setTimeout` is used
160
+ // to wait until the next event loop iteration by which time the event
161
+ // has actually been marked as redacted.
162
+ setTimeout ( ( ) => {
163
+ this . emit ( "Relations.redaction" ) ;
164
+ } , 0 ) ;
165
+ }
166
+
114
167
/**
115
168
* Get all events in this collection grouped by key and sorted by descending
116
169
* event count in each group.
@@ -119,6 +172,7 @@ export default class Relations {
119
172
*
120
173
* @return {Array }
121
174
* An array of [key, events] pairs sorted by descending event count.
175
+ * The events are stored in a Set (which preserves insertion order).
122
176
*/
123
177
getSortedAnnotationsByKey ( ) {
124
178
if ( this . relationType !== "m.annotation" ) {
0 commit comments