Add timeline pagination

This commit is contained in:
tezlm 2023-12-07 08:50:14 -08:00
parent 20b78d0bea
commit 14acca5b62
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
17 changed files with 175 additions and 37 deletions

8
dist/src/net.d.ts vendored
View file

@ -19,13 +19,13 @@ export declare class Network {
roomId: t.RoomId; roomId: t.RoomId;
dir: "b" | "f"; dir: "b" | "f";
limit?: number; limit?: number;
from: string; from?: string;
to?: string; to?: string;
}): Promise<t.MessagesResponse>; }): Promise<t.MessagesResponse>;
fetchRelations(roomId: t.RoomId, eventId: t.EventId, opts: { fetchRelations(roomId: t.RoomId, eventId: t.EventId, opts: {
relType?: string; relType?: string;
eventType?: string; eventType?: string;
limit?: string; limit?: number;
dir: "b" | "f"; dir: "b" | "f";
from?: string; from?: string;
to?: string; to?: string;
@ -34,8 +34,8 @@ export declare class Network {
from?: string; from?: string;
limit?: number; limit?: number;
roomIds?: Array<t.RoomId>; roomIds?: Array<t.RoomId>;
watching: boolean; watching?: boolean;
include: Array<t.IncludeThreads>; include?: Array<t.IncludeThreads>;
}): Promise<t.ThreadsResponse>; }): Promise<t.ThreadsResponse>;
fetchInbox(opts: { fetchInbox(opts: {
roomIds?: Array<t.RoomId>; roomIds?: Array<t.RoomId>;

8
dist/src/net.js vendored
View file

@ -91,7 +91,9 @@ export class Network {
}); });
} }
async fetchMessages(opts) { async fetchMessages(opts) {
let path = `/_matrix/client/v3/rooms/${e(opts.roomId)}/messages?limit=${opts.limit || 50}&from=${e(opts.from)}&dir=${e(opts.dir)}`; let path = `/_matrix/client/v3/rooms/${e(opts.roomId)}/messages?limit=${opts.limit || 50}&dir=${e(opts.dir)}`;
if (opts.from)
path += `&from=${e(opts.from)}`;
if (opts.to) if (opts.to)
path += `&to=${e(opts.to)}`; path += `&to=${e(opts.to)}`;
return this.fetch({ method: "GET", path }); return this.fetch({ method: "GET", path });
@ -114,7 +116,7 @@ export class Network {
async fetchThreads(opts) { async fetchThreads(opts) {
return this.fetch({ return this.fetch({
method: "POST", method: "POST",
path: `/_matrix/client/v1/threads?limit=${opts.limit}${opts.from ? `&from=${e(opts.from)}` : ""}`, path: `/_matrix/client/v1/threads?limit=${opts.limit || 50}${opts.from ? `&from=${e(opts.from)}` : ""}`,
body: { body: {
watching: opts.watching, watching: opts.watching,
room_ids: opts.roomIds, room_ids: opts.roomIds,
@ -125,7 +127,7 @@ export class Network {
async fetchInbox(opts) { async fetchInbox(opts) {
return this.fetch({ return this.fetch({
method: "POST", method: "POST",
path: `/_matrix/client/v1/inbox?limit=${opts.limit}${opts.from ? `&from=${e(opts.from)}` : ""}`, path: `/_matrix/client/v1/inbox?limit=${opts.limit || 50}${opts.from ? `&from=${e(opts.from)}` : ""}`,
body: { body: {
filter: opts.filter, filter: opts.filter,
room_ids: opts.roomIds, room_ids: opts.roomIds,

2
dist/src/net.js.map vendored

File diff suppressed because one or more lines are too long

5
dist/src/room.d.ts vendored
View file

@ -1,8 +1,9 @@
import TypedEmitter from "typed-emitter"; import TypedEmitter from "typed-emitter";
import { ApiEphemeralEvent, SyncResponseRoom, Unreads } from "./api.js"; import { ApiEphemeralEvent, EventId, SyncResponseRoom, Unreads } from "./api.js";
import { Client } from "./client.js"; import { Client } from "./client.js";
import { Event, StateEvent } from "./event.js"; import { Event, StateEvent } from "./event.js";
import { TimelineSet } from "./timeline.js"; import { TimelineSet } from "./timeline.js";
import { Thread } from "./thread.js";
type RoomEvents = { type RoomEvents = {
timeline: (event: Event) => void; timeline: (event: Event) => void;
ephemeral: (event: ApiEphemeralEvent) => void; ephemeral: (event: ApiEphemeralEvent) => void;
@ -20,6 +21,8 @@ export declare class Room extends Room_base {
id: string; id: string;
private state; private state;
timelines: TimelineSet; timelines: TimelineSet;
events: Map<EventId, Event>;
threads: Map<EventId, Thread>;
constructor(client: Client, id: string, data: SyncResponseRoom); constructor(client: Client, id: string, data: SyncResponseRoom);
_merge(data: SyncResponseRoom): void; _merge(data: SyncResponseRoom): void;
getState(type: string, stateKey?: string): StateEvent | null; getState(type: string, stateKey?: string): StateEvent | null;

16
dist/src/room.js vendored
View file

@ -44,6 +44,18 @@ export class Room extends EventEmitter {
writable: true, writable: true,
value: void 0 value: void 0
}); });
Object.defineProperty(this, "events", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
Object.defineProperty(this, "threads", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
this.timelines = new TimelineSet(this); this.timelines = new TimelineSet(this);
this.timelines.live.prevBatch = data.prev_batch || null; this.timelines.live.prevBatch = data.prev_batch || null;
this._merge(data); this._merge(data);
@ -71,12 +83,12 @@ export class Room extends EventEmitter {
getAllState(type) { getAllState(type) {
return [...this.state.get(type)?.values() ?? []]; return [...this.state.get(type)?.values() ?? []];
} }
// TODO: return event // TODO: local echo, return event
async sendState(type, stateKey, content) { async sendState(type, stateKey, content) {
// const { event_id } = await this.client.net.sendState(this.id, type, stateKey, content); // const { event_id } = await this.client.net.sendState(this.id, type, stateKey, content);
await this.client.net.sendState(this.id, type, stateKey, content); await this.client.net.sendState(this.id, type, stateKey, content);
} }
// TODO: return event // TODO: local echo(?), return event
async sendEvent(type, content) { async sendEvent(type, content) {
await this.client.net.sendEvent(this.id, type, nanoid(), content); await this.client.net.sendEvent(this.id, type, nanoid(), content);
} }

View file

@ -1 +1 @@
{"version":3,"file":"room.js","sourceRoot":"","sources":["../../src/room.ts"],"names":[],"mappings":"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"} {"version":3,"file":"room.js","sourceRoot":"","sources":["../../src/room.ts"],"names":[],"mappings":"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"}

View file

@ -10,6 +10,7 @@ declare const Thread_base: new () => TypedEmitter<ThreadEvents>;
export declare class Thread extends Thread_base { export declare class Thread extends Thread_base {
baseEvent: Event; baseEvent: Event;
room: Room; room: Room;
timeline: null;
constructor(baseEvent: Event); constructor(baseEvent: Event);
} }
export {}; export {};

6
dist/src/thread.js vendored
View file

@ -14,6 +14,12 @@ export class Thread extends EventEmitter {
writable: true, writable: true,
value: this.baseEvent.room value: this.baseEvent.room
}); });
Object.defineProperty(this, "timeline", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
} }
} }
//# sourceMappingURL=thread.js.map //# sourceMappingURL=thread.js.map

View file

@ -1 +1 @@
{"version":3,"file":"thread.js","sourceRoot":"","sources":["../../src/thread.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAclC,MAAM,OAAO,MAAO,SAAS,YAAgE;IAG3F,YAAmB,SAAgB;QACjC,KAAK,EAAE,CAAC;QADE;;;;mBAAO,SAAS;WAAO;QAF5B;;;;mBAAa,IAAI,CAAC,SAAS,CAAC,IAAI;WAAC;IAIxC,CAAC;CACF"} {"version":3,"file":"thread.js","sourceRoot":"","sources":["../../src/thread.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAclC,MAAM,OAAO,MAAO,SAAS,YAAgE;IAI3F,YAAmB,SAAgB;QACjC,KAAK,EAAE,CAAC;QADE;;;;mBAAO,SAAS;WAAO;QAH5B;;;;mBAAa,IAAI,CAAC,SAAS,CAAC,IAAI;WAAC;QACjC;;;;mBAAiB,IAAI;WAAC;IAI7B,CAAC;CACF"}

View file

@ -16,7 +16,7 @@ export declare class RoomTimeline implements Timeline {
} }
export declare class ThreadTimeline implements Timeline { export declare class ThreadTimeline implements Timeline {
thread: Thread; thread: Thread;
events: never[]; events: Array<Event>;
isLive: boolean; isLive: boolean;
prevBatch: string | null; prevBatch: string | null;
nextBatch: string | null; nextBatch: string | null;
@ -28,5 +28,6 @@ export declare class TimelineSet {
private timelines; private timelines;
constructor(room: Room); constructor(room: Room);
forEvent(eventId: EventId): Promise<RoomTimeline>; forEvent(eventId: EventId): Promise<RoomTimeline>;
paginate(timeline: Timeline, dir: "f" | "b", limit?: number): Promise<boolean>;
_appendEvents(events: Array<Event>): void; _appendEvents(events: Array<Event>): void;
} }

66
dist/src/timeline.js vendored
View file

@ -116,13 +116,71 @@ export class TimelineSet {
return tl; return tl;
} }
// Paginate a timeline for more events // Paginate a timeline for more events
// public paginate(timeline: Timeline, dir: "f" | "b", limit: number = 50) { // TODO: fuse two neighboring timelines together
// throw "todo"; async paginate(timeline, dir, limit = 50) {
// } const { net } = this.room.client;
if (timeline instanceof ThreadTimeline) {
const from = (dir === "f" ? timeline.nextBatch : timeline.prevBatch) || undefined;
if (!from)
return false;
const data = await net.fetchRelations(this.room.id, timeline.thread.baseEvent.id, {
dir,
from,
limit,
});
if (dir === "f") {
const events = data.chunk.map(raw => new Event(this.room, raw));
timeline.nextBatch = data.next_batch;
timeline.events.push(...events);
for (const event of events)
this.room.events.set(event.id, event);
}
else {
const events = data.chunk.map(raw => new Event(this.room, raw));
timeline.prevBatch = data.prev_batch;
timeline.events.push(...events);
for (const event of events)
this.room.events.set(event.id, event);
}
return true;
}
else if (timeline instanceof RoomTimeline) {
const from = (dir === "f" ? timeline.nextBatch : timeline.prevBatch) || undefined;
if (!from)
return false;
const data = await net.fetchMessages({
roomId: this.room.id,
dir,
limit,
from,
});
if (dir === "f") {
const events = data.chunk.map(raw => new Event(this.room, raw));
timeline.nextBatch = data.end;
timeline.events.push(...events);
for (const event of events)
this.room.events.set(event.id, event);
}
else {
const events = data.chunk.reverse().map(raw => new Event(this.room, raw));
timeline.prevBatch = data.end;
timeline.events.push(...events);
for (const event of events)
this.room.events.set(event.id, event);
}
return true;
}
else {
throw new Error("todo");
}
}
_appendEvents(events) { _appendEvents(events) {
// FIXME: merge timelines together // FIXME: merge timelines together
for (const event of events)
this.room.events.set(event.id, event);
for (const timeline of this.timelines) { for (const timeline of this.timelines) {
if (timeline.isLive) // if (timeline.isLive) timeline.events.push(...events);
if (timeline.isLive && timeline instanceof RoomTimeline)
timeline.events.push(...events); timeline.events.push(...events);
} }
} }

View file

@ -1 +1 @@
{"version":3,"file":"timeline.js","sourceRoot":"","sources":["../../src/timeline.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAK7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAQnC,MAAM,OAAO,YAAY;IAOvB,YAAmB,IAAU;QAAjB;;;;mBAAO,IAAI;WAAM;QANtB;;;;mBAAuB,EAAE;WAAC;QAC1B;;;;mBAAkB,KAAK;WAAC;QAE/B;;;;mBAA2B,IAAI;WAAC;QAChC;;;;mBAA2B,IAAI;WAAC;IAIhC,CAAC;CAQF;AAED,MAAM,OAAO,cAAc;IAOzB,YAAmB,MAAc;QAArB;;;;mBAAO,MAAM;WAAQ;QAN1B;;;;mBAAS,EAAE;WAAC;QACZ;;;;mBAAkB,KAAK;WAAC;QAE/B;;;;mBAA2B,IAAI;WAAC;QAChC;;;;mBAA2B,IAAI;WAAC;IAEI,CAAC;CACtC;AAED,MAAM,OAAO,WAAW;IAItB,iEAAiE;IAEjE,YAAmB,IAAU;QAAjB;;;;mBAAO,IAAI;WAAM;QAL7B,uEAAuE;QAChE;;;;;WAAmB;QAClB;;;;mBAA2B,IAAI,GAAG,EAAE;WAAC;QAI3C,IAAI,CAAC,IAAI,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,0EAA0E;IAC1E,sFAAsF;IACtF,kBAAkB;IAClB,oCAAoC;IACpC,8CAA8C;IAC9C,kBAAkB;IAClB,IAAI;IAEJ,yCAAyC;IAClC,KAAK,CAAC,QAAQ,CAAC,OAAgB;QACpC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC/E,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvC,EAAE,CAAC,MAAM,GAAG,OAAO,CAAC,aAAa;aAC9B,OAAO,EAAE;aACT,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;aACvB,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC;aAC5B,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;QACzC,EAAE,CAAC,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC;QAC7B,EAAE,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC;QAC3B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,sCAAsC;IACtC,4EAA4E;IAC5E,kBAAkB;IAClB,IAAI;IAEJ,aAAa,CAAC,MAAoB;QAChC,kCAAkC;QAClC,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACtC,IAAI,QAAQ,CAAC,MAAM;gBAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;CACF"} {"version":3,"file":"timeline.js","sourceRoot":"","sources":["../../src/timeline.ts"],"names":[],"mappings":"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"}

File diff suppressed because one or more lines are too long

View file

@ -102,8 +102,9 @@ export class Network {
}); });
} }
public async fetchMessages(opts: { roomId: t.RoomId, dir: "b" | "f", limit?: number, from: string, to?: string }): Promise<t.MessagesResponse> { public async fetchMessages(opts: { roomId: t.RoomId, dir: "b" | "f", limit?: number, from?: string, to?: string }): Promise<t.MessagesResponse> {
let path = `/_matrix/client/v3/rooms/${e(opts.roomId)}/messages?limit=${opts.limit || 50}&from=${e(opts.from)}&dir=${e(opts.dir)}`; let path = `/_matrix/client/v3/rooms/${e(opts.roomId)}/messages?limit=${opts.limit || 50}&dir=${e(opts.dir)}`;
if (opts.from) path += `&from=${e(opts.from)}`;
if (opts.to) path += `&to=${e(opts.to)}`; if (opts.to) path += `&to=${e(opts.to)}`;
return this.fetch({ method: "GET", path }); return this.fetch({ method: "GET", path });
} }
@ -114,7 +115,7 @@ export class Network {
opts: { opts: {
relType?: string, relType?: string,
eventType?: string, eventType?: string,
limit?: string, limit?: number,
dir: "b" | "f", dir: "b" | "f",
from?: string, from?: string,
to?: string, to?: string,
@ -129,10 +130,10 @@ export class Network {
return this.fetch({ method: "GET", path }); return this.fetch({ method: "GET", path });
} }
public async fetchThreads(opts: { from?: string, limit?: number, roomIds?: Array<t.RoomId>, watching: boolean, include: Array<t.IncludeThreads> }): Promise<t.ThreadsResponse> { public async fetchThreads(opts: { from?: string, limit?: number, roomIds?: Array<t.RoomId>, watching?: boolean, include?: Array<t.IncludeThreads> }): Promise<t.ThreadsResponse> {
return this.fetch({ return this.fetch({
method: "POST", method: "POST",
path: `/_matrix/client/v1/threads?limit=${opts.limit}${opts.from ? `&from=${e(opts.from)}`: ""}`, path: `/_matrix/client/v1/threads?limit=${opts.limit || 50}${opts.from ? `&from=${e(opts.from)}`: ""}`,
body: { body: {
watching: opts.watching, watching: opts.watching,
room_ids: opts.roomIds, room_ids: opts.roomIds,
@ -144,7 +145,7 @@ export class Network {
public async fetchInbox(opts: { roomIds?: Array<t.RoomId>, from?: string, filter?: t.InboxFilter, limit?: number }): Promise<t.InboxResponse> { public async fetchInbox(opts: { roomIds?: Array<t.RoomId>, from?: string, filter?: t.InboxFilter, limit?: number }): Promise<t.InboxResponse> {
return this.fetch({ return this.fetch({
method: "POST", method: "POST",
path: `/_matrix/client/v1/inbox?limit=${opts.limit}${opts.from ? `&from=${e(opts.from)}`: ""}`, path: `/_matrix/client/v1/inbox?limit=${opts.limit || 50}${opts.from ? `&from=${e(opts.from)}`: ""}`,
body: { body: {
filter: opts.filter, filter: opts.filter,
room_ids: opts.roomIds, room_ids: opts.roomIds,

View file

@ -1,10 +1,11 @@
import EventEmitter from "events"; import EventEmitter from "events";
import TypedEmitter from "typed-emitter"; import TypedEmitter from "typed-emitter";
import { ApiEphemeralEvent, SyncResponseRoom, Unreads } from "./api.js"; import { ApiEphemeralEvent, EventId, SyncResponseRoom, Unreads } from "./api.js";
import { Client } from "./client.js"; import { Client } from "./client.js";
import { Event, StateEvent } from "./event.js"; import { Event, StateEvent } from "./event.js";
import { TimelineSet } from "./timeline.js"; import { TimelineSet } from "./timeline.js";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { Thread } from "./thread.js";
type RoomEvents = { type RoomEvents = {
// an event is appended to this room's live timeline // an event is appended to this room's live timeline
@ -31,6 +32,8 @@ export class Room extends (EventEmitter as unknown as new () => TypedEmitter<Roo
private state: Map<string, Map<string, StateEvent>> = new Map(); private state: Map<string, Map<string, StateEvent>> = new Map();
public timelines: TimelineSet; public timelines: TimelineSet;
public events: Map<EventId, Event> = new Map();
public threads: Map<EventId, Thread> = new Map();
// public members: Members; // public members: Members;
/* /*
@ -83,13 +86,13 @@ room.unban(userid)
return [...this.state.get(type)?.values() ?? []]; return [...this.state.get(type)?.values() ?? []];
} }
// TODO: return event // TODO: local echo, return event
async sendState(type: string, stateKey: string, content: any) { async sendState(type: string, stateKey: string, content: any) {
// const { event_id } = await this.client.net.sendState(this.id, type, stateKey, content); // const { event_id } = await this.client.net.sendState(this.id, type, stateKey, content);
await this.client.net.sendState(this.id, type, stateKey, content); await this.client.net.sendState(this.id, type, stateKey, content);
} }
// TODO: return event // TODO: local echo(?), return event
async sendEvent(type: string, content: any) { async sendEvent(type: string, content: any) {
await this.client.net.sendEvent(this.id, type, nanoid(), content); await this.client.net.sendEvent(this.id, type, nanoid(), content);
} }

View file

@ -14,6 +14,7 @@ type ThreadEvents = {
export class Thread extends (EventEmitter as unknown as new () => TypedEmitter<ThreadEvents>) { export class Thread extends (EventEmitter as unknown as new () => TypedEmitter<ThreadEvents>) {
public room: Room = this.baseEvent.room; public room: Room = this.baseEvent.room;
public timeline: null = null;
constructor(public baseEvent: Event) { constructor(public baseEvent: Event) {
super(); super();

View file

@ -7,7 +7,12 @@ import { Event } from "./event.js";
import { Thread } from "./thread.js"; import { Thread } from "./thread.js";
export interface Timeline { export interface Timeline {
// if this timeline is actively receiving new events
isLive: boolean, isLive: boolean,
// isPaginating: boolean,
// the events in this timeline
events: Array<Event>, events: Array<Event>,
} }
@ -18,9 +23,7 @@ export class RoomTimeline implements Timeline {
prevBatch: string | null = null; prevBatch: string | null = null;
nextBatch: string | null = null; nextBatch: string | null = null;
constructor(public room: Room) { constructor(public room: Room) {}
}
// concat(other: RoomTimeline): RoomTimeline { // concat(other: RoomTimeline): RoomTimeline {
// if (this.events.length === 0) return other; // if (this.events.length === 0) return other;
@ -31,7 +34,7 @@ export class RoomTimeline implements Timeline {
} }
export class ThreadTimeline implements Timeline { export class ThreadTimeline implements Timeline {
public events = []; public events: Array<Event> = [];
public isLive: boolean = false; public isLive: boolean = false;
prevBatch: string | null = null; prevBatch: string | null = null;
@ -76,14 +79,61 @@ export class TimelineSet {
} }
// Paginate a timeline for more events // Paginate a timeline for more events
// public paginate(timeline: Timeline, dir: "f" | "b", limit: number = 50) { // TODO: fuse two neighboring timelines together
// throw "todo"; public async paginate(timeline: Timeline, dir: "f" | "b", limit: number = 50): Promise<boolean> {
// } const { net } = this.room.client;
if (timeline instanceof ThreadTimeline) {
const from = (dir === "f" ? timeline.nextBatch : timeline.prevBatch) || undefined;
if (!from) return false;
const data = await net.fetchRelations(this.room.id, timeline.thread.baseEvent.id, {
dir,
from,
limit,
});
if (dir === "f") {
const events = data.chunk.map(raw => new Event(this.room, raw));
timeline.nextBatch = data.next_batch;
timeline.events.push(...events);
for (const event of events) this.room.events.set(event.id, event);
} else {
const events = data.chunk.map(raw => new Event(this.room, raw));
timeline.prevBatch = data.prev_batch;
timeline.events.push(...events);
for (const event of events) this.room.events.set(event.id, event);
}
return true;
} else if (timeline instanceof RoomTimeline) {
const from = (dir === "f" ? timeline.nextBatch : timeline.prevBatch) || undefined;
if (!from) return false;
const data = await net.fetchMessages({
roomId: this.room.id,
dir,
limit,
from,
});
if (dir === "f") {
const events = data.chunk.map(raw => new Event(this.room, raw));
timeline.nextBatch = data.end;
timeline.events.push(...events);
for (const event of events) this.room.events.set(event.id, event);
} else {
const events = data.chunk.reverse().map(raw => new Event(this.room, raw));
timeline.prevBatch = data.end;
timeline.events.push(...events);
for (const event of events) this.room.events.set(event.id, event);
}
return true;
} else {
throw new Error("todo");
}
}
_appendEvents(events: Array<Event>) { _appendEvents(events: Array<Event>) {
// FIXME: merge timelines together // FIXME: merge timelines together
for (const event of events) this.room.events.set(event.id, event);
for (const timeline of this.timelines) { for (const timeline of this.timelines) {
if (timeline.isLive) timeline.events.push(...events); // if (timeline.isLive) timeline.events.push(...events);
if (timeline.isLive && timeline instanceof RoomTimeline) timeline.events.push(...events);
} }
} }
} }