UNPKG

pubnub

Version:

Publish & Subscribe Real-time Messaging with PubNub

214 lines (213 loc) 9 kB
"use strict"; /** * Events dispatcher module. */ var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EventDispatcher = void 0; const subscribe_1 = require("../endpoints/subscribe"); /** * Real-time events dispatcher. * * Class responsible for listener management and invocation. * * @internal */ class EventDispatcher { constructor() { /** * Whether listeners has been added or not. */ this.hasListeners = false; /** * List of registered event handlers. * * **Note:** the First element is reserved for type-based event handlers. */ this.listeners = [{ count: -1, listener: {} }]; } /** * Set a connection status change event handler. * * @param listener - Listener function, which will be called each time when the connection status changes. */ set onStatus(listener) { this.updateTypeOrObjectListener({ add: !!listener, listener, type: 'status' }); } /** * Set a new message handler. * * @param listener - Listener function, which will be called each time when a new message * is received from the real-time network. */ set onMessage(listener) { this.updateTypeOrObjectListener({ add: !!listener, listener, type: 'message' }); } /** * Set a new presence events handler. * * @param listener - Listener function, which will be called each time when a new * presence event is received from the real-time network. */ set onPresence(listener) { this.updateTypeOrObjectListener({ add: !!listener, listener, type: 'presence' }); } /** * Set a new signal handler. * * @param listener - Listener function, which will be called each time when a new signal * is received from the real-time network. */ set onSignal(listener) { this.updateTypeOrObjectListener({ add: !!listener, listener, type: 'signal' }); } /** * Set a new app context event handler. * * @param listener - Listener function, which will be called each time when a new * app context event is received from the real-time network. */ set onObjects(listener) { this.updateTypeOrObjectListener({ add: !!listener, listener, type: 'objects' }); } /** * Set a new message reaction event handler. * * @param listener - Listener function, which will be called each time when a * new message reaction event is received from the real-time network. */ set onMessageAction(listener) { this.updateTypeOrObjectListener({ add: !!listener, listener, type: 'messageAction' }); } /** * Set a new file handler. * * @param listener - Listener function, which will be called each time when a new file * is received from the real-time network. */ set onFile(listener) { this.updateTypeOrObjectListener({ add: !!listener, listener, type: 'file' }); } /** * Dispatch received a real-time update. * * @param event - A real-time event from multiplexed subscription. */ handleEvent(event) { if (!this.hasListeners) return; if (event.type === subscribe_1.PubNubEventType.Message) this.announce('message', event.data); else if (event.type === subscribe_1.PubNubEventType.Signal) this.announce('signal', event.data); else if (event.type === subscribe_1.PubNubEventType.Presence) this.announce('presence', event.data); else if (event.type === subscribe_1.PubNubEventType.AppContext) { const { data: objectEvent } = event; const { message: object } = objectEvent; this.announce('objects', objectEvent); if (object.type === 'uuid') { const { message, channel } = objectEvent, restEvent = __rest(objectEvent, ["message", "channel"]); const { event, type } = object, restObject = __rest(object, ["event", "type"]); const userEvent = Object.assign(Object.assign({}, restEvent), { spaceId: channel, message: Object.assign(Object.assign({}, restObject), { event: event === 'set' ? 'updated' : 'removed', type: 'user' }) }); this.announce('user', userEvent); } else if (object.type === 'channel') { const { message, channel } = objectEvent, restEvent = __rest(objectEvent, ["message", "channel"]); const { event, type } = object, restObject = __rest(object, ["event", "type"]); const spaceEvent = Object.assign(Object.assign({}, restEvent), { spaceId: channel, message: Object.assign(Object.assign({}, restObject), { event: event === 'set' ? 'updated' : 'removed', type: 'space' }) }); this.announce('space', spaceEvent); } else if (object.type === 'membership') { const { message, channel } = objectEvent, restEvent = __rest(objectEvent, ["message", "channel"]); const { event, data } = object, restObject = __rest(object, ["event", "data"]); const { uuid, channel: channelMeta } = data, restData = __rest(data, ["uuid", "channel"]); const membershipEvent = Object.assign(Object.assign({}, restEvent), { spaceId: channel, message: Object.assign(Object.assign({}, restObject), { event: event === 'set' ? 'updated' : 'removed', data: Object.assign(Object.assign({}, restData), { user: uuid, space: channelMeta }) }) }); this.announce('membership', membershipEvent); } } else if (event.type === subscribe_1.PubNubEventType.MessageAction) this.announce('messageAction', event.data); else if (event.type === subscribe_1.PubNubEventType.Files) this.announce('file', event.data); } /** * Dispatch received connection status change. * * @param status - Status object which should be emitter for all status listeners. */ handleStatus(status) { if (!this.hasListeners) return; this.announce('status', status); } /** * Add events handler. * * @param listener - Events listener configuration object, which lets specify handlers for multiple types of events. */ addListener(listener) { this.updateTypeOrObjectListener({ add: true, listener }); } removeListener(listener) { this.updateTypeOrObjectListener({ add: false, listener }); } removeAllListeners() { this.listeners = [{ count: -1, listener: {} }]; this.hasListeners = false; } updateTypeOrObjectListener(parameters) { if (parameters.type) { if (typeof parameters.listener === 'function') this.listeners[0].listener[parameters.type] = parameters.listener; else delete this.listeners[0].listener[parameters.type]; } else if (parameters.listener && typeof parameters.listener !== 'function') { let listenerObject; let listenerExists = false; for (listenerObject of this.listeners) { if (listenerObject.listener === parameters.listener) { if (parameters.add) { listenerObject.count++; listenerExists = true; } else { listenerObject.count--; if (listenerObject.count === 0) this.listeners.splice(this.listeners.indexOf(listenerObject), 1); } break; } } if (parameters.add && !listenerExists) this.listeners.push({ count: 1, listener: parameters.listener }); } this.hasListeners = this.listeners.length > 1 || Object.keys(this.listeners[0]).length > 0; } /** * Announce a real-time event to all listeners. * * @param type - Type of event which should be announced. * @param event - Announced real-time event payload. */ announce(type, event) { this.listeners.forEach(({ listener }) => { const typedListener = listener[type]; // @ts-expect-error Dynamic events mapping. if (typedListener) typedListener(event); }); } } exports.EventDispatcher = EventDispatcher;