laravel-echo
Version:
Laravel Echo library for beautiful Pusher and Socket.IO integration
165 lines (135 loc) • 4.05 kB
text/typescript
import { EventFormatter } from "../util";
import { Channel } from "./channel";
import type { Socket } from "socket.io-client";
import type { EchoOptionsWithDefaults } from "../connector";
import type { BroadcastDriver } from "../echo";
/**
* This class represents a Socket.io channel.
*/
export class SocketIoChannel extends Channel {
/**
* The Socket.io client instance.
*/
socket: Socket;
/**
* The name of the channel.
*/
name: string;
/**
* The event formatter.
*/
eventFormatter: EventFormatter;
/**
* The event callbacks applied to the socket.
*/
events: Record<string, any> = {};
/**
* User supplied callbacks for events on this channel.
*/
private listeners: Record<string, CallableFunction[]> = {};
/**
* Create a new class instance.
*/
constructor(
socket: Socket,
name: string,
options: EchoOptionsWithDefaults<BroadcastDriver>,
) {
super();
this.name = name;
this.socket = socket;
this.options = options;
this.eventFormatter = new EventFormatter(this.options.namespace);
this.subscribe();
}
/**
* Subscribe to a Socket.io channel.
*/
subscribe(): void {
this.socket.emit("subscribe", {
channel: this.name,
auth: this.options.auth || {},
});
}
/**
* Unsubscribe from channel and ubind event callbacks.
*/
unsubscribe(): void {
this.unbind();
this.socket.emit("unsubscribe", {
channel: this.name,
auth: this.options.auth || {},
});
}
/**
* Listen for an event on the channel instance.
*/
listen(event: string, callback: CallableFunction): this {
this.on(this.eventFormatter.format(event), callback);
return this;
}
/**
* Stop listening for an event on the channel instance.
*/
stopListening(event: string, callback?: CallableFunction): this {
this.unbindEvent(this.eventFormatter.format(event), callback);
return this;
}
/**
* Register a callback to be called anytime a subscription succeeds.
*/
subscribed(callback: CallableFunction): this {
this.on("connect", (socket: Socket) => {
callback(socket);
});
return this;
}
/**
* Register a callback to be called anytime an error occurs.
*/
error(_callback: CallableFunction): this {
return this;
}
/**
* Bind the channel's socket to an event and store the callback.
*/
on(event: string, callback: CallableFunction): this {
this.listeners[event] = this.listeners[event] || [];
if (!this.events[event]) {
this.events[event] = (channel: string, data: unknown) => {
if (this.name === channel && this.listeners[event]) {
this.listeners[event].forEach((cb) => cb(data));
}
};
this.socket.on(event, this.events[event]);
}
this.listeners[event].push(callback);
return this;
}
/**
* Unbind the channel's socket from all stored event callbacks.
*/
unbind(): void {
Object.keys(this.events).forEach((event) => {
this.unbindEvent(event);
});
}
/**
* Unbind the listeners for the given event.
*/
protected unbindEvent(event: string, callback?: CallableFunction): void {
this.listeners[event] = this.listeners[event] || [];
if (callback) {
this.listeners[event] = this.listeners[event].filter(
(cb) => cb !== callback,
);
}
if (!callback || this.listeners[event].length === 0) {
if (this.events[event]) {
this.socket.removeListener(event, this.events[event]);
delete this.events[event];
}
delete this.listeners[event];
}
}
}