You've already forked matrix-js-sdk
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:
@@ -744,6 +744,7 @@ EventTimelineSet.prototype._aggregateRelations = function(event) {
|
|||||||
relationsWithEventType = relationsWithRelType[eventType] = new Relations(
|
relationsWithEventType = relationsWithRelType[eventType] = new Relations(
|
||||||
relationType,
|
relationType,
|
||||||
eventType,
|
eventType,
|
||||||
|
this.room,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import EventEmitter from 'events';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A container for relation events that supports easy access to common ways of
|
* 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
|
* 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
|
* The typical way to get one of these containers is via
|
||||||
* EventTimelineSet#getRelationsForEvent.
|
* EventTimelineSet#getRelationsForEvent.
|
||||||
*/
|
*/
|
||||||
export default class Relations {
|
export default class Relations extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* @param {String} relationType
|
* @param {String} relationType
|
||||||
* The type of relation involved, such as "m.annotation", "m.reference",
|
* The type of relation involved, such as "m.annotation", "m.reference",
|
||||||
* "m.replace", etc.
|
* "m.replace", etc.
|
||||||
* @param {String} eventType
|
* @param {String} eventType
|
||||||
* The relation event's type, such as "m.reaction", etc.
|
* 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.relationType = relationType;
|
||||||
this.eventType = eventType;
|
this.eventType = eventType;
|
||||||
this._events = [];
|
this._relations = new Set();
|
||||||
this._annotationsByKey = {};
|
this._annotationsByKey = {};
|
||||||
this._annotationsBySender = {};
|
this._annotationsBySender = {};
|
||||||
this._sortedAnnotationsByKey = [];
|
this._sortedAnnotationsByKey = [];
|
||||||
|
|
||||||
|
if (room) {
|
||||||
|
room.on("Room.beforeRedaction", this._onBeforeRedaction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,11 +76,11 @@ export default class Relations {
|
|||||||
this._aggregateAnnotation(key, event);
|
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
|
* These are currently in the order of insertion to this collection, which
|
||||||
* won't match timeline order in the case of scrollback.
|
* won't match timeline order in the case of scrollback.
|
||||||
@@ -79,8 +89,8 @@ export default class Relations {
|
|||||||
* @return {Array}
|
* @return {Array}
|
||||||
* Relation events in insertion order.
|
* Relation events in insertion order.
|
||||||
*/
|
*/
|
||||||
getEvents() {
|
getRelations() {
|
||||||
return this._events;
|
return [...this._relations];
|
||||||
}
|
}
|
||||||
|
|
||||||
_aggregateAnnotation(key, event) {
|
_aggregateAnnotation(key, event) {
|
||||||
@@ -90,16 +100,16 @@ export default class Relations {
|
|||||||
|
|
||||||
let eventsForKey = this._annotationsByKey[key];
|
let eventsForKey = this._annotationsByKey[key];
|
||||||
if (!eventsForKey) {
|
if (!eventsForKey) {
|
||||||
eventsForKey = this._annotationsByKey[key] = [];
|
eventsForKey = this._annotationsByKey[key] = new Set();
|
||||||
this._sortedAnnotationsByKey.push([key, eventsForKey]);
|
this._sortedAnnotationsByKey.push([key, eventsForKey]);
|
||||||
}
|
}
|
||||||
// Add the new event to the list for this key
|
// Add the new event to the set for this key
|
||||||
eventsForKey.push(event);
|
eventsForKey.add(event);
|
||||||
// Re-sort the [key, events] pairs in descending order of event count
|
// Re-sort the [key, events] pairs in descending order of event count
|
||||||
this._sortedAnnotationsByKey.sort((a, b) => {
|
this._sortedAnnotationsByKey.sort((a, b) => {
|
||||||
const aEvents = a[1];
|
const aEvents = a[1];
|
||||||
const bEvents = b[1];
|
const bEvents = b[1];
|
||||||
return bEvents.length - aEvents.length;
|
return bEvents.size - aEvents.size;
|
||||||
});
|
});
|
||||||
|
|
||||||
const sender = event.getSender();
|
const sender = event.getSender();
|
||||||
@@ -111,6 +121,49 @@ export default class Relations {
|
|||||||
eventsFromSender.push(event);
|
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
|
* Get all events in this collection grouped by key and sorted by descending
|
||||||
* event count in each group.
|
* event count in each group.
|
||||||
@@ -119,6 +172,7 @@ export default class Relations {
|
|||||||
*
|
*
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
* An array of [key, events] pairs sorted by descending event count.
|
* An array of [key, events] pairs sorted by descending event count.
|
||||||
|
* The events are stored in a Set (which preserves insertion order).
|
||||||
*/
|
*/
|
||||||
getSortedAnnotationsByKey() {
|
getSortedAnnotationsByKey() {
|
||||||
if (this.relationType !== "m.annotation") {
|
if (this.relationType !== "m.annotation") {
|
||||||
|
|||||||
@@ -1011,6 +1011,7 @@ Room.prototype._addLiveEvent = function(event, duplicateStrategy) {
|
|||||||
// if we know about this event, redact its contents now.
|
// if we know about this event, redact its contents now.
|
||||||
const redactedEvent = this.getUnfilteredTimelineSet().findEventById(redactId);
|
const redactedEvent = this.getUnfilteredTimelineSet().findEventById(redactId);
|
||||||
if (redactedEvent) {
|
if (redactedEvent) {
|
||||||
|
this.emit("Room.beforeRedaction", redactedEvent, event, this);
|
||||||
redactedEvent.makeRedacted(event);
|
redactedEvent.makeRedacted(event);
|
||||||
this.emit("Room.redaction", event, this);
|
this.emit("Room.redaction", event, this);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user