1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-28 05:03:59 +03:00
This commit is contained in:
Matthew Hodgson
2016-09-08 00:18:17 +01:00
parent dac820f957
commit fc495a5f1e
7 changed files with 125 additions and 42 deletions

View File

@@ -18,6 +18,13 @@ limitations under the License.
* @module filter-component * @module filter-component
*/ */
/**
* Checks if a value matches a given field value, which may be a * terminated
* wildcard pattern.
* @param {String} actual_value The value to be compared
* @param {String} filter_value The filter pattern to be compared
* @return {bool} true if the actual_value matches the filter_value
*/
function _matches_wildcard(actual_value, filter_value) { function _matches_wildcard(actual_value, filter_value) {
if (filter_value.endsWith("*")) { if (filter_value.endsWith("*")) {
var type_prefix = filter_value.slice(0, -1); var type_prefix = filter_value.slice(0, -1);
@@ -29,10 +36,15 @@ function _matches_wildcard(actual_value, filter_value) {
} }
/** /**
* A FilterComponent is a section of a Filter definition which defines the * FilterComponent is a section of a Filter definition which defines the
* types, rooms, senders filters etc to be applied to a particular type of resource. * types, rooms, senders filters etc to be applied to a particular type of resource.
* This is all ported over from synapse's Filter object.
* *
* This is all ported from synapse's Filter object. * N.B. that synapse refers to these as 'Filters', and what js-sdk refers to as
* 'Filters' are referred to as 'FilterCollections'.
*
* @constructor
* @param {Object} the definition of this filter JSON, e.g. { 'contains_url': true }
*/ */
function FilterComponent(filter_json) { function FilterComponent(filter_json) {
this.filter_json = filter_json; this.filter_json = filter_json;
@@ -51,11 +63,11 @@ function FilterComponent(filter_json) {
/** /**
* Checks with the filter component matches the given event * Checks with the filter component matches the given event
* * @param {MatrixEvent} event event to be checked against the filter
* Takes a MatrixEvent object * @return {bool} true if the event matches the filter
*/ */
FilterComponent.prototype.check = function(event) { FilterComponent.prototype.check = function(event) {
return this.checkFields( return this._checkFields(
event.getRoomId(), event.getRoomId(),
event.getSender(), event.getSender(),
event.getType(), event.getType(),
@@ -64,9 +76,14 @@ FilterComponent.prototype.check = function(event) {
}; };
/** /**
* Checks whether the filter matches the given event fields. * Checks whether the filter component matches the given event fields.
* @param {String} room_id the room_id for the event being checked
* @param {String} sender the sender of the event being checked
* @param {String} event_type the type of the event being checked
* @param {String} contains_url whether the event contains a content.url field
* @return {bool} true if the event fields match the filter
*/ */
FilterComponent.prototype.checkFields = FilterComponent.prototype._checkFields =
function(room_id, sender, event_type, contains_url) function(room_id, sender, event_type, contains_url)
{ {
var literal_keys = { var literal_keys = {
@@ -102,10 +119,20 @@ FilterComponent.prototype.checkFields =
return true; return true;
}; };
/**
* Filters a list of events down to those which match this filter component
* @param {MatrixEvent[]} events Events to be checked againt the filter component
* @return {MatrixEvent[]} events which matched the filter component
*/
FilterComponent.prototype.filter = function(events) { FilterComponent.prototype.filter = function(events) {
return events.filter(this.check, this); return events.filter(this.check, this);
}; };
/**
* Returns the limit field for a given filter component, providing a default of
* 10 if none is otherwise specified. Cargo-culted from Synapse.
* @return {Number} the limit for this filter component.
*/
FilterComponent.prototype.limit = function() { FilterComponent.prototype.limit = function() {
return this.filter_json.limit !== undefined ? this.filter_json.limit : 10; return this.filter_json.limit !== undefined ? this.filter_json.limit : 10;
}; };

View File

@@ -127,11 +127,16 @@ Filter.prototype.setDefinition = function(definition) {
); );
// don't bother porting this from synapse yet: // don't bother porting this from synapse yet:
// this._room_state_filter = new FilterComponent(room_filter_json.state || {}); // this._room_state_filter =
// this._room_ephemeral_filter = new FilterComponent(room_filter_json.ephemeral || {}); // new FilterComponent(room_filter_json.state || {});
// this._room_account_data_filter = new FilterComponent(room_filter_json.account_data || {}); // this._room_ephemeral_filter =
// this._presence_filter = new FilterComponent(definition.presence || {}); // new FilterComponent(room_filter_json.ephemeral || {});
// this._account_data_filter = new FilterComponent(definition.account_data || {}); // this._room_account_data_filter =
// new FilterComponent(room_filter_json.account_data || {});
// this._presence_filter =
// new FilterComponent(definition.presence || {});
// this._account_data_filter =
// new FilterComponent(definition.account_data || {});
}; };
/** /**
@@ -145,6 +150,8 @@ Filter.prototype.getRoomTimelineFilterComponent = function() {
/** /**
* Filter the list of events based on whether they are allowed in a timeline * Filter the list of events based on whether they are allowed in a timeline
* based on this filter * based on this filter
* @param {MatrixEvent[]} events the list of events being filtered
* @return {MatrixEvent[]} the list of events which match the filter
*/ */
Filter.prototype.filterRoomTimeline = function(events) { Filter.prototype.filterRoomTimeline = function(events) {
return this._room_timeline_filter.filter(this._room_filter.filter(events)); return this._room_timeline_filter.filter(this._room_filter.filter(events));

View File

@@ -41,8 +41,7 @@ if (DEBUG) {
* be continuous. Each timeline lists a series of events, as well as tracking * be continuous. Each timeline lists a series of events, as well as tracking
* the room state at the start and the end of the timeline (if appropriate). * the room state at the start and the end of the timeline (if appropriate).
* It also tracks forward and backward pagination tokens, as well as containing * It also tracks forward and backward pagination tokens, as well as containing
* links to the * links to the next timeline in the sequence.
* next timeline in the sequence.
* *
* <p>There is one special timeline - the 'live' timeline, which represents the * <p>There is one special timeline - the 'live' timeline, which represents the
* timeline to which events are being added in real-time as they are received * timeline to which events are being added in real-time as they are received
@@ -52,6 +51,13 @@ if (DEBUG) {
* *
* <p>In order that we can find events from their ids later, we also maintain a * <p>In order that we can find events from their ids later, we also maintain a
* map from event_id to timeline and index. * map from event_id to timeline and index.
*
* @constructor
* @param {?String} roomId the roomId of this timelineSet's room, if any
* @param {?Room} room the optional room for this timelineSet
* @param {Object} opts hash of options inherited from Room.
* opts.timelineSupport gives whether timeline support is enabled
* opts.filter is the filter object, if any, for this timelineSet.
*/ */
function EventTimelineSet(roomId, room, opts) { function EventTimelineSet(roomId, room, opts) {
this.roomId = roomId; this.roomId = roomId;
@@ -69,15 +75,17 @@ function EventTimelineSet(roomId, room, opts) {
utils.inherits(EventTimelineSet, EventEmitter); utils.inherits(EventTimelineSet, EventEmitter);
/** /**
* Get the filter object this timeline list is filtered on * Get the filter object this timeline set is filtered on, if any
* @return {?Filter} the optional filter for this timelineSet
*/ */
EventTimelineSet.prototype.getFilter = function() { EventTimelineSet.prototype.getFilter = function() {
return this._filter; return this._filter;
}; };
/** /**
* Set the filter object this timeline list is filtered on * Set the filter object this timeline set is filtered on
* (passed to the server when paginating via /messages). * (passed to the server when paginating via /messages).
* @param {Filter} filter the filter for this timelineSet
*/ */
EventTimelineSet.prototype.setFilter = function(filter) { EventTimelineSet.prototype.setFilter = function(filter) {
this._filter = filter; this._filter = filter;
@@ -93,7 +101,9 @@ EventTimelineSet.prototype.setFilter = function(filter) {
* @throws If <code>opts.pendingEventOrdering</code> was not 'detached' * @throws If <code>opts.pendingEventOrdering</code> was not 'detached'
*/ */
EventTimelineSet.prototype.getPendingEvents = function() { EventTimelineSet.prototype.getPendingEvents = function() {
if (!this.room) return []; if (!this.room) {
return [];
}
if (this._filter) { if (this._filter) {
return this._filter.filterRoomTimeline(this.room.getPendingEvents()); return this._filter.filterRoomTimeline(this.room.getPendingEvents());
@@ -108,14 +118,25 @@ EventTimelineSet.prototype.getPendingEvents = function() {
* *
* @return {module:models/event-timeline~EventTimeline} live timeline * @return {module:models/event-timeline~EventTimeline} live timeline
*/ */
EventTimelineSet.prototype.getLiveTimeline = function(filterId) { EventTimelineSet.prototype.getLiveTimeline = function() {
return this._liveTimeline; return this._liveTimeline;
}; };
/**
* Return the timeline (if any) this event is in.
* @param {String} eventId the eventId being sought
* @return {module:models/event-timeline~EventTimeline} timeline
*/
EventTimelineSet.prototype.eventIdToTimeline = function(eventId) { EventTimelineSet.prototype.eventIdToTimeline = function(eventId) {
return this._eventIdToTimeline[eventId]; return this._eventIdToTimeline[eventId];
}; };
/**
* Track a new event as if it were in the same timeline as an old event,
* replacing it.
* @param {String} oldEventId event ID of the original event
* @param {String} newEventId event ID of the replacement event
*/
EventTimelineSet.prototype.replaceEventId = function(oldEventId, newEventId) { EventTimelineSet.prototype.replaceEventId = function(oldEventId, newEventId) {
var existingTimeline = this._eventIdToTimeline[oldEventId]; var existingTimeline = this._eventIdToTimeline[oldEventId];
if (existingTimeline) { if (existingTimeline) {
@@ -387,7 +408,10 @@ EventTimelineSet.prototype.addEventsToTimeline = function(events, toStartOfTimel
}; };
/** /**
* Add event to the live timeline * Add an event to the end of this live timeline.
*
* @param {MatrixEvent} event Event to be added
* @param {string?} duplicateStrategy 'ignore' or 'replace'
*/ */
EventTimelineSet.prototype.addLiveEvent = function(event, duplicateStrategy) { EventTimelineSet.prototype.addLiveEvent = function(event, duplicateStrategy) {
if (this._filter) { if (this._filter) {
@@ -441,10 +465,9 @@ EventTimelineSet.prototype.addLiveEvent = function(event, duplicateStrategy) {
* @param {boolean} toStartOfTimeline * @param {boolean} toStartOfTimeline
* *
* @fires module:client~MatrixClient#event:"Room.timeline" * @fires module:client~MatrixClient#event:"Room.timeline"
*
* @private
*/ */
EventTimelineSet.prototype.addEventToTimeline = function(event, timeline, toStartOfTimeline) { EventTimelineSet.prototype.addEventToTimeline = function(event, timeline,
toStartOfTimeline) {
var eventId = event.getId(); var eventId = event.getId();
timeline.addEvent(event, toStartOfTimeline); timeline.addEvent(event, toStartOfTimeline);
this._eventIdToTimeline[eventId] = timeline; this._eventIdToTimeline[eventId] = timeline;
@@ -454,10 +477,23 @@ EventTimelineSet.prototype.addEventToTimeline = function(event, timeline, toStar
liveEvent: !toStartOfTimeline && timeline == this._liveTimeline, liveEvent: !toStartOfTimeline && timeline == this._liveTimeline,
timelineSet: this, timelineSet: this,
}; };
this.emit("Room.timeline", event, this.room, Boolean(toStartOfTimeline), false, data); this.emit("Room.timeline", event, this.room,
Boolean(toStartOfTimeline), false, data);
}; };
EventTimelineSet.prototype.replaceOrAddEvent = function(localEvent, oldEventId, newEventId) { /**
* Replaces event with ID oldEventId with one with newEventId, if oldEventId is
* recognised. Otherwise, add to the live timeline.
*
* @param {MatrixEvent} localEvent the new event to be added to the timeline
* @param {String} oldEventId the ID of the original event
* @param {boolean} newEventId the ID of the replacement event
*
* @fires module:client~MatrixClient#event:"Room.timeline"
*/
EventTimelineSet.prototype.replaceOrAddEvent = function(localEvent, oldEventId,
newEventId) {
// XXX: why don't we infer newEventId from localEvent?
var existingTimeline = this._eventIdToTimeline[oldEventId]; var existingTimeline = this._eventIdToTimeline[oldEventId];
if (existingTimeline) { if (existingTimeline) {
delete this._eventIdToTimeline[oldEventId]; delete this._eventIdToTimeline[oldEventId];

View File

@@ -94,7 +94,7 @@ EventTimeline.prototype.getRoomId = function() {
/** /**
* Get the filter for this timeline's timelineSet (if any) * Get the filter for this timeline's timelineSet (if any)
* @return {Filter}} filter * @return {Filter} filter
*/ */
EventTimeline.prototype.getFilter = function() { EventTimeline.prototype.getFilter = function() {
return this._eventTimelineSet.getFilter(); return this._eventTimelineSet.getFilter();
@@ -262,6 +262,10 @@ EventTimeline.prototype.addEvent = function(event, atStart) {
/** /**
* Static helper method to set sender and target properties * Static helper method to set sender and target properties
*
* @param {MatrixEvent} event the event whose metadata is to be set
* @param {RoomState} stateContext the room state to be queried
* @param {bool} toStartOfTimeline if true the event's forwardLooking flag is set false
*/ */
EventTimeline.setEventMetadata = function(event, stateContext, toStartOfTimeline) { EventTimeline.setEventMetadata = function(event, stateContext, toStartOfTimeline) {
// set sender and target properties // set sender and target properties

View File

@@ -193,7 +193,7 @@ Room.prototype.getPendingEvents = function() {
* *
* @return {module:models/event-timeline~EventTimeline} live timeline * @return {module:models/event-timeline~EventTimeline} live timeline
*/ */
Room.prototype.getLiveTimeline = function(filterId) { Room.prototype.getLiveTimeline = function() {
return this._timelineSets[0].getLiveTimeline(); return this._timelineSets[0].getLiveTimeline();
}; };
@@ -227,19 +227,23 @@ Room.prototype._fixUpLegacyTimelineFields = function() {
// state at the start and end of that timeline. These are more // state at the start and end of that timeline. These are more
// for backwards-compatibility than anything else. // for backwards-compatibility than anything else.
this.timeline = this._timelineSets[0].getLiveTimeline().getEvents(); this.timeline = this._timelineSets[0].getLiveTimeline().getEvents();
this.oldState = this._timelineSets[0].getLiveTimeline().getState(EventTimeline.BACKWARDS); this.oldState = this._timelineSets[0].getLiveTimeline()
this.currentState = this._timelineSets[0].getLiveTimeline().getState(EventTimeline.FORWARDS); .getState(EventTimeline.BACKWARDS);
this.currentState = this._timelineSets[0].getLiveTimeline()
.getState(EventTimeline.FORWARDS);
}; };
/** /**
* Return the timeline sets for this room * Return the timeline sets for this room.
* @return {EventTimelineSet[]} array of timeline sets for this room
*/ */
Room.prototype.getTimelineSets = function() { Room.prototype.getTimelineSets = function() {
return this._timelineSets; return this._timelineSets;
}; };
/** /**
* Return the notification timeline set for this room * Return the shared notification timeline set
* @return {EventTimelineSet} notification timeline set
*/ */
Room.prototype.getNotifTimelineSet = function() { Room.prototype.getNotifTimelineSet = function() {
return this._notifTimelineSet; return this._notifTimelineSet;
@@ -252,7 +256,6 @@ Room.prototype.getNotifTimelineSet = function() {
* @return {?module:models/event-timeline~EventTimeline} timeline containing * @return {?module:models/event-timeline~EventTimeline} timeline containing
* the given event, or null if unknown * the given event, or null if unknown
*/ */
Room.prototype.getTimelineForEvent = function(eventId) { Room.prototype.getTimelineForEvent = function(eventId) {
return this._timelineSets[0].getTimelineForEvent(eventId); return this._timelineSets[0].getTimelineForEvent(eventId);
}; };
@@ -501,6 +504,8 @@ Room.prototype.getOrCreateFilteredTimelineSet = function(filter) {
/** /**
* Forget the timelineSet for this room with the given filter * Forget the timelineSet for this room with the given filter
*
* @param {Filter} filter the filter whose timelineSet is to be forgotten
*/ */
Room.prototype.removeFilteredTimelineSet = function(filter) { Room.prototype.removeFilteredTimelineSet = function(filter) {
var timelineSet = this._filteredTimelineSets[filter.filterId]; var timelineSet = this._filteredTimelineSets[filter.filterId];
@@ -634,14 +639,17 @@ Room.prototype.addPendingEvent = function(event, txnId) {
var timelineSet = this._timelineSets[i]; var timelineSet = this._timelineSets[i];
if (timelineSet.getFilter()) { if (timelineSet.getFilter()) {
if (this._filter.filterRoomTimeline([event]).length) { if (this._filter.filterRoomTimeline([event]).length) {
timelineSet.addEventToTimeline(event, timelineSet.getLiveTimeline(), false); timelineSet.addEventToTimeline(event,
timelineSet.getLiveTimeline(), false);
} }
} }
else { else {
timelineSet.addEventToTimeline(event, timelineSet.getLiveTimeline(), false); timelineSet.addEventToTimeline(event,
timelineSet.getLiveTimeline(), false);
} }
} }
// notifications are receive-only, so we don't need to worry about this._notifTimelineSet. // notifications are receive-only, so we don't need to worry
// about this._notifTimelineSet.
} }
this.emit("Room.localEchoUpdated", event, this, null, null); this.emit("Room.localEchoUpdated", event, this, null, null);
@@ -823,13 +831,14 @@ Room.prototype.addLiveEvents = function(events, duplicateStrategy) {
var liveTimeline = this._timelineSets[i].getLiveTimeline(); var liveTimeline = this._timelineSets[i].getLiveTimeline();
if (liveTimeline.getPaginationToken(EventTimeline.FORWARDS)) { if (liveTimeline.getPaginationToken(EventTimeline.FORWARDS)) {
throw new Error( throw new Error(
"live timeline "+i+" is no longer live - it has a pagination token (" + "live timeline " + i + " is no longer live - it has a pagination token " +
liveTimeline.getPaginationToken(EventTimeline.FORWARDS) + ")" "(" + liveTimeline.getPaginationToken(EventTimeline.FORWARDS) + ")"
); );
} }
if (liveTimeline.getNeighbouringTimeline(EventTimeline.FORWARDS)) { if (liveTimeline.getNeighbouringTimeline(EventTimeline.FORWARDS)) {
throw new Error( throw new Error(
"live timeline "+i+" is no longer live - it has a neighbouring timeline" "live timeline " + i + " is no longer live - " +
"it has a neighbouring timeline"
); );
} }
} }