@@ -13,28 +13,30 @@ enum CollabEvents {
1313 UPDATE_REQUEST = "update_request" ,
1414 UPDATE_CURSOR_REQUEST = "update_cursor_request" ,
1515 RECONNECT_REQUEST = "reconnect_request" ,
16+ END_SESSION_REQUEST = "end_session_request" ,
1617
1718 // Send
1819 ROOM_READY = "room_ready" ,
1920 DOCUMENT_READY = "document_ready" ,
21+ DOCUMENT_NOT_FOUND = "document_not_found" ,
2022 UPDATE = "updateV2" ,
2123 UPDATE_CURSOR = "update_cursor" ,
22- PARTNER_LEFT = "partner_left" ,
24+ END_SESSION = "end_session" ,
25+ PARTNER_DISCONNECTED = "partner_disconnected" ,
2326}
2427
2528const EXPIRY_TIME = 3600 ;
26- const CONNECTION_DELAY = 3000 ; // time window to allow for page re-renders / refresh
29+ const CONNECTION_DELAY = 3000 ; // time window to allow for page re-renders
2730
2831const userConnections = new Map < string , NodeJS . Timeout | null > ( ) ;
2932const collabSessions = new Map < string , Doc > ( ) ;
3033const partnerReadiness = new Map < string , boolean > ( ) ;
3134
3235export const handleWebsocketCollabEvents = ( socket : Socket ) => {
33- socket . on ( CollabEvents . JOIN , async ( uid : string , roomId : string ) => {
36+ socket . on ( CollabEvents . JOIN , ( uid : string , roomId : string ) => {
3437 const connectionKey = `${ uid } :${ roomId } ` ;
3538 if ( userConnections . has ( connectionKey ) ) {
3639 clearTimeout ( userConnections . get ( connectionKey ) ! ) ;
37- return ;
3840 }
3941 userConnections . set ( connectionKey , null ) ;
4042
@@ -47,11 +49,10 @@ export const handleWebsocketCollabEvents = (socket: Socket) => {
4749 socket . join ( roomId ) ;
4850 socket . data . roomId = roomId ;
4951
50- if (
51- io . sockets . adapter . rooms . get ( roomId ) ?. size === 2 &&
52- ! collabSessions . has ( roomId )
53- ) {
54- createCollabSession ( roomId ) ;
52+ if ( io . sockets . adapter . rooms . get ( roomId ) ?. size === 2 ) {
53+ if ( ! collabSessions . has ( roomId ) ) {
54+ createCollabSession ( roomId ) ;
55+ }
5556 io . to ( roomId ) . emit ( CollabEvents . ROOM_READY , true ) ;
5657 }
5758 } ) ;
@@ -107,7 +108,8 @@ export const handleWebsocketCollabEvents = (socket: Socket) => {
107108 if ( doc ) {
108109 applyUpdateV2 ( doc , new Uint8Array ( update ) ) ;
109110 } else {
110- // TODO: error handling
111+ io . to ( roomId ) . emit ( CollabEvents . DOCUMENT_NOT_FOUND ) ;
112+ io . sockets . adapter . rooms . delete ( roomId ) ;
111113 }
112114 }
113115 ) ;
@@ -124,41 +126,45 @@ export const handleWebsocketCollabEvents = (socket: Socket) => {
124126
125127 socket . on (
126128 CollabEvents . LEAVE ,
127- ( uid : string , roomId : string , isImmediate : boolean ) => {
129+ ( uid : string , roomId : string , isPartnerNotified : boolean ) => {
128130 const connectionKey = `${ uid } :${ roomId } ` ;
129- if ( isImmediate || ! userConnections . has ( connectionKey ) ) {
131+ if ( userConnections . has ( connectionKey ) ) {
132+ clearTimeout ( userConnections . get ( connectionKey ) ! ) ;
133+ }
134+
135+ if ( isPartnerNotified ) {
130136 handleUserLeave ( uid , roomId , socket ) ;
131137 return ;
132138 }
133139
134- clearTimeout ( userConnections . get ( connectionKey ) ! ) ;
135-
136140 const connectionTimeout = setTimeout ( ( ) => {
137141 handleUserLeave ( uid , roomId , socket ) ;
142+ io . to ( roomId ) . emit ( CollabEvents . PARTNER_DISCONNECTED ) ;
138143 } , CONNECTION_DELAY ) ;
139144
140145 userConnections . set ( connectionKey , connectionTimeout ) ;
141146 }
142147 ) ;
143148
144- socket . on ( CollabEvents . RECONNECT_REQUEST , async ( roomId : string ) => {
145- // TODO: Handle recconnection
146- socket . join ( roomId ) ;
147-
148- const doc = getDocument ( roomId ) ;
149- const storeData = await redisClient . get ( `collaboration: ${ roomId } ` ) ;
149+ socket . on (
150+ CollabEvents . END_SESSION_REQUEST ,
151+ ( roomId : string , sessionDuration : number ) => {
152+ socket . to ( roomId ) . emit ( CollabEvents . END_SESSION , sessionDuration ) ;
153+ }
154+ ) ;
150155
151- if ( storeData ) {
152- const tempDoc = new Doc ( ) ;
153- const update = Buffer . from ( storeData , "base64" ) ;
154- applyUpdateV2 ( tempDoc , new Uint8Array ( update ) ) ;
155- const tempText = tempDoc . getText ( ) . toString ( ) ;
156+ socket . on ( CollabEvents . RECONNECT_REQUEST , ( roomId : string ) => {
157+ const room = io . sockets . adapter . rooms . get ( roomId ) ;
158+ if ( ! room || room . size < 2 ) {
159+ socket . join ( roomId ) ;
160+ socket . data . roomId = roomId ;
161+ }
156162
157- const text = doc . getText ( ) ;
158- doc . transact ( ( ) => {
159- text . delete ( 0 , text . length ) ;
160- text . insert ( 0 , tempText ) ;
161- } ) ;
163+ if (
164+ io . sockets . adapter . rooms . get ( roomId ) ?. size === 2 &&
165+ ! collabSessions . has ( roomId )
166+ ) {
167+ restoreDocument ( roomId ) ;
162168 }
163169 } ) ;
164170} ;
@@ -172,6 +178,7 @@ const removeCollabSession = (roomId: string) => {
172178 collabSessions . get ( roomId ) ?. destroy ( ) ;
173179 collabSessions . delete ( roomId ) ;
174180 partnerReadiness . delete ( roomId ) ;
181+ redisClient . del ( roomId ) ;
175182} ;
176183
177184const getDocument = ( roomId : string ) => {
@@ -188,28 +195,38 @@ const getDocument = (roomId: string) => {
188195 return doc ;
189196} ;
190197
191- const saveDocument = async ( roomId : string , doc : Doc ) => {
198+ const saveDocument = ( roomId : string , doc : Doc ) => {
192199 const docState = encodeStateAsUpdateV2 ( doc ) ;
193200 const docAsString = Buffer . from ( docState ) . toString ( "base64" ) ;
194- await redisClient . set ( `collaboration:${ roomId } ` , docAsString , {
201+ redisClient . set ( `collaboration:${ roomId } ` , docAsString , {
195202 EX : EXPIRY_TIME ,
196203 } ) ;
197204} ;
198205
206+ const restoreDocument = async ( roomId : string ) => {
207+ const doc = getDocument ( roomId ) ;
208+ const storeData = await redisClient . get ( `collaboration:${ roomId } ` ) ;
209+
210+ if ( storeData ) {
211+ const tempDoc = new Doc ( ) ;
212+ const update = Buffer . from ( storeData , "base64" ) ;
213+ applyUpdateV2 ( tempDoc , new Uint8Array ( update ) ) ;
214+ const tempText = tempDoc . getText ( ) . toString ( ) ;
215+
216+ const text = doc . getText ( ) ;
217+ doc . transact ( ( ) => {
218+ text . delete ( 0 , text . length ) ;
219+ text . insert ( 0 , tempText ) ;
220+ } ) ;
221+ }
222+ } ;
223+
199224const handleUserLeave = ( uid : string , roomId : string , socket : Socket ) => {
200225 const connectionKey = `${ uid } :${ roomId } ` ;
201- if ( userConnections . has ( connectionKey ) ) {
202- clearTimeout ( userConnections . get ( connectionKey ) ! ) ;
203- userConnections . delete ( connectionKey ) ;
204- }
226+ userConnections . delete ( connectionKey ) ;
205227
206228 socket . leave ( roomId ) ;
207229 socket . disconnect ( ) ;
208230
209- const room = io . sockets . adapter . rooms . get ( roomId ) ;
210- if ( ! room || room . size === 0 ) {
211- removeCollabSession ( roomId ) ;
212- } else {
213- io . to ( roomId ) . emit ( CollabEvents . PARTNER_LEFT ) ;
214- }
231+ removeCollabSession ( roomId ) ;
215232} ;
0 commit comments