@@ -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" ;
@@ -146,6 +147,7 @@ export type EventHandlerMap = { [RoomWidgetClientEvent.PendingEventsChanged]: ()
146147export class RoomWidgetClient extends MatrixClient {
147148 private room ?: Room ;
148149 private readonly widgetApiReady : Promise < void > ;
150+ private readonly roomStateSynced : Promise < void > ;
149151 private lifecycle ?: AbortController ;
150152 private syncState : SyncState | null = null ;
151153
@@ -199,6 +201,11 @@ export class RoomWidgetClient extends MatrixClient {
199201 } ;
200202
201203 this . widgetApiReady = new Promise < void > ( ( resolve ) => this . widgetApi . once ( "ready" , resolve ) ) ;
204+ this . roomStateSynced = capabilities . receiveState ?. length
205+ ? new Promise < void > ( ( resolve ) =>
206+ this . widgetApi . once ( `action:${ WidgetApiToWidgetAction . UpdateState } ` , resolve ) ,
207+ )
208+ : Promise . resolve ( ) ;
202209
203210 // Request capabilities for the functionality this client needs to support
204211 if (
@@ -251,6 +258,7 @@ export class RoomWidgetClient extends MatrixClient {
251258
252259 widgetApi . on ( `action:${ WidgetApiToWidgetAction . SendEvent } ` , this . onEvent ) ;
253260 widgetApi . on ( `action:${ WidgetApiToWidgetAction . SendToDevice } ` , this . onToDevice ) ;
261+ widgetApi . on ( `action:${ WidgetApiToWidgetAction . UpdateState } ` , this . onStateUpdate ) ;
254262
255263 // Open communication with the host
256264 widgetApi . start ( ) ;
@@ -286,37 +294,16 @@ export class RoomWidgetClient extends MatrixClient {
286294
287295 await this . widgetApiReady ;
288296
289- // Backfill the requested events
290- // We only get the most recent event for every type + state key combo,
291- // so it doesn't really matter what order we inject them in
292- await Promise . all (
293- this . capabilities . receiveState ?. map ( async ( { eventType, stateKey } ) => {
294- const rawEvents = await this . widgetApi . readStateEvents ( eventType , undefined , stateKey , [ this . roomId ] ) ;
295- const events = rawEvents . map ( ( rawEvent ) => new MatrixEvent ( rawEvent as Partial < IEvent > ) ) ;
296-
297- if ( this . syncApi instanceof SyncApi ) {
298- // Passing undefined for `stateAfterEventList` allows will make `injectRoomEvents` run in legacy mode
299- // -> state events in `timelineEventList` will update the state.
300- await this . syncApi . injectRoomEvents ( this . room ! , undefined , events ) ;
301- } else {
302- await this . syncApi ! . injectRoomEvents ( this . room ! , events ) ; // Sliding Sync
303- }
304- events . forEach ( ( event ) => {
305- this . emit ( ClientEvent . Event , event ) ;
306- logger . info ( `Backfilled event ${ event . getId ( ) } ${ event . getType ( ) } ${ event . getStateKey ( ) } ` ) ;
307- } ) ;
308- } ) ?? [ ] ,
309- ) ;
310-
311297 if ( opts . clientWellKnownPollPeriod !== undefined ) {
312298 this . clientWellKnownIntervalID = setInterval ( ( ) => {
313299 this . fetchClientWellKnown ( ) ;
314300 } , 1000 * opts . clientWellKnownPollPeriod ) ;
315301 this . fetchClientWellKnown ( ) ;
316302 }
317303
304+ await this . roomStateSynced ;
318305 this . setSyncState ( SyncState . Syncing ) ;
319- logger . info ( "Finished backfilling events " ) ;
306+ logger . info ( "Finished initial sync " ) ;
320307
321308 this . matrixRTC . start ( ) ;
322309
@@ -327,6 +314,7 @@ export class RoomWidgetClient extends MatrixClient {
327314 public stopClient ( ) : void {
328315 this . widgetApi . off ( `action:${ WidgetApiToWidgetAction . SendEvent } ` , this . onEvent ) ;
329316 this . widgetApi . off ( `action:${ WidgetApiToWidgetAction . SendToDevice } ` , this . onToDevice ) ;
317+ this . widgetApi . off ( `action:${ WidgetApiToWidgetAction . UpdateState } ` , this . onStateUpdate ) ;
330318
331319 super . stopClient ( ) ;
332320 this . lifecycle ! . abort ( ) ; // Signal to other async tasks that the client has stopped
@@ -604,36 +592,15 @@ export class RoomWidgetClient extends MatrixClient {
604592 // Only inject once we have update the txId
605593 await this . updateTxId ( event ) ;
606594
607- // The widget API does not tell us whether a state event came from `state_after` or not so we assume legacy behaviour for now.
608595 if ( this . syncApi instanceof SyncApi ) {
609- // The code will want to be something like:
610- // ```
611- // if (!params.addToTimeline && !params.addToState) {
612- // // Passing undefined for `stateAfterEventList` makes `injectRoomEvents` run in "legacy mode"
613- // // -> state events part of the `timelineEventList` parameter will update the state.
614- // this.injectRoomEvents(this.room!, [], undefined, [event]);
615- // } else {
616- // this.injectRoomEvents(this.room!, undefined, params.addToState ? [event] : [], params.addToTimeline ? [event] : []);
617- // }
618- // ```
619-
620- // Passing undefined for `stateAfterEventList` allows will make `injectRoomEvents` run in legacy mode
621- // -> state events in `timelineEventList` will update the state.
622- await this . syncApi . injectRoomEvents ( this . room ! , [ ] , undefined , [ event ] ) ;
596+ await this . syncApi . injectRoomEvents ( this . room ! , undefined , [ ] , [ event ] ) ;
623597 } else {
624- // The code will want to be something like:
625- // ```
626- // if (!params.addToTimeline && !params.addToState) {
627- // this.injectRoomEvents(this.room!, [], [event]);
628- // } else {
629- // this.injectRoomEvents(this.room!, params.addToState ? [event] : [], params.addToTimeline ? [event] : []);
630- // }
631- // ```
632- await this . syncApi ! . injectRoomEvents ( this . room ! , [ ] , [ event ] ) ; // Sliding Sync
598+ // Sliding Sync
599+ await this . syncApi ! . injectRoomEvents ( this . room ! , [ ] , [ event ] ) ;
633600 }
634601 this . emit ( ClientEvent . Event , event ) ;
635602 this . setSyncState ( SyncState . Syncing ) ;
636- logger . info ( `Received event ${ event . getId ( ) } ${ event . getType ( ) } ${ event . getStateKey ( ) } ` ) ;
603+ logger . info ( `Received event ${ event . getId ( ) } ${ event . getType ( ) } ` ) ;
637604 } else {
638605 const { event_id : eventId , room_id : roomId } = ev . detail . data ;
639606 logger . info ( `Received event ${ eventId } for a different room ${ roomId } ; discarding` ) ;
@@ -658,6 +625,32 @@ export class RoomWidgetClient extends MatrixClient {
658625 await this . ack ( ev ) ;
659626 } ;
660627
628+ private onStateUpdate = async ( ev : CustomEvent < IUpdateStateToWidgetActionRequest > ) : Promise < void > => {
629+ ev . preventDefault ( ) ;
630+
631+ for ( const rawEvent of ev . detail . data . state ) {
632+ // Verify the room ID matches, since it's possible for the client to
633+ // send us state updates from other rooms if this widget is always
634+ // on screen
635+ if ( rawEvent . room_id === this . roomId ) {
636+ const event = new MatrixEvent ( rawEvent as Partial < IEvent > ) ;
637+
638+ if ( this . syncApi instanceof SyncApi ) {
639+ await this . syncApi . injectRoomEvents ( this . room ! , undefined , [ event ] ) ;
640+ } else {
641+ // Sliding Sync
642+ await this . syncApi ! . injectRoomEvents ( this . room ! , [ event ] ) ;
643+ }
644+ logger . info ( `Updated state entry ${ event . getType ( ) } ${ event . getStateKey ( ) } to ${ event . getId ( ) } ` ) ;
645+ } else {
646+ const { event_id : eventId , room_id : roomId } = ev . detail . data ;
647+ logger . info ( `Received state entry ${ eventId } for a different room ${ roomId } ; discarding` ) ;
648+ }
649+ }
650+
651+ await this . ack ( ev ) ;
652+ } ;
653+
661654 private async watchTurnServers ( ) : Promise < void > {
662655 const servers = this . widgetApi . getTurnServers ( ) ;
663656 const onClientStopped = ( ) : void => {
0 commit comments