@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
138 lines (123 loc) • 4.77 kB
text/typescript
import { useEffect, useState } from 'react';
import { OccupancyListener } from '../../core/occupancy.js';
import { wrapRoomPromise } from '../helper/room-promise.js';
import { ChatStatusResponse } from '../types/chat-status-response.js';
import { Listenable } from '../types/listenable.js';
import { StatusParams } from '../types/status-params.js';
import { useEventListenerRef } from './internal/use-event-listener-ref.js';
import { useRoomLogger } from './internal/use-logger.js';
import { useRoomContext } from './internal/use-room-context.js';
import { useRoomStatus } from './internal/use-room-status.js';
import { useChatConnection } from './use-chat-connection.js';
/**
* The options for the {@link useOccupancy} hook.
*/
export interface UseOccupancyParams extends StatusParams, Listenable<OccupancyListener> {
/**
* A listener that will be called whenever an occupancy event is received.
* The listener is removed when the component unmounts.
*/
listener?: OccupancyListener;
}
/**
* The response type from the {@link useOccupancy} hook.
*/
export interface UseOccupancyResponse extends ChatStatusResponse {
/**
* The current number of users connected to the room, kept up to date by the hook.
*/
readonly connections: number;
/**
* The current number of users present in the room, kept up to date by the hook.
*/
readonly presenceMembers: number;
}
/**
* A hook that provides access to the rooms occupancy information.
* It will use the instance belonging to the nearest {@link ChatRoomProvider} in the component tree.
* @param params - Allows the registering of optional callbacks.
* @returns UseOccupancyResponse
*/
export const useOccupancy = (params?: UseOccupancyParams): UseOccupancyResponse => {
const { currentStatus: connectionStatus, error: connectionError } = useChatConnection({
onStatusChange: params?.onConnectionStatusChange,
});
const context = useRoomContext('useOccupancy');
const { status: roomStatus, error: roomError } = useRoomStatus(params);
const logger = useRoomLogger();
logger.trace('useOccupancy();', { params });
const [occupancyMetrics, setOccupancyMetrics] = useState<{ connections: number; presenceMembers: number }>({
connections: 0,
presenceMembers: 0,
});
// create stable references for the listeners
const listenerRef = useEventListenerRef(params?.listener);
const onDiscontinuityRef = useEventListenerRef(params?.onDiscontinuity);
// if provided, subscribes the user provided discontinuity listener
useEffect(() => {
if (!onDiscontinuityRef) return;
return wrapRoomPromise(
context.room,
(room) => {
logger.debug('useOccupancy(); applying onDiscontinuity listener');
const { off } = room.onDiscontinuity(onDiscontinuityRef);
return () => {
logger.debug('useOccupancy(); removing onDiscontinuity listener');
off();
};
},
logger,
).unmount();
}, [context, onDiscontinuityRef, logger]);
// subscribe to occupancy events internally, to update the state metrics
useEffect(() => {
const roomPromise = wrapRoomPromise(
context.room,
(room) => {
logger.debug('useOccupancy(); applying internal listener');
// Set the initial metrics from current(), or 0 if not available
const currentOccupancy = room.occupancy.current();
setOccupancyMetrics({
connections: currentOccupancy?.connections ?? 0,
presenceMembers: currentOccupancy?.presenceMembers ?? 0,
});
const { unsubscribe } = room.occupancy.subscribe((occupancyEvent) => {
setOccupancyMetrics({
connections: occupancyEvent.occupancy.connections,
presenceMembers: occupancyEvent.occupancy.presenceMembers,
});
});
return () => {
logger.debug('useOccupancy(); cleaning up internal listener');
unsubscribe();
};
},
logger,
);
return roomPromise.unmount();
}, [context, logger]);
// if provided, subscribes the user provided listener to occupancy events
useEffect(() => {
if (!listenerRef) return;
return wrapRoomPromise(
context.room,
(room) => {
logger.debug('useOccupancy(); applying listener');
const { unsubscribe } = room.occupancy.subscribe(listenerRef);
return () => {
logger.debug('useOccupancy(); cleaning up listener');
unsubscribe();
};
},
logger,
).unmount();
}, [listenerRef, context, logger]);
return {
connectionStatus,
connectionError,
roomStatus,
roomError,
connections: occupancyMetrics.connections,
presenceMembers: occupancyMetrics.presenceMembers,
};
};