1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-28 05:03:59 +03:00

Merge pull request #80 from matrix-org/rav/keep_redactions

Keep redacted events in the timeline
This commit is contained in:
Richard van der Hoff
2016-02-17 21:41:42 +00:00
3 changed files with 132 additions and 27 deletions

View File

@@ -21,7 +21,6 @@ limitations under the License.
* @module models/event * @module models/event
*/ */
/** /**
* Enum for event statuses. * Enum for event statuses.
* @readonly * @readonly
@@ -189,5 +188,83 @@ module.exports.MatrixEvent.prototype = {
getUnsigned: function() { getUnsigned: function() {
return this.event.unsigned || {}; 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},
}; };

View File

@@ -118,7 +118,6 @@ function Room(roomId, opts) {
this.summary = null; this.summary = null;
this.storageToken = opts.storageToken; this.storageToken = opts.storageToken;
this._opts = opts; this._opts = opts;
this._redactions = [];
this._txnToEvent = {}; // Pending in-flight requests { string: MatrixEvent } this._txnToEvent = {}; // Pending in-flight requests { string: MatrixEvent }
// receipts should clobber based on receipt_type and user_id pairs hence // 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 // 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; 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 * Get one of the notification counts for this room
* @param {String} type The type of notification count to get. default: 'total' * @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, Room.prototype._addEventToTimeline = function(event, timeline, toStartOfTimeline,
spliceBeforeLocalEcho) { spliceBeforeLocalEcho) {
var eventId = event.getId(); 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); timeline.addEvent(event, toStartOfTimeline, spliceBeforeLocalEcho);
this._eventIdToTimeline[eventId] = timeline; this._eventIdToTimeline[eventId] = timeline;
}
var data = { var data = {
timeline: timeline, 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; var spliceBeforeLocalEcho = !isLocalEcho && addLocalEchoToEnd;
if (!this._eventIdToTimeline[events[i].getId()]) { 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. * Fires whenever the name of a room is updated.
* @event module:client~MatrixClient#"Room.name" * @event module:client~MatrixClient#"Room.name"

View File

@@ -83,7 +83,8 @@ SyncApi.prototype.createRoom = function(roomId) {
pendingEventOrdering: this.opts.pendingEventOrdering, pendingEventOrdering: this.opts.pendingEventOrdering,
timelineSupport: client.timelineSupport, 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); this._registerStateListeners(room);
return room; return room;
}; };