@@ -28,6 +28,7 @@ import {
2828 WidgetApiAction ,
2929 IWidgetApiResponse ,
3030 IWidgetApiResponseData ,
31+ IUpdateStateToWidgetActionRequest ,
3132} from "matrix-widget-api" ;
3233
3334import { MatrixEvent , IEvent , IContent , EventStatus } from "./models/event.ts" ;
@@ -136,6 +137,7 @@ export type EventHandlerMap = { [RoomWidgetClientEvent.PendingEventsChanged]: ()
136137export class RoomWidgetClient extends MatrixClient {
137138 private room ?: Room ;
138139 private readonly widgetApiReady : Promise < void > ;
140+ private readonly roomStateSynced : Promise < void > ;
139141 private lifecycle ?: AbortController ;
140142 private syncState : SyncState | null = null ;
141143
@@ -189,6 +191,11 @@ export class RoomWidgetClient extends MatrixClient {
189191 } ;
190192
191193 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 ( ) ;
192199
193200 // Request capabilities for the functionality this client needs to support
194201 if (
@@ -241,6 +248,7 @@ export class RoomWidgetClient extends MatrixClient {
241248
242249 widgetApi . on ( `action:${ WidgetApiToWidgetAction . SendEvent } ` , this . onEvent ) ;
243250 widgetApi . on ( `action:${ WidgetApiToWidgetAction . SendToDevice } ` , this . onToDevice ) ;
251+ widgetApi . on ( `action:${ WidgetApiToWidgetAction . UpdateState } ` , this . onStateUpdate ) ;
244252
245253 // Open communication with the host
246254 widgetApi . start ( ) ;
@@ -276,37 +284,16 @@ export class RoomWidgetClient extends MatrixClient {
276284
277285 await this . widgetApiReady ;
278286
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-
301287 if ( opts . clientWellKnownPollPeriod !== undefined ) {
302288 this . clientWellKnownIntervalID = setInterval ( ( ) => {
303289 this . fetchClientWellKnown ( ) ;
304290 } , 1000 * opts . clientWellKnownPollPeriod ) ;
305291 this . fetchClientWellKnown ( ) ;
306292 }
307293
294+ await this . roomStateSynced ;
308295 this . setSyncState ( SyncState . Syncing ) ;
309- logger . info ( "Finished backfilling events " ) ;
296+ logger . info ( "Finished initial sync " ) ;
310297
311298 this . matrixRTC . start ( ) ;
312299
@@ -317,6 +304,7 @@ export class RoomWidgetClient extends MatrixClient {
317304 public stopClient ( ) : void {
318305 this . widgetApi . off ( `action:${ WidgetApiToWidgetAction . SendEvent } ` , this . onEvent ) ;
319306 this . widgetApi . off ( `action:${ WidgetApiToWidgetAction . SendToDevice } ` , this . onToDevice ) ;
307+ this . widgetApi . off ( `action:${ WidgetApiToWidgetAction . UpdateState } ` , this . onStateUpdate ) ;
320308
321309 super . stopClient ( ) ;
322310 this . lifecycle ! . abort ( ) ; // Signal to other async tasks that the client has stopped
@@ -574,36 +562,15 @@ export class RoomWidgetClient extends MatrixClient {
574562 // Only inject once we have update the txId
575563 await this . updateTxId ( event ) ;
576564
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.
578565 if ( this . syncApi instanceof SyncApi ) {
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 ] ) ;
566+ this . syncApi . injectRoomEvents ( this . room ! , undefined , [ ] , [ event ] ) ;
593567 } else {
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
568+ // Sliding Sync
569+ this . syncApi ! . injectRoomEvents ( this . room ! , [ ] , [ event ] ) ;
603570 }
604571 this . emit ( ClientEvent . Event , event ) ;
605572 this . setSyncState ( SyncState . Syncing ) ;
606- logger . info ( `Received event ${ event . getId ( ) } ${ event . getType ( ) } ${ event . getStateKey ( ) } ` ) ;
573+ logger . info ( `Received event ${ event . getId ( ) } ${ event . getType ( ) } ` ) ;
607574 } else {
608575 const { event_id : eventId , room_id : roomId } = ev . detail . data ;
609576 logger . info ( `Received event ${ eventId } for a different room ${ roomId } ; discarding` ) ;
@@ -628,6 +595,32 @@ export class RoomWidgetClient extends MatrixClient {
628595 await this . ack ( ev ) ;
629596 } ;
630597
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+ this . syncApi . injectRoomEvents ( this . room ! , undefined , [ event ] ) ;
610+ } else {
611+ // Sliding Sync
612+ 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+
631624 private async watchTurnServers ( ) : Promise < void > {
632625 const servers = this . widgetApi . getTurnServers ( ) ;
633626 const onClientStopped = ( ) : void => {
0 commit comments