You've already forked matrix-js-sdk
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:
@@ -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},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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();
|
||||||
|
timeline.addEvent(event, toStartOfTimeline, spliceBeforeLocalEcho);
|
||||||
if (this._redactions.indexOf(eventId) >= 0) {
|
this._eventIdToTimeline[eventId] = timeline;
|
||||||
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 = {
|
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"
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user