@@ -4,7 +4,7 @@ import { debounce } from '@ember/runloop';
4
4
import Service , { service } from '@ember/service' ;
5
5
import { cached , tracked } from '@glimmer/tracking' ;
6
6
7
- import { task } from 'ember-concurrency' ;
7
+ import { task , timeout , restartableTask } from 'ember-concurrency' ;
8
8
import window from 'ember-window-mock' ;
9
9
import { cloneDeep } from 'lodash' ;
10
10
import {
@@ -14,6 +14,12 @@ import {
14
14
type EmittedEvents ,
15
15
type ISendEventResponse ,
16
16
} from 'matrix-js-sdk' ;
17
+ import {
18
+ SlidingSync ,
19
+ MSC3575List ,
20
+ SlidingSyncEvent ,
21
+ } from 'matrix-js-sdk/lib/sliding-sync' ;
22
+ import { SlidingSyncState } from 'matrix-js-sdk/lib/sliding-sync' ;
17
23
import stringify from 'safe-stable-stringify' ;
18
24
import { md5 } from 'super-fast-md5' ;
19
25
import { TrackedMap } from 'tracked-built-ins' ;
@@ -115,6 +121,10 @@ import type * as MatrixSDK from 'matrix-js-sdk';
115
121
const { matrixURL } = ENV ;
116
122
const MAX_CARD_SIZE_KB = 60 ;
117
123
const STATE_EVENTS_OF_INTEREST = [ 'm.room.create' , 'm.room.name' ] ;
124
+ const SLIDING_SYNC_AI_ROOM_LIST_NAME = 'ai-room' ;
125
+ const SLIDING_SYNC_AUTH_ROOM_LIST_NAME = 'auth-room' ;
126
+ const SLIDING_SYNC_LIST_RANGE_SIZE = 2 ;
127
+ const SLIDING_SYNC_LIST_TIMELINE_LIMIT = 1 ;
118
128
119
129
const realmEventsLogger = logger ( 'realm:events' ) ;
120
130
@@ -139,6 +149,12 @@ export default class MatrixService extends Service {
139
149
@tracked private _isNewUser = false ;
140
150
@tracked private postLoginCompleted = false ;
141
151
@tracked private _currentRoomId : string | undefined ;
152
+ @tracked private timelineLoadingState : Map < string , boolean > =
153
+ new TrackedMap ( ) ;
154
+ @tracked private currentAiRoomListRange = SLIDING_SYNC_LIST_RANGE_SIZE ;
155
+ @tracked private totalAiRooms ?: number ;
156
+ @tracked private totalAuthRooms ?: number ;
157
+ @tracked private currentAuthRoomListRange = SLIDING_SYNC_LIST_RANGE_SIZE ;
142
158
143
159
profile = getMatrixProfile ( this , ( ) => this . userId ) ;
144
160
@@ -164,6 +180,7 @@ export default class MatrixService extends Service {
164
180
new TrackedMap ( ) ;
165
181
private cardHashes : Map < string , string > = new Map ( ) ; // hashes <> event id
166
182
private skillCardHashes : Map < string , string > = new Map ( ) ; // hashes <> event id
183
+ private slidingSync : SlidingSync | undefined ;
167
184
168
185
constructor ( owner : Owner ) {
169
186
super ( owner ) ;
@@ -181,6 +198,7 @@ export default class MatrixService extends Service {
181
198
set currentRoomId ( value : string | undefined ) {
182
199
this . _currentRoomId = value ;
183
200
if ( value ) {
201
+ this . loadAllTimelineEvents ( value ) ;
184
202
window . localStorage . setItem ( CurrentRoomIdPersistenceKey , value ) ;
185
203
} else {
186
204
window . localStorage . removeItem ( CurrentRoomIdPersistenceKey ) ;
@@ -475,8 +493,9 @@ export default class MatrixService extends Service {
475
493
this . bindEventListeners ( ) ;
476
494
477
495
try {
478
- await this . _client . startClient ( ) ;
479
- let accountDataContent = await this . _client . getAccountDataFromServer < {
496
+ this . initSlidingSync ( ) ;
497
+ await this . client . startClient ( { slidingSync : this . slidingSync } ) ;
498
+ let accountDataContent = await this . client . getAccountDataFromServer < {
480
499
realms : string [ ] ;
481
500
} > ( APP_BOXEL_REALMS_EVENT_TYPE ) ;
482
501
await this . realmServer . setAvailableRealmURLs (
@@ -498,6 +517,126 @@ export default class MatrixService extends Service {
498
517
}
499
518
}
500
519
520
+ private initSlidingSync ( ) {
521
+ let lists : Map < string , MSC3575List > = new Map ( ) ;
522
+ lists . set ( SLIDING_SYNC_AI_ROOM_LIST_NAME , {
523
+ ranges : [ [ 0 , SLIDING_SYNC_LIST_RANGE_SIZE ] ] ,
524
+ filters : {
525
+ is_dm : false ,
526
+ } ,
527
+ timeline_limit : SLIDING_SYNC_LIST_TIMELINE_LIMIT ,
528
+ required_state : [ [ '*' , '*' ] ] ,
529
+ } ) ;
530
+ lists . set ( SLIDING_SYNC_AUTH_ROOM_LIST_NAME , {
531
+ ranges : [ [ 0 , SLIDING_SYNC_LIST_RANGE_SIZE ] ] ,
532
+ filters : {
533
+ is_dm : true ,
534
+ } ,
535
+ timeline_limit : 1 ,
536
+ required_state : [ [ '*' , '*' ] ] ,
537
+ } ) ;
538
+ this . slidingSync = new SlidingSync (
539
+ this . client . baseUrl ,
540
+ lists ,
541
+ {
542
+ timeline_limit : SLIDING_SYNC_LIST_TIMELINE_LIMIT ,
543
+ } ,
544
+ this . client as any ,
545
+ 3000 ,
546
+ ) ;
547
+
548
+ this . slidingSync . on ( SlidingSyncEvent . Lifecycle , ( state , response ) => {
549
+ if ( state !== SlidingSyncState . Complete || ! response ) {
550
+ return ;
551
+ }
552
+
553
+ // Handle AI rooms
554
+ if (
555
+ response . lists [ SLIDING_SYNC_AI_ROOM_LIST_NAME ] ?. ops ?. [ 0 ] . op === 'SYNC'
556
+ ) {
557
+ if (
558
+ response . lists ?. [ SLIDING_SYNC_AI_ROOM_LIST_NAME ] ?. count !== undefined
559
+ ) {
560
+ this . totalAiRooms =
561
+ response . lists [ SLIDING_SYNC_AI_ROOM_LIST_NAME ] . count ;
562
+ const currentRange =
563
+ response . lists [ SLIDING_SYNC_AI_ROOM_LIST_NAME ] . ops ?. [ 0 ] ?. range ;
564
+
565
+ if ( currentRange ) {
566
+ this . currentAiRoomListRange = currentRange [ 1 ] ;
567
+ this . maybeLoadMoreAiRooms . perform ( ) ;
568
+ }
569
+ }
570
+ }
571
+
572
+ // Handle Auth rooms
573
+ if (
574
+ response . lists [ SLIDING_SYNC_AUTH_ROOM_LIST_NAME ] ?. ops ?. [ 0 ] . op === 'SYNC'
575
+ ) {
576
+ if (
577
+ response . lists ?. [ SLIDING_SYNC_AUTH_ROOM_LIST_NAME ] ?. count !==
578
+ undefined
579
+ ) {
580
+ this . totalAuthRooms =
581
+ response . lists [ SLIDING_SYNC_AUTH_ROOM_LIST_NAME ] . count ;
582
+ const currentAuthRange =
583
+ response . lists [ SLIDING_SYNC_AUTH_ROOM_LIST_NAME ] . ops ?. [ 0 ] ?. range ;
584
+
585
+ if ( currentAuthRange ) {
586
+ this . currentAuthRoomListRange = currentAuthRange [ 1 ] ;
587
+ this . maybeLoadMoreAuthRooms . perform ( ) ;
588
+ }
589
+ }
590
+ }
591
+ } ) ;
592
+
593
+ return this . slidingSync ;
594
+ }
595
+
596
+ private maybeLoadMoreAiRooms = restartableTask ( async ( ) => {
597
+ if ( ! this . totalAiRooms || ! this . slidingSync ) {
598
+ return ;
599
+ }
600
+
601
+ while ( this . currentAiRoomListRange < this . totalAiRooms ) {
602
+ const nextRange = Math . min (
603
+ this . currentAiRoomListRange + 2 ,
604
+ this . totalAiRooms ,
605
+ ) ;
606
+
607
+ try {
608
+ await this . slidingSync . setListRanges ( 'ai-room' , [ [ 0 , nextRange ] ] ) ;
609
+ this . currentAiRoomListRange = nextRange ;
610
+ await timeout ( 500 ) ;
611
+ } catch ( error ) {
612
+ console . error ( 'Error expanding room range:' , error ) ;
613
+ break ;
614
+ }
615
+ }
616
+ } ) ;
617
+
618
+ private maybeLoadMoreAuthRooms = restartableTask ( async ( ) => {
619
+ if ( ! this . totalAuthRooms || ! this . slidingSync ) {
620
+ return ;
621
+ }
622
+
623
+ while ( this . currentAuthRoomListRange < this . totalAuthRooms ) {
624
+ const nextRange = Math . min (
625
+ this . currentAuthRoomListRange + 2 ,
626
+ this . totalAuthRooms ,
627
+ ) ;
628
+
629
+ try {
630
+ await this . slidingSync . setListRanges ( 'auth-room' , [ [ 0 , nextRange ] ] ) ;
631
+ this . currentAuthRoomListRange = nextRange ;
632
+ await timeout ( 500 ) ;
633
+ } catch ( error ) {
634
+ console . error ( 'Error expanding auth room range:' , error ) ;
635
+ break ;
636
+ }
637
+ }
638
+ } ) ;
639
+
501
640
private async loginToRealms ( ) {
502
641
// This is where we would actually load user-specific choices out of the
503
642
// user's profile based on this.client.getUserId();
@@ -534,7 +673,7 @@ export default class MatrixService extends Service {
534
673
| CommandResultWithOutputContent
535
674
| CommandDefinitionsContent ,
536
675
) {
537
- let roomData = await this . ensureRoomData ( roomId ) ;
676
+ let roomData = this . ensureRoomData ( roomId ) ;
538
677
return roomData . mutex . dispatch ( async ( ) => {
539
678
if ( 'data' in content ) {
540
679
const encodedContent = {
@@ -1008,7 +1147,7 @@ export default class MatrixService extends Service {
1008
1147
}
1009
1148
1010
1149
async setPowerLevel ( roomId : string , userId : string , powerLevel : number ) {
1011
- let roomData = await this . ensureRoomData ( roomId ) ;
1150
+ let roomData = this . ensureRoomData ( roomId ) ;
1012
1151
await roomData . mutex . dispatch ( async ( ) => {
1013
1152
return this . client . setPowerLevel ( roomId , userId , powerLevel ) ;
1014
1153
} ) ;
@@ -1045,7 +1184,7 @@ export default class MatrixService extends Service {
1045
1184
content : Record < string , any > ,
1046
1185
stateKey : string = '' ,
1047
1186
) {
1048
- let roomData = await this . ensureRoomData ( roomId ) ;
1187
+ let roomData = this . ensureRoomData ( roomId ) ;
1049
1188
await roomData . mutex . dispatch ( async ( ) => {
1050
1189
return this . client . sendStateEvent ( roomId , eventType , content , stateKey ) ;
1051
1190
} ) ;
@@ -1059,7 +1198,7 @@ export default class MatrixService extends Service {
1059
1198
content : Record < string , any > ,
1060
1199
) => Promise < Record < string , any > > ,
1061
1200
) {
1062
- let roomData = await this . ensureRoomData ( roomId ) ;
1201
+ let roomData = this . ensureRoomData ( roomId ) ;
1063
1202
await roomData . mutex . dispatch ( async ( ) => {
1064
1203
let currentContent = await this . getStateEventSafe (
1065
1204
roomId ,
@@ -1077,21 +1216,21 @@ export default class MatrixService extends Service {
1077
1216
}
1078
1217
1079
1218
async leave ( roomId : string ) {
1080
- let roomData = await this . ensureRoomData ( roomId ) ;
1219
+ let roomData = this . ensureRoomData ( roomId ) ;
1081
1220
await roomData . mutex . dispatch ( async ( ) => {
1082
1221
return this . client . leave ( roomId ) ;
1083
1222
} ) ;
1084
1223
}
1085
1224
1086
1225
async forget ( roomId : string ) {
1087
- let roomData = await this . ensureRoomData ( roomId ) ;
1226
+ let roomData = this . ensureRoomData ( roomId ) ;
1088
1227
await roomData . mutex . dispatch ( async ( ) => {
1089
1228
return this . client . forget ( roomId ) ;
1090
1229
} ) ;
1091
1230
}
1092
1231
1093
1232
async setRoomName ( roomId : string , name : string ) {
1094
- let roomData = await this . ensureRoomData ( roomId ) ;
1233
+ let roomData = this . ensureRoomData ( roomId ) ;
1095
1234
await roomData . mutex . dispatch ( async ( ) => {
1096
1235
return this . client . setRoomName ( roomId , name ) ;
1097
1236
} ) ;
@@ -1131,11 +1270,43 @@ export default class MatrixService extends Service {
1131
1270
return await this . client . isUsernameAvailable ( username ) ;
1132
1271
}
1133
1272
1134
- async getRoomState ( roomId : string ) {
1135
- return this . client
1136
- . getRoom ( roomId )
1137
- ?. getLiveTimeline ( )
1138
- . getState ( 'f' as MatrixSDK . Direction ) ;
1273
+ private async loadAllTimelineEvents ( roomId : string ) {
1274
+ let roomData = this . ensureRoomData ( roomId ) ;
1275
+ let room = this . client . getRoom ( roomId ) ;
1276
+
1277
+ if ( ! room ) {
1278
+ throw new Error ( `Cannot find room with id ${ roomId } ` ) ;
1279
+ }
1280
+
1281
+ this . timelineLoadingState . set ( roomId , true ) ;
1282
+ try {
1283
+ while ( room . oldState . paginationToken != null ) {
1284
+ await this . client . scrollback ( room ) ;
1285
+ await timeout ( 1000 ) ;
1286
+ let rs = room . getLiveTimeline ( ) . getState ( 'f' as MatrixSDK . Direction ) ;
1287
+ if ( rs ) {
1288
+ roomData . notifyRoomStateUpdated ( rs ) ;
1289
+ }
1290
+ }
1291
+
1292
+ const timeline = room . getLiveTimeline ( ) ;
1293
+ const events = timeline . getEvents ( ) ;
1294
+ for ( let event of events ) {
1295
+ await this . processDecryptedEvent ( this . buildEventForProcessing ( event ) ) ;
1296
+ }
1297
+ } catch ( error ) {
1298
+ console . error ( 'Error loading timeline events:' , error ) ;
1299
+ throw error ;
1300
+ } finally {
1301
+ this . timelineLoadingState . set ( roomId , false ) ;
1302
+ }
1303
+ }
1304
+
1305
+ get isLoadingTimeline ( ) {
1306
+ if ( ! this . currentRoomId ) {
1307
+ return false ;
1308
+ }
1309
+ return this . timelineLoadingState . get ( this . currentRoomId ) ?? false ;
1139
1310
}
1140
1311
1141
1312
async sendActiveLLMEvent ( roomId : string , model : string ) {
@@ -1152,18 +1323,14 @@ export default class MatrixService extends Service {
1152
1323
`bug: roomId is undefined for event ${ JSON . stringify ( event , null , 2 ) } ` ,
1153
1324
) ;
1154
1325
}
1155
- let roomData = await this . ensureRoomData ( roomId ) ;
1326
+ let roomData = this . ensureRoomData ( roomId ) ;
1156
1327
roomData . addEvent ( event , oldEventId ) ;
1157
1328
}
1158
1329
1159
- private async ensureRoomData ( roomId : string ) {
1330
+ private ensureRoomData ( roomId : string ) {
1160
1331
let roomData = this . getRoomData ( roomId ) ;
1161
1332
if ( ! roomData ) {
1162
1333
roomData = new Room ( roomId ) ;
1163
- let rs = await this . getRoomState ( roomId ) ;
1164
- if ( rs ) {
1165
- roomData . notifyRoomStateUpdated ( rs ) ;
1166
- }
1167
1334
this . setRoomData ( roomId , roomData ) ;
1168
1335
}
1169
1336
return roomData ;
@@ -1282,7 +1449,7 @@ export default class MatrixService extends Service {
1282
1449
}
1283
1450
roomStates = Array . from ( roomStateMap . values ( ) ) ;
1284
1451
for ( let rs of roomStates ) {
1285
- let roomData = await this . ensureRoomData ( rs . roomId ) ;
1452
+ let roomData = this . ensureRoomData ( rs . roomId ) ;
1286
1453
roomData . notifyRoomStateUpdated ( rs ) ;
1287
1454
}
1288
1455
roomStateUpdatesDrained ! ( ) ;
@@ -1482,11 +1649,6 @@ export default class MatrixService extends Service {
1482
1649
) {
1483
1650
this . commandService . executeCommandEventsIfNeeded ( event ) ;
1484
1651
}
1485
-
1486
- if ( room . oldState . paginationToken != null ) {
1487
- // we need to scroll back to capture any room events fired before this one
1488
- await this . client ?. scrollback ( room ) ;
1489
- }
1490
1652
}
1491
1653
1492
1654
async activateCodingSkill ( ) {
0 commit comments