UNPKG

@ably/chat

Version:

Ably Chat is a set of purpose-built APIs for a host of chat features enabling you to create 1:1, 1:Many, Many:1 and Many:Many chat rooms for any scale. It is designed to meet a wide range of chat use cases, such as livestreams, in-game communication, cust

225 lines (190 loc) 5.36 kB
import * as Ably from 'ably'; import { Logger } from './logger.js'; import { StatusSubscription } from './subscription.js'; import EventEmitter, { emitterHasListeners, wrap } from './utils/event-emitter.js'; /** * The different states that a room can be in throughout its lifecycle. */ export enum RoomStatus { /** * The library is currently initializing the room. This state is a temporary state used in React prior * to the room being resolved. */ Initializing = 'initializing', /** * A temporary state for when the room object is first initialized. */ Initialized = 'initialized', /** * The library is currently attempting to attach the room. */ Attaching = 'attaching', /** * The room is currently attached and receiving events. */ Attached = 'attached', /** * The room is currently detaching and will not receive events. */ Detaching = 'detaching', /** * The room is currently detached and will not receive events. */ Detached = 'detached', /** * The room is in an extended state of detachment, but will attempt to re-attach when able. */ Suspended = 'suspended', /** * The room is currently detached and will not attempt to re-attach. User intervention is required. */ Failed = 'failed', /** * The room is in the process of releasing. Attempting to use a room in this state may result in undefined behavior. */ Releasing = 'releasing', /** * The room has been released and is no longer usable. */ Released = 'released', } /** * Represents a change in the status of the room. */ export interface RoomStatusChange { /** * The new status of the room. */ current: RoomStatus; /** * The previous status of the room. */ previous: RoomStatus; /** * An error that provides a reason why the room has * entered the new status, if applicable. */ error?: Ably.ErrorInfo; } /** * A function that can be called when the room status changes. * @param change The change in status. */ export type RoomStatusListener = (change: RoomStatusChange) => void; /** * Represents the status of a Room. */ export interface RoomLifecycle { /** * The current status of the room. */ get status(): RoomStatus; /** * The current error, if any, that caused the room to enter the current status. */ get error(): Ably.ErrorInfo | undefined; /** * Registers a listener that will be called whenever the room status changes. * @param listener The function to call when the status changes. * @returns An object that can be used to unregister the listener. */ onChange(listener: RoomStatusListener): StatusSubscription; } /** * An internal interface for the status of a room, which can be used to separate critical * internal functionality from user listeners. * @internal */ export interface InternalRoomLifecycle extends RoomLifecycle { /** * Sets the status of the room. * @param params The new status of the room. */ setStatus(params: NewRoomStatus): void; } /** * A new room status that can be set. */ export interface NewRoomStatus { /** * The new status of the room. */ status: RoomStatus; /** * An error that provides a reason why the room has * entered the new status, if applicable. */ error?: Ably.ErrorInfo; } type RoomStatusEventsMap = Record<RoomStatus, RoomStatusChange>; /** * An implementation of the `Status` interface. * @internal */ export class DefaultRoomLifecycle implements InternalRoomLifecycle { private _status: RoomStatus = RoomStatus.Initialized; private _error?: Ably.ErrorInfo; private readonly _logger: Logger; private readonly _emitter = new EventEmitter<RoomStatusEventsMap>(); /** * Constructs a new DefaultRoomLifecycle instance. * @param logger An instance of the Logger. */ constructor(logger: Logger) { this._logger = logger; } /** * @inheritdoc */ get status(): RoomStatus { return this._status; } /** * @inheritdoc */ get error(): Ably.ErrorInfo | undefined { return this._error; } /** * @inheritdoc */ onChange(listener: RoomStatusListener): StatusSubscription { const wrapped = wrap(listener); this._emitter.on(wrapped); return { off: () => { this._emitter.off(wrapped); }, }; } setStatus(params: NewRoomStatus): void { const change: RoomStatusChange = { current: params.status, error: params.error, previous: this._status, }; this._status = change.current; this._error = change.error; this._logger.info(`room status changed`, { ...change }); this._emitter.emit(change.current, change); } /** * Disposes of the room lifecycle instance, removing all listeners. * This method should be called when the room is being released to ensure proper cleanup. * @internal */ dispose(): void { this._logger.trace('DefaultRoomLifecycle.dispose();'); // Remove all user-level listeners this._emitter.off(); this._logger.debug('DefaultRoomLifecycle.dispose(); disposed successfully'); } /** * Checks if there are any listeners registered by users. * @internal * @returns true if there are listeners, false otherwise. */ hasListeners(): boolean { return emitterHasListeners(this._emitter); } }