11using System ;
2- using System . Collections ;
32using System . Collections . Generic ;
43using System . Diagnostics ;
54using System . Linq ;
@@ -21,13 +20,15 @@ public class ParseLiveQueryController : IParseLiveQueryController
2120
2221 private int LastRequestId { get ; set ; } = 0 ;
2322
23+ private string ClientId { get ; set ; }
24+
2425 /// <summary>
2526 /// Gets or sets the timeout duration, in milliseconds, used by the ParseLiveQueryController
2627 /// for various operations, such as establishing a connection or completing a subscription.
2728 /// </summary>
2829 /// <remarks>
2930 /// This property determines the maximum amount of time the controller will wait for an operation
30- /// to complete before throwing a <see cref="TimeoutException"/>. It is used in operations such as:
31+ /// to complete before throwing a <see cref="TimeoutException"/>. It is used in operations such as
3132 /// - Connecting to the LiveQuery server.
3233 /// - Subscribing to a query.
3334 /// - Unsubscribing from a query.
@@ -36,17 +37,23 @@ public class ParseLiveQueryController : IParseLiveQueryController
3637 public int TimeOut { get ; set ; } = 5000 ;
3738
3839 /// <summary>
39- /// Event triggered when an error occurs during Parse Live Query operations .
40+ /// Event triggered when an error occurs during the operation of the ParseLiveQueryController .
4041 /// </summary>
4142 /// <remarks>
42- /// This event provides detailed information about the encountered error through the event arguments,
43- /// which consist of a dictionary containing key-value pairs describing the error context and specifics.
44- /// It can be used to log, handle, or analyze the errors that arise during subscription, connection,
45- /// or message processing operations. Common scenarios triggering this event include protocol issues,
46- /// connectivity problems, or invalid message formats.
43+ /// This event provides details about a live query operation failure, such as specific error messages,
44+ /// error codes, and whether automatic reconnection is recommended.
45+ /// It is raised in scenarios like:
46+ /// - Receiving an error response from the LiveQuery server.
47+ /// - Issues with subscriptions, unsubscriptions, or query updates.
48+ /// Subscribers to this event can use the provided <see cref="ParseLiveQueryErrorEventArgs"/> to
49+ /// understand the error and implement appropriate handling mechanisms.
4750 /// </remarks>
48- public event EventHandler < IDictionary < string , object > > Error ;
51+ public event EventHandler < ParseLiveQueryErrorEventArgs > Error ;
4952
53+ /// <summary>
54+ /// Represents the state of a connection to the Parse LiveQuery server, indicating whether the connection is closed,
55+ /// in the process of connecting, or fully established.
56+ /// </summary>
5057 public enum ParseLiveQueryState
5158 {
5259 /// <summary>
@@ -71,7 +78,8 @@ public enum ParseLiveQueryState
7178 /// such as when a connection is established or closed, or when an error occurs.
7279 /// </remarks>
7380 public ParseLiveQueryState State { get ; private set ; }
74- ArrayList SubscriptionIds { get ; }
81+
82+ HashSet < int > SubscriptionIds { get ; } = new HashSet < int > { } ;
7583
7684 CancellationTokenSource ConnectionSignal { get ; set ; }
7785 private IDictionary < int , CancellationTokenSource > SubscriptionSignals { get ; } = new Dictionary < int , CancellationTokenSource > { } ;
@@ -83,89 +91,143 @@ public enum ParseLiveQueryState
8391 public ParseLiveQueryController ( IWebSocketClient webSocketClient )
8492 {
8593 WebSocketClient = webSocketClient ;
86- SubscriptionIds = new ArrayList ( ) ;
8794 State = ParseLiveQueryState . Closed ;
8895 }
8996
9097 private void ProcessMessage ( IDictionary < string , object > message )
9198 {
9299 int requestId ;
100+ string clientId ;
101+ ParseLiveQuerySubscription subscription ;
93102 switch ( message [ "op" ] )
94103 {
95104 case "connected" :
96105 State = ParseLiveQueryState . Connected ;
106+ ClientId = message [ "clientId" ] as string ;
97107 ConnectionSignal ? . Cancel ( ) ;
98- // Connected?.Invoke(this, EventArgs.Empty);
99108 break ;
100109
101- case "subscribed" :
102- requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
103- SubscriptionIds . Add ( requestId ) ;
104- if ( SubscriptionSignals . TryGetValue ( requestId , out CancellationTokenSource subscriptionSignal ) )
110+ case "subscribed" : // Response from subscription and subscription update
111+ clientId = message [ "clientId" ] as string ;
112+ if ( clientId == ClientId )
105113 {
106- subscriptionSignal ? . Cancel ( ) ;
114+ requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
115+ SubscriptionIds . Add ( requestId ) ;
116+ if ( SubscriptionSignals . TryGetValue ( requestId , out CancellationTokenSource subscriptionSignal ) )
117+ {
118+ subscriptionSignal ? . Cancel ( ) ;
119+ }
107120 }
108- // Subscribed?.Invoke(this, requestId);
109121 break ;
110122
111- // TODO subscription update case
112-
113123 case "unsubscribed" :
114- requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
115- SubscriptionIds . Remove ( requestId ) ;
116- if ( UnsubscriptionSignals . TryGetValue ( requestId , out CancellationTokenSource unsubscriptionSignal ) )
124+ clientId = message [ "clientId" ] as string ;
125+ if ( clientId == ClientId )
117126 {
118- unsubscriptionSignal ? . Cancel ( ) ;
127+ requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
128+ SubscriptionIds . Remove ( requestId ) ;
129+ if ( UnsubscriptionSignals . TryGetValue ( requestId , out CancellationTokenSource unsubscriptionSignal ) )
130+ {
131+ unsubscriptionSignal ? . Cancel ( ) ;
132+ }
119133 }
120- // Unsubscribed?.Invoke(this, requestId);
121134 break ;
122135
123136 case "error" :
124137 if ( ( bool ) message [ "reconnect" ] )
125138 {
126- OpenAsync ( ) ;
139+ ConnectAsync ( ) ;
127140 }
128- string errorMessage = message [ "error" ] as string ;
129- Error ? . Invoke ( this , message ) ;
141+
142+ ParseLiveQueryErrorEventArgs errorArgs = new ParseLiveQueryErrorEventArgs
143+ {
144+ Error = message [ "error" ] as string ,
145+ Code = Convert . ToInt32 ( message [ "code" ] ) ,
146+ Reconnected = ( bool ) message [ "reconnect" ]
147+ } ;
148+ Error ? . Invoke ( this , errorArgs ) ;
130149 break ;
131150
132151 case "create" :
133- requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
134- if ( Subscriptions . TryGetValue ( requestId , out ParseLiveQuerySubscription subscription ) )
152+ clientId = message [ "clientId" ] as string ;
153+ if ( clientId == ClientId )
135154 {
136- subscription . OnCreate ( message ) ;
155+ requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
156+ if ( Subscriptions . TryGetValue ( requestId , out subscription ) )
157+ {
158+ ParseLiveQueryEventArgs args = new ParseLiveQueryEventArgs ( )
159+ {
160+ Object = message [ "object" ]
161+ } ;
162+ subscription . OnCreate ( args ) ;
163+ }
137164 }
138165 break ;
139166
140167 case "enter" :
141- requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
142- if ( Subscriptions . TryGetValue ( requestId , out subscription ) )
168+ clientId = message [ "clientId" ] as string ;
169+ if ( clientId == ClientId )
143170 {
144- subscription . OnEnter ( message ) ;
171+ requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
172+ if ( Subscriptions . TryGetValue ( requestId , out subscription ) )
173+ {
174+ ParseLiveQueryEventArgs args = new ParseLiveQueryEventArgs ( )
175+ {
176+ Object = message [ "object" ] ,
177+ Original = message [ "original" ]
178+ } ;
179+ subscription . OnEnter ( args ) ;
180+ }
145181 }
146182 break ;
147183
148184 case "update" :
149- requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
150- if ( Subscriptions . TryGetValue ( requestId , out subscription ) )
185+ clientId = message [ "clientId" ] as string ;
186+ if ( clientId == ClientId )
151187 {
152- subscription . OnUpdate ( message ) ;
188+ requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
189+ if ( Subscriptions . TryGetValue ( requestId , out subscription ) )
190+ {
191+ ParseLiveQueryEventArgs args = new ParseLiveQueryEventArgs ( )
192+ {
193+ Object = message [ "object" ] ,
194+ Original = message [ "original" ]
195+ } ;
196+ subscription . OnUpdate ( args ) ;
197+ }
153198 }
154199 break ;
155200
156201 case "leave" :
157- requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
158- if ( Subscriptions . TryGetValue ( requestId , out subscription ) )
202+ clientId = message [ "clientId" ] as string ;
203+ if ( clientId == ClientId )
159204 {
160- subscription . OnLeave ( message ) ;
205+ requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
206+ if ( Subscriptions . TryGetValue ( requestId , out subscription ) )
207+ {
208+ ParseLiveQueryEventArgs args = new ParseLiveQueryEventArgs ( )
209+ {
210+ Object = message [ "object" ] ,
211+ Original = message [ "original" ]
212+ } ;
213+ subscription . OnLeave ( args ) ;
214+ }
161215 }
162216 break ;
163217
164218 case "delete" :
165- requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
166- if ( Subscriptions . TryGetValue ( requestId , out subscription ) )
219+ clientId = message [ "clientId" ] as string ;
220+ if ( clientId == ClientId )
167221 {
168- subscription . OnDelete ( message ) ;
222+ requestId = Convert . ToInt32 ( message [ "requestId" ] ) ;
223+ if ( Subscriptions . TryGetValue ( requestId , out subscription ) )
224+ {
225+ ParseLiveQueryEventArgs args = new ParseLiveQueryEventArgs ( )
226+ {
227+ Object = message [ "object" ] ,
228+ } ;
229+ subscription . OnDelete ( args ) ;
230+ }
169231 }
170232 break ;
171233
@@ -369,7 +431,7 @@ public async Task UnsubscribeAsync(int requestId, CancellationToken cancellation
369431 }
370432
371433 /// <summary>
372- /// Closes the live query connection, resets the state to closed , and clears all active subscriptions and signals.
434+ /// Closes the live query connection, resets the state to close , and clears all active subscriptions and signals.
373435 /// </summary>
374436 /// <param name="cancellationToken">
375437 /// A token to monitor for cancellation requests while closing the connection.
0 commit comments