Skip to content

Commit 6a64e19

Browse files
authored
Merge pull request #13 from doppar/techmahedy-websocket
airbender.js code refactored and improve error handling
2 parents d7a1ddd + 45b4852 commit 6a64e19

File tree

1 file changed

+52
-39
lines changed

1 file changed

+52
-39
lines changed
Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Doppar.js - WebSocket Broadcasting Client
2+
* Airbender.js - WebSocket Broadcasting Client
33
*
44
* A powerful, WebSocket client for Doppar framework
55
* Supports public, private, and presence channels with automatic reconnection
@@ -8,7 +8,7 @@
88
* @author Mahedi Hasan
99
*/
1010

11-
class Doppar {
11+
class Airbender {
1212
constructor(options = {}) {
1313
this.options = {
1414
host: options.host || "ws://127.0.0.1:6001",
@@ -40,9 +40,6 @@ class Doppar {
4040
const protocol = this.options.encrypted ? "wss" : "ws";
4141
const host = this.options.host.replace(/^(ws|wss):\/\//, "");
4242
const url = `${protocol}://${host}`;
43-
44-
console.log(`[Doppar] Connecting to ${url}...`);
45-
4643
this.socket = new WebSocket(url);
4744
this.setupEventHandlers();
4845
}
@@ -52,7 +49,6 @@ class Doppar {
5249
*/
5350
setupEventHandlers() {
5451
this.socket.onopen = () => {
55-
console.log("[Doppar] Connection established");
5652
this.isConnected = true;
5753
this.reconnectCount = 0;
5854

@@ -64,7 +60,16 @@ class Doppar {
6460
};
6561

6662
this.socket.onmessage = (event) => {
67-
this.handleMessage(JSON.parse(event.data));
63+
let parsed;
64+
65+
try {
66+
parsed = JSON.parse(event.data);
67+
} catch (e) {
68+
console.error("[Doppar] Invalid JSON from server:", event.data);
69+
return;
70+
}
71+
72+
this.handleMessage(parsed);
6873
};
6974

7075
this.socket.onerror = (error) => {
@@ -73,7 +78,6 @@ class Doppar {
7378
};
7479

7580
this.socket.onclose = () => {
76-
console.log("[Doppar] Connection closed");
7781
this.isConnected = false;
7882
this.socketId = null;
7983
this.trigger("disconnected");
@@ -100,7 +104,7 @@ class Doppar {
100104
break;
101105

102106
case "doppar:subscription_succeeded":
103-
this.handleSubscriptionSucceeded(channel);
107+
this.handleSubscriptionSucceeded(channel, JSON.parse(data));
104108
break;
105109

106110
case "doppar:member_added":
@@ -142,8 +146,6 @@ class Doppar {
142146
this.socketId = data.socket_id;
143147
this.isReady = true;
144148

145-
console.log(`[Doppar] Socket ID: ${this.socketId}`);
146-
147149
// Trigger new "ready" event
148150
this.trigger("ready", this.socketId);
149151

@@ -160,12 +162,11 @@ class Doppar {
160162
/**
161163
* Handle subscription succeeded
162164
*/
163-
handleSubscriptionSucceeded(channelName) {
165+
handleSubscriptionSucceeded(channelName, payload) {
164166
const channel = this.channels.get(channelName);
165167
if (channel) {
166168
channel.subscribed = true;
167-
channel.trigger("subscribed");
168-
console.log(`[Doppar] Subscribed to ${channelName}`);
169+
channel.trigger("subscribed", payload);
169170
}
170171
}
171172

@@ -188,7 +189,10 @@ class Doppar {
188189
handleMemberAdded(channelName, data) {
189190
const channel = this.channels.get(channelName);
190191
if (channel && channel.type === "presence") {
191-
channel.members.push(data);
192+
if (!channel.members.find((m) => m.user_id === data.user_id)) {
193+
channel.members.push(data);
194+
}
195+
192196
channel.trigger("member-added", data);
193197
}
194198
}
@@ -214,7 +218,7 @@ class Doppar {
214218
return this.channels.get(channelName);
215219
}
216220

217-
const channel = new DopparChannel(this, channelName, "public");
221+
const channel = new AirbenderChannel(this, channelName, "public");
218222
this.channels.set(channelName, channel);
219223
channel.subscribe();
220224

@@ -233,7 +237,7 @@ class Doppar {
233237
return this.channels.get(fullName);
234238
}
235239

236-
const channel = new DopparPrivateChannel(this, fullName);
240+
const channel = new AirbenderPrivateChannel(this, fullName);
237241
this.channels.set(fullName, channel);
238242
channel.subscribe();
239243

@@ -252,7 +256,7 @@ class Doppar {
252256
return this.channels.get(fullName);
253257
}
254258

255-
const channel = new DopparPresenceChannel(this, fullName);
259+
const channel = new AirbenderPresenceChannel(this, fullName);
256260
this.channels.set(fullName, channel);
257261
channel.subscribe();
258262

@@ -274,8 +278,12 @@ class Doppar {
274278
* Send message to server
275279
*/
276280
send(message) {
277-
if (this.isConnected && this.socket.readyState === WebSocket.OPEN) {
278-
this.socket.send(JSON.stringify(message));
281+
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
282+
try {
283+
this.socket.send(JSON.stringify(message));
284+
} catch (e) {
285+
this.messageQueue.push(message);
286+
}
279287
} else {
280288
this.messageQueue.push(message);
281289
}
@@ -286,19 +294,26 @@ class Doppar {
286294
*/
287295
processMessageQueue() {
288296
while (this.messageQueue.length > 0) {
289-
const message = this.messageQueue.shift();
290-
this.send(message);
297+
const queue = [...this.messageQueue];
298+
this.messageQueue = [];
299+
queue.forEach((msg) => this.send(msg));
291300
}
292301
}
293302

303+
cleanup() {
304+
if (!this.socket) return;
305+
this.socket.onopen = null;
306+
this.socket.onmessage = null;
307+
this.socket.onerror = null;
308+
this.socket.onclose = null;
309+
}
310+
294311
/**
295312
* Reconnect to server
296313
*/
297314
reconnect() {
315+
this.cleanup();
298316
this.reconnectCount++;
299-
console.log(
300-
`[Doppar] Reconnecting (${this.reconnectCount}/${this.options.reconnectAttempts})...`
301-
);
302317

303318
setTimeout(() => {
304319
this.connect();
@@ -345,7 +360,7 @@ class Doppar {
345360
/**
346361
* Base Channel Class
347362
*/
348-
class DopparChannel {
363+
class AirbenderChannel {
349364
constructor(doppar, name, type = "public") {
350365
this.doppar = doppar;
351366
this.name = name;
@@ -370,10 +385,8 @@ class DopparChannel {
370385
* Resubscribe to channel (after reconnection)
371386
*/
372387
resubscribe() {
373-
if (this.subscribed) {
374-
this.subscribed = false;
375-
this.subscribe();
376-
}
388+
this.subscribed = false;
389+
this.subscribe();
377390
}
378391

379392
/**
@@ -426,7 +439,7 @@ class DopparChannel {
426439
/**
427440
* Private Channel Class
428441
*/
429-
class DopparPrivateChannel extends DopparChannel {
442+
class AirbenderPrivateChannel extends AirbenderChannel {
430443
constructor(doppar, name) {
431444
super(doppar, name, "private");
432445
}
@@ -495,7 +508,7 @@ class DopparPrivateChannel extends DopparChannel {
495508
/**
496509
* Presence Channel Class
497510
*/
498-
class DopparPresenceChannel extends DopparPrivateChannel {
511+
class AirbenderPresenceChannel extends AirbenderPrivateChannel {
499512
constructor(doppar, name) {
500513
super(doppar, name);
501514
this.type = "presence";
@@ -520,11 +533,11 @@ class DopparPresenceChannel extends DopparPrivateChannel {
520533
* Get here (current members)
521534
*/
522535
here(callback) {
523-
return this.listen("subscribed", (data) => {
524-
if (data && data.presence) {
525-
callback(this.members);
526-
}
527-
});
536+
if (this.subscribed && this.members.length > 0) {
537+
callback(this.members);
538+
}
539+
540+
return super.listen("subscribed", () => callback(this.members));
528541
}
529542

530543
/**
@@ -544,8 +557,8 @@ class DopparPresenceChannel extends DopparPrivateChannel {
544557

545558
// Export for use in different environments
546559
if (typeof module !== "undefined" && module.exports) {
547-
module.exports = Doppar;
560+
module.exports = Airbender;
548561
}
549562
if (typeof window !== "undefined") {
550-
window.Doppar = Doppar;
563+
window.Airbender = Airbender;
551564
}

0 commit comments

Comments
 (0)