@@ -7,11 +7,12 @@ import { assert } from "@fluidframework/core-utils/internal";
7
7
8
8
import type { ClientConnectionId } from "./baseTypes.js" ;
9
9
import type { InternalTypes } from "./exposedInternalTypes.js" ;
10
- import type {
11
- ClientSessionId ,
12
- IPresence ,
13
- ISessionClient ,
14
- PresenceEvents ,
10
+ import {
11
+ SessionClientStatus ,
12
+ type ClientSessionId ,
13
+ type IPresence ,
14
+ type ISessionClient ,
15
+ type PresenceEvents ,
15
16
} from "./presence.js" ;
16
17
import type { PresenceStatesInternal } from "./presenceStates.js" ;
17
18
import type { PresenceStates , PresenceStatesSchema } from "./types.js" ;
@@ -32,7 +33,7 @@ export interface SystemWorkspaceDatastore {
32
33
/**
33
34
* There is no implementation class for this interface.
34
35
* It is a simple structure. Most complicated aspect is that
35
- * `currentConnectionId ()` member is replaced with a new
36
+ * `connectionId ()` member is replaced with a new
36
37
* function when a more recent connection is added.
37
38
*
38
39
* See {@link SystemWorkspaceImpl.ensureAttendee}.
@@ -58,6 +59,13 @@ export interface SystemWorkspace
58
59
* @param clientConnectionId - The new client connection id.
59
60
*/
60
61
onConnectionAdded ( clientConnectionId : ClientConnectionId ) : void ;
62
+
63
+ /**
64
+ * Removes the client connection id from the system workspace.
65
+ *
66
+ * @param clientConnectionId - The client connection id to remove.
67
+ */
68
+ removeClientConnectionId ( clientConnectionId : ClientConnectionId ) : void ;
61
69
}
62
70
63
71
class SystemWorkspaceImpl implements PresenceStatesInternal , SystemWorkspace {
@@ -74,14 +82,17 @@ class SystemWorkspaceImpl implements PresenceStatesInternal, SystemWorkspace {
74
82
public constructor (
75
83
clientSessionId : ClientSessionId ,
76
84
private readonly datastore : SystemWorkspaceDatastore ,
77
- public readonly events : IEmitter < Pick < PresenceEvents , "attendeeJoined" > > ,
85
+ public readonly events : IEmitter <
86
+ Pick < PresenceEvents , "attendeeJoined" | "attendeeDisconnected" >
87
+ > ,
78
88
) {
79
89
this . selfAttendee = {
80
90
sessionId : clientSessionId ,
81
91
order : 0 ,
82
- currentConnectionId : ( ) => {
92
+ connectionId : ( ) => {
83
93
throw new Error ( "Client has never been connected" ) ;
84
94
} ,
95
+ getStatus : ( ) => SessionClientStatus . Disconnected ,
85
96
} ;
86
97
this . attendees . set ( clientSessionId , this . selfAttendee ) ;
87
98
}
@@ -139,10 +150,26 @@ class SystemWorkspaceImpl implements PresenceStatesInternal, SystemWorkspace {
139
150
value : this . selfAttendee . sessionId ,
140
151
} ;
141
152
142
- this . selfAttendee . currentConnectionId = ( ) => clientConnectionId ;
153
+ this . selfAttendee . connectionId = ( ) => clientConnectionId ;
154
+ this . selfAttendee . getStatus = ( ) => SessionClientStatus . Connected ;
143
155
this . attendees . set ( clientConnectionId , this . selfAttendee ) ;
144
156
}
145
157
158
+ public removeClientConnectionId ( clientConnectionId : ClientConnectionId ) : void {
159
+ const attendee = this . attendees . get ( clientConnectionId ) ;
160
+ if ( ! attendee ) {
161
+ return ;
162
+ }
163
+
164
+ // If the last known connectionID is different from the connection id being removed, the attendee has reconnected,
165
+ // therefore we should not change the attendee connection status or emit a disconnect event.
166
+ const attendeeReconnected = attendee . connectionId ( ) !== clientConnectionId ;
167
+ if ( ! attendeeReconnected ) {
168
+ attendee . getStatus = ( ) => SessionClientStatus . Disconnected ;
169
+ this . events . emit ( "attendeeDisconnected" , attendee ) ;
170
+ }
171
+ }
172
+
146
173
public getAttendees ( ) : ReadonlySet < ISessionClient > {
147
174
return new Set ( this . attendees . values ( ) ) ;
148
175
}
@@ -174,7 +201,7 @@ class SystemWorkspaceImpl implements PresenceStatesInternal, SystemWorkspace {
174
201
clientConnectionId : ClientConnectionId ,
175
202
order : number ,
176
203
) : { attendee : SessionClient ; isNew : boolean } {
177
- const currentConnectionId = ( ) : ClientConnectionId => clientConnectionId ;
204
+ const connectionId = ( ) : ClientConnectionId => clientConnectionId ;
178
205
let attendee = this . attendees . get ( clientSessionId ) ;
179
206
let isNew = false ;
180
207
if ( attendee === undefined ) {
@@ -183,15 +210,16 @@ class SystemWorkspaceImpl implements PresenceStatesInternal, SystemWorkspace {
183
210
attendee = {
184
211
sessionId : clientSessionId ,
185
212
order,
186
- currentConnectionId,
213
+ connectionId,
214
+ getStatus : ( ) => SessionClientStatus . Connected ,
187
215
} ;
188
216
this . attendees . set ( clientSessionId , attendee ) ;
189
217
isNew = true ;
190
218
} else if ( order > attendee . order ) {
191
219
// The given association is newer than the one we have.
192
220
// Update the order and current connection id.
193
221
attendee . order = order ;
194
- attendee . currentConnectionId = currentConnectionId ;
222
+ attendee . connectionId = connectionId ;
195
223
}
196
224
// Always update entry for the connection id. (Okay if already set.)
197
225
this . attendees . set ( clientConnectionId , attendee ) ;
0 commit comments