Skip to content

Commit e4590ca

Browse files
RiotRobothughns
andauthored
Revert "Distinguish room state and timeline events in embedded clients (#4574)" (#4656) (#4657)
Co-authored-by: Hugh Nimmo-Smith <[email protected]>
1 parent 8065388 commit e4590ca

File tree

3 files changed

+67
-84
lines changed

3 files changed

+67
-84
lines changed

spec/unit/embedded.spec.ts

+16-40
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
WidgetApiToWidgetAction,
2929
MatrixCapabilities,
3030
ITurnServer,
31+
IRoomEvent,
3132
IOpenIDCredentials,
3233
ISendEventFromWidgetResponseData,
3334
WidgetApiResponseError,
@@ -634,20 +635,12 @@ describe("RoomWidgetClient", () => {
634635
});
635636

636637
it("receives", async () => {
637-
const init = makeClient({ receiveState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
638+
await makeClient({ receiveState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
638639
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
639640
expect(widgetApi.requestCapabilityToReceiveState).toHaveBeenCalledWith("org.example.foo", "bar");
640-
// Client needs to be told that the room state is loaded
641-
widgetApi.emit(
642-
`action:${WidgetApiToWidgetAction.UpdateState}`,
643-
new CustomEvent(`action:${WidgetApiToWidgetAction.UpdateState}`, { detail: { data: { state: [] } } }),
644-
);
645-
await init;
646641

647642
const emittedEvent = new Promise<MatrixEvent>((resolve) => client.once(ClientEvent.Event, resolve));
648643
const emittedSync = new Promise<SyncState>((resolve) => client.once(ClientEvent.Sync, resolve));
649-
// Let's assume that a state event comes in but it doesn't actually
650-
// update the state of the room just yet (maybe it's unauthorized)
651644
widgetApi.emit(
652645
`action:${WidgetApiToWidgetAction.SendEvent}`,
653646
new CustomEvent(`action:${WidgetApiToWidgetAction.SendEvent}`, { detail: { data: event } }),
@@ -656,43 +649,26 @@ describe("RoomWidgetClient", () => {
656649
// The client should've emitted about the received event
657650
expect((await emittedEvent).getEffectiveEvent()).toEqual(event);
658651
expect(await emittedSync).toEqual(SyncState.Syncing);
659-
// However it should not have changed the room state
652+
// It should've also inserted the event into the room object
660653
const room = client.getRoom("!1:example.org");
661-
expect(room!.currentState.getStateEvents("org.example.foo", "bar")).toBe(null);
662-
663-
// Now assume that the state event becomes favored by state
664-
// resolution for whatever reason and enters into the current state
665-
// of the room
666-
widgetApi.emit(
667-
`action:${WidgetApiToWidgetAction.UpdateState}`,
668-
new CustomEvent(`action:${WidgetApiToWidgetAction.UpdateState}`, {
669-
detail: { data: { state: [event] } },
670-
}),
671-
);
672-
// It should now have changed the room state
654+
expect(room).not.toBeNull();
673655
expect(room!.currentState.getStateEvents("org.example.foo", "bar")?.getEffectiveEvent()).toEqual(event);
674656
});
675657

676-
it("ignores state updates for other rooms", async () => {
677-
const init = makeClient({ receiveState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
678-
// Client needs to be told that the room state is loaded
679-
widgetApi.emit(
680-
`action:${WidgetApiToWidgetAction.UpdateState}`,
681-
new CustomEvent(`action:${WidgetApiToWidgetAction.UpdateState}`, { detail: { data: { state: [] } } }),
658+
it("backfills", async () => {
659+
widgetApi.readStateEvents.mockImplementation(async (eventType, limit, stateKey) =>
660+
eventType === "org.example.foo" && (limit ?? Infinity) > 0 && stateKey === "bar"
661+
? [event as IRoomEvent]
662+
: [],
682663
);
683-
await init;
684664

685-
// Now a room we're not interested in receives a state update
686-
widgetApi.emit(
687-
`action:${WidgetApiToWidgetAction.UpdateState}`,
688-
new CustomEvent(`action:${WidgetApiToWidgetAction.UpdateState}`, {
689-
detail: { data: { state: [{ ...event, room_id: "!other-room:example.org" }] } },
690-
}),
691-
);
692-
// No change to the room state
693-
for (const room of client.getRooms()) {
694-
expect(room.currentState.getStateEvents("org.example.foo", "bar")).toBe(null);
695-
}
665+
await makeClient({ receiveState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
666+
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
667+
expect(widgetApi.requestCapabilityToReceiveState).toHaveBeenCalledWith("org.example.foo", "bar");
668+
669+
const room = client.getRoom("!1:example.org");
670+
expect(room).not.toBeNull();
671+
expect(room!.currentState.getStateEvents("org.example.foo", "bar")?.getEffectiveEvent()).toEqual(event);
696672
});
697673
});
698674

src/embedded.ts

+48-41
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import {
2828
WidgetApiAction,
2929
IWidgetApiResponse,
3030
IWidgetApiResponseData,
31-
IUpdateStateToWidgetActionRequest,
3231
} from "matrix-widget-api";
3332

3433
import { MatrixEvent, IEvent, IContent, EventStatus } from "./models/event.ts";
@@ -137,7 +136,6 @@ export type EventHandlerMap = { [RoomWidgetClientEvent.PendingEventsChanged]: ()
137136
export class RoomWidgetClient extends MatrixClient {
138137
private room?: Room;
139138
private readonly widgetApiReady: Promise<void>;
140-
private readonly roomStateSynced: Promise<void>;
141139
private lifecycle?: AbortController;
142140
private syncState: SyncState | null = null;
143141

@@ -191,11 +189,6 @@ export class RoomWidgetClient extends MatrixClient {
191189
};
192190

193191
this.widgetApiReady = new Promise<void>((resolve) => this.widgetApi.once("ready", resolve));
194-
this.roomStateSynced = capabilities.receiveState?.length
195-
? new Promise<void>((resolve) =>
196-
this.widgetApi.once(`action:${WidgetApiToWidgetAction.UpdateState}`, resolve),
197-
)
198-
: Promise.resolve();
199192

200193
// Request capabilities for the functionality this client needs to support
201194
if (
@@ -248,7 +241,6 @@ export class RoomWidgetClient extends MatrixClient {
248241

249242
widgetApi.on(`action:${WidgetApiToWidgetAction.SendEvent}`, this.onEvent);
250243
widgetApi.on(`action:${WidgetApiToWidgetAction.SendToDevice}`, this.onToDevice);
251-
widgetApi.on(`action:${WidgetApiToWidgetAction.UpdateState}`, this.onStateUpdate);
252244

253245
// Open communication with the host
254246
widgetApi.start();
@@ -284,16 +276,37 @@ export class RoomWidgetClient extends MatrixClient {
284276

285277
await this.widgetApiReady;
286278

279+
// Backfill the requested events
280+
// We only get the most recent event for every type + state key combo,
281+
// so it doesn't really matter what order we inject them in
282+
await Promise.all(
283+
this.capabilities.receiveState?.map(async ({ eventType, stateKey }) => {
284+
const rawEvents = await this.widgetApi.readStateEvents(eventType, undefined, stateKey, [this.roomId]);
285+
const events = rawEvents.map((rawEvent) => new MatrixEvent(rawEvent as Partial<IEvent>));
286+
287+
if (this.syncApi instanceof SyncApi) {
288+
// Passing undefined for `stateAfterEventList` allows will make `injectRoomEvents` run in legacy mode
289+
// -> state events in `timelineEventList` will update the state.
290+
await this.syncApi.injectRoomEvents(this.room!, undefined, events);
291+
} else {
292+
await this.syncApi!.injectRoomEvents(this.room!, events); // Sliding Sync
293+
}
294+
events.forEach((event) => {
295+
this.emit(ClientEvent.Event, event);
296+
logger.info(`Backfilled event ${event.getId()} ${event.getType()} ${event.getStateKey()}`);
297+
});
298+
}) ?? [],
299+
);
300+
287301
if (opts.clientWellKnownPollPeriod !== undefined) {
288302
this.clientWellKnownIntervalID = setInterval(() => {
289303
this.fetchClientWellKnown();
290304
}, 1000 * opts.clientWellKnownPollPeriod);
291305
this.fetchClientWellKnown();
292306
}
293307

294-
await this.roomStateSynced;
295308
this.setSyncState(SyncState.Syncing);
296-
logger.info("Finished initial sync");
309+
logger.info("Finished backfilling events");
297310

298311
this.matrixRTC.start();
299312

@@ -304,7 +317,6 @@ export class RoomWidgetClient extends MatrixClient {
304317
public stopClient(): void {
305318
this.widgetApi.off(`action:${WidgetApiToWidgetAction.SendEvent}`, this.onEvent);
306319
this.widgetApi.off(`action:${WidgetApiToWidgetAction.SendToDevice}`, this.onToDevice);
307-
this.widgetApi.off(`action:${WidgetApiToWidgetAction.UpdateState}`, this.onStateUpdate);
308320

309321
super.stopClient();
310322
this.lifecycle!.abort(); // Signal to other async tasks that the client has stopped
@@ -562,15 +574,36 @@ export class RoomWidgetClient extends MatrixClient {
562574
// Only inject once we have update the txId
563575
await this.updateTxId(event);
564576

577+
// The widget API does not tell us whether a state event came from `state_after` or not so we assume legacy behaviour for now.
565578
if (this.syncApi instanceof SyncApi) {
566-
await this.syncApi.injectRoomEvents(this.room!, undefined, [], [event]);
579+
// The code will want to be something like:
580+
// ```
581+
// if (!params.addToTimeline && !params.addToState) {
582+
// // Passing undefined for `stateAfterEventList` makes `injectRoomEvents` run in "legacy mode"
583+
// // -> state events part of the `timelineEventList` parameter will update the state.
584+
// this.injectRoomEvents(this.room!, [], undefined, [event]);
585+
// } else {
586+
// this.injectRoomEvents(this.room!, undefined, params.addToState ? [event] : [], params.addToTimeline ? [event] : []);
587+
// }
588+
// ```
589+
590+
// Passing undefined for `stateAfterEventList` allows will make `injectRoomEvents` run in legacy mode
591+
// -> state events in `timelineEventList` will update the state.
592+
await this.syncApi.injectRoomEvents(this.room!, [], undefined, [event]);
567593
} else {
568-
// Sliding Sync
569-
await this.syncApi!.injectRoomEvents(this.room!, [], [event]);
594+
// The code will want to be something like:
595+
// ```
596+
// if (!params.addToTimeline && !params.addToState) {
597+
// this.injectRoomEvents(this.room!, [], [event]);
598+
// } else {
599+
// this.injectRoomEvents(this.room!, params.addToState ? [event] : [], params.addToTimeline ? [event] : []);
600+
// }
601+
// ```
602+
await this.syncApi!.injectRoomEvents(this.room!, [], [event]); // Sliding Sync
570603
}
571604
this.emit(ClientEvent.Event, event);
572605
this.setSyncState(SyncState.Syncing);
573-
logger.info(`Received event ${event.getId()} ${event.getType()}`);
606+
logger.info(`Received event ${event.getId()} ${event.getType()} ${event.getStateKey()}`);
574607
} else {
575608
const { event_id: eventId, room_id: roomId } = ev.detail.data;
576609
logger.info(`Received event ${eventId} for a different room ${roomId}; discarding`);
@@ -595,32 +628,6 @@ export class RoomWidgetClient extends MatrixClient {
595628
await this.ack(ev);
596629
};
597630

598-
private onStateUpdate = async (ev: CustomEvent<IUpdateStateToWidgetActionRequest>): Promise<void> => {
599-
ev.preventDefault();
600-
601-
for (const rawEvent of ev.detail.data.state) {
602-
// Verify the room ID matches, since it's possible for the client to
603-
// send us state updates from other rooms if this widget is always
604-
// on screen
605-
if (rawEvent.room_id === this.roomId) {
606-
const event = new MatrixEvent(rawEvent as Partial<IEvent>);
607-
608-
if (this.syncApi instanceof SyncApi) {
609-
await this.syncApi.injectRoomEvents(this.room!, undefined, [event]);
610-
} else {
611-
// Sliding Sync
612-
await this.syncApi!.injectRoomEvents(this.room!, [event]);
613-
}
614-
logger.info(`Updated state entry ${event.getType()} ${event.getStateKey()} to ${event.getId()}`);
615-
} else {
616-
const { event_id: eventId, room_id: roomId } = ev.detail.data;
617-
logger.info(`Received state entry ${eventId} for a different room ${roomId}; discarding`);
618-
}
619-
}
620-
621-
await this.ack(ev);
622-
};
623-
624631
private async watchTurnServers(): Promise<void> {
625632
const servers = this.widgetApi.getTurnServers();
626633
const onClientStopped = (): void => {

yarn.lock

+3-3
Original file line numberDiff line numberDiff line change
@@ -4854,9 +4854,9 @@ matrix-mock-request@^2.5.0:
48544854
expect "^28.1.0"
48554855

48564856
matrix-widget-api@^1.10.0:
4857-
version "1.12.0"
4858-
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.12.0.tgz#b3d22bab1670051c8eeee66bb96d08b33148bc99"
4859-
integrity sha512-6JRd9fJGGvuBRhcTg9wX+Skn/Q1wox3jdp5yYQKJ6pPw4urW9bkTR90APBKVDB1vorJKT44jml+lCzkDMRBjww==
4857+
version "1.10.0"
4858+
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.10.0.tgz#d31ea073a5871a1fb1a511ef900b0c125a37bf55"
4859+
integrity sha512-rkAJ29briYV7TJnfBVLVSKtpeBrBju15JZFSDP6wj8YdbCu1bdmlplJayQ+vYaw1x4fzI49Q+Nz3E85s46sRDw==
48604860
dependencies:
48614861
"@types/events" "^3.0.0"
48624862
events "^3.2.0"

0 commit comments

Comments
 (0)