1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-12-05 17:02:07 +03:00

Update relation collections after redaction

This watches for redactions of relations and updates the relations collection
to match, including various aggregations. In addition, a redaction event is
emitted on the redaction collection to notify consumers of the change.

Part of https://github.com/vector-im/riot-web/issues/9574
Part of https://github.com/vector-im/riot-web/issues/9485
This commit is contained in:
J. Ryan Stinnett
2019-05-09 15:08:16 +01:00
parent 761806c678
commit 53d8cf0852
3 changed files with 67 additions and 11 deletions

View File

@@ -744,6 +744,7 @@ EventTimelineSet.prototype._aggregateRelations = function(event) {
relationsWithEventType = relationsWithRelType[eventType] = new Relations(
relationType,
eventType,
this.room,
);
}

View File

@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import EventEmitter from 'events';
/**
* A container for relation events that supports easy access to common ways of
* aggregating such events. Each instance holds events that of a single relation
@@ -22,21 +24,29 @@ limitations under the License.
* The typical way to get one of these containers is via
* EventTimelineSet#getRelationsForEvent.
*/
export default class Relations {
export default class Relations extends EventEmitter {
/**
* @param {String} relationType
* The type of relation involved, such as "m.annotation", "m.reference",
* "m.replace", etc.
* @param {String} eventType
* The relation event's type, such as "m.reaction", etc.
* @param {?Room} room
* Room for this container. May be null for non-room cases, such as the
* notification timeline.
*/
constructor(relationType, eventType) {
constructor(relationType, eventType, room) {
super();
this.relationType = relationType;
this.eventType = eventType;
this._events = [];
this._relations = new Set();
this._annotationsByKey = {};
this._annotationsBySender = {};
this._sortedAnnotationsByKey = [];
if (room) {
room.on("Room.beforeRedaction", this._onBeforeRedaction);
}
}
/**
@@ -66,11 +76,11 @@ export default class Relations {
this._aggregateAnnotation(key, event);
}
this._events.push(event);
this._relations.add(event);
}
/**
* Get all events in this collection.
* Get all relation events in this collection.
*
* These are currently in the order of insertion to this collection, which
* won't match timeline order in the case of scrollback.
@@ -79,8 +89,8 @@ export default class Relations {
* @return {Array}
* Relation events in insertion order.
*/
getEvents() {
return this._events;
getRelations() {
return [...this._relations];
}
_aggregateAnnotation(key, event) {
@@ -90,16 +100,16 @@ export default class Relations {
let eventsForKey = this._annotationsByKey[key];
if (!eventsForKey) {
eventsForKey = this._annotationsByKey[key] = [];
eventsForKey = this._annotationsByKey[key] = new Set();
this._sortedAnnotationsByKey.push([key, eventsForKey]);
}
// Add the new event to the list for this key
eventsForKey.push(event);
// Add the new event to the set for this key
eventsForKey.add(event);
// Re-sort the [key, events] pairs in descending order of event count
this._sortedAnnotationsByKey.sort((a, b) => {
const aEvents = a[1];
const bEvents = b[1];
return bEvents.length - aEvents.length;
return bEvents.size - aEvents.size;
});
const sender = event.getSender();
@@ -111,6 +121,49 @@ export default class Relations {
eventsFromSender.push(event);
}
/**
* For relations that are about to be redacted, remove them from aggregation
* data sets and emit an update event.
*
* @param {MatrixEvent} redactedEvent
* The original relation event that is about to be redacted.
*/
_onBeforeRedaction = (redactedEvent) => {
if (!this._relations.has(redactedEvent)) {
return;
}
if (this.relationType === "m.annotation") {
// Remove the redacted annotation from aggregation by key
const content = redactedEvent.getContent();
const relation = content && content["m.relates_to"];
if (!relation) {
return;
}
const key = relation.key;
const eventsForKey = this._annotationsByKey[key];
if (!eventsForKey) {
return;
}
eventsForKey.delete(redactedEvent);
// Re-sort the [key, events] pairs in descending order of event count
this._sortedAnnotationsByKey.sort((a, b) => {
const aEvents = a[1];
const bEvents = b[1];
return bEvents.size - aEvents.size;
});
}
// Dispatch a redaction event on this collection. `setTimeout` is used
// to wait until the next event loop iteration by which time the event
// has actually been marked as redacted.
setTimeout(() => {
this.emit("Relations.redaction");
}, 0);
}
/**
* Get all events in this collection grouped by key and sorted by descending
* event count in each group.
@@ -119,6 +172,7 @@ export default class Relations {
*
* @return {Array}
* An array of [key, events] pairs sorted by descending event count.
* The events are stored in a Set (which preserves insertion order).
*/
getSortedAnnotationsByKey() {
if (this.relationType !== "m.annotation") {

View File

@@ -1011,6 +1011,7 @@ Room.prototype._addLiveEvent = function(event, duplicateStrategy) {
// if we know about this event, redact its contents now.
const redactedEvent = this.getUnfilteredTimelineSet().findEventById(redactId);
if (redactedEvent) {
this.emit("Room.beforeRedaction", redactedEvent, event, this);
redactedEvent.makeRedacted(event);
this.emit("Room.redaction", event, this);