-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathuse-occupancy.ts
139 lines (126 loc) · 4.82 KB
/
use-occupancy.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import { Occupancy, OccupancyListener } from '@ably/chat';
import { useEffect, useState } from 'react';
import { wrapRoomPromise } from '../helper/room-promise.js';
import { useEventListenerRef } from '../helper/use-event-listener-ref.js';
import { useEventualRoomProperty } from '../helper/use-eventual-room.js';
import { useRoomContext } from '../helper/use-room-context.js';
import { useRoomStatus } from '../helper/use-room-status.js';
import { ChatStatusResponse } from '../types/chat-status-response.js';
import { Listenable } from '../types/listenable.js';
import { StatusParams } from '../types/status-params.js';
import { useChatConnection } from './use-chat-connection.js';
import { useLogger } from './use-logger.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;
/**
* Provides access to the underlying {@link Occupancy} instance of the room.
*/
readonly occupancy?: Occupancy;
}
/**
* A hook that provides access to the {@link Occupancy} instance of the room.
* 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 = useLogger();
logger.trace('useOccupancy();', { params, roomId: context.roomId });
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', { roomId: context.roomId });
const { off } = room.occupancy.onDiscontinuity(onDiscontinuityRef);
return () => {
logger.debug('useOccupancy(); removing onDiscontinuity listener', { roomId: context.roomId });
off();
};
},
logger,
context.roomId,
).unmount();
}, [context, onDiscontinuityRef, logger]);
// subscribe to occupancy events internally, to update the state metrics
useEffect(() => {
return wrapRoomPromise(
context.room,
(room) => {
logger.debug('useOccupancy(); applying internal listener', { roomId: context.roomId });
const { unsubscribe } = room.occupancy.subscribe((occupancyEvent) => {
setOccupancyMetrics({
connections: occupancyEvent.connections,
presenceMembers: occupancyEvent.presenceMembers,
});
});
return () => {
logger.debug('useOccupancy(); cleaning up internal listener', { roomId: context.roomId });
unsubscribe();
};
},
logger,
context.roomId,
).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', { roomId: context.roomId });
const { unsubscribe } = room.occupancy.subscribe(listenerRef);
return () => {
logger.debug('useOccupancy(); cleaning up listener', { roomId: context.roomId });
unsubscribe();
};
},
logger,
context.roomId,
).unmount();
}, [listenerRef, context, logger]);
return {
occupancy: useEventualRoomProperty((room) => room.occupancy),
connectionStatus,
connectionError,
roomStatus,
roomError,
connections: occupancyMetrics.connections,
presenceMembers: occupancyMetrics.presenceMembers,
};
};