UNPKG

pubnub

Version:

Publish & Subscribe Real-time Messaging with PubNub

95 lines (75 loc) 3.13 kB
/** * Presence Event Engine module. * * @internal */ import { Dependencies, PresenceEventEngineDispatcher } from './dispatcher'; import { HeartbeatInactiveState } from './states/heartbeat_inactive'; import { Dispatcher, Engine } from '../core'; import * as effects from './effects'; import * as events from './events'; /** * Presence Event Engine Core. * * @internal */ export class PresenceEventEngine { private readonly engine: Engine<events.Events, effects.Effects>; private dispatcher: Dispatcher<effects.Effects, Dependencies>; get _engine() { return this.engine; } private _unsubscribeEngine!: () => void; constructor(private dependencies: Dependencies) { this.engine = new Engine(dependencies.config.logger()); this.dispatcher = new PresenceEventEngineDispatcher(this.engine, dependencies); dependencies.config.logger().debug('PresenceEventEngine', 'Create presence event engine.'); this._unsubscribeEngine = this.engine.subscribe((change) => { if (change.type === 'invocationDispatched') { this.dispatcher.dispatch(change.invocation); } }); this.engine.start(HeartbeatInactiveState, undefined); } channels: string[] = []; groups: string[] = []; join({ channels, groups }: { channels?: string[]; groups?: string[] }) { this.channels = [...this.channels, ...(channels ?? []).filter((channel) => !this.channels.includes(channel))]; this.groups = [...this.groups, ...(groups ?? []).filter((group) => !this.groups.includes(group))]; // Don't make any transitions if there is no channels and groups. if (this.channels.length === 0 && this.groups.length === 0) return; this.engine.transition(events.joined(this.channels.slice(0), this.groups.slice(0))); } leave({ channels, groups }: { channels?: string[]; groups?: string[] }) { // Update internal channel tracking to prevent stale heartbeat requests if (channels) this.channels = this.channels.filter((channel) => !channels.includes(channel)); if (groups) this.groups = this.groups.filter((group) => !groups.includes(group)); if (this.dependencies.presenceState) { channels?.forEach((c) => delete this.dependencies.presenceState[c]); groups?.forEach((g) => delete this.dependencies.presenceState[g]); } this.engine.transition(events.left(channels ?? [], groups ?? [])); } leaveAll(isOffline: boolean = false) { // Clear presence state for all current channels and groups if (this.dependencies.presenceState) { this.channels.forEach((c) => delete this.dependencies.presenceState[c]); this.groups.forEach((g) => delete this.dependencies.presenceState[g]); } // Reset internal channel and group tracking this.channels = []; this.groups = []; this.engine.transition(events.leftAll(isOffline)); } reconnect() { this.engine.transition(events.reconnect()); } disconnect(isOffline: boolean = false) { this.engine.transition(events.disconnect(isOffline)); } dispose() { this.disconnect(true); this._unsubscribeEngine(); this.dispatcher.dispose(); } }