diff --git a/src/client.ts b/src/client.ts
index 926f21c97..aff7be4d1 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -4839,12 +4839,11 @@ export class MatrixClient extends EventEmitter {
* null.
* @return {module:http-api.MatrixError} Rejects: with an error response.
*/
- public scrollback(room: Room, limit: number, callback?: Callback): Promise {
+ public scrollback(room: Room, limit = 30, callback?: Callback): Promise {
if (utils.isFunction(limit)) {
callback = limit as any as Callback; // legacy
limit = undefined;
}
- limit = limit || 30;
let timeToWaitMs = 0;
let info = this.ongoingScrollbacks[room.roomId] || {};
@@ -5025,7 +5024,7 @@ export class MatrixClient extends EventEmitter {
// XXX: Intended private, used in code.
public createMessagesRequest(
roomId: string,
- fromToken: string,
+ fromToken: string | null,
limit = 30,
dir: Direction,
timelineFilter?: Filter,
@@ -5033,11 +5032,14 @@ export class MatrixClient extends EventEmitter {
const path = utils.encodeUri("/rooms/$roomId/messages", { $roomId: roomId });
const params: Record = {
- from: fromToken,
limit: limit.toString(),
- dir,
+ dir: dir,
};
+ if (fromToken) {
+ params.from = fromToken;
+ }
+
let filter = null;
if (this.clientOpts.lazyLoadMembers) {
// create a shallow copy of LAZY_LOADING_MESSAGES_FILTER,
@@ -5086,11 +5088,6 @@ export class MatrixClient extends EventEmitter {
const dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
const token = eventTimeline.getPaginationToken(dir);
- if (!token) {
- // no token - no results.
- return Promise.resolve(false);
- }
-
const pendingRequest = eventTimeline.paginationRequests[dir];
if (pendingRequest) {
diff --git a/src/filter-component.ts b/src/filter-component.ts
index e5cdc2ec3..16665acb2 100644
--- a/src/filter-component.ts
+++ b/src/filter-component.ts
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
+import { UNSTABLE_FILTER_RELATION_SENDERS, UNSTABLE_FILTER_RELATION_TYPES } from "./filter";
import { MatrixEvent } from "./models/event";
/**
@@ -89,6 +90,8 @@ export class FilterComponent {
senders: this.filterJson.senders || null,
not_senders: this.filterJson.not_senders || [],
contains_url: this.filterJson.contains_url || null,
+ [UNSTABLE_FILTER_RELATION_SENDERS.name]: UNSTABLE_FILTER_RELATION_SENDERS.findIn(this.filterJson),
+ [UNSTABLE_FILTER_RELATION_TYPES.name]: UNSTABLE_FILTER_RELATION_TYPES.findIn(this.filterJson),
};
}
diff --git a/src/filter.ts b/src/filter.ts
index e42eea955..eb619e78d 100644
--- a/src/filter.ts
+++ b/src/filter.ts
@@ -65,7 +65,7 @@ export interface IFilterDefinition {
export interface IRoomEventFilter extends IFilterComponent {
lazy_load_members?: boolean;
include_redundant_members?: boolean;
- types?: EventType[] | string[];
+ types?: Array;
[UNSTABLE_FILTER_RELATION_TYPES.name]?: Array;
[UNSTABLE_FILTER_RELATION_SENDERS.name]?: string[];
}
diff --git a/src/models/event-timeline-set.ts b/src/models/event-timeline-set.ts
index b5c6e4800..11dea131f 100644
--- a/src/models/event-timeline-set.ts
+++ b/src/models/event-timeline-set.ts
@@ -209,7 +209,7 @@ export class EventTimelineSet extends EventEmitter {
*
* @fires module:client~MatrixClient#event:"Room.timelineReset"
*/
- public resetLiveTimeline(backPaginationToken: string, forwardPaginationToken?: string): void {
+ public resetLiveTimeline(backPaginationToken?: string, forwardPaginationToken?: string): void {
// Each EventTimeline has RoomState objects tracking the state at the start
// and end of that timeline. The copies at the end of the live timeline are
// special because they will have listeners attached to monitor changes to
diff --git a/src/models/event-timeline.ts b/src/models/event-timeline.ts
index f755fb647..fb0602735 100644
--- a/src/models/event-timeline.ts
+++ b/src/models/event-timeline.ts
@@ -34,13 +34,13 @@ export class EventTimeline {
* Symbolic constant for methods which take a 'direction' argument:
* refers to the start of the timeline, or backwards in time.
*/
- static BACKWARDS = Direction.Backward;
+ public static readonly BACKWARDS = Direction.Backward;
/**
* Symbolic constant for methods which take a 'direction' argument:
* refers to the end of the timeline, or forwards in time.
*/
- static FORWARDS = Direction.Forward;
+ public static readonly FORWARDS = Direction.Forward;
/**
* Static helper method to set sender and target properties
diff --git a/src/models/room.ts b/src/models/room.ts
index 751bc97f6..0f4c9c5ce 100644
--- a/src/models/room.ts
+++ b/src/models/room.ts
@@ -20,8 +20,8 @@ limitations under the License.
import { EventEmitter } from "events";
-import { DuplicateStrategy, EventTimelineSet } from "./event-timeline-set";
-import { EventTimeline } from "./event-timeline";
+import { EventTimelineSet, DuplicateStrategy } from "./event-timeline-set";
+import { Direction, EventTimeline } from "./event-timeline";
import { getHttpUriForMxc } from "../content-repo";
import * as utils from "../utils";
import { normalize } from "../utils";
@@ -110,6 +110,13 @@ export enum NotificationCountType {
Total = "total",
}
+export interface ICreateFilterOpts {
+ // Populate the filtered timeline with already loaded events in the room
+ // timeline. Useful to disable for some filters that can't be achieved by the
+ // client in an efficient manner
+ prepopulateTimeline?: boolean;
+}
+
export class Room extends EventEmitter {
private readonly reEmitter: ReEmitter;
private txnToEvent: Record = {}; // Pending in-flight requests { string: MatrixEvent }
@@ -793,7 +800,7 @@ export class Room extends EventEmitter {
* timeline which would otherwise be unable to paginate forwards without this token).
* Removing just the old live timeline whilst preserving previous ones is not supported.
*/
- public resetLiveTimeline(backPaginationToken: string, forwardPaginationToken: string): void {
+ public resetLiveTimeline(backPaginationToken: string | null, forwardPaginationToken: string | null): void {
for (let i = 0; i < this.timelineSets.length; i++) {
this.timelineSets[i].resetLiveTimeline(
backPaginationToken, forwardPaginationToken,
@@ -1222,9 +1229,14 @@ export class Room extends EventEmitter {
/**
* Add a timelineSet for this room with the given filter
* @param {Filter} filter The filter to be applied to this timelineSet
+ * @param {Object=} opts Configuration options
+ * @param {*} opts.storageToken Optional.
* @return {EventTimelineSet} The timelineSet
*/
- public getOrCreateFilteredTimelineSet(filter: Filter): EventTimelineSet {
+ public getOrCreateFilteredTimelineSet(
+ filter: Filter,
+ { prepopulateTimeline = true }: ICreateFilterOpts = {},
+ ): EventTimelineSet {
if (this.filteredTimelineSets[filter.filterId]) {
return this.filteredTimelineSets[filter.filterId];
}
@@ -1234,30 +1246,39 @@ export class Room extends EventEmitter {
this.filteredTimelineSets[filter.filterId] = timelineSet;
this.timelineSets.push(timelineSet);
- // populate up the new timelineSet with filtered events from our live
- // unfiltered timeline.
- //
- // XXX: This is risky as our timeline
- // may have grown huge and so take a long time to filter.
- // see https://github.com/vector-im/vector-web/issues/2109
-
const unfilteredLiveTimeline = this.getLiveTimeline();
+ // Not all filter are possible to replicate client-side only
+ // When that's the case we do not want to prepopulate from the live timeline
+ // as we would get incorrect results compared to what the server would send back
+ if (prepopulateTimeline) {
+ // populate up the new timelineSet with filtered events from our live
+ // unfiltered timeline.
+ //
+ // XXX: This is risky as our timeline
+ // may have grown huge and so take a long time to filter.
+ // see https://github.com/vector-im/vector-web/issues/2109
- unfilteredLiveTimeline.getEvents().forEach(function(event) {
- timelineSet.addLiveEvent(event);
- });
+ unfilteredLiveTimeline.getEvents().forEach(function(event) {
+ timelineSet.addLiveEvent(event);
+ });
- // find the earliest unfiltered timeline
- let timeline = unfilteredLiveTimeline;
- while (timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS)) {
- timeline = timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS);
+ // find the earliest unfiltered timeline
+ let timeline = unfilteredLiveTimeline;
+ while (timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS)) {
+ timeline = timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS);
+ }
+
+ timelineSet.getLiveTimeline().setPaginationToken(
+ timeline.getPaginationToken(EventTimeline.BACKWARDS),
+ EventTimeline.BACKWARDS,
+ );
+ } else {
+ const livePaginationToken = unfilteredLiveTimeline.getPaginationToken(Direction.Forward);
+ timelineSet
+ .getLiveTimeline()
+ .setPaginationToken(livePaginationToken, Direction.Backward);
}
- timelineSet.getLiveTimeline().setPaginationToken(
- timeline.getPaginationToken(EventTimeline.BACKWARDS),
- EventTimeline.BACKWARDS,
- );
-
// alternatively, we could try to do something like this to try and re-paginate
// in the filtered events from nothing, but Mark says it's an abuse of the API
// to do so: