@@ -28,6 +28,7 @@ import {
28
28
type WidgetApiAction ,
29
29
type IWidgetApiResponse ,
30
30
type IWidgetApiResponseData ,
31
+ type IUpdateStateToWidgetActionRequest ,
31
32
} from "matrix-widget-api" ;
32
33
33
34
import { MatrixEvent , type IEvent , type IContent , EventStatus } from "./models/event.ts" ;
@@ -135,6 +136,7 @@ export type EventHandlerMap = { [RoomWidgetClientEvent.PendingEventsChanged]: ()
135
136
export class RoomWidgetClient extends MatrixClient {
136
137
private room ?: Room ;
137
138
private readonly widgetApiReady : Promise < void > ;
139
+ private readonly roomStateSynced : Promise < void > ;
138
140
private lifecycle ?: AbortController ;
139
141
private syncState : SyncState | null = null ;
140
142
@@ -188,6 +190,11 @@ export class RoomWidgetClient extends MatrixClient {
188
190
} ;
189
191
190
192
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 ( ) ;
191
198
192
199
// Request capabilities for the functionality this client needs to support
193
200
if (
@@ -240,6 +247,7 @@ export class RoomWidgetClient extends MatrixClient {
240
247
241
248
widgetApi . on ( `action:${ WidgetApiToWidgetAction . SendEvent } ` , this . onEvent ) ;
242
249
widgetApi . on ( `action:${ WidgetApiToWidgetAction . SendToDevice } ` , this . onToDevice ) ;
250
+ widgetApi . on ( `action:${ WidgetApiToWidgetAction . UpdateState } ` , this . onStateUpdate ) ;
243
251
244
252
// Open communication with the host
245
253
widgetApi . start ( ) ;
@@ -275,37 +283,16 @@ export class RoomWidgetClient extends MatrixClient {
275
283
276
284
await this . widgetApiReady ;
277
285
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
-
300
286
if ( opts . clientWellKnownPollPeriod !== undefined ) {
301
287
this . clientWellKnownIntervalID = setInterval ( ( ) => {
302
288
this . fetchClientWellKnown ( ) ;
303
289
} , 1000 * opts . clientWellKnownPollPeriod ) ;
304
290
this . fetchClientWellKnown ( ) ;
305
291
}
306
292
293
+ await this . roomStateSynced ;
307
294
this . setSyncState ( SyncState . Syncing ) ;
308
- logger . info ( "Finished backfilling events " ) ;
295
+ logger . info ( "Finished initial sync " ) ;
309
296
310
297
this . matrixRTC . start ( ) ;
311
298
@@ -316,6 +303,7 @@ export class RoomWidgetClient extends MatrixClient {
316
303
public stopClient ( ) : void {
317
304
this . widgetApi . off ( `action:${ WidgetApiToWidgetAction . SendEvent } ` , this . onEvent ) ;
318
305
this . widgetApi . off ( `action:${ WidgetApiToWidgetAction . SendToDevice } ` , this . onToDevice ) ;
306
+ this . widgetApi . off ( `action:${ WidgetApiToWidgetAction . UpdateState } ` , this . onStateUpdate ) ;
319
307
320
308
super . stopClient ( ) ;
321
309
this . lifecycle ! . abort ( ) ; // Signal to other async tasks that the client has stopped
@@ -600,36 +588,15 @@ export class RoomWidgetClient extends MatrixClient {
600
588
// Only inject once we have update the txId
601
589
await this . updateTxId ( event ) ;
602
590
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.
604
591
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 ] ) ;
619
593
} 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 ] ) ;
629
596
}
630
597
this . emit ( ClientEvent . Event , event ) ;
631
598
this . setSyncState ( SyncState . Syncing ) ;
632
- logger . info ( `Received event ${ event . getId ( ) } ${ event . getType ( ) } ${ event . getStateKey ( ) } ` ) ;
599
+ logger . info ( `Received event ${ event . getId ( ) } ${ event . getType ( ) } ` ) ;
633
600
} else {
634
601
const { event_id : eventId , room_id : roomId } = ev . detail . data ;
635
602
logger . info ( `Received event ${ eventId } for a different room ${ roomId } ; discarding` ) ;
@@ -654,6 +621,32 @@ export class RoomWidgetClient extends MatrixClient {
654
621
await this . ack ( ev ) ;
655
622
} ;
656
623
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
+
657
650
private async watchTurnServers ( ) : Promise < void > {
658
651
const servers = this . widgetApi . getTurnServers ( ) ;
659
652
const onClientStopped = ( ) : void => {
0 commit comments