@@ -28,6 +28,7 @@ import {
2828 type WidgetApiAction ,
2929 type IWidgetApiResponse ,
3030 type IWidgetApiResponseData ,
31+ type IUpdateStateToWidgetActionRequest ,
3132} from "matrix-widget-api" ;
3233
3334import { MatrixEvent , type IEvent , type IContent , EventStatus } from "./models/event.ts" ;
@@ -135,6 +136,7 @@ export type EventHandlerMap = { [RoomWidgetClientEvent.PendingEventsChanged]: ()
135136export class RoomWidgetClient extends MatrixClient {
136137 private room ?: Room ;
137138 private readonly widgetApiReady : Promise < void > ;
139+ private readonly roomStateSynced : Promise < void > ;
138140 private lifecycle ?: AbortController ;
139141 private syncState : SyncState | null = null ;
140142
@@ -188,6 +190,11 @@ export class RoomWidgetClient extends MatrixClient {
188190 } ;
189191
190192 this . widgetApiReady = new Promise < void > ( ( resolve ) => this . widgetApi . once ( "ready" , resolve ) ) ;
193+ this . roomStateSynced = capabilities . receiveState ?. length
194+ ? new Promise < void > ( ( resolve ) =>
195+ this . widgetApi . once ( `action:${ WidgetApiToWidgetAction . UpdateState } ` , resolve ) ,
196+ )
197+ : Promise . resolve ( ) ;
191198
192199 // Request capabilities for the functionality this client needs to support
193200 if (
@@ -240,6 +247,7 @@ export class RoomWidgetClient extends MatrixClient {
240247
241248 widgetApi . on ( `action:${ WidgetApiToWidgetAction . SendEvent } ` , this . onEvent ) ;
242249 widgetApi . on ( `action:${ WidgetApiToWidgetAction . SendToDevice } ` , this . onToDevice ) ;
250+ widgetApi . on ( `action:${ WidgetApiToWidgetAction . UpdateState } ` , this . onStateUpdate ) ;
243251
244252 // Open communication with the host
245253 widgetApi . start ( ) ;
@@ -275,37 +283,16 @@ export class RoomWidgetClient extends MatrixClient {
275283
276284 await this . widgetApiReady ;
277285
278- // Backfill the requested events
279- // We only get the most recent event for every type + state key combo,
280- // so it doesn't really matter what order we inject them in
281- await Promise . all (
282- this . capabilities . receiveState ?. map ( async ( { eventType, stateKey } ) => {
283- const rawEvents = await this . widgetApi . readStateEvents ( eventType , undefined , stateKey , [ this . roomId ] ) ;
284- const events = rawEvents . map ( ( rawEvent ) => new MatrixEvent ( rawEvent as Partial < IEvent > ) ) ;
285-
286- if ( this . syncApi instanceof SyncApi ) {
287- // Passing undefined for `stateAfterEventList` allows will make `injectRoomEvents` run in legacy mode
288- // -> state events in `timelineEventList` will update the state.
289- await this . syncApi . injectRoomEvents ( this . room ! , undefined , events ) ;
290- } else {
291- await this . syncApi ! . injectRoomEvents ( this . room ! , events ) ; // Sliding Sync
292- }
293- events . forEach ( ( event ) => {
294- this . emit ( ClientEvent . Event , event ) ;
295- logger . info ( `Backfilled event ${ event . getId ( ) } ${ event . getType ( ) } ${ event . getStateKey ( ) } ` ) ;
296- } ) ;
297- } ) ?? [ ] ,
298- ) ;
299-
300286 if ( opts . clientWellKnownPollPeriod !== undefined ) {
301287 this . clientWellKnownIntervalID = setInterval ( ( ) => {
302288 this . fetchClientWellKnown ( ) ;
303289 } , 1000 * opts . clientWellKnownPollPeriod ) ;
304290 this . fetchClientWellKnown ( ) ;
305291 }
306292
293+ await this . roomStateSynced ;
307294 this . setSyncState ( SyncState . Syncing ) ;
308- logger . info ( "Finished backfilling events " ) ;
295+ logger . info ( "Finished initial sync " ) ;
309296
310297 this . matrixRTC . start ( ) ;
311298
@@ -316,6 +303,7 @@ export class RoomWidgetClient extends MatrixClient {
316303 public stopClient ( ) : void {
317304 this . widgetApi . off ( `action:${ WidgetApiToWidgetAction . SendEvent } ` , this . onEvent ) ;
318305 this . widgetApi . off ( `action:${ WidgetApiToWidgetAction . SendToDevice } ` , this . onToDevice ) ;
306+ this . widgetApi . off ( `action:${ WidgetApiToWidgetAction . UpdateState } ` , this . onStateUpdate ) ;
319307
320308 super . stopClient ( ) ;
321309 this . lifecycle ! . abort ( ) ; // Signal to other async tasks that the client has stopped
@@ -600,36 +588,15 @@ export class RoomWidgetClient extends MatrixClient {
600588 // Only inject once we have update the txId
601589 await this . updateTxId ( event ) ;
602590
603- // The widget API does not tell us whether a state event came from `state_after` or not so we assume legacy behaviour for now.
604591 if ( this . syncApi instanceof SyncApi ) {
605- // The code will want to be something like:
606- // ```
607- // if (!params.addToTimeline && !params.addToState) {
608- // // Passing undefined for `stateAfterEventList` makes `injectRoomEvents` run in "legacy mode"
609- // // -> state events part of the `timelineEventList` parameter will update the state.
610- // this.injectRoomEvents(this.room!, [], undefined, [event]);
611- // } else {
612- // this.injectRoomEvents(this.room!, undefined, params.addToState ? [event] : [], params.addToTimeline ? [event] : []);
613- // }
614- // ```
615-
616- // Passing undefined for `stateAfterEventList` allows will make `injectRoomEvents` run in legacy mode
617- // -> state events in `timelineEventList` will update the state.
618- await this . syncApi . injectRoomEvents ( this . room ! , [ ] , undefined , [ event ] ) ;
592+ await this . syncApi . injectRoomEvents ( this . room ! , undefined , [ ] , [ event ] ) ;
619593 } else {
620- // The code will want to be something like:
621- // ```
622- // if (!params.addToTimeline && !params.addToState) {
623- // this.injectRoomEvents(this.room!, [], [event]);
624- // } else {
625- // this.injectRoomEvents(this.room!, params.addToState ? [event] : [], params.addToTimeline ? [event] : []);
626- // }
627- // ```
628- await this . syncApi ! . injectRoomEvents ( this . room ! , [ ] , [ event ] ) ; // Sliding Sync
594+ // Sliding Sync
595+ await this . syncApi ! . injectRoomEvents ( this . room ! , [ ] , [ event ] ) ;
629596 }
630597 this . emit ( ClientEvent . Event , event ) ;
631598 this . setSyncState ( SyncState . Syncing ) ;
632- logger . info ( `Received event ${ event . getId ( ) } ${ event . getType ( ) } ${ event . getStateKey ( ) } ` ) ;
599+ logger . info ( `Received event ${ event . getId ( ) } ${ event . getType ( ) } ` ) ;
633600 } else {
634601 const { event_id : eventId , room_id : roomId } = ev . detail . data ;
635602 logger . info ( `Received event ${ eventId } for a different room ${ roomId } ; discarding` ) ;
@@ -654,6 +621,32 @@ export class RoomWidgetClient extends MatrixClient {
654621 await this . ack ( ev ) ;
655622 } ;
656623
624+ private onStateUpdate = async ( ev : CustomEvent < IUpdateStateToWidgetActionRequest > ) : Promise < void > => {
625+ ev . preventDefault ( ) ;
626+
627+ for ( const rawEvent of ev . detail . data . state ) {
628+ // Verify the room ID matches, since it's possible for the client to
629+ // send us state updates from other rooms if this widget is always
630+ // on screen
631+ if ( rawEvent . room_id === this . roomId ) {
632+ const event = new MatrixEvent ( rawEvent as Partial < IEvent > ) ;
633+
634+ if ( this . syncApi instanceof SyncApi ) {
635+ await this . syncApi . injectRoomEvents ( this . room ! , undefined , [ event ] ) ;
636+ } else {
637+ // Sliding Sync
638+ await this . syncApi ! . injectRoomEvents ( this . room ! , [ event ] ) ;
639+ }
640+ logger . info ( `Updated state entry ${ event . getType ( ) } ${ event . getStateKey ( ) } to ${ event . getId ( ) } ` ) ;
641+ } else {
642+ const { event_id : eventId , room_id : roomId } = ev . detail . data ;
643+ logger . info ( `Received state entry ${ eventId } for a different room ${ roomId } ; discarding` ) ;
644+ }
645+ }
646+
647+ await this . ack ( ev ) ;
648+ } ;
649+
657650 private async watchTurnServers ( ) : Promise < void > {
658651 const servers = this . widgetApi . getTurnServers ( ) ;
659652 const onClientStopped = ( ) : void => {
0 commit comments