import { DiscontinuityListener, OnDiscontinuitySubscriptionResponse, Presence, PresenceData, PresenceEvents, PresenceListener, PresenceMember, PresenceSubscriptionResponse, } from '@ably/chat'; import { RealtimeChannel, RealtimePresenceParams } from 'ably'; import { nanoid } from 'nanoid'; import { jsonRpc, jsonRpcBlocking, registerCallback } from './jsonrpc'; import { RpcRealtimeChannel } from './pubsub'; import { RpcOnDiscontinuitySubscriptionResponse } from './RpcOnDiscontinuitySubscriptionResponse'; export class RpcPresence implements Presence { private readonly _instanceId: string; constructor(instanceId: string) { this._instanceId = instanceId; } async get(params?: RealtimePresenceParams): Promise { const { response } = await jsonRpc.request('Presence.get', { refId: this._instanceId, args: { params, }, }); return response; } async isUserPresent(clientId: string): Promise { const { response } = await jsonRpc.request('Presence.isUserPresent', { refId: this._instanceId, args: { clientId, }, }); return response; } async enter(data?: PresenceData): Promise { await jsonRpc.request('Presence.enter', { refId: this._instanceId, args: { data, }, }); } async update(data?: PresenceData): Promise { await jsonRpc.request('Presence.update', { refId: this._instanceId, args: { data, }, }); } async leave(data?: PresenceData): Promise { await jsonRpc.request('Presence.leave', { refId: this._instanceId, args: { data, }, }); } subscribe( eventOrEvents: PresenceEvents | PresenceEvents[], listener?: PresenceListener, ): PresenceSubscriptionResponse; subscribe(listener?: PresenceListener): PresenceSubscriptionResponse; subscribe(eventOrEvents?: unknown, listener?: unknown): PresenceSubscriptionResponse { const callbackId = nanoid(); if (typeof eventOrEvents === 'string' || Array.isArray(eventOrEvents)) { const events = Array.isArray(eventOrEvents) ? eventOrEvents : [eventOrEvents]; registerCallback(callbackId, listener); const { refId } = jsonRpcBlocking.request('Presence.subscribe_eventsAndListener', { refId: this._instanceId, callbackId, args: { events, }, }); return new RpcPresenceSubscriptionResponse(refId); } else { registerCallback(callbackId, listener); const { refId } = jsonRpcBlocking.request('Presence.subscribe_listener', { refId: this._instanceId, callbackId, }); return new RpcPresenceSubscriptionResponse(refId); } } unsubscribeAll(): void { jsonRpcBlocking.request('Presence.unsubscribeAll', { refId: this._instanceId }); } get channel(): Promise { return jsonRpc .request('Presence#channel', { refId: this._instanceId, }) .then(({ refId }) => new RpcRealtimeChannel(refId)); } onDiscontinuity(listener: DiscontinuityListener): OnDiscontinuitySubscriptionResponse { const callbackId = nanoid(); registerCallback(callbackId, listener); const { refId } = jsonRpcBlocking.request('Presence.onDiscontinuity', { refId: this._instanceId, callbackId, }); return new RpcOnDiscontinuitySubscriptionResponse(refId); } } export class RpcPresenceSubscriptionResponse implements PresenceSubscriptionResponse { private readonly _instanceId: string; constructor(instanceId: string) { this._instanceId = instanceId; // it is common to destructure this object when received from the subscription function, // so we need to bind its functions to preserve reference to this this.unsubscribe = this.unsubscribe.bind(this); } unsubscribe(): void { jsonRpcBlocking.request('PresenceSubscriptionResponse.unsubscribe', { refId: this._instanceId }); } }