You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-26 17:03:12 +03:00
Merge pull request #80 from matrix-org/rav/keep_redactions
Keep redacted events in the timeline
This commit is contained in:
@@ -21,7 +21,6 @@ limitations under the License.
|
||||
* @module models/event
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Enum for event statuses.
|
||||
* @readonly
|
||||
@@ -189,5 +188,83 @@ module.exports.MatrixEvent.prototype = {
|
||||
|
||||
getUnsigned: function() {
|
||||
return this.event.unsigned || {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the content of an event in the same way it would be by the server
|
||||
* if it were redacted before it was sent to us
|
||||
*
|
||||
* @param {Object} the raw event causing the redaction
|
||||
*/
|
||||
makeRedacted: function(redaction_event) {
|
||||
if (!this.event.unsigned) {
|
||||
this.event.unsigned = {};
|
||||
}
|
||||
this.event.unsigned.redacted_because = redaction_event;
|
||||
|
||||
var key;
|
||||
for (key in this.event) {
|
||||
if (!this.event.hasOwnProperty(key)) { continue; }
|
||||
if (!_REDACT_KEEP_KEY_MAP[key]) {
|
||||
delete this.event[key];
|
||||
}
|
||||
}
|
||||
|
||||
var keeps = _REDACT_KEEP_CONTENT_MAP[this.getType()] || {};
|
||||
for (key in this.event.content) {
|
||||
if (!this.event.content.hasOwnProperty(key)) { continue; }
|
||||
if (!keeps[key]) {
|
||||
delete this.event.content[key];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if this event has been redacted
|
||||
*
|
||||
* @return {boolean} True if this event has been redacted
|
||||
*/
|
||||
isRedacted: function() {
|
||||
return Boolean(this.getUnsigned().redacted_because);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/* http://matrix.org/docs/spec/r0.0.1/client_server.html#redactions says:
|
||||
*
|
||||
* the server should strip off any keys not in the following list:
|
||||
* event_id
|
||||
* type
|
||||
* room_id
|
||||
* user_id
|
||||
* state_key
|
||||
* prev_state
|
||||
* content
|
||||
* [we keep 'unsigned' as well, since that is created by the local server]
|
||||
*
|
||||
* The content object should also be stripped of all keys, unless it is one of
|
||||
* one of the following event types:
|
||||
* m.room.member allows key membership
|
||||
* m.room.create allows key creator
|
||||
* m.room.join_rules allows key join_rule
|
||||
* m.room.power_levels allows keys ban, events, events_default, kick,
|
||||
* redact, state_default, users, users_default.
|
||||
* m.room.aliases allows key aliases
|
||||
*/
|
||||
// a map giving the keys we keep when an event is redacted
|
||||
var _REDACT_KEEP_KEY_MAP = [
|
||||
'event_id', 'type', 'room_id', 'user_id', 'state_key', 'prev_state',
|
||||
'content', 'unsigned',
|
||||
].reduce(function(ret, val) { ret[val] = 1; return ret; }, {});
|
||||
|
||||
// a map from event type to the .content keys we keep when an event is redacted
|
||||
var _REDACT_KEEP_CONTENT_MAP = {
|
||||
'm.room.member': {'membership': 1},
|
||||
'm.room.create': {'creator': 1},
|
||||
'm.room.join_rules': {'join_rule': 1},
|
||||
'm.room.power_levels': {'ban': 1, 'events': 1, 'events_default': 1,
|
||||
'kick': 1, 'redact': 1, 'state_default': 1,
|
||||
'users': 1, 'users_default': 1,
|
||||
},
|
||||
'm.room.aliases': {'aliases': 1},
|
||||
};
|
||||
|
||||
@@ -118,7 +118,6 @@ function Room(roomId, opts) {
|
||||
this.summary = null;
|
||||
this.storageToken = opts.storageToken;
|
||||
this._opts = opts;
|
||||
this._redactions = [];
|
||||
this._txnToEvent = {}; // Pending in-flight requests { string: MatrixEvent }
|
||||
// receipts should clobber based on receipt_type and user_id pairs hence
|
||||
// the form of this structure. This is sub-optimal for the exposed APIs
|
||||
@@ -216,6 +215,22 @@ Room.prototype.getTimelineForEvent = function(eventId) {
|
||||
return (res === undefined) ? null : res;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an event which is stored in our timelines
|
||||
*
|
||||
* @param {string} eventId event ID to look for
|
||||
* @return {?module:models/event~MatrixEvent} the given event, or undefined if unknown
|
||||
*/
|
||||
Room.prototype.findEventById = function(eventId) {
|
||||
var tl = this.getTimelineForEvent(eventId);
|
||||
if (!tl) {
|
||||
return undefined;
|
||||
}
|
||||
return utils.findElement(tl.getEvents(),
|
||||
function(ev) { return ev.getId() == eventId; });
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get one of the notification counts for this room
|
||||
* @param {String} type The type of notification count to get. default: 'total'
|
||||
@@ -523,29 +538,8 @@ Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline,
|
||||
Room.prototype._addEventToTimeline = function(event, timeline, toStartOfTimeline,
|
||||
spliceBeforeLocalEcho) {
|
||||
var eventId = event.getId();
|
||||
|
||||
if (this._redactions.indexOf(eventId) >= 0) {
|
||||
return; // do not add the redacted event.
|
||||
}
|
||||
|
||||
if (event.getType() === "m.room.redaction") {
|
||||
var redactId = event.event.redacts;
|
||||
|
||||
// try to remove the element
|
||||
var removed = this.removeEvent(redactId);
|
||||
if (!removed) {
|
||||
// redactions will trickle in BEFORE the event redacted so make
|
||||
// a note of the redacted event; we'll check it later.
|
||||
this._redactions.push(event.event.redacts);
|
||||
}
|
||||
// NB: We continue to add the redaction event to the timeline so clients
|
||||
// can say "so and so redacted an event" if they wish to.
|
||||
}
|
||||
|
||||
if (this._redactions.indexOf(eventId) < 0) {
|
||||
timeline.addEvent(event, toStartOfTimeline, spliceBeforeLocalEcho);
|
||||
this._eventIdToTimeline[eventId] = timeline;
|
||||
}
|
||||
|
||||
var data = {
|
||||
timeline: timeline,
|
||||
@@ -599,6 +593,28 @@ Room.prototype._addLiveEvents = function(events) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (events[i].getType() === "m.room.redaction") {
|
||||
var redactId = events[i].event.redacts;
|
||||
|
||||
// if we know about this event, redact its contents now.
|
||||
var redactedEvent = this.findEventById(redactId);
|
||||
if (redactedEvent) {
|
||||
redactedEvent.makeRedacted(events[i]);
|
||||
this.emit("Room.redaction", events[i], this);
|
||||
|
||||
// TODO: we stash user displaynames (among other things) in
|
||||
// RoomMember objects which are then attached to other events
|
||||
// (in the sender and target fields). We should get those
|
||||
// RoomMember objects to update themselves when the events that
|
||||
// they are based on are changed.
|
||||
}
|
||||
|
||||
// NB: We continue to add the redaction event to the timeline so
|
||||
// clients can say "so and so redacted an event" if they wish to. Also
|
||||
// this may be needed to trigger an update.
|
||||
}
|
||||
|
||||
var spliceBeforeLocalEcho = !isLocalEcho && addLocalEchoToEnd;
|
||||
|
||||
if (!this._eventIdToTimeline[events[i].getId()]) {
|
||||
@@ -1147,6 +1163,17 @@ module.exports = Room;
|
||||
* });
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fires when an event we had previously received is redacted.
|
||||
*
|
||||
* (Note this is *not* fired when the redaction happens before we receive the
|
||||
* event).
|
||||
*
|
||||
* @event module:client~MatrixClient#"Room.redaction"
|
||||
* @param {MatrixEvent} event The matrix event which was redacted
|
||||
* @param {Room} room The room containing the redacted event
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fires whenever the name of a room is updated.
|
||||
* @event module:client~MatrixClient#"Room.name"
|
||||
|
||||
@@ -83,7 +83,8 @@ SyncApi.prototype.createRoom = function(roomId) {
|
||||
pendingEventOrdering: this.opts.pendingEventOrdering,
|
||||
timelineSupport: client.timelineSupport,
|
||||
});
|
||||
reEmit(client, room, ["Room.name", "Room.timeline", "Room.receipt", "Room.tags"]);
|
||||
reEmit(client, room, ["Room.name", "Room.timeline", "Room.redaction",
|
||||
"Room.receipt", "Room.tags"]);
|
||||
this._registerStateListeners(room);
|
||||
return room;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user