pubnub
Version:
Publish & Subscribe Real-time Messaging with PubNub
114 lines (101 loc) • 3.61 kB
text/typescript
/**
* Presence Event Engine effects dispatcher.
*
* @internal
*/
import { PrivateClientConfiguration } from '../../core/interfaces/configuration';
import { Payload, ResultCallback } from '../../core/types/api';
import StatusCategory from '../../core/constants/categories';
import { asyncHandler, Dispatcher, Engine } from '../core';
import PNOperations from '../../core/constants/operations';
import * as Presence from '../../core/types/api/presence';
import { PubNubError } from '../../errors/pubnub-error';
import * as effects from './effects';
import * as events from './events';
/**
* Presence Event Engine dependencies set (configuration).
*
* @internal
*/
export type Dependencies = {
heartbeat: (
parameters: Presence.CancelablePresenceHeartbeatParameters,
callback?: ResultCallback<Presence.PresenceHeartbeatResponse>,
) => Promise<Presence.PresenceHeartbeatResponse | void>;
leave: (parameters: Presence.PresenceLeaveParameters) => void;
heartbeatDelay: () => Promise<void>;
config: PrivateClientConfiguration;
presenceState: Record<string, Payload>;
/* eslint-disable @typescript-eslint/no-explicit-any */
emitStatus: (status: any) => void;
};
/**
* Presence Event Engine dispatcher.
*
* Dispatcher responsible for presence events handling and corresponding effects execution.
*
* @internal
*/
export class PresenceEventEngineDispatcher extends Dispatcher<effects.Effects, Dependencies> {
constructor(engine: Engine<events.Events, effects.Effects>, dependencies: Dependencies) {
super(dependencies, dependencies.config.logger());
this.on(
effects.heartbeat.type,
asyncHandler(async (payload, abortSignal, { heartbeat, presenceState, config }) => {
abortSignal.throwIfAborted();
try {
const result = await heartbeat({
abortSignal: abortSignal,
channels: payload.channels,
channelGroups: payload.groups,
...(config.maintainPresenceState && { state: presenceState }),
heartbeat: config.presenceTimeout!,
});
engine.transition(events.heartbeatSuccess(200));
} catch (e) {
if (e instanceof PubNubError) {
if (e.status && e.status.category == StatusCategory.PNCancelledCategory) return;
engine.transition(events.heartbeatFailure(e));
}
}
}),
);
this.on(
effects.leave.type,
asyncHandler(async (payload, _, { leave, config }) => {
if (!config.suppressLeaveEvents) {
try {
leave({
channels: payload.channels,
channelGroups: payload.groups,
});
} catch (e) {}
}
}),
);
this.on(
effects.wait.type,
asyncHandler(async (_, abortSignal, { heartbeatDelay }) => {
abortSignal.throwIfAborted();
await heartbeatDelay();
abortSignal.throwIfAborted();
return engine.transition(events.timesUp());
}),
);
this.on(
effects.emitStatus.type,
asyncHandler(async (payload, _, { emitStatus, config }) => {
if (config.announceFailedHeartbeats && payload?.error === true) {
emitStatus({ ...payload, operation: PNOperations.PNHeartbeatOperation });
} else if (config.announceSuccessfulHeartbeats && payload.statusCode === 200) {
emitStatus({
...payload,
error: false,
operation: PNOperations.PNHeartbeatOperation,
category: StatusCategory.PNAcknowledgmentCategory,
});
}
}),
);
}
}