UNPKG

pubnub

Version:

Publish & Subscribe Real-time Messaging with PubNub

120 lines (106 loc) 3.98 kB
/** * Initial subscription handshake (disconnected) state. * * @internal */ import { Effects, emitStatus, handshake } from '../effects'; import { disconnect, Events, handshakeFailure, handshakeSuccess, restore, subscriptionChange, unsubscribeAll, } from '../events'; import * as Subscription from '../../core/types/api/subscription'; import categoryConstants from '../../core/constants/categories'; import { HandshakeStoppedState } from './handshake_stopped'; import { HandshakeFailedState } from './handshake_failed'; import { UnsubscribedState } from './unsubscribed'; import { ReceivingState } from './receiving'; import { State } from '../core/state'; import { PubNubAPIError } from '../../errors/pubnub-api-error'; import RequestOperation from '../../core/constants/operations'; import { adjustedTimetokenBy, referenceSubscribeTimetoken } from '../../core/utils'; /** * Context which represent current Subscription Event Engine data state. * * @internal */ export type HandshakingStateContext = { channels: string[]; groups: string[]; cursor?: Subscription.SubscriptionCursor; onDemand?: boolean; }; /** * Initial subscription handshake (disconnected) state. * * State in which Subscription Event Engine tries to receive the subscription cursor for the next sequential * subscribe REST API calls. * * @internal */ export const HandshakingState = new State<HandshakingStateContext, Events, Effects>('HANDSHAKING'); HandshakingState.onEnter((context) => handshake(context.channels, context.groups, context.onDemand ?? false)); HandshakingState.onExit(() => handshake.cancel); HandshakingState.on(subscriptionChange.type, (context, { payload }) => { if (payload.channels.length === 0 && payload.groups.length === 0) return UnsubscribedState.with(undefined); return HandshakingState.with({ channels: payload.channels, groups: payload.groups, cursor: context.cursor, onDemand: true, }); }); HandshakingState.on(handshakeSuccess.type, (context, { payload }) => ReceivingState.with( { channels: context.channels, groups: context.groups, cursor: { timetoken: !!context.cursor?.timetoken ? context.cursor?.timetoken : payload.timetoken, region: payload.region, }, referenceTimetoken: referenceSubscribeTimetoken(payload.timetoken, context.cursor?.timetoken), }, [ emitStatus({ category: categoryConstants.PNConnectedCategory, affectedChannels: context.channels.slice(0), affectedChannelGroups: context.groups.slice(0), currentTimetoken: !!context.cursor?.timetoken ? context.cursor?.timetoken : payload.timetoken, }), ], ), ); HandshakingState.on(handshakeFailure.type, (context, event) => HandshakeFailedState.with({ ...context, reason: event.payload }, [ emitStatus({ category: categoryConstants.PNConnectionErrorCategory, error: event.payload.status?.category }), ]), ); HandshakingState.on(disconnect.type, (context, event) => { if (!event.payload.isOffline) return HandshakeStoppedState.with({ ...context }); else { const errorReason = PubNubAPIError.create(new Error('Network connection error')).toPubNubError( RequestOperation.PNSubscribeOperation, ); return HandshakeFailedState.with({ ...context, reason: errorReason }, [ emitStatus({ category: categoryConstants.PNConnectionErrorCategory, error: errorReason.status?.category, }), ]); } }); HandshakingState.on(restore.type, (context, { payload }) => { if (payload.channels.length === 0 && payload.groups.length === 0) return UnsubscribedState.with(undefined); return HandshakingState.with({ channels: payload.channels, groups: payload.groups, cursor: { timetoken: `${payload.cursor.timetoken}`, region: payload.cursor.region || context?.cursor?.region || 0 }, onDemand: true, }); }); HandshakingState.on(unsubscribeAll.type, (_) => UnsubscribedState.with());