UNPKG

@laravel/echo-react

Version:

React hooks for seamless integration with Laravel Echo.

1,033 lines (1,032 loc) 25.7 kB
import H from "pusher-js"; import { useCallback as d, useRef as f, useEffect as x } from "react"; class I { constructor() { this.notificationCreatedEvent = ".Illuminate\\Notifications\\Events\\BroadcastNotificationCreated"; } /** * Listen for a whisper event on the channel instance. */ listenForWhisper(t, e) { return this.listen(".client-" + t, e); } /** * Listen for an event on the channel instance. */ notification(t) { return this.listen(this.notificationCreatedEvent, t); } /** * Stop listening for notification events on the channel instance. */ stopListeningForNotification(t) { return this.stopListening(this.notificationCreatedEvent, t); } /** * Stop listening for a whisper event on the channel instance. */ stopListeningForWhisper(t, e) { return this.stopListening(".client-" + t, e); } } class A { /** * Create a new class instance. */ constructor(t) { this.namespace = t; } /** * Format the given event name. */ format(t) { return [".", "\\"].includes(t.charAt(0)) ? t.substring(1) : (this.namespace && (t = this.namespace + "." + t), t.replace(/\./g, "\\")); } /** * Set the event namespace. */ setNamespace(t) { this.namespace = t; } } function B(s) { try { new s(); } catch (t) { if (t instanceof Error && t.message.includes("is not a constructor")) return !1; } return !0; } class T extends I { /** * Create a new class instance. */ constructor(t, e, n) { super(), this.name = e, this.pusher = t, this.options = n, this.eventFormatter = new A(this.options.namespace), this.subscribe(); } /** * Subscribe to a Pusher channel. */ subscribe() { this.subscription = this.pusher.subscribe(this.name); } /** * Unsubscribe from a Pusher channel. */ unsubscribe() { this.pusher.unsubscribe(this.name); } /** * Listen for an event on the channel instance. */ listen(t, e) { return this.on(this.eventFormatter.format(t), e), this; } /** * Listen for all events on the channel instance. */ listenToAll(t) { return this.subscription.bind_global((e, n) => { if (e.startsWith("pusher:")) return; let i = String(this.options.namespace ?? "").replace( /\./g, "\\" ), r = e.startsWith(i) ? e.substring(i.length + 1) : "." + e; t(r, n); }), this; } /** * Stop listening for an event on the channel instance. */ stopListening(t, e) { return e ? this.subscription.unbind( this.eventFormatter.format(t), e ) : this.subscription.unbind(this.eventFormatter.format(t)), this; } /** * Stop listening for all events on the channel instance. */ stopListeningToAll(t) { return t ? this.subscription.unbind_global(t) : this.subscription.unbind_global(), this; } /** * Register a callback to be called anytime a subscription succeeds. */ subscribed(t) { return this.on("pusher:subscription_succeeded", () => { t(); }), this; } /** * Register a callback to be called anytime a subscription error occurs. */ error(t) { return this.on("pusher:subscription_error", (e) => { t(e); }), this; } /** * Bind a channel to an event. */ on(t, e) { return this.subscription.bind(t, e), this; } } class V extends T { /** * Send a whisper event to other clients in the channel. */ whisper(t, e) { return this.pusher.channels.channels[this.name].trigger( `client-${t}`, e ), this; } } class q extends T { /** * Send a whisper event to other clients in the channel. */ whisper(t, e) { return this.pusher.channels.channels[this.name].trigger( `client-${t}`, e ), this; } } class U extends V { /** * Register a callback to be called anytime the member list changes. */ here(t) { return this.on("pusher:subscription_succeeded", (e) => { t(Object.keys(e.members).map((n) => e.members[n])); }), this; } /** * Listen for someone joining the channel. */ joining(t) { return this.on("pusher:member_added", (e) => { t(e.info); }), this; } /** * Send a whisper event to other clients in the channel. */ whisper(t, e) { return this.pusher.channels.channels[this.name].trigger( `client-${t}`, e ), this; } /** * Listen for someone leaving the channel. */ leaving(t) { return this.on("pusher:member_removed", (e) => { t(e.info); }), this; } } class L extends I { /** * Create a new class instance. */ constructor(t, e, n) { super(), this.events = {}, this.listeners = {}, this.name = e, this.socket = t, this.options = n, this.eventFormatter = new A(this.options.namespace), this.subscribe(); } /** * Subscribe to a Socket.io channel. */ subscribe() { this.socket.emit("subscribe", { channel: this.name, auth: this.options.auth || {} }); } /** * Unsubscribe from channel and ubind event callbacks. */ unsubscribe() { this.unbind(), this.socket.emit("unsubscribe", { channel: this.name, auth: this.options.auth || {} }); } /** * Listen for an event on the channel instance. */ listen(t, e) { return this.on(this.eventFormatter.format(t), e), this; } /** * Stop listening for an event on the channel instance. */ stopListening(t, e) { return this.unbindEvent(this.eventFormatter.format(t), e), this; } /** * Register a callback to be called anytime a subscription succeeds. */ subscribed(t) { return this.on("connect", (e) => { t(e); }), this; } /** * Register a callback to be called anytime an error occurs. */ error(t) { return this; } /** * Bind the channel's socket to an event and store the callback. */ on(t, e) { return this.listeners[t] = this.listeners[t] || [], this.events[t] || (this.events[t] = (n, i) => { this.name === n && this.listeners[t] && this.listeners[t].forEach((r) => r(i)); }, this.socket.on(t, this.events[t])), this.listeners[t].push(e), this; } /** * Unbind the channel's socket from all stored event callbacks. */ unbind() { Object.keys(this.events).forEach((t) => { this.unbindEvent(t); }); } /** * Unbind the listeners for the given event. */ unbindEvent(t, e) { this.listeners[t] = this.listeners[t] || [], e && (this.listeners[t] = this.listeners[t].filter( (n) => n !== e )), (!e || this.listeners[t].length === 0) && (this.events[t] && (this.socket.removeListener(t, this.events[t]), delete this.events[t]), delete this.listeners[t]); } } class O extends L { /** * Send a whisper event to other clients in the channel. */ whisper(t, e) { return this.socket.emit("client event", { channel: this.name, event: `client-${t}`, data: e }), this; } } class K extends O { /** * Register a callback to be called anytime the member list changes. */ here(t) { return this.on("presence:subscribed", (e) => { t(e.map((n) => n.user_info)); }), this; } /** * Listen for someone joining the channel. */ joining(t) { return this.on( "presence:joining", (e) => t(e.user_info) ), this; } /** * Send a whisper event to other clients in the channel. */ whisper(t, e) { return this.socket.emit("client event", { channel: this.name, event: `client-${t}`, data: e }), this; } /** * Listen for someone leaving the channel. */ leaving(t) { return this.on( "presence:leaving", (e) => t(e.user_info) ), this; } } class E extends I { /** * Subscribe to a channel. */ subscribe() { } /** * Unsubscribe from a channel. */ unsubscribe() { } /** * Listen for an event on the channel instance. */ listen(t, e) { return this; } /** * Listen for all events on the channel instance. */ listenToAll(t) { return this; } /** * Stop listening for an event on the channel instance. */ stopListening(t, e) { return this; } /** * Register a callback to be called anytime a subscription succeeds. */ subscribed(t) { return this; } /** * Register a callback to be called anytime an error occurs. */ error(t) { return this; } /** * Bind a channel to an event. */ on(t, e) { return this; } } class j extends E { /** * Send a whisper event to other clients in the channel. */ whisper(t, e) { return this; } } class N extends E { /** * Send a whisper event to other clients in the channel. */ whisper(t, e) { return this; } } class X extends j { /** * Register a callback to be called anytime the member list changes. */ here(t) { return this; } /** * Listen for someone joining the channel. */ joining(t) { return this; } /** * Send a whisper event to other clients in the channel. */ whisper(t, e) { return this; } /** * Listen for someone leaving the channel. */ leaving(t) { return this; } } const F = class $ { /** * Create a new class instance. */ constructor(t) { this.setOptions(t), this.connect(); } /** * Merge the custom options with the defaults. */ setOptions(t) { this.options = { ...$._defaultOptions, ...t, broadcaster: t.broadcaster }; let e = this.csrfToken(); e && (this.options.auth.headers["X-CSRF-TOKEN"] = e, this.options.userAuthentication.headers["X-CSRF-TOKEN"] = e), e = this.options.bearerToken, e && (this.options.auth.headers.Authorization = "Bearer " + e, this.options.userAuthentication.headers.Authorization = "Bearer " + e); } /** * Extract the CSRF token from the page. */ csrfToken() { var t, e; return typeof window < "u" && (t = window.Laravel) != null && t.csrfToken ? window.Laravel.csrfToken : this.options.csrfToken ? this.options.csrfToken : typeof document < "u" && typeof document.querySelector == "function" ? ((e = document.querySelector('meta[name="csrf-token"]')) == null ? void 0 : e.getAttribute("content")) ?? null : null; } }; F._defaultOptions = { auth: { headers: {} }, authEndpoint: "/broadcasting/auth", userAuthentication: { endpoint: "/broadcasting/user-auth", headers: {} }, csrfToken: null, bearerToken: null, host: null, key: null, namespace: "App.Events" }; let _ = F; class w extends _ { constructor() { super(...arguments), this.channels = {}; } /** * Create a fresh Pusher connection. */ connect() { if (typeof this.options.client < "u") this.pusher = this.options.client; else if (this.options.Pusher) this.pusher = new this.options.Pusher( this.options.key, this.options ); else if (typeof window < "u" && typeof window.Pusher < "u") this.pusher = new window.Pusher(this.options.key, this.options); else throw new Error( "Pusher client not found. Should be globally available or passed via options.client" ); } /** * Sign in the user via Pusher user authentication (https://pusher.com/docs/channels/using_channels/user-authentication/). */ signin() { this.pusher.signin(); } /** * Listen for an event on a channel instance. */ listen(t, e, n) { return this.channel(t).listen(e, n); } /** * Get a channel instance by name. */ channel(t) { return this.channels[t] || (this.channels[t] = new T( this.pusher, t, this.options )), this.channels[t]; } /** * Get a private channel instance by name. */ privateChannel(t) { return this.channels["private-" + t] || (this.channels["private-" + t] = new V( this.pusher, "private-" + t, this.options )), this.channels["private-" + t]; } /** * Get a private encrypted channel instance by name. */ encryptedPrivateChannel(t) { return this.channels["private-encrypted-" + t] || (this.channels["private-encrypted-" + t] = new q( this.pusher, "private-encrypted-" + t, this.options )), this.channels["private-encrypted-" + t]; } /** * Get a presence channel instance by name. */ presenceChannel(t) { return this.channels["presence-" + t] || (this.channels["presence-" + t] = new U( this.pusher, "presence-" + t, this.options )), this.channels["presence-" + t]; } /** * Leave the given channel, as well as its private and presence variants. */ leave(t) { [ t, "private-" + t, "private-encrypted-" + t, "presence-" + t ].forEach((e) => { this.leaveChannel(e); }); } /** * Leave the given channel. */ leaveChannel(t) { this.channels[t] && (this.channels[t].unsubscribe(), delete this.channels[t]); } /** * Get the socket ID for the connection. */ socketId() { return this.pusher.connection.socket_id; } /** * Disconnect Pusher connection. */ disconnect() { this.pusher.disconnect(); } } class Q extends _ { constructor() { super(...arguments), this.channels = {}; } /** * Create a fresh Socket.io connection. */ connect() { let t = this.getSocketIO(); this.socket = t( this.options.host ?? void 0, this.options ), this.socket.io.on("reconnect", () => { Object.values(this.channels).forEach((e) => { e.subscribe(); }); }); } /** * Get socket.io module from global scope or options. */ getSocketIO() { if (typeof this.options.client < "u") return this.options.client; if (typeof window < "u" && typeof window.io < "u") return window.io; throw new Error( "Socket.io client not found. Should be globally available or passed via options.client" ); } /** * Listen for an event on a channel instance. */ listen(t, e, n) { return this.channel(t).listen(e, n); } /** * Get a channel instance by name. */ channel(t) { return this.channels[t] || (this.channels[t] = new L( this.socket, t, this.options )), this.channels[t]; } /** * Get a private channel instance by name. */ privateChannel(t) { return this.channels["private-" + t] || (this.channels["private-" + t] = new O( this.socket, "private-" + t, this.options )), this.channels["private-" + t]; } /** * Get a presence channel instance by name. */ presenceChannel(t) { return this.channels["presence-" + t] || (this.channels["presence-" + t] = new K( this.socket, "presence-" + t, this.options )), this.channels["presence-" + t]; } /** * Leave the given channel, as well as its private and presence variants. */ leave(t) { [t, "private-" + t, "presence-" + t].forEach((e) => { this.leaveChannel(e); }); } /** * Leave the given channel. */ leaveChannel(t) { this.channels[t] && (this.channels[t].unsubscribe(), delete this.channels[t]); } /** * Get the socket ID for the connection. */ socketId() { return this.socket.id; } /** * Disconnect Socketio connection. */ disconnect() { this.socket.disconnect(); } } class S extends _ { constructor() { super(...arguments), this.channels = {}; } /** * Create a fresh connection. */ connect() { } /** * Listen for an event on a channel instance. */ listen(t, e, n) { return new E(); } /** * Get a channel instance by name. */ channel(t) { return new E(); } /** * Get a private channel instance by name. */ privateChannel(t) { return new j(); } /** * Get a private encrypted channel instance by name. */ encryptedPrivateChannel(t) { return new N(); } /** * Get a presence channel instance by name. */ presenceChannel(t) { return new X(); } /** * Leave the given channel, as well as its private and presence variants. */ leave(t) { } /** * Leave the given channel. */ leaveChannel(t) { } /** * Get the socket ID for the connection. */ socketId() { return "fake-socket-id"; } /** * Disconnect the connection. */ disconnect() { } } class W { /** * Create a new class instance. */ constructor(t) { this.options = t, this.connect(), this.options.withoutInterceptors || this.registerInterceptors(); } /** * Get a channel instance by name. */ channel(t) { return this.connector.channel(t); } /** * Create a new connection. */ connect() { if (this.options.broadcaster === "reverb") this.connector = new w({ ...this.options, cluster: "" }); else if (this.options.broadcaster === "pusher") this.connector = new w(this.options); else if (this.options.broadcaster === "ably") this.connector = new w({ ...this.options, cluster: "", broadcaster: "pusher" }); else if (this.options.broadcaster === "socket.io") this.connector = new Q(this.options); else if (this.options.broadcaster === "null") this.connector = new S(this.options); else if (typeof this.options.broadcaster == "function" && B(this.options.broadcaster)) this.connector = new this.options.broadcaster(this.options); else throw new Error( `Broadcaster ${typeof this.options.broadcaster} ${String(this.options.broadcaster)} is not supported.` ); } /** * Disconnect from the Echo server. */ disconnect() { this.connector.disconnect(); } /** * Get a presence channel instance by name. */ join(t) { return this.connector.presenceChannel(t); } /** * Leave the given channel, as well as its private and presence variants. */ leave(t) { this.connector.leave(t); } /** * Leave the given channel. */ leaveChannel(t) { this.connector.leaveChannel(t); } /** * Leave all channels. */ leaveAllChannels() { for (const t in this.connector.channels) this.leaveChannel(t); } /** * Listen for an event on a channel instance. */ listen(t, e, n) { return this.connector.listen(t, e, n); } /** * Get a private channel instance by name. */ private(t) { return this.connector.privateChannel(t); } /** * Get a private encrypted channel instance by name. */ encryptedPrivate(t) { if (this.connectorSupportsEncryptedPrivateChannels(this.connector)) return this.connector.encryptedPrivateChannel(t); throw new Error( `Broadcaster ${typeof this.options.broadcaster} ${String( this.options.broadcaster )} does not support encrypted private channels.` ); } connectorSupportsEncryptedPrivateChannels(t) { return t instanceof w || t instanceof S; } /** * Get the Socket ID for the connection. */ socketId() { return this.connector.socketId(); } /** * Register 3rd party request interceptiors. These are used to automatically * send a connections socket id to a Laravel app with a X-Socket-Id header. */ registerInterceptors() { typeof Vue < "u" && Vue != null && Vue.http && this.registerVueRequestInterceptor(), typeof axios == "function" && this.registerAxiosRequestInterceptor(), typeof jQuery == "function" && this.registerjQueryAjaxSetup(), typeof Turbo == "object" && this.registerTurboRequestInterceptor(); } /** * Register a Vue HTTP interceptor to add the X-Socket-ID header. */ registerVueRequestInterceptor() { Vue.http.interceptors.push( (t, e) => { this.socketId() && t.headers.set("X-Socket-ID", this.socketId()), e(); } ); } /** * Register an Axios HTTP interceptor to add the X-Socket-ID header. */ registerAxiosRequestInterceptor() { axios.interceptors.request.use( (t) => (this.socketId() && (t.headers["X-Socket-Id"] = this.socketId()), t) ); } /** * Register jQuery AjaxPrefilter to add the X-Socket-ID header. */ registerjQueryAjaxSetup() { typeof jQuery.ajax < "u" && jQuery.ajaxPrefilter( (t, e, n) => { this.socketId() && n.setRequestHeader("X-Socket-Id", this.socketId()); } ); } /** * Register the Turbo Request interceptor to add the X-Socket-ID header. */ registerTurboRequestInterceptor() { document.addEventListener( "turbo:before-fetch-request", (t) => { t.detail.fetchOptions.headers["X-Socket-Id"] = this.socketId(); } ); } } let b = null, p = null; const z = () => { if (b) return b; if (!p) throw new Error( "Echo has not been configured. Please call `configureEcho()`." ); return p.Pusher ?? (p.Pusher = H), b = new W(p), b; }, J = (s) => { p = { ...{ reverb: { broadcaster: "reverb", key: (typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_REVERB_APP_KEY : undefined), wsHost: (typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_REVERB_HOST : undefined), wsPort: (typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_REVERB_PORT : undefined), wssPort: (typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_REVERB_PORT : undefined), forceTLS: ((typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_REVERB_SCHEME : undefined) ?? "https") === "https", enabledTransports: ["ws", "wss"] }, pusher: { broadcaster: "pusher", key: (typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_PUSHER_APP_KEY : undefined), cluster: (typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_PUSHER_APP_CLUSTER : undefined), forceTLS: !0, wsHost: (typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_PUSHER_HOST : undefined), wsPort: (typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_PUSHER_PORT : undefined), wssPort: (typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_PUSHER_PORT : undefined), enabledTransports: ["ws", "wss"] }, "socket.io": { broadcaster: "socket.io", host: (typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_SOCKET_IO_HOST : undefined) }, null: { broadcaster: "null" }, ably: { broadcaster: "pusher", key: (typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_ABLY_PUBLIC_KEY : undefined), wsHost: "realtime-pusher.ably.io", wsPort: 443, disableStats: !0, encrypted: !0 } }[s.broadcaster], ...s }, b && (b = null); }, y = () => z(), Z = () => p !== null, C = (s) => Array.isArray(s) ? s : [s], h = {}, Y = (s) => { const t = y(); return s.visibility === "presence" ? t.join(s.name) : s.visibility === "private" ? t.private(s.name) : t.channel(s.name); }, D = (s, t) => { h[s.id] && (h[s.id].count -= 1, !(h[s.id].count > 0) && (t ? y().leave(s.name) : y().leaveChannel(s.id), delete h[s.id])); }, R = (s) => { if (h[s.id]) return h[s.id].count += 1, h[s.id].connection; const t = Y(s); return h[s.id] = { count: 1, connection: t }, t; }, g = (s, t = [], e = () => { }, n = [], i = "private") => { const r = { name: s, id: ["private", "presence"].includes(i) ? `${i}-${s}` : s, visibility: i }, c = d(e, n), a = f(!1), u = f(!1), l = f( R(r) ), v = C(t), o = d(() => { a.current && (v.forEach((m) => { l.current.stopListening(m, c); }), a.current = !1); }, n), P = d(() => { a.current || (v.forEach((m) => { l.current.listen(m, c); }), a.current = !0); }, n), k = d((m = !1) => { o(), D(r, m); }, n); return x(() => (u.current && (l.current = R(r)), u.current = !0, P(), k), n), { /** * Leave the channel */ leaveChannel: k, /** * Leave the channel and also its associated private and presence channels */ leave: () => k(!0), /** * Stop listening for event(s) without leaving the channel */ stopListening: o, /** * Listen for event(s) */ listen: P, /** * Channel instance */ channel: () => l.current }; }, tt = (s, t = () => { }, e = [], n = []) => { const i = g( s, [], t, n, "private" ), r = f( C(e).map((o) => o.includes(".") ? [o, o.replace(/\./g, "\\")] : [o, o.replace(/\\/g, ".")]).flat() ), c = f(!1), a = f(!1), u = d( (o) => { c.current && (r.current.length === 0 || r.current.includes(o.type)) && t(o); }, n.concat(r.current).concat([t]) ), l = d(() => { c.current || (a.current || i.channel().notification(u), c.current = !0, a.current = !0); }, [u]), v = d(() => { c.current && (i.channel().stopListeningForNotification(u), c.current = !1); }, [u]); return x(() => (l(), () => v()), n.concat(r.current)), { ...i, /** * Stop listening for notification events */ stopListening: v, /** * Listen for notification events */ listen: l }; }, et = (s, t = [], e = () => { }, n = []) => g( s, t, e, n, "presence" ), st = (s, t = [], e = () => { }, n = []) => g( s, t, e, n, "public" ), nt = (s, t, e = [], n = () => { }, i = []) => g( `${s}.${t}`, C(e).map((r) => r.startsWith(".") ? r : `.${r}`), n, i, "private" ); export { J as configureEcho, y as echo, Z as echoIsConfigured, g as useEcho, nt as useEchoModel, tt as useEchoNotification, et as useEchoPresence, st as useEchoPublic }; //# sourceMappingURL=index.js.map