@@ -4,6 +4,10 @@ import type { z } from 'zod';
4
4
5
5
export default class RealtimeClient {
6
6
private client : Pusher | null = null ;
7
+ #preConnectEventHandlers = new Map < keyof EventDataMap , Function [ ] > ( ) ;
8
+ #preConnectBroadcastHandlers = new Map < keyof EventDataMap , Function [ ] > ( ) ;
9
+ #connectionTimeout: NodeJS . Timeout | null = null ;
10
+
7
11
constructor (
8
12
private config : {
9
13
appKey : string ;
@@ -12,6 +16,7 @@ export default class RealtimeClient {
12
16
authEndpoint : string ;
13
17
}
14
18
) { }
19
+
15
20
public async connect ( { orgShortCode } : { orgShortCode : string } ) {
16
21
if ( this . client ) return ;
17
22
const client = new Pusher ( this . config . appKey , {
@@ -44,13 +49,22 @@ export default class RealtimeClient {
44
49
45
50
client . signin ( ) ;
46
51
this . client = client ;
52
+ this . bindEvents ( ) ;
47
53
return new Promise < void > ( ( resolve , reject ) => {
54
+ this . #connectionTimeout = setTimeout ( ( ) => {
55
+ this . client = null ;
56
+ reject ( new Error ( 'Connection timeout' ) ) ;
57
+ } , 10000 ) ;
58
+
48
59
client . bind ( 'pusher:signin_success' , ( ) => {
49
60
client . unbind ( 'pusher:signin_success' ) ;
61
+ if ( this . #connectionTimeout) clearTimeout ( this . #connectionTimeout) ;
50
62
resolve ( ) ;
51
63
} ) ;
64
+
52
65
client . bind ( 'pusher:error' , ( e : unknown ) => {
53
66
this . client = null ;
67
+ if ( this . #connectionTimeout) clearTimeout ( this . #connectionTimeout) ;
54
68
reject ( e ) ;
55
69
} ) ;
56
70
} ) ;
@@ -59,6 +73,7 @@ export default class RealtimeClient {
59
73
public disconnect ( ) {
60
74
if ( this . client ) {
61
75
this . client . disconnect ( ) ;
76
+ if ( this . #connectionTimeout) clearTimeout ( this . #connectionTimeout) ;
62
77
this . client = null ;
63
78
}
64
79
}
@@ -67,24 +82,64 @@ export default class RealtimeClient {
67
82
event : T ,
68
83
callback : ( data : z . infer < EventDataMap [ T ] > ) => Promise < void >
69
84
) {
70
- if ( ! this . client ) return ;
71
- this . client . bind ( event , ( e : unknown ) =>
72
- callback ( eventDataMaps [ event ] . parse ( e ) )
73
- ) ;
85
+ if ( ! this . client ) {
86
+ const existing = this . #preConnectEventHandlers. get ( event ) ?? [ ] ;
87
+ existing . push ( callback ) ;
88
+ this . #preConnectEventHandlers. set ( event , existing ) ;
89
+ } else {
90
+ this . client . bind ( event , ( e : unknown ) =>
91
+ callback ( eventDataMaps [ event ] . parse ( e ) )
92
+ ) ;
93
+ }
74
94
}
75
95
76
96
public off < const T extends keyof EventDataMap > ( event : T ) {
77
- if ( ! this . client ) return ;
78
- this . client . unbind ( event ) ;
97
+ if ( ! this . client ) {
98
+ // eslint-disable-next-line drizzle/enforce-delete-with-where
99
+ this . #preConnectEventHandlers. delete ( event ) ;
100
+ } else {
101
+ this . client . unbind ( event ) ;
102
+ }
79
103
}
80
104
81
105
public onBroadcast < const T extends keyof EventDataMap > (
82
106
event : T ,
83
107
callback : ( data : z . infer < EventDataMap [ T ] > ) => Promise < void >
84
108
) {
109
+ if ( ! this . client ) {
110
+ const existing = this . #preConnectBroadcastHandlers. get ( event ) ?? [ ] ;
111
+ existing . push ( callback ) ;
112
+ this . #preConnectBroadcastHandlers. set ( event , existing ) ;
113
+ } else {
114
+ this . client
115
+ . subscribe ( 'broadcasts' )
116
+ . bind ( event , ( e : unknown ) => callback ( eventDataMaps [ event ] . parse ( e ) ) ) ;
117
+ }
118
+ }
119
+
120
+ public get isConnected ( ) {
121
+ return ! ! this . client ;
122
+ }
123
+
124
+ private bindEvents ( ) {
85
125
if ( ! this . client ) return ;
86
- this . client
87
- . subscribe ( 'broadcasts' )
88
- . bind ( event , ( e : unknown ) => callback ( eventDataMaps [ event ] . parse ( e ) ) ) ;
126
+ for ( const [ event , handlers ] of this . #preConnectEventHandlers) {
127
+ handlers . forEach ( ( handler ) => {
128
+ if ( ! this . client ) return ;
129
+ this . client . bind ( event , ( e : unknown ) =>
130
+ handler ( eventDataMaps [ event ] . parse ( e ) )
131
+ ) ;
132
+ } ) ;
133
+ }
134
+ for ( const [ event , handlers ] of this . #preConnectBroadcastHandlers) {
135
+ handlers . forEach ( ( handler ) => {
136
+ if ( ! this . client ) return ;
137
+ this . client
138
+ . subscribe ( 'broadcasts' )
139
+ . bind ( event , ( e : unknown ) => handler ( eventDataMaps [ event ] . parse ( e ) ) ) ;
140
+ } ) ;
141
+ }
142
+ this . #preConnectEventHandlers. clear ( ) ;
143
+ this . #preConnectBroadcastHandlers. clear ( ) ;
89
144
}
90
145
}
0 commit comments