@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
139 lines (127 loc) • 4.58 kB
text/typescript
import { Logger } from '../../core/logger.js';
import { Room } from '../../core/room.js';
/**
* RoomPromise is a wrapper around a promise that resolves to a Room instance.
*
* It is designed to better integrate into the React lifecycle, and control whether an unmount
* function needs to be called depending on where the promise resolution occurs relative to the
* component lifecycle.
*/
export interface RoomPromise {
/**
* Returns a function to be called when the component is unmounted. If the room promise has resolved at the time,
* of calling, then the unmount function returned by the onResolve callback will be called.
*
* Multiple calls are no-op.
*
* This should be used in conjunction with React's useEffect hook to ensure that resources are cleaned up.
*
* Example usage:
*
* ```ts
* useEffect(() => {
* const roomPromise: RoomPromise;
* return roomPromise.unmount();
* }, []);
*
* @returns A function that should be called when the component is unmounted.
*/
unmount: () => () => void;
}
/**
* A callback that can be returned by the onResolve callback to clean up any resources.
*/
type UnmountCallback = () => void;
/**
* A callback that is called when the promise resolves to a Room instance.
*/
export type RoomResolutionCallback = (room: Room) => UnmountCallback;
/**
* Default implementation of RoomPromise.
*/
class DefaultRoomPromise implements RoomPromise {
private readonly _logger: Logger;
private readonly _onResolve: RoomResolutionCallback;
private _onUnmount?: UnmountCallback;
private _unmounted = false;
/**
* Creates a new DefaultRoomPromise and starts the resolution of the promise.
* @param room The promise that resolves to a Room instance.
* @param onResolve The callback that is called when the promise resolves to a Room instance.
* @param logger The logger to use for logging.
*/
constructor(room: Promise<Room>, onResolve: RoomResolutionCallback, logger: Logger) {
this._onResolve = onResolve;
this._logger = logger;
this.mount(room).catch((error: unknown) => {
this._logger.trace('DefaultRoomPromise(); mount error', { error: error });
});
}
/**
* Wait for the room promise to resolve, then execute the onResolve callback, storing its response as an unmount function.
* If the component is unmounted before the promise resolves, then this will do nothing.
* @param promise The promise that resolves to a Room instance.
* @returns A promise that we simply resolve when it's done.
*/
async mount(promise: Promise<Room>): Promise<void> {
this._logger.debug('DefaultRoomPromise(); mount');
try {
const room = await promise;
if (this._unmounted) {
return;
}
this._logger.debug('DefaultRoomPromise(); mount resolved');
this._onUnmount = this._onResolve(room);
} catch (error) {
this._logger.error('DefaultRoomPromise(); mount error', { error });
}
}
/**
* Returns a function to be called when the component is unmounted. If the room promise has resolved at the time
* of calling, then the unmount function returned by the onResolve callback will be called.
*
* Multiple calls are no-op.
*
* Example usage:
*
* ```ts
* useEffect(() => {
* const roomPromise = wrapRoomPromise(...);
* return roomPromise.unmount();
* }, []);
* ```
* @returns A function that should be called when the component is unmounted.
*/
unmount() {
if (this._unmounted) {
return () => {
// noop
};
}
return () => {
this._logger.debug('DefaultRoomPromise(); unmount');
this._unmounted = true;
this._onUnmount?.();
};
}
}
/**
* Provides a convenient way to wrap a promise that resolves to a Room instance, and execute a callback.
* This should be used in conjunction with React's useEffect hook to ensure that resources are cleaned up.
*
* Example usage:
*
* ```ts
* useEffect(() => {
* const roomPromise = wrapRoomPromise(...);
* return roomPromise.unmount();
* }, []);
* ```
* @internal
* @param room The promise that resolves to a Room instance.
* @param onResolve The callback that is called when the promise resolves to a Room instance.
* @param logger The logger to use for logging.
* @returns A RoomPromise instance that can be used to clean up resources.
*/
export const wrapRoomPromise = (room: Promise<Room>, onResolve: RoomResolutionCallback, logger: Logger): RoomPromise =>
new DefaultRoomPromise(room, onResolve, logger);