@@ -4,9 +4,13 @@ import {
4
4
ParticipantStream ,
5
5
ParticipantSubscription ,
6
6
ServerMessage ,
7
- VideoSubscription ,
8
7
} from "./sfu.ts" ;
9
- import { atom , type PreinitializedWritableAtom } from "nanostores" ;
8
+ import {
9
+ atom ,
10
+ map ,
11
+ type PreinitializedMapStore ,
12
+ type PreinitializedWritableAtom ,
13
+ } from "nanostores" ;
10
14
11
15
const MAX_DOWNSTREAMS = 16 ;
12
16
const LAST_N_AUDIO = 3 ;
@@ -44,12 +48,12 @@ export class ClientCore {
44
48
45
49
#videoSlots: VideoSlot [ ] ;
46
50
#audioSlots: RTCRtpTransceiver [ ] ;
47
-
48
- #participants: Record < ParticipantId , ParticipantMeta > ;
49
51
#timeoutId: ReturnType < typeof setTimeout > | null ;
50
52
51
- onStateChanged = ( state : RTCPeerConnectionState ) => { } ;
52
- onNewParticipant = ( participant : ParticipantMeta ) => { } ;
53
+ $participants : PreinitializedMapStore <
54
+ Record < ParticipantId , PreinitializedMapStore < ParticipantMeta > >
55
+ > ;
56
+ $state : PreinitializedWritableAtom < RTCPeerConnectionState > ;
53
57
54
58
constructor ( cfg : ClientCoreConfig ) {
55
59
this . #sfuUrl = cfg . sfuUrl ;
@@ -60,17 +64,17 @@ export class ClientCore {
60
64
this . #closed = false ;
61
65
this . #videoSlots = [ ] ;
62
66
this . #audioSlots = [ ] ;
63
- this . #participants = { } ;
64
67
this . #sequence = 0 ;
65
68
this . #timeoutId = null ;
69
+ this . $participants = map ( { } ) ;
70
+ this . $state = atom ( "new" ) ;
66
71
67
72
this . #pc = new RTCPeerConnection ( ) ;
68
73
this . #pc. onconnectionstatechange = ( ) => {
69
74
const connectionState = this . #pc. connectionState ;
70
75
console . debug ( `PeerConnection state changed: ${ connectionState } ` ) ;
71
- if ( connectionState === "connected" ) {
72
- this . onStateChanged ( connectionState ) ;
73
- } else if (
76
+ this . $state . set ( connectionState ) ;
77
+ if (
74
78
connectionState === "failed" || connectionState === "closed" ||
75
79
connectionState === "disconnected"
76
80
) {
@@ -154,15 +158,18 @@ export class ClientCore {
154
158
for ( const stream of streams ) {
155
159
if ( ! stream . media ) {
156
160
// participant has left
157
- delete this . # participants[ stream . participantId ] ;
161
+ this . $ participants. setKey ( stream . participantId , undefined ) ;
158
162
continue ;
159
163
}
160
164
161
- if ( stream . participantId in this . #participants) {
162
- const participant = this . #participants[ stream . participantId ] ;
163
- participant . media = stream . media ;
164
- participant . externalParticipantId = stream . externalParticipantId ;
165
- participant . participantId = stream . participantId ;
165
+ if ( stream . participantId in this . $participants . get ( ) ) {
166
+ const participant = this . $participants . get ( ) [ stream . participantId ] ;
167
+ participant . setKey ( "media" , stream . media ) ;
168
+ participant . setKey (
169
+ "externalParticipantId" ,
170
+ stream . externalParticipantId ,
171
+ ) ;
172
+ participant . setKey ( "participantId" , stream . participantId ) ;
166
173
} else {
167
174
const meta : ParticipantMeta = {
168
175
externalParticipantId : stream . externalParticipantId ,
@@ -171,15 +178,20 @@ export class ClientCore {
171
178
stream : new MediaStream ( ) ,
172
179
maxHeight : 0 , // default invisible until the UI tells us to render
173
180
} ;
174
- this . #participants[ stream . participantId ] = meta ;
181
+
182
+ const reactiveMeta = atom ( meta ) ;
183
+ reactiveMeta . listen ( ( _ ) => {
184
+ this . #triggerSubscriptionFeedback( ) ;
185
+ } ) ;
186
+ this . $participants . setKey ( stream . participantId , atom ( meta ) ) ;
175
187
newParticipants . push ( meta ) ;
176
188
}
177
189
}
178
190
179
191
// TODO: should we bin pack the old participants first?
180
192
for ( const slot of this . #videoSlots) {
181
193
if ( slot . participantId ) {
182
- if ( slot . participantId in this . # participants) {
194
+ if ( slot . participantId in this . $ participants) {
183
195
continue ;
184
196
}
185
197
@@ -207,22 +219,14 @@ export class ClientCore {
207
219
this . #closed = true ;
208
220
}
209
221
210
- updateSubscription ( participantId : string , maxHeight : number ) {
211
- if ( participantId in this . #participants) {
212
- this . #participants[ participantId ] . maxHeight = maxHeight ;
213
- }
214
-
215
- this . #triggerSubscriptionFeedback( ) ;
216
- }
217
-
218
222
#triggerSubscriptionFeedback( ) {
219
223
if ( this . #timeoutId) {
220
224
return ;
221
225
}
222
226
223
227
this . #timeoutId = setTimeout ( ( ) => {
224
228
const subscriptions : ParticipantSubscription [ ] = Object . values (
225
- this . # participants,
229
+ this . $ participants,
226
230
) . map ( ( p ) => ( {
227
231
participantId : p . participantId ,
228
232
videoSettings : {
0 commit comments