@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
234 lines (202 loc) • 6.39 kB
text/typescript
import * as Ably from 'ably';
import { MessageReactionType } from './events.js';
/**
* The default values for RoomOptions.
*/
const DefaultRoomOptions: Omit<InternalRoomOptions, 'isReactClient'> = {
/**
* The default typing options for a chat room.
*/
typing: {
/**
* The default time that a client will wait between sending one typing heartbeat and the next.
*
* Spec: CHA-T10.
*/
heartbeatThrottleMs: 10000,
},
/**
* The default occupancy options for a chat room.
*/
occupancy: {
/**
* Whether to enable occupancy events.
*/
enableEvents: false,
},
/**
* The default presence options for the room.
*/
presence: {
enableEvents: true,
},
/**
* The default options for messages.
*/
messages: {
rawMessageReactions: false,
defaultMessageReactionType: MessageReactionType.Distinct,
},
};
/**
* Represents the message options for a chat room.
*/
export interface MessageOptions {
/**
* Whether to enable receiving raw individual message reactions from the
* realtime channel. Set to true if subscribing to raw message reactions.
*
* Note reaction summaries (aggregates) are always available regardless of
* this setting.
* @defaultValue false
*/
rawMessageReactions?: boolean;
/**
* The default message reaction type to use for sending message reactions.
*
* Any message reaction type can be sent regardless of this setting by specifying the `type` parameter
* in the {@link MessagesReactions.send} method.
* @defaultValue {@link MessageReactionType.Distinct}
*/
defaultMessageReactionType?: MessageReactionType;
}
/**
* Represents the typing options for a chat room.
*/
export interface TypingOptions {
/**
* A throttle, in milliseconds, that enforces the minimum time interval between consecutive `typing.started`
* events sent by the client to the server.
* If typing.start() is called, the first call will emit an event immediately.
* Later calls will no-op until the time has elapsed.
* Calling typing.stop() will immediately send a `typing.stopped` event to the server and reset the interval,
* allowing the client to send another `typing.started` event immediately.
* @defaultValue 10000
*/
heartbeatThrottleMs?: number;
}
/**
* Represents the occupancy options for a chat room.
*/
export interface OccupancyOptions {
/**
* Whether to enable occupancy events.
*
* Note that enabling this feature will increase the number of messages received by the client as additional
* messages will be sent by the server to indicate occupancy changes.
* @defaultValue false
*/
enableEvents?: boolean;
}
/**
* Represents the presence options for a chat room.
*/
export interface PresenceOptions {
/**
* Whether or not the client should receive presence events from the server. This setting
* can be disabled if you are using presence in your Chat Room, but this particular client does not
* need to receive the messages.
* @defaultValue true
*/
enableEvents?: boolean;
}
/**
* Represents the options for a given chat room.
*/
export interface RoomOptions {
/**
* The typing options for the room.
*/
typing?: TypingOptions;
/**
* The occupancy options for the room.
*/
occupancy?: OccupancyOptions;
/**
* The presence options for the room.
*/
presence?: PresenceOptions;
/**
* The message options for the room.
*/
messages?: MessageOptions;
}
/**
* Represents the normalized typing options for a chat room, which makes every property required.
*/
export type InternalTypingOptions = Required<TypingOptions>;
/**
* Represents the normalized occupancy options for a chat room. Everything becomes required.
*/
export type InternalOccupancyOptions = Required<OccupancyOptions>;
/**
* Represents the normalized presence options for a chat room. Everything becomes required.
*/
export type InternalPresenceOptions = Required<PresenceOptions>;
/**
* Represents the normalized message options for a chat room. Everything becomes required.
*/
export type InternalMessageOptions = Required<MessageOptions>;
/**
* Represents the normalized options for a chat room.
*/
export interface InternalRoomOptions {
/**
* Are we running the client in a React environment?
*/
isReactClient: boolean;
/**
* Typing options with everything made mandatory.
*/
typing: InternalTypingOptions;
/**
* Occupancy options with everything made mandatory.
*/
occupancy: InternalOccupancyOptions;
/**
* Presence options with everything made mandatory.
*/
presence: InternalPresenceOptions;
/**
* Message options with everything made mandatory.
*/
messages: InternalMessageOptions;
}
/**
* Creates an {@link ErrorInfo} for invalid room configuration.
* @param reason The reason for the invalid room configuration.
* @returns An ErrorInfo.
*/
const invalidRoomConfiguration = (reason: string): Error =>
new Ably.ErrorInfo(`invalid room configuration: ${reason}`, 40001, 400);
export const validateRoomOptions = (options: InternalRoomOptions): void => {
validateTypingOptions(options.typing);
};
const validateTypingOptions = (options: InternalTypingOptions): void => {
if (options.heartbeatThrottleMs <= 0) {
throw invalidRoomConfiguration('typing heartbeat interval must be greater than 0');
}
};
const normalizeTypingOptions = (options: RoomOptions | undefined): InternalTypingOptions => ({
...DefaultRoomOptions.typing,
...options?.typing,
});
const normalizeOccupancyOptions = (options: RoomOptions | undefined): InternalOccupancyOptions => ({
...DefaultRoomOptions.occupancy,
...options?.occupancy,
});
const normalizePresenceOptions = (options: RoomOptions | undefined): InternalPresenceOptions => ({
...DefaultRoomOptions.presence,
...options?.presence,
});
const normalizeMessageOptions = (options: RoomOptions | undefined): InternalMessageOptions => ({
...DefaultRoomOptions.messages,
...options?.messages,
});
export const normalizeRoomOptions = (options: RoomOptions | undefined, react: boolean): InternalRoomOptions => ({
typing: normalizeTypingOptions(options),
occupancy: normalizeOccupancyOptions(options),
presence: normalizePresenceOptions(options),
messages: normalizeMessageOptions(options),
isReactClient: react,
});