Skip to content

Commit 5ba7290

Browse files
committed
Distinguish room state and timeline events when dealing with widgets
1 parent 0130443 commit 5ba7290

File tree

2 files changed

+80
-73
lines changed

2 files changed

+80
-73
lines changed

src/stores/widgets/StopGapWidget.ts

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@
66
* Please see LICENSE files in the repository root for full details.
77
*/
88

9-
import { Room, MatrixEvent, MatrixEventEvent, MatrixClient, ClientEvent } from "matrix-js-sdk/src/matrix";
9+
import {
10+
Room,
11+
MatrixEvent,
12+
MatrixEventEvent,
13+
MatrixClient,
14+
ClientEvent,
15+
RoomStateEvent,
16+
} from "matrix-js-sdk/src/matrix";
1017
import { KnownMembership } from "matrix-js-sdk/src/types";
1118
import {
1219
ClientWidgetApi,
@@ -26,7 +33,6 @@ import {
2633
WidgetApiFromWidgetAction,
2734
WidgetKind,
2835
} from "matrix-widget-api";
29-
import { Optional } from "matrix-events-sdk";
3036
import { EventEmitter } from "events";
3137
import { logger } from "matrix-js-sdk/src/logger";
3238

@@ -56,6 +62,7 @@ import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
5662
import Modal from "../../Modal";
5763
import ErrorDialog from "../../components/views/dialogs/ErrorDialog";
5864
import { SdkContextClass } from "../../contexts/SDKContext";
65+
import { UPDATE_EVENT } from "../AsyncStore";
5966

6067
// TODO: Destroy all of this code
6168

@@ -151,6 +158,7 @@ export class StopGapWidget extends EventEmitter {
151158
private mockWidget: ElementWidget;
152159
private scalarToken?: string;
153160
private roomId?: string;
161+
private viewedRoomId: string | null = null;
154162
private kind: WidgetKind;
155163
private readonly virtual: boolean;
156164
private readUpToMap: { [roomId: string]: string } = {}; // room ID to event ID
@@ -177,17 +185,6 @@ export class StopGapWidget extends EventEmitter {
177185
this.stickyPromise = appTileProps.stickyPromise;
178186
}
179187

180-
private get eventListenerRoomId(): Optional<string> {
181-
// When widgets are listening to events, we need to make sure they're only
182-
// receiving events for the right room. In particular, room widgets get locked
183-
// to the room they were added in while account widgets listen to the currently
184-
// active room.
185-
186-
if (this.roomId) return this.roomId;
187-
188-
return SdkContextClass.instance.roomViewStore.getRoomId();
189-
}
190-
191188
public get widgetApi(): ClientWidgetApi | null {
192189
return this.messaging;
193190
}
@@ -259,6 +256,15 @@ export class StopGapWidget extends EventEmitter {
259256
});
260257
}
261258
};
259+
260+
private onRoomViewStoreUpdate = (): void => {
261+
const roomId = SdkContextClass.instance.roomViewStore.getRoomId() ?? null;
262+
if (roomId !== this.viewedRoomId) {
263+
this.messaging!.setViewedRoomId(roomId);
264+
this.viewedRoomId = roomId;
265+
}
266+
};
267+
262268
/**
263269
* This starts the messaging for the widget if it is not in the state `started` yet.
264270
* @param iframe the iframe the widget should use
@@ -285,6 +291,17 @@ export class StopGapWidget extends EventEmitter {
285291
this.messaging.on("capabilitiesNotified", () => this.emit("capabilitiesNotified"));
286292
this.messaging.on(`action:${WidgetApiFromWidgetAction.OpenModalWidget}`, this.onOpenModal);
287293

294+
// When widgets are listening to events, we need to make sure they're only
295+
// receiving events for the right room
296+
if (this.roomId === undefined) {
297+
// Account widgets listen to the currently active room
298+
this.messaging.setViewedRoomId(SdkContextClass.instance.roomViewStore.getRoomId() ?? null);
299+
SdkContextClass.instance.roomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate);
300+
} else {
301+
// Room widgets get looked to the room they were added in
302+
this.messaging.setViewedRoomId(this.roomId);
303+
}
304+
288305
// Always attach a handler for ViewRoom, but permission check it internally
289306
this.messaging.on(`action:${ElementWidgetActions.ViewRoom}`, (ev: CustomEvent<IViewRoomApiRequest>) => {
290307
ev.preventDefault(); // stop the widget API from auto-rejecting this
@@ -329,6 +346,7 @@ export class StopGapWidget extends EventEmitter {
329346
// Attach listeners for feeding events - the underlying widget classes handle permissions for us
330347
this.client.on(ClientEvent.Event, this.onEvent);
331348
this.client.on(MatrixEventEvent.Decrypted, this.onEventDecrypted);
349+
this.client.on(RoomStateEvent.Events, this.onStateUpdate);
332350
this.client.on(ClientEvent.ToDeviceEvent, this.onToDeviceEvent);
333351

334352
this.messaging.on(
@@ -457,8 +475,11 @@ export class StopGapWidget extends EventEmitter {
457475
WidgetMessagingStore.instance.stopMessaging(this.mockWidget, this.roomId);
458476
this.messaging = null;
459477

478+
SdkContextClass.instance.roomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate);
479+
460480
this.client.off(ClientEvent.Event, this.onEvent);
461481
this.client.off(MatrixEventEvent.Decrypted, this.onEventDecrypted);
482+
this.client.off(RoomStateEvent.Events, this.onStateUpdate);
462483
this.client.off(ClientEvent.ToDeviceEvent, this.onToDeviceEvent);
463484
}
464485

@@ -471,6 +492,14 @@ export class StopGapWidget extends EventEmitter {
471492
this.feedEvent(ev);
472493
};
473494

495+
private onStateUpdate = (ev: MatrixEvent): void => {
496+
if (this.messaging === null) return;
497+
const raw = ev.getEffectiveEvent();
498+
this.messaging.feedStateUpdate(raw as IRoomEvent).catch((e) => {
499+
logger.error("Error sending state update to widget: ", e);
500+
});
501+
};
502+
474503
private onToDeviceEvent = async (ev: MatrixEvent): Promise<void> => {
475504
await this.client.decryptEventIfNeeded(ev);
476505
if (ev.isDecryptionFailure()) return;
@@ -570,7 +599,7 @@ export class StopGapWidget extends EventEmitter {
570599
this.eventsToFeed.add(ev);
571600
} else {
572601
const raw = ev.getEffectiveEvent();
573-
this.messaging.feedEvent(raw as IRoomEvent, this.eventListenerRoomId!).catch((e) => {
602+
this.messaging.feedEvent(raw as IRoomEvent).catch((e) => {
574603
logger.error("Error sending event to widget: ", e);
575604
});
576605
}

src/stores/widgets/StopGapWidgetDriver.ts

Lines changed: 37 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
MatrixCapabilities,
2020
OpenIDRequestState,
2121
SimpleObservable,
22-
Symbols,
2322
Widget,
2423
WidgetDriver,
2524
WidgetEventCapability,
@@ -36,7 +35,6 @@ import {
3635
IContent,
3736
MatrixError,
3837
MatrixEvent,
39-
Room,
4038
Direction,
4139
THREAD_RELATION_TYPE,
4240
SendDelayedEventResponse,
@@ -468,70 +466,44 @@ export class StopGapWidgetDriver extends WidgetDriver {
468466
}
469467
}
470468

471-
private pickRooms(roomIds?: (string | Symbols.AnyRoom)[]): Room[] {
472-
const client = MatrixClientPeg.get();
473-
if (!client) throw new Error("Not attached to a client");
474-
475-
const targetRooms = roomIds
476-
? roomIds.includes(Symbols.AnyRoom)
477-
? client.getVisibleRooms(SettingsStore.getValue("feature_dynamic_room_predecessors"))
478-
: roomIds.map((r) => client.getRoom(r))
479-
: [client.getRoom(SdkContextClass.instance.roomViewStore.getRoomId()!)];
480-
return targetRooms.filter((r) => !!r) as Room[];
481-
}
482-
483-
public async readRoomEvents(
469+
public async readRoomTimeline(
470+
roomId: string,
484471
eventType: string,
485472
msgtype: string | undefined,
486-
limitPerRoom: number,
487-
roomIds?: (string | Symbols.AnyRoom)[],
473+
stateKey: string | undefined,
474+
limit: number,
475+
since: string | undefined,
488476
): Promise<IRoomEvent[]> {
489-
limitPerRoom = limitPerRoom > 0 ? Math.min(limitPerRoom, Number.MAX_SAFE_INTEGER) : Number.MAX_SAFE_INTEGER; // relatively arbitrary
490-
491-
const rooms = this.pickRooms(roomIds);
492-
const allResults: IRoomEvent[] = [];
493-
for (const room of rooms) {
494-
const results: MatrixEvent[] = [];
495-
const events = room.getLiveTimeline().getEvents(); // timelines are most recent last
496-
for (let i = events.length - 1; i > 0; i--) {
497-
if (results.length >= limitPerRoom) break;
498-
499-
const ev = events[i];
500-
if (ev.getType() !== eventType || ev.isState()) continue;
501-
if (eventType === EventType.RoomMessage && msgtype && msgtype !== ev.getContent()["msgtype"]) continue;
502-
results.push(ev);
503-
}
504-
505-
results.forEach((e) => allResults.push(e.getEffectiveEvent() as IRoomEvent));
477+
limit = limit > 0 ? Math.min(limit, Number.MAX_SAFE_INTEGER) : Number.MAX_SAFE_INTEGER; // relatively arbitrary
478+
479+
const room = MatrixClientPeg.safeGet().getRoom(roomId);
480+
if (room === null) return [];
481+
const results: MatrixEvent[] = [];
482+
const events = room.getLiveTimeline().getEvents(); // timelines are most recent last
483+
for (let i = events.length - 1; i > 0; i--) {
484+
const ev = events[i];
485+
if (results.length >= limit) break;
486+
if (since !== undefined && ev.getId() === since) break;
487+
488+
if (ev.getType() !== eventType || ev.isState()) continue;
489+
if (eventType === EventType.RoomMessage && msgtype && msgtype !== ev.getContent()["msgtype"]) continue;
490+
if (ev.getStateKey() !== undefined && stateKey !== undefined && ev.getStateKey() !== stateKey) continue;
491+
results.push(ev);
506492
}
507-
return allResults;
493+
494+
return results.map((e) => e.getEffectiveEvent() as IRoomEvent);
508495
}
509496

510-
public async readStateEvents(
511-
eventType: string,
512-
stateKey: string | undefined,
513-
limitPerRoom: number,
514-
roomIds?: (string | Symbols.AnyRoom)[],
515-
): Promise<IRoomEvent[]> {
516-
limitPerRoom = limitPerRoom > 0 ? Math.min(limitPerRoom, Number.MAX_SAFE_INTEGER) : Number.MAX_SAFE_INTEGER; // relatively arbitrary
517-
518-
const rooms = this.pickRooms(roomIds);
519-
const allResults: IRoomEvent[] = [];
520-
for (const room of rooms) {
521-
const results: MatrixEvent[] = [];
522-
const state = room.currentState.events.get(eventType);
523-
if (state) {
524-
if (stateKey === "" || !!stateKey) {
525-
const forKey = state.get(stateKey);
526-
if (forKey) results.push(forKey);
527-
} else {
528-
results.push(...Array.from(state.values()));
529-
}
530-
}
497+
public async readRoomState(roomId: string, eventType: string, stateKey: string | undefined): Promise<IRoomEvent[]> {
498+
const room = MatrixClientPeg.safeGet().getRoom(roomId);
499+
if (room === null) return [];
500+
const state = room.getLiveTimeline().getState(Direction.Forward);
501+
if (state === undefined) return [];
531502

532-
results.slice(0, limitPerRoom).forEach((e) => allResults.push(e.getEffectiveEvent() as IRoomEvent));
533-
}
534-
return allResults;
503+
if (stateKey === undefined)
504+
return state.getStateEvents(eventType).map((e) => e.getEffectiveEvent() as IRoomEvent);
505+
const event = state.getStateEvents(eventType, stateKey);
506+
return event === null ? [] : [event.getEffectiveEvent() as IRoomEvent];
535507
}
536508

537509
public async askOpenID(observer: SimpleObservable<IOpenIDUpdate>): Promise<void> {
@@ -692,6 +664,12 @@ export class StopGapWidgetDriver extends WidgetDriver {
692664
return { file: blob };
693665
}
694666

667+
public getKnownRooms(): string[] {
668+
return MatrixClientPeg.safeGet()
669+
.getVisibleRooms(SettingsStore.getValue("feature_dynamic_room_predecessors"))
670+
.map((r) => r.roomId);
671+
}
672+
695673
/**
696674
* Expresses a {@link MatrixError} as a JSON payload
697675
* for use by Widget API error responses.

0 commit comments

Comments
 (0)